From 2f706b6ab0baa1db2ff2bdc894185ffdabb6f486 Mon Sep 17 00:00:00 2001 From: shaohuzhang1 Date: Tue, 19 Mar 2024 20:38:28 +0800 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20=E9=82=AE=E7=AE=B1=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/setting/migrations/0002_systemsetting.py | 25 ++++++ apps/setting/models/__init__.py | 1 + apps/setting/serializers/system_setting.py | 67 ++++++++++++++++ apps/setting/swagger_api/system_setting.py | 77 +++++++++++++++++++ apps/setting/urls.py | 3 +- apps/setting/views/__init__.py | 1 + apps/setting/views/system_setting.py | 57 ++++++++++++++ apps/users/serializers/user_serializers.py | 20 ++++- 8 files changed, 246 insertions(+), 5 deletions(-) create mode 100644 apps/setting/migrations/0002_systemsetting.py create mode 100644 apps/setting/serializers/system_setting.py create mode 100644 apps/setting/swagger_api/system_setting.py create mode 100644 apps/setting/views/system_setting.py 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)}邮件发送失败") From f8862c85509ca99084bfa33ea8a19367dd298c1e Mon Sep 17 00:00:00 2001 From: maninhill <41712985+maninhill@users.noreply.github.com> Date: Wed, 20 Mar 2024 10:24:44 +0800 Subject: [PATCH 2/8] Update README.md --- README.md | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 37904c30f..3e629e978 100644 --- a/README.md +++ b/README.md @@ -10,35 +10,28 @@ MaxKB 是一款基于 LLM 大语言模型的知识库问答系统。 -- **多模型**:支持对接主流的大模型,包括本地私有大模型(如 Llama 2)、Azure OpenAI 和百度千帆大模型等; +- **多模型支持**:支持对接主流的大模型,包括本地私有大模型(如 Llama 2)、Azure OpenAI 和百度千帆大模型等; - **开箱即用**:支持直接上传文档、自动爬取在线文档,支持文本自动拆分、向量化,智能问答交互体验好; - **无缝嵌入**:支持零编码快速嵌入到第三方业务系统。 ## 快速开始 -``` -docker run -d --name=maxkb -p 8080:8080 1panel/maxkb -``` -## 自定义持久化数据 - -- 在主机系统上创建一个存储数据的目录,例如:/opt/maxkb/data -- 在主机系统上创建一个存储配置文件的目录,例如:/opt/maxkb/conf ``` docker run --name=maxkb -p 8080:8000 -v /opt/maxkb/data:/var/lib/postgresql/data -v /opt/maxkb/conf:/opt/maxkb/conf -d 1panel/maxkb ``` + 也可以通过 [1Panel 应用商店](https://apps.fit2cloud.com/1panel) 快速部署 MaxKB + Ollama(Llama 2),30 分钟内即可上线基于本地大模型的知识库问答系统。 - - ## UI 展示 + - - + + - - + +
Demo1Demo2MaxKB Demo1MaxKB Demo2
Demo3Demo4MaxKB Demo3MaxKB Demo4
@@ -46,7 +39,6 @@ docker run --name=maxkb -p 8080:8000 -v /opt/maxkb/data:/var/lib/postgresql/data [论坛](https://bbs.fit2cloud.com/c/mk/11) - ## 技术栈 - 前端:[Vue.js](https://cn.vuejs.org/) From 1f5a9fdad2d6b27eb7264292c9e5418fa633f582 Mon Sep 17 00:00:00 2001 From: maninhill <41712985+maninhill@users.noreply.github.com> Date: Wed, 20 Mar 2024 10:34:32 +0800 Subject: [PATCH 3/8] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3e629e978..8988c3242 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ MaxKB 是一款基于 LLM 大语言模型的知识库问答系统。 ## 快速开始 ``` -docker run --name=maxkb -p 8080:8000 -v /opt/maxkb/data:/var/lib/postgresql/data -v /opt/maxkb/conf:/opt/maxkb/conf -d 1panel/maxkb +docker run -d --name=maxkb -p 8080:8000 -v /opt/maxkb/data:/var/lib/postgresql/data 1panel/maxkb ``` 也可以通过 [1Panel 应用商店](https://apps.fit2cloud.com/1panel) 快速部署 MaxKB + Ollama(Llama 2),30 分钟内即可上线基于本地大模型的知识库问答系统。 From 2aeb8acfb3d7a8f2eaaca13cda6c04faba26c925 Mon Sep 17 00:00:00 2001 From: maninhill <41712985+maninhill@users.noreply.github.com> Date: Wed, 20 Mar 2024 10:36:20 +0800 Subject: [PATCH 4/8] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8988c3242..8de5ae0d4 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ MaxKB 是一款基于 LLM 大语言模型的知识库问答系统。 docker run -d --name=maxkb -p 8080:8000 -v /opt/maxkb/data:/var/lib/postgresql/data 1panel/maxkb ``` -也可以通过 [1Panel 应用商店](https://apps.fit2cloud.com/1panel) 快速部署 MaxKB + Ollama(Llama 2),30 分钟内即可上线基于本地大模型的知识库问答系统。 +也可以通过 [1Panel 应用商店](https://apps.fit2cloud.com/1panel) 快速部署 MaxKB + Ollama + Llama 2,30 分钟内即可上线基于本地大模型的知识库问答系统。 ## UI 展示 From eba8cb90459746dd5d89541dafd10e7852c3450b Mon Sep 17 00:00:00 2001 From: wangdan-fit2cloud Date: Wed, 20 Mar 2024 10:38:59 +0800 Subject: [PATCH 5/8] =?UTF-8?q?feat:=20=E7=94=A8=E6=88=B7=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/src/api/application-overview.ts | 2 +- ui/src/api/log.ts | 2 +- ui/src/api/type/user.ts | 4 +- ui/src/api/user-manage.ts | 77 +++++++++ ui/src/router/modules/setting.ts | 22 ++- .../component/CreateMemberDialog.vue | 0 .../component/PermissionSetting.vue | 1 + ui/src/views/{setting => team}/index.vue | 0 ui/src/views/{setting => team}/utils.ts | 0 .../user-manage/component/UserDialog.vue | 143 ++++++++++++++++ .../user-manage/component/UserPwdDialog.vue | 128 +++++++++++++++ ui/src/views/user-manage/index.vue | 155 ++++++++++++++++++ 12 files changed, 526 insertions(+), 8 deletions(-) create mode 100644 ui/src/api/user-manage.ts rename ui/src/views/{setting => team}/component/CreateMemberDialog.vue (100%) rename ui/src/views/{setting => team}/component/PermissionSetting.vue (99%) rename ui/src/views/{setting => team}/index.vue (100%) rename ui/src/views/{setting => team}/utils.ts (100%) create mode 100644 ui/src/views/user-manage/component/UserDialog.vue create mode 100644 ui/src/views/user-manage/component/UserPwdDialog.vue create mode 100644 ui/src/views/user-manage/index.vue diff --git a/ui/src/api/application-overview.ts b/ui/src/api/application-overview.ts index 361990193..940088353 100644 --- a/ui/src/api/application-overview.ts +++ b/ui/src/api/application-overview.ts @@ -1,5 +1,5 @@ import { Result } from '@/request/Result' -import { get, post, postStream, del, put } from '@/request/index' +import { get, post, del, put } from '@/request/index' import { type Ref } from 'vue' diff --git a/ui/src/api/log.ts b/ui/src/api/log.ts index 4c989b619..26f9245fe 100644 --- a/ui/src/api/log.ts +++ b/ui/src/api/log.ts @@ -1,5 +1,5 @@ import { Result } from '@/request/Result' -import { get, post, postStream, del, put } from '@/request/index' +import { get, post, del, put } from '@/request/index' import type { pageRequest } from '@/api/type/common' import { type Ref } from 'vue' diff --git a/ui/src/api/type/user.ts b/ui/src/api/type/user.ts index 6462354f8..04cbd4140 100644 --- a/ui/src/api/type/user.ts +++ b/ui/src/api/type/user.ts @@ -89,11 +89,11 @@ interface ResetPasswordRequest { /** * 邮箱 */ - email: string + email?: string /** * 验证码 */ - code: string + code?: string /** * 密码 */ diff --git a/ui/src/api/user-manage.ts b/ui/src/api/user-manage.ts new file mode 100644 index 000000000..ceed082b6 --- /dev/null +++ b/ui/src/api/user-manage.ts @@ -0,0 +1,77 @@ +import { Result } from '@/request/Result' +import { get, post, del, put } from '@/request/index' +import type { pageRequest } from '@/api/type/common' +import { type Ref } from 'vue' + +const prefix = '/user_manage' +/** + * 用户分页列表 + * @param 参数 + * page { + "current_page": "string", + "page_size": "string", + } + * @query 参数 + email_or_username: string + */ +const getUserManage: ( + page: pageRequest, + email_or_username: string, + loading?: Ref +) => Promise> = (page, email_or_username, loading) => { + return get( + `${prefix}/${page.current_page}/${page.page_size}`, + email_or_username ? { email_or_username } : undefined, + loading + ) +} + +/** + * 删除用户 + * @param 参数 user_id, + */ +const delUserManage: (user_id: string, loading?: Ref) => Promise> = ( + user_id, + loading +) => { + return del(`${prefix}/${user_id}`, undefined, {}, loading) +} + +/** + * 创建用户 + */ +const postUserManage: (data: any, loading?: Ref) => Promise> = ( + data, + loading +) => { + return post(`${prefix}`, data, undefined, loading) +} + +/** + * 编辑用户 + */ +const putUserManage: ( + user_id: string, + data: any, + loading?: Ref +) => Promise> = (user_id, data, loading) => { + return put(`${prefix}/${user_id}`, data, undefined, loading) +} +/** + * 修改用户密码 + */ +const putUserManagePassword: ( + user_id: string, + data: any, + loading?: Ref +) => Promise> = (user_id, data, loading) => { + return put(`${prefix}/${user_id}/re_password`, data, undefined, loading) +} + +export default { + getUserManage, + delUserManage, + postUserManage, + putUserManage, + putUserManagePassword +} diff --git a/ui/src/router/modules/setting.ts b/ui/src/router/modules/setting.ts index 2aa5a92b8..f37dbd491 100644 --- a/ui/src/router/modules/setting.ts +++ b/ui/src/router/modules/setting.ts @@ -3,20 +3,34 @@ const settingRouter = { path: '/setting', name: 'setting', meta: { icon: 'Setting', title: '系统设置', permission: 'SETTING:READ' }, - redirect: '/setting', + redirect: '/user', component: Layout, children: [ { - path: '/setting', - name: 'setting', + path: '/user', + name: 'user', + meta: { + icon: 'User', + iconActive: 'UserFilled', + title: '用户管理', + activeMenu: '/setting', + parentPath: '/setting', + parentName: 'setting' + }, + component: () => import('@/views/user-manage/index.vue') + }, + { + path: '/team', + name: 'team', meta: { icon: 'app-team', iconActive: 'app-team-active', title: '团队管理', + activeMenu: '/setting', parentPath: '/setting', parentName: 'setting' }, - component: () => import('@/views/setting/index.vue') + component: () => import('@/views/team/index.vue') }, { path: '/template', diff --git a/ui/src/views/setting/component/CreateMemberDialog.vue b/ui/src/views/team/component/CreateMemberDialog.vue similarity index 100% rename from ui/src/views/setting/component/CreateMemberDialog.vue rename to ui/src/views/team/component/CreateMemberDialog.vue diff --git a/ui/src/views/setting/component/PermissionSetting.vue b/ui/src/views/team/component/PermissionSetting.vue similarity index 99% rename from ui/src/views/setting/component/PermissionSetting.vue rename to ui/src/views/team/component/PermissionSetting.vue index a234318d6..19886dbe5 100644 --- a/ui/src/views/setting/component/PermissionSetting.vue +++ b/ui/src/views/team/component/PermissionSetting.vue @@ -144,3 +144,4 @@ onMounted(() => { }) +../utils \ No newline at end of file diff --git a/ui/src/views/setting/index.vue b/ui/src/views/team/index.vue similarity index 100% rename from ui/src/views/setting/index.vue rename to ui/src/views/team/index.vue diff --git a/ui/src/views/setting/utils.ts b/ui/src/views/team/utils.ts similarity index 100% rename from ui/src/views/setting/utils.ts rename to ui/src/views/team/utils.ts diff --git a/ui/src/views/user-manage/component/UserDialog.vue b/ui/src/views/user-manage/component/UserDialog.vue new file mode 100644 index 000000000..b39829fd7 --- /dev/null +++ b/ui/src/views/user-manage/component/UserDialog.vue @@ -0,0 +1,143 @@ + + + diff --git a/ui/src/views/user-manage/component/UserPwdDialog.vue b/ui/src/views/user-manage/component/UserPwdDialog.vue new file mode 100644 index 000000000..3be7f5e61 --- /dev/null +++ b/ui/src/views/user-manage/component/UserPwdDialog.vue @@ -0,0 +1,128 @@ + + + diff --git a/ui/src/views/user-manage/index.vue b/ui/src/views/user-manage/index.vue new file mode 100644 index 000000000..165d133e1 --- /dev/null +++ b/ui/src/views/user-manage/index.vue @@ -0,0 +1,155 @@ + + + From 96135be28df0444e8bf32f1b1841de5058a823fd Mon Sep 17 00:00:00 2001 From: maninhill <41712985+maninhill@users.noreply.github.com> Date: Wed, 20 Mar 2024 10:41:25 +0800 Subject: [PATCH 6/8] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8de5ae0d4..933349272 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,9 @@ docker run -d --name=maxkb -p 8080:8000 -v /opt/maxkb/data:/var/lib/postgresql/d ## 技术栈 - 前端:[Vue.js](https://cn.vuejs.org/) -- 后端:[Django](https://www.djangoproject.com/) +- 后端:[Python / Django](https://www.djangoproject.com/) - Langchain:[Langchain](https://www.langchain.com/) -- 向量数据库:[PostgreSQL](https://www.postgresql.org/) +- 向量数据库:[PostgreSQL / pgvector](https://www.postgresql.org/) - 大模型:Azure OpenAI、百度千帆大模型、[Ollama](https://github.com/ollama/ollama) ## License From c39221063ea8cb094c39309813f5b8367914e266 Mon Sep 17 00:00:00 2001 From: shaohuzhang1 Date: Wed, 20 Mar 2024 10:52:24 +0800 Subject: [PATCH 7/8] =?UTF-8?q?fix:=20=E9=82=AE=E7=AE=B1=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/setting/models/system_management.py | 31 ++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 apps/setting/models/system_management.py diff --git a/apps/setting/models/system_management.py b/apps/setting/models/system_management.py new file mode 100644 index 000000000..a4f4273f0 --- /dev/null +++ b/apps/setting/models/system_management.py @@ -0,0 +1,31 @@ +# coding=utf-8 +""" + @project: maxkb + @Author:虎 + @file: system_management.py + @date:2024/3/19 13:47 + @desc: 邮箱管理 +""" +import uuid + +from django.db import models + +from common.mixins.app_model_mixin import AppModelMixin + + +class SettingType(models.IntegerChoices): + """系统设置类型""" + EMAIL = 0, '邮箱' + + +class SystemSetting(AppModelMixin): + """ + 系统设置 + """ + type = models.IntegerField(primary_key=True, verbose_name='设置类型', max_length=5, choices=SettingType.choices, + default=SettingType.EMAIL) + + meta = models.JSONField(verbose_name="配置数据", default=dict) + + class Meta: + db_table = "system_setting" From 40031bd29d012c477c88d74dd51a79db57add68c Mon Sep 17 00:00:00 2001 From: shaohuzhang1 Date: Wed, 20 Mar 2024 11:57:17 +0800 Subject: [PATCH 8/8] =?UTF-8?q?feat:=20=E5=AF=86=E9=92=A5=E5=AF=B9?= =?UTF-8?q?=E5=AD=98=E5=82=A8=E5=88=B0=E6=95=B0=E6=8D=AE=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/util/rsa_util.py | 50 ++++++++++++++++++++---- apps/setting/models/system_management.py | 2 + 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/apps/common/util/rsa_util.py b/apps/common/util/rsa_util.py index ed950776a..a8e361e8c 100644 --- a/apps/common/util/rsa_util.py +++ b/apps/common/util/rsa_util.py @@ -11,7 +11,16 @@ import os from Crypto.Cipher import PKCS1_v1_5 as PKCS1_cipher from Crypto.PublicKey import RSA +from django.db.models import QuerySet +from django.core import cache +from setting.models import SystemSetting, SettingType + +import threading + +lock = threading.Lock() +rsa_cache = cache.caches['default'] +cache_key = "rsa_key" # 对密钥加密的密码 secret_code = "mac_kb_password" @@ -31,15 +40,40 @@ def generate(): def get_key_pair(): - if not os.path.exists("/opt/maxkb/conf/receiver.pem"): + rsa_value = rsa_cache.get(cache_key) + if rsa_value is None: + lock.acquire() + rsa_value = rsa_cache.get(cache_key) + if rsa_value is not None: + return rsa_value + try: + rsa_value = get_key_pair_by_sql() + rsa_cache.set(cache_key, rsa_value) + finally: + lock.release() + return rsa_value + + +def get_key_pair_by_sql(): + system_setting = QuerySet(SystemSetting).filter(type=SettingType.RSA.value).first() + if system_setting is None: kv = generate() - private_file_out = open("/opt/maxkb/conf/private.pem", "wb") - private_file_out.write(kv.get('value')) - private_file_out.close() - receiver_file_out = open("/opt/maxkb/conf/receiver.pem", "wb") - receiver_file_out.write(kv.get('key')) - receiver_file_out.close() - return {'key': open("/opt/maxkb/conf/receiver.pem").read(), 'value': open("/opt/maxkb/conf/private.pem").read()} + system_setting = SystemSetting(type=SettingType.RSA.value, + meta={'key': kv.get('key').decode(), 'value': kv.get('value').decode()}) + system_setting.save() + return system_setting.meta + + +# def get_key_pair(): +# if not os.path.exists("/opt/maxkb/conf/receiver.pem"): +# kv = generate() +# private_file_out = open("/opt/maxkb/conf/private.pem", "wb") +# private_file_out.write(kv.get('value')) +# private_file_out.close() +# receiver_file_out = open("/opt/maxkb/conf/receiver.pem", "wb") +# receiver_file_out.write(kv.get('key')) +# receiver_file_out.close() +# return {'key': open("/opt/maxkb/conf/receiver.pem").read(), 'value': open("/opt/maxkb/conf/private.pem").read()} def encrypt(msg, public_key: str | None = None): diff --git a/apps/setting/models/system_management.py b/apps/setting/models/system_management.py index a4f4273f0..84060b792 100644 --- a/apps/setting/models/system_management.py +++ b/apps/setting/models/system_management.py @@ -17,6 +17,8 @@ class SettingType(models.IntegerChoices): """系统设置类型""" EMAIL = 0, '邮箱' + RSA = 1, "私钥秘钥" + class SystemSetting(AppModelMixin): """