diff --git a/apps/setting/migrations/0002_systemsetting.py b/apps/setting/migrations/0002_systemsetting.py new file mode 100644 index 000000000..1293ca5df --- /dev/null +++ b/apps/setting/migrations/0002_systemsetting.py @@ -0,0 +1,25 @@ +# Generated by Django 4.1.10 on 2024-03-19 16:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('setting', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='SystemSetting', + fields=[ + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')), + ('type', models.IntegerField(choices=[('0', '邮箱')], default='0', max_length=5, primary_key=True, serialize=False, verbose_name='设置类型')), + ('meta', models.JSONField(default=dict, verbose_name='配置数据')), + ], + options={ + 'db_table': 'system_setting', + }, + ), + ] diff --git a/apps/setting/models/__init__.py b/apps/setting/models/__init__.py index 1de0764b2..155129ebb 100644 --- a/apps/setting/models/__init__.py +++ b/apps/setting/models/__init__.py @@ -8,3 +8,4 @@ """ from .team_management import * from .model_management import * +from .system_management import * diff --git a/apps/setting/serializers/system_setting.py b/apps/setting/serializers/system_setting.py new file mode 100644 index 000000000..a66b15805 --- /dev/null +++ b/apps/setting/serializers/system_setting.py @@ -0,0 +1,67 @@ +# coding=utf-8 +""" + @project: maxkb + @Author:虎 + @file: system_setting.py + @date:2024/3/19 16:29 + @desc: +""" +from django.core.mail.backends.smtp import EmailBackend +from django.db.models import QuerySet +from rest_framework import serializers + +from common.exception.app_exception import AppApiException +from common.util.field_message import ErrMessage +from setting.models.system_management import SystemSetting, SettingType + + +class SystemSettingSerializer(serializers.Serializer): + class EmailSerializer(serializers.Serializer): + @staticmethod + def one(): + system_setting = QuerySet(SystemSetting).filter(type=SettingType.EMAIL.value).first() + if system_setting is None: + return {} + return system_setting.meta + + class Create(serializers.Serializer): + email_host = serializers.CharField(required=True, error_messages=ErrMessage.char("SMTP 主机")) + email_port = serializers.IntegerField(required=True, error_messages=ErrMessage.char("SMTP 端口")) + email_host_user = serializers.CharField(required=True, error_messages=ErrMessage.char("发件人邮箱")) + email_host_password = serializers.CharField(required=True, error_messages=ErrMessage.char("密码")) + email_use_tls = serializers.BooleanField(required=True, error_messages=ErrMessage.char("是否开启TLS")) + email_use_ssl = serializers.BooleanField(required=True, error_messages=ErrMessage.char("是否开启SSL")) + from_email = serializers.EmailField(required=True, error_messages=ErrMessage.char("发送人邮箱")) + + def is_valid(self, *, raise_exception=False): + super().is_valid(raise_exception=True) + try: + EmailBackend(self.data.get("email_host"), + self.data.get("email_port"), + self.data.get("email_host_user"), + self.data.get("email_host_password"), + self.data.get("email_use_tls"), + False, + self.data.get("email_use_ssl") + ).open() + except Exception as e: + raise AppApiException(1004, "邮箱校验失败") + + def update_or_save(self): + self.is_valid(raise_exception=True) + system_setting = QuerySet(SystemSetting).filter(type=SettingType.EMAIL.value).first() + if system_setting is None: + system_setting = SystemSetting(type=SettingType.EMAIL.value) + system_setting.meta = self.to_email_meta() + system_setting.save() + return system_setting.meta + + def to_email_meta(self): + return {'email_host': self.data.get('email_host'), + 'email_port': self.data.get('email_port'), + 'email_host_user': self.data.get('email_host_user'), + 'email_host_password': self.data.get('email_host_password'), + 'email_use_tls': self.data.get('email_use_tls'), + 'email_use_ssl': self.data.get('email_use_ssl'), + 'from_email': self.data.get('from_email') + } diff --git a/apps/setting/swagger_api/system_setting.py b/apps/setting/swagger_api/system_setting.py new file mode 100644 index 000000000..1246ff27d --- /dev/null +++ b/apps/setting/swagger_api/system_setting.py @@ -0,0 +1,77 @@ +# coding=utf-8 +""" + @project: maxkb + @Author:虎 + @file: system_setting.py + @date:2024/3/19 16:05 + @desc: +""" +from drf_yasg import openapi + +from common.mixins.api_mixin import ApiMixin + + +class SystemSettingEmailApi(ApiMixin): + @staticmethod + def get_request_body_api(): + return openapi.Schema(type=openapi.TYPE_OBJECT, + title="邮箱相关参数", + description="邮箱相关参数", + required=['email_host', 'email_port', 'email_host_user', 'email_host_password', + 'email_use_tls', 'email_use_ssl', 'from_email'], + properties={ + 'email_host': openapi.Schema(type=openapi.TYPE_STRING, + title="SMTP 主机", + description="SMTP 主机"), + 'email_port': openapi.Schema(type=openapi.TYPE_NUMBER, + title="SMTP 端口", + description="SMTP 端口"), + 'email_host_user': openapi.Schema(type=openapi.TYPE_STRING, + title="发件人邮箱", + description="发件人邮箱"), + 'email_host_password': openapi.Schema(type=openapi.TYPE_STRING, + title="密码", + description="密码"), + 'email_use_tls': openapi.Schema(type=openapi.TYPE_BOOLEAN, + title="是否开启TLS", + description="是否开启TLS"), + 'email_use_ssl': openapi.Schema(type=openapi.TYPE_BOOLEAN, + title="是否开启SSL", + description="是否开启SSL"), + 'from_email': openapi.Schema(type=openapi.TYPE_STRING, + title="发送人邮箱", + description="发送人邮箱") + } + ) + + @staticmethod + def get_response_body_api(): + return openapi.Schema(type=openapi.TYPE_OBJECT, + title="邮箱相关参数", + description="邮箱相关参数", + required=['email_host', 'email_port', 'email_host_user', 'email_host_password', + 'email_use_tls', 'email_use_ssl', 'from_email'], + properties={ + 'email_host': openapi.Schema(type=openapi.TYPE_STRING, + title="SMTP 主机", + description="SMTP 主机"), + 'email_port': openapi.Schema(type=openapi.TYPE_NUMBER, + title="SMTP 端口", + description="SMTP 端口"), + 'email_host_user': openapi.Schema(type=openapi.TYPE_STRING, + title="发件人邮箱", + description="发件人邮箱"), + 'email_host_password': openapi.Schema(type=openapi.TYPE_STRING, + title="密码", + description="密码"), + 'email_use_tls': openapi.Schema(type=openapi.TYPE_BOOLEAN, + title="是否开启TLS", + description="是否开启TLS"), + 'email_use_ssl': openapi.Schema(type=openapi.TYPE_BOOLEAN, + title="是否开启SSL", + description="是否开启SSL"), + 'from_email': openapi.Schema(type=openapi.TYPE_STRING, + title="发送人邮箱", + description="发送人邮箱") + } + ) diff --git a/apps/setting/urls.py b/apps/setting/urls.py index f58679046..460facb34 100644 --- a/apps/setting/urls.py +++ b/apps/setting/urls.py @@ -15,6 +15,7 @@ urlpatterns = [ path('provider/model_form', views.Provide.ModelForm.as_view(), name="provider/model_form"), path('model', views.Model.as_view(), name='model'), - path('model/', views.Model.Operate.as_view(), name='model/operate') + path('model/', views.Model.Operate.as_view(), name='model/operate'), + path('email_setting', views.SystemSetting.Email.as_view(), name='email_setting') ] diff --git a/apps/setting/views/__init__.py b/apps/setting/views/__init__.py index f959267f2..0885ef978 100644 --- a/apps/setting/views/__init__.py +++ b/apps/setting/views/__init__.py @@ -8,3 +8,4 @@ """ from .Team import * from .model import * +from .system_setting import * diff --git a/apps/setting/views/system_setting.py b/apps/setting/views/system_setting.py new file mode 100644 index 000000000..fdf19d67d --- /dev/null +++ b/apps/setting/views/system_setting.py @@ -0,0 +1,57 @@ +# coding=utf-8 +""" + @project: maxkb + @Author:虎 + @file: system_setting.py + @date:2024/3/19 16:01 + @desc: +""" + +from drf_yasg.utils import swagger_auto_schema +from rest_framework.decorators import action +from rest_framework.request import Request +from rest_framework.views import APIView + +from common.auth import TokenAuth, has_permissions +from common.constants.permission_constants import PermissionConstants, RoleConstants +from common.response import result +from setting.serializers.system_setting import SystemSettingSerializer +from setting.swagger_api.system_setting import SystemSettingEmailApi + + +class SystemSetting(APIView): + class Email(APIView): + authentication_classes = [TokenAuth] + + @action(methods=['PUT'], detail=False) + @swagger_auto_schema(operation_summary="创建或者修改邮箱设置", + operation_id="创建或者修改邮箱设置", + request_body=SystemSettingEmailApi.get_request_body_api(), tags=["邮箱设置"], + responses=result.get_api_response(SystemSettingEmailApi.get_response_body_api())) + @has_permissions(RoleConstants.ADMIN) + def put(self, request: Request): + return result.success( + SystemSettingSerializer.EmailSerializer.Create( + data=request.data).update_or_save()) + + @action(methods=['POST'], detail=False) + @swagger_auto_schema(operation_summary="测试邮箱设置", + operation_id="测试邮箱设置", + request_body=SystemSettingEmailApi.get_request_body_api(), + responses=result.get_default_response(), + tags=["邮箱设置"]) + @has_permissions(RoleConstants.ADMIN) + def post(self, request: Request): + return result.success( + SystemSettingSerializer.EmailSerializer.Create( + data=request.data).is_valid()) + + @action(methods=['GET'], detail=False) + @swagger_auto_schema(operation_summary="获取邮箱设置", + operation_id="获取邮箱设置", + responses=result.get_api_response(SystemSettingEmailApi.get_response_body_api()), + tags=["邮箱设置"]) + @has_permissions(RoleConstants.ADMIN) + def get(self, request: Request): + return result.success( + SystemSettingSerializer.EmailSerializer.one()) diff --git a/apps/users/serializers/user_serializers.py b/apps/users/serializers/user_serializers.py index 0302e6b00..ba38bc182 100644 --- a/apps/users/serializers/user_serializers.py +++ b/apps/users/serializers/user_serializers.py @@ -14,6 +14,7 @@ import uuid from django.core import validators, signing, cache from django.core.mail import send_mail +from django.core.mail.backends.smtp import EmailBackend from django.db import transaction from django.db.models import Q, QuerySet from drf_yasg import openapi @@ -28,9 +29,8 @@ from common.mixins.api_mixin import ApiMixin from common.response.result import get_api_response from common.util.field_message import ErrMessage from common.util.lock import lock -from setting.models import Team +from setting.models import Team, SystemSetting, SettingType from smartdoc.conf import PROJECT_DIR -from smartdoc.settings import EMAIL_ADDRESS from users.models.user import User, password_encrypt, get_user_dynamics_permission user_cache = cache.caches['user_cache'] @@ -345,13 +345,25 @@ class SendEmailSerializer(ApiMixin, serializers.Serializer): code_cache_key_lock = code_cache_key + "_lock" # 设置缓存 user_cache.set(code_cache_key_lock, code, timeout=datetime.timedelta(minutes=1)) + system_setting = QuerySet(SystemSetting).filter(type=SettingType.EMAIL.value).first() + if system_setting is None: + user_cache.delete(code_cache_key_lock) + raise AppApiException(1004, "邮箱未设置,请联系管理员设置") try: + connection = EmailBackend(system_setting.meta.get("email_host"), + system_setting.meta.get('email_port'), + system_setting.meta.get('email_host_user'), + system_setting.meta.get('email_host_password'), + system_setting.meta.get('email_use_tls'), + False, + system_setting.meta.get('email_use_ssl') + ) # 发送邮件 send_mail(f'【MaxKB 智能知识库-{"用户注册" if state == "register" else "修改密码"}】', '', html_message=f'{content.replace("${code}", code)}', - from_email=EMAIL_ADDRESS, - recipient_list=[email], fail_silently=False) + from_email=system_setting.meta.get('from_email'), + recipient_list=[email], fail_silently=False, connection=connection) except Exception as e: user_cache.delete(code_cache_key_lock) raise AppApiException(500, f"{str(e)}邮件发送失败")