mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-29 07:52:50 +00:00
feat: support create user
This commit is contained in:
parent
a91d8016f8
commit
bca128ec39
|
|
@ -0,0 +1,43 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: qabot
|
||||
@Author:虎
|
||||
@file: exception_code_constants.py
|
||||
@date:2023/9/4 14:09
|
||||
@desc: 异常常量类
|
||||
"""
|
||||
from enum import Enum
|
||||
|
||||
from common.exception.app_exception import AppApiException
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class ExceptionCodeConstantsValue:
|
||||
def __init__(self, code, message):
|
||||
self.code = code
|
||||
self.message = message
|
||||
|
||||
def get_message(self):
|
||||
return self.message
|
||||
|
||||
def get_code(self):
|
||||
return self.code
|
||||
|
||||
def to_app_api_exception(self):
|
||||
return AppApiException(code=self.code, message=self.message)
|
||||
|
||||
|
||||
class ExceptionCodeConstants(Enum):
|
||||
INCORRECT_USERNAME_AND_PASSWORD = ExceptionCodeConstantsValue(1000, _('The username or password is incorrect'))
|
||||
NOT_AUTHENTICATION = ExceptionCodeConstantsValue(1001, _('Please log in first and bring the user Token'))
|
||||
EMAIL_SEND_ERROR = ExceptionCodeConstantsValue(1002, _('Email sending failed'))
|
||||
EMAIL_FORMAT_ERROR = ExceptionCodeConstantsValue(1003, _('Email format error'))
|
||||
EMAIL_IS_EXIST = ExceptionCodeConstantsValue(1004, _('The email has been registered, please log in directly'))
|
||||
EMAIL_IS_NOT_EXIST = ExceptionCodeConstantsValue(1005, _('The email is not registered, please register first'))
|
||||
CODE_ERROR = ExceptionCodeConstantsValue(1005,
|
||||
_('The verification code is incorrect or the verification code has expired'))
|
||||
USERNAME_IS_EXIST = ExceptionCodeConstantsValue(1006, _('The username has been registered, please log in directly'))
|
||||
USERNAME_ERROR = ExceptionCodeConstantsValue(1006,
|
||||
_('The username cannot be empty and must be between 6 and 20 characters long.'))
|
||||
PASSWORD_NOT_EQ_RE_PASSWORD = ExceptionCodeConstantsValue(1007,
|
||||
_('Password and confirmation password are inconsistent'))
|
||||
|
|
@ -111,6 +111,8 @@ class PermissionConstants(Enum):
|
|||
"""
|
||||
USER_READ = Permission(group=Group.USER, operate=Operate.READ, role_list=[RoleConstants.ADMIN,
|
||||
RoleConstants.USER])
|
||||
USER_CREATE = Permission(group=Group.USER, operate=Operate.CREATE,
|
||||
role_list=[RoleConstants.ADMIN, RoleConstants.USER])
|
||||
USER_EDIT = Permission(group=Group.USER, operate=Operate.EDIT, role_list=[RoleConstants.ADMIN])
|
||||
USER_DELETE = Permission(group=Group.USER, operate=Operate.DELETE, role_list=[RoleConstants.ADMIN])
|
||||
|
||||
|
|
@ -153,11 +155,12 @@ class PermissionConstants(Enum):
|
|||
KNOWLEDGE_MODULE_EDIT = Permission(group=Group.KNOWLEDGE, operate=Operate.EDIT, role_list=[RoleConstants.ADMIN,
|
||||
RoleConstants.USER])
|
||||
KNOWLEDGE_MODULE_DELETE = Permission(group=Group.KNOWLEDGE, operate=Operate.DELETE, role_list=[RoleConstants.ADMIN,
|
||||
RoleConstants.USER])
|
||||
RoleConstants.USER])
|
||||
KNOWLEDGE_READ = Permission(group=Group.KNOWLEDGE, operate=Operate.READ, role_list=[RoleConstants.ADMIN,
|
||||
RoleConstants.USER])
|
||||
RoleConstants.USER])
|
||||
KNOWLEDGE_CREATE = Permission(group=Group.KNOWLEDGE, operate=Operate.CREATE, role_list=[RoleConstants.ADMIN,
|
||||
RoleConstants.USER])
|
||||
RoleConstants.USER])
|
||||
|
||||
def get_workspace_application_permission(self):
|
||||
return lambda r, kwargs: Permission(group=self.value.group, operate=self.value.operate,
|
||||
resource_path=
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎
|
||||
@file: db_model_manage.py
|
||||
@date:2024/7/22 17:00
|
||||
@desc:
|
||||
"""
|
||||
from importlib import import_module
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def new_instance_by_class_path(class_path: str):
|
||||
parts = class_path.rpartition('.')
|
||||
package_path = parts[0]
|
||||
class_name = parts[2]
|
||||
module = import_module(package_path)
|
||||
HandlerClass = getattr(module, class_name)
|
||||
return HandlerClass()
|
||||
|
||||
|
||||
class DBModelManage:
|
||||
model_dict = {}
|
||||
|
||||
@staticmethod
|
||||
def get_model(model_name):
|
||||
return DBModelManage.model_dict.get(model_name)
|
||||
|
||||
@staticmethod
|
||||
def init():
|
||||
handles = [new_instance_by_class_path(class_path) for class_path in
|
||||
(settings.MODEL_HANDLES if hasattr(settings, 'MODEL_HANDLES') else [])]
|
||||
for h in handles:
|
||||
model_dict = h.get_model_dict()
|
||||
DBModelManage.model_dict = {**DBModelManage.model_dict, **model_dict}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎
|
||||
@file: base_handle.py
|
||||
@date:2024/7/22 17:02
|
||||
@desc:
|
||||
"""
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
|
||||
class IBaseModelHandle(ABC):
|
||||
@abstractmethod
|
||||
def get_model_dict(self):
|
||||
pass
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎
|
||||
@file: default_base_model_handle.py
|
||||
@date:2024/7/22 17:06
|
||||
@desc:
|
||||
"""
|
||||
from common.models.handle.base_handle import IBaseModelHandle
|
||||
|
||||
|
||||
class DefaultBaseModelHandle(IBaseModelHandle):
|
||||
def get_model_dict(self):
|
||||
return {}
|
||||
|
|
@ -17,10 +17,12 @@ from functools import reduce
|
|||
from typing import List, Dict
|
||||
|
||||
from django.core.files.uploadedfile import InMemoryUploadedFile
|
||||
from django.db.models import QuerySet
|
||||
from django.utils.translation import gettext as _
|
||||
from pydub import AudioSegment
|
||||
|
||||
from ..exception.app_exception import AppApiException
|
||||
from ..models.db_model_manage import DBModelManage
|
||||
|
||||
|
||||
def password_encrypt(row_password):
|
||||
|
|
@ -211,3 +213,23 @@ def query_params_to_single_dict(query_params: Dict):
|
|||
filter(lambda item: item is not None, [({key: value} if value is not None and len(value) > 0 else None) for
|
||||
key, value in
|
||||
query_params.items()])), {})
|
||||
|
||||
|
||||
def valid_license(model=None, count=None, message=None):
|
||||
def inner(func):
|
||||
def run(*args, **kwargs):
|
||||
xpack_cache = DBModelManage.get_model('xpack_cache')
|
||||
is_license_valid = xpack_cache.get('XPACK_LICENSE_IS_VALID', False) if xpack_cache is not None else False
|
||||
record_count = QuerySet(model).count()
|
||||
|
||||
if not is_license_valid and record_count >= count:
|
||||
error_message = message or _(
|
||||
'Limit {count} exceeded, please contact us (https://fit2cloud.com/).').format(
|
||||
count=count)
|
||||
raise AppApiException(400, error_message)
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return run
|
||||
|
||||
return inner
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ from drf_spectacular.utils import OpenApiParameter
|
|||
|
||||
from common.mixins.api_mixin import APIMixin
|
||||
from common.result import ResultSerializer
|
||||
from users.serializers.user import UserProfileResponse
|
||||
from users.serializers.user import UserProfileResponse, CreateUserSerializer
|
||||
|
||||
|
||||
class ApiUserProfileResponse(ResultSerializer):
|
||||
|
|
@ -25,6 +25,10 @@ class UserProfileAPI(APIMixin):
|
|||
def get_response():
|
||||
return ApiUserProfileResponse
|
||||
|
||||
@staticmethod
|
||||
def get_request():
|
||||
return CreateUserSerializer
|
||||
|
||||
|
||||
class TestWorkspacePermissionUserApi(APIMixin):
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -6,18 +6,35 @@
|
|||
@date:2025/4/14 19:18
|
||||
@desc:
|
||||
"""
|
||||
from rest_framework import serializers
|
||||
import re
|
||||
|
||||
from django.db import transaction
|
||||
from django.db.models import QuerySet, Q
|
||||
from rest_framework import serializers
|
||||
import uuid_utils.compat as uuid
|
||||
from common.constants.exception_code_constants import ExceptionCodeConstants
|
||||
from common.constants.permission_constants import RoleConstants
|
||||
from common.utils.common import valid_license, password_encrypt
|
||||
from users.models import User
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.core import validators
|
||||
|
||||
|
||||
class UserProfileResponse(serializers.ModelSerializer):
|
||||
is_edit_password = serializers.BooleanField(required=True, label="是否修改密码")
|
||||
permissions = serializers.ListField(required=True, label="权限")
|
||||
is_edit_password = serializers.BooleanField(required=True, label=_('Is Edit Password'))
|
||||
permissions = serializers.ListField(required=True, label=_('permissions'))
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['id', 'username', 'email', 'role', 'permissions', 'language', 'is_edit_password']
|
||||
fields = ['id', 'username', 'nick_name', 'email', 'role', 'permissions', 'language', 'is_edit_password']
|
||||
|
||||
|
||||
class CreateUserSerializer(serializers.ModelSerializer):
|
||||
username = serializers.CharField(required=True, label=_('Username'))
|
||||
password = serializers.CharField(required=True, label=_('Password'))
|
||||
email = serializers.EmailField(required=True, label=_('Email'))
|
||||
nick_name = serializers.CharField(required=False, label=_('Nick name'))
|
||||
phone = serializers.CharField(required=False, label=_('Phone'))
|
||||
|
||||
|
||||
class UserProfileSerializer(serializers.Serializer):
|
||||
|
|
@ -30,8 +47,67 @@ class UserProfileSerializer(serializers.Serializer):
|
|||
"""
|
||||
return {'id': user.id,
|
||||
'username': user.username,
|
||||
'nick_name': user.nick_name,
|
||||
'email': user.email,
|
||||
'role': user.role,
|
||||
'permissions': [str(p) for p in []],
|
||||
'is_edit_password': user.password == 'd880e722c47a34d8e9fce789fc62389d' if user.role == 'ADMIN' else False,
|
||||
'language': user.language}
|
||||
|
||||
|
||||
class UserManageSerializer(serializers.Serializer):
|
||||
class UserInstance(serializers.Serializer):
|
||||
email = serializers.EmailField(
|
||||
required=True,
|
||||
label=_("Email"),
|
||||
validators=[validators.EmailValidator(message=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.message,
|
||||
code=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.code)])
|
||||
|
||||
username = serializers.CharField(required=True,
|
||||
label=_("Username"),
|
||||
max_length=20,
|
||||
min_length=6,
|
||||
validators=[
|
||||
validators.RegexValidator(regex=re.compile("^.{6,20}$"),
|
||||
message=_(
|
||||
'Username must be 6-20 characters long'))
|
||||
])
|
||||
password = serializers.CharField(required=True, label=_("Password"), max_length=20, min_length=6,
|
||||
validators=[validators.RegexValidator(regex=re.compile(
|
||||
"^(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z_!@#$%^&*`~.()-+=]+$)(?![a-z0-9]+$)(?![a-z_!@#$%^&*`~()-+=]+$)"
|
||||
"(?![0-9_!@#$%^&*`~()-+=]+$)[a-zA-Z0-9_!@#$%^&*`~.()-+=]{6,20}$")
|
||||
, message=_(
|
||||
"The password must be 6-20 characters long and must be a combination of letters, numbers, and special characters."))])
|
||||
|
||||
nick_name = serializers.CharField(required=False, label=_("Nick name"), max_length=64,
|
||||
allow_null=True, allow_blank=True)
|
||||
phone = serializers.CharField(required=False, label=_("Phone"), max_length=20,
|
||||
allow_null=True, allow_blank=True)
|
||||
|
||||
def is_valid(self, *, raise_exception=True):
|
||||
super().is_valid(raise_exception=True)
|
||||
username = self.data.get('username')
|
||||
email = self.data.get('email')
|
||||
u = QuerySet(User).filter(Q(username=username) | Q(email=email)).first()
|
||||
if u is not None:
|
||||
if u.email == email:
|
||||
raise ExceptionCodeConstants.EMAIL_IS_EXIST.value.to_app_api_exception()
|
||||
if u.username == username:
|
||||
raise ExceptionCodeConstants.USERNAME_IS_EXIST.value.to_app_api_exception()
|
||||
|
||||
@valid_license(model=User, count=2,
|
||||
message=_(
|
||||
'The community version supports up to 2 users. If you need more users, please contact us (https://fit2cloud.com/).'))
|
||||
@transaction.atomic
|
||||
def save(self, instance, with_valid=True):
|
||||
if with_valid:
|
||||
UserManageSerializer.UserInstance(data=instance).is_valid(raise_exception=True)
|
||||
|
||||
user = User(id=uuid.uuid7(), email=instance.get('email'),
|
||||
phone="" if instance.get('phone') is None else instance.get('phone'),
|
||||
nick_name="" if instance.get('nick_name') is None else instance.get('nick_name')
|
||||
, username=instance.get('username'), password=password_encrypt(instance.get('password')),
|
||||
role=RoleConstants.USER.name, source="LOCAL",
|
||||
is_active=True)
|
||||
user.save()
|
||||
return UserProfileSerializer(user).data
|
||||
|
|
|
|||
|
|
@ -9,5 +9,11 @@ urlpatterns = [
|
|||
path('user/captcha', views.CaptchaView.as_view(), name='captcha'),
|
||||
path('user/test', views.TestPermissionsUserView.as_view(), name="test"),
|
||||
path('workspace/<str:workspace_id>/user/profile', views.TestWorkspacePermissionUserView.as_view(),
|
||||
name="test_workspace_id_permission")
|
||||
name="test_workspace_id_permission"),
|
||||
path("user_manage", views.UserManage.as_view(), name="user_manage"),
|
||||
# path("user_manage/<str:user_id>", views.UserManage.Operate.as_view(), name="user_manage_operate"),
|
||||
# path("user_manage/<str:user_id>/re_password", views.UserManage.RePassword.as_view(),
|
||||
# name="user_manage_re_password"),
|
||||
# path("user_manage/<int:current_page>/<int:page_size>", views.UserManage.Page.as_view(),
|
||||
# name="user_manage_re_password"),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ from common.auth.authentication import has_permissions
|
|||
from common.constants.permission_constants import PermissionConstants, Permission, Group, Operate
|
||||
from common.result import result
|
||||
from users.api.user import UserProfileAPI, TestWorkspacePermissionUserApi
|
||||
from users.serializers.user import UserProfileSerializer
|
||||
from users.serializers.user import UserProfileSerializer, UserManageSerializer
|
||||
|
||||
|
||||
class UserProfileView(APIView):
|
||||
|
|
@ -56,3 +56,17 @@ class TestWorkspacePermissionUserView(APIView):
|
|||
@has_permissions(PermissionConstants.USER_EDIT.get_workspace_permission())
|
||||
def get(self, request: Request, workspace_id):
|
||||
return result.success(UserProfileSerializer().profile(request.user))
|
||||
|
||||
|
||||
class UserManage(APIView):
|
||||
authentication_classes = [TokenAuth]
|
||||
|
||||
@extend_schema(methods=['POST'],
|
||||
description=_("Create user"),
|
||||
operation_id=_("Create user"),
|
||||
tags=[_("User management")],
|
||||
request=UserProfileAPI.get_request(),
|
||||
responses=UserProfileAPI.get_response())
|
||||
@has_permissions(PermissionConstants.USER_CREATE)
|
||||
def post(self, request: Request):
|
||||
return result.success(UserManageSerializer().save(request.data))
|
||||
|
|
|
|||
Loading…
Reference in New Issue