diff --git a/apps/application/api/application_api_key.py b/apps/application/api/application_api_key.py new file mode 100644 index 000000000..6c5c6fa2a --- /dev/null +++ b/apps/application/api/application_api_key.py @@ -0,0 +1,33 @@ +from drf_spectacular.types import OpenApiTypes +from drf_spectacular.utils import OpenApiParameter + +from common.mixins.api_mixin import APIMixin + + +class ApplicationKeyCreateAPI(APIMixin): + @staticmethod + def get_parameters(): + return [ + OpenApiParameter( + name="workspace_id", + description="工作空间id", + type=OpenApiTypes.STR, + location='path', + required=True, + ), + OpenApiParameter( + name="application_id", + description="application ID", + type=OpenApiTypes.STR, + location='path', + required=True, + ) + ] + + # class Operate(APIMixin): + # @staticmethod + # def s(): + # pass + + # def get_response(): + # return ApplicationKeyCreateResponse diff --git a/apps/application/migrations/0001_initial.py b/apps/application/migrations/0001_initial.py new file mode 100644 index 000000000..4c6dd91a6 --- /dev/null +++ b/apps/application/migrations/0001_initial.py @@ -0,0 +1,78 @@ +# Generated by Django 5.2.1 on 2025-05-26 10:19 + +import application.models.application +import django.db.models.deletion +import mptt.fields +import uuid_utils.compat +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('models_provider', '0001_initial'), + ('users', '0002_alter_user_nick_name'), + ] + + operations = [ + migrations.CreateModel( + name='ApplicationFolder', + fields=[ + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')), + ('id', models.CharField(editable=False, max_length=64, primary_key=True, serialize=False, verbose_name='主键id')), + ('name', models.CharField(max_length=64, verbose_name='文件夹名称')), + ('desc', models.CharField(blank=True, max_length=200, null=True, verbose_name='描述')), + ('workspace_id', models.CharField(db_index=True, default='default', max_length=64, verbose_name='工作空间id')), + ('lft', models.PositiveIntegerField(editable=False)), + ('rght', models.PositiveIntegerField(editable=False)), + ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)), + ('level', models.PositiveIntegerField(editable=False)), + ('parent', mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='application.applicationfolder')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='users.user', verbose_name='用户id')), + ], + options={ + 'db_table': 'application_folder', + }, + ), + migrations.CreateModel( + name='Application', + fields=[ + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')), + ('id', models.UUIDField(default=uuid_utils.compat.uuid7, editable=False, primary_key=True, serialize=False, verbose_name='主键id')), + ('workspace_id', models.CharField(db_index=True, default='default', max_length=64, verbose_name='工作空间id')), + ('name', models.CharField(max_length=128, verbose_name='应用名称')), + ('desc', models.CharField(default='', max_length=512, verbose_name='引用描述')), + ('prologue', models.CharField(default='', max_length=40960, verbose_name='开场白')), + ('dialogue_number', models.IntegerField(default=0, verbose_name='会话数量')), + ('dataset_setting', models.JSONField(default=application.models.application.get_dataset_setting_dict, verbose_name='数据集参数设置')), + ('model_setting', models.JSONField(default=application.models.application.get_model_setting_dict, verbose_name='模型参数相关设置')), + ('model_params_setting', models.JSONField(default=dict, verbose_name='模型参数相关设置')), + ('tts_model_params_setting', models.JSONField(default=dict, verbose_name='模型参数相关设置')), + ('problem_optimization', models.BooleanField(default=False, verbose_name='问题优化')), + ('icon', models.CharField(default='/ui/favicon.ico', max_length=256, verbose_name='应用icon')), + ('work_flow', models.JSONField(default=dict, verbose_name='工作流数据')), + ('type', models.CharField(choices=[('SIMPLE', '简易'), ('WORK_FLOW', '工作流')], default='SIMPLE', max_length=256, verbose_name='应用类型')), + ('problem_optimization_prompt', models.CharField(blank=True, default='()里面是用户问题,根据上下文回答揣测用户问题({question}) 要求: 输出一个补全问题,并且放在标签中', max_length=102400, null=True, verbose_name='问题优化提示词')), + ('tts_model_enable', models.BooleanField(default=False, verbose_name='语音合成模型是否启用')), + ('stt_model_enable', models.BooleanField(default=False, verbose_name='语音识别模型是否启用')), + ('tts_type', models.CharField(default='BROWSER', max_length=20, verbose_name='语音播放类型')), + ('tts_autoplay', models.BooleanField(default=False, verbose_name='自动播放')), + ('stt_autosend', models.BooleanField(default=False, verbose_name='自动发送')), + ('clean_time', models.IntegerField(default=180, verbose_name='清理时间')), + ('file_upload_enable', models.BooleanField(default=False, verbose_name='文件上传是否启用')), + ('file_upload_setting', models.JSONField(default=dict, verbose_name='文件上传相关设置')), + ('model', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, to='models_provider.model')), + ('stt_model', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stt_model_id', to='models_provider.model')), + ('tts_model', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tts_model_id', to='models_provider.model')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='users.user')), + ('folder', models.ForeignKey(default='root', on_delete=django.db.models.deletion.DO_NOTHING, to='application.applicationfolder', verbose_name='文件夹id')), + ], + options={ + 'db_table': 'application', + }, + ), + ] diff --git a/apps/application/migrations/0002_applicationapikey.py b/apps/application/migrations/0002_applicationapikey.py new file mode 100644 index 000000000..8629039fb --- /dev/null +++ b/apps/application/migrations/0002_applicationapikey.py @@ -0,0 +1,35 @@ +# Generated by Django 5.2.1 on 2025-05-26 10:21 + +import django.contrib.postgres.fields +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('application', '0001_initial'), + ('users', '0002_alter_user_nick_name'), + ] + + operations = [ + migrations.CreateModel( + name='ApplicationApiKey', + fields=[ + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')), + ('id', models.UUIDField(default=uuid.uuid1, editable=False, primary_key=True, serialize=False, verbose_name='主键id')), + ('secret_key', models.CharField(max_length=1024, unique=True, verbose_name='秘钥')), + ('workspace_id', models.CharField(db_index=True, default='default', max_length=64, verbose_name='工作空间id')), + ('is_active', models.BooleanField(default=True, verbose_name='是否开启')), + ('allow_cross_domain', models.BooleanField(default=False, verbose_name='是否允许跨域')), + ('cross_domain_list', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=128), default=list, size=None, verbose_name='跨域列表')), + ('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='application.application', verbose_name='应用id')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.user', verbose_name='用户id')), + ], + options={ + 'db_table': 'application_api_key', + }, + ), + ] diff --git a/apps/application/models/application_api_key.py b/apps/application/models/application_api_key.py new file mode 100644 index 000000000..69fad9ba6 --- /dev/null +++ b/apps/application/models/application_api_key.py @@ -0,0 +1,26 @@ + +import uuid + +from django.contrib.postgres.fields import ArrayField +from django.db import models + +from application.models import Application +from common.mixins.app_model_mixin import AppModelMixin + +from users.models import User + + +class ApplicationApiKey(AppModelMixin): + id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid1, editable=False, verbose_name="主键id") + secret_key = models.CharField(max_length=1024, verbose_name="秘钥", unique=True) + user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="用户id") + workspace_id = models.CharField(max_length=64, verbose_name="工作空间id", default="default", db_index=True) + application = models.ForeignKey(Application, on_delete=models.CASCADE, verbose_name="应用id") + is_active = models.BooleanField(default=True, verbose_name="是否开启") + allow_cross_domain = models.BooleanField(default=False, verbose_name="是否允许跨域") + cross_domain_list = ArrayField(verbose_name="跨域列表", + base_field=models.CharField(max_length=128, blank=True) + , default=list) + + class Meta: + db_table = "application_api_key" \ No newline at end of file diff --git a/apps/application/serializers/application_api_key.py b/apps/application/serializers/application_api_key.py new file mode 100644 index 000000000..dc352169f --- /dev/null +++ b/apps/application/serializers/application_api_key.py @@ -0,0 +1,69 @@ +import hashlib +import uuid_utils.compat as uuid +from baidubce.services.bmr.bmr_client import application + +from django.db.models import QuerySet +from rest_framework import serializers +from django.utils.translation import gettext_lazy as _ + +from application.models import Application +from application.models.application_api_key import ApplicationApiKey +from common.exception.app_exception import AppApiException + + +class ApplicationKeySerializerModel(serializers.ModelSerializer): + class Meta: + model = ApplicationApiKey + fields = "__all__" + +class Edit(serializers.Serializer): + pass + + +class ApplicationKeySerializer(serializers.Serializer): + user_id = serializers.UUIDField(required=True, label=_('user id')) + workspace_id = serializers.CharField(required=True, label=_('workspace id')) + application_id = serializers.UUIDField(required=True, label=_('application id')) + + + + + + def is_valid(self, *, raise_exception=False): + super().is_valid(raise_exception=True) + application_id = self.data.get("application_id") + application = QuerySet(Application).filter(id=application_id).first() + if application is None: + raise AppApiException(1001, _("Application does not exist")) + + def generate(self, with_valid=True): + if with_valid: + self.is_valid(raise_exception=True) + application_id = self.data.get("application_id") + application = QuerySet(Application).filter(id=application_id).first() + secret_key = 'application-' + hashlib.md5(str(uuid.uuid1()).encode()).hexdigest() + application_api_key = ApplicationApiKey(id=uuid.uuid1(), + secret_key=secret_key, + user_id=application.user_id, + application_id=application_id) + application_api_key.save() + return ApplicationKeySerializerModel(application_api_key).data + + def list(self,with_valid=True): + if with_valid: + self.is_valid(raise_exception=True) + application_id = self.data.get("application_id") + return [ApplicationKeySerializerModel(application_api_key).data for application_api_key in + QuerySet(ApplicationApiKey).filter(application_id = application_id)] + + class Operate(serializers.Serializer): + user_id = serializers.UUIDField(required=True, label=_('user id')) + workspace_id = serializers.CharField(required=True, label=_('workspace id')) + application_id = serializers.UUIDField(required=True, label=_('application id')) + + + + def edit(self, instance, with_valid=True): + if with_valid: + self.is_valid(raise_exception=True) + diff --git a/apps/application/urls.py b/apps/application/urls.py new file mode 100644 index 000000000..b4c63f627 --- /dev/null +++ b/apps/application/urls.py @@ -0,0 +1,9 @@ +from django.urls import path + +from . import views + +app_name = 'application' + +urlpatterns = [ + path('workspace//application//application_key', views.ApplicationKey.as_view()), +] diff --git a/apps/application/views/__init__.py b/apps/application/views/__init__.py index 9b6036777..c168364ef 100644 --- a/apps/application/views/__init__.py +++ b/apps/application/views/__init__.py @@ -6,3 +6,4 @@ @date:2025/5/9 18:51 @desc: """ +from .application_api_key import * \ No newline at end of file diff --git a/apps/application/views/application_api_key.py b/apps/application/views/application_api_key.py new file mode 100644 index 000000000..419472e01 --- /dev/null +++ b/apps/application/views/application_api_key.py @@ -0,0 +1,58 @@ +from drf_spectacular.utils import extend_schema +from rest_framework.request import Request +from rest_framework.views import APIView +from django.utils.translation import gettext_lazy as _ + +from application.api.application_api_key import ApplicationKeyCreateAPI +from application.serializers.application_api_key import ApplicationKeySerializer +from common.auth import TokenAuth +from common.result import result, success + + +class ApplicationKey(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['POST'], + description=_('Create application ApiKey'), + summary=_('Create application ApiKey'), + operation_id=_('Create application ApiKey'), # type: ignore + parameters=ApplicationKeyCreateAPI.get_parameters(), + tags=[_('Application Api Key')] # type: ignore + ) + def post(self,request: Request, application_id: str, workspace_id: str): + return result.success(ApplicationKeySerializer( + data={'application_id': application_id, 'user_id': request.user.id, + 'workspace_id':workspace_id}).generate()) + + @extend_schema( + methods=['GET'], + description=_('GET application ApiKey List'), + summary=_('Create application ApiKey List'), + operation_id=_('Create application ApiKey List'), # type: ignore + parameters=ApplicationKeyCreateAPI.get_parameters(), + tags=[_('Application Api Key')] # type: ignore + ) + def get(self,request: Request, application_id: str, workspace_id: str): + return result,success(ApplicationKeySerializer( + data={'application_id':application_id, 'user_id':request.user.id, + 'workspace_id':workspace_id}).list()) + + + class Operate(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['GET'], + description=_('GET application ApiKey List'), + summary=_('Create application ApiKey List'), + operation_id=_('Create application ApiKey List'), # type: ignore + parameters=ApplicationKeyCreateAPI.get_parameters(), + tags=[_('Application Api Key')] # type: ignore + ) + def put(self, request: Request, application_id: str, workspace_id: str): + return result.success(ApplicationKeySerializer.Operate( + + ) + ) +