feat: application api key (#3224)

This commit is contained in:
shaohuzhang1 2025-06-09 20:25:58 +08:00 committed by GitHub
parent dfba894e88
commit e8886d5c65
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 214 additions and 27 deletions

View File

@ -1,10 +1,22 @@
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter
from application.serializers.application_api_key import EditApplicationKeySerializer, ApplicationKeySerializerModel
from common.mixins.api_mixin import APIMixin
from common.result import ResultSerializer
class ApplicationKeyCreateAPI(APIMixin):
class ApplicationKeyListResult(ResultSerializer):
def get_data(self):
return ApplicationKeySerializerModel(many=True)
class ApplicationKeyResult(ResultSerializer):
def get_data(self):
return ApplicationKeySerializerModel()
class ApplicationKeyAPI(APIMixin):
@staticmethod
def get_parameters():
return [
@ -24,10 +36,26 @@ class ApplicationKeyCreateAPI(APIMixin):
)
]
# class Operate(APIMixin):
# @staticmethod
# def s():
# pass
@staticmethod
def get_response():
return ApplicationKeyResult
# def get_response():
# return ApplicationKeyCreateResponse
class List(APIMixin):
@staticmethod
def get_response():
return ApplicationKeyListResult
class Operate(APIMixin):
@staticmethod
def get_parameters():
return [*ApplicationKeyAPI.get_parameters(), OpenApiParameter(
name="api_key_id",
description="ApiKeyId",
type=OpenApiTypes.STR,
location='path',
required=True,
)]
@staticmethod
def get_request():
return EditApplicationKeySerializer

View File

@ -7,6 +7,7 @@ from rest_framework import serializers
from application.models import Application
from application.models.application_api_key import ApplicationApiKey
from common.cache_data.application_api_key_cache import get_application_api_key, del_application_api_key
from common.exception.app_exception import AppApiException
@ -16,12 +17,19 @@ class ApplicationKeySerializerModel(serializers.ModelSerializer):
fields = "__all__"
class Edit(serializers.Serializer):
pass
class EditApplicationKeySerializer(serializers.Serializer):
is_active = serializers.BooleanField(required=False, label=_("Availability"))
allow_cross_domain = serializers.BooleanField(required=False,
label=_("Is cross-domain allowed"))
cross_domain_list = serializers.ListSerializer(required=False,
child=serializers.CharField(required=True,
label=_("Cross-domain address")),
label=_("Cross-domain list"))
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'))
@ -53,10 +61,37 @@ class ApplicationKeySerializer(serializers.Serializer):
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'))
api_key_id = serializers.UUIDField(required=True, label=_('ApiKeyId'))
def delete(self, with_valid=True):
if with_valid:
self.is_valid(raise_exception=True)
api_key_id = self.data.get("api_key_id")
application_id = self.data.get('application_id')
application_api_key = QuerySet(ApplicationApiKey).filter(id=api_key_id,
application_id=application_id).first()
del_application_api_key(application_api_key.secret_key)
application_api_key.delete()
def edit(self, instance, with_valid=True):
if with_valid:
self.is_valid(raise_exception=True)
EditApplicationKeySerializer(data=instance).is_valid(raise_exception=True)
api_key_id = self.data.get("api_key_id")
application_id = self.data.get('application_id')
application_api_key = QuerySet(ApplicationApiKey).filter(id=api_key_id,
application_id=application_id).first()
if application_api_key is None:
raise AppApiException(500, _('APIKey does not exist'))
if 'is_active' in instance and instance.get('is_active') is not None:
application_api_key.is_active = instance.get('is_active')
if 'allow_cross_domain' in instance and instance.get('allow_cross_domain') is not None:
application_api_key.allow_cross_domain = instance.get('allow_cross_domain')
if 'cross_domain_list' in instance and instance.get('cross_domain_list') is not None:
application_api_key.cross_domain_list = instance.get('cross_domain_list')
application_api_key.save()
# 写入缓存
get_application_api_key(application_api_key.secret_key, False)
return True

View File

@ -13,6 +13,8 @@ urlpatterns = [
path('workspace/<str:workspace_id>/application/<str:application_id>', views.Application.Operate.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/application_key',
views.ApplicationKey.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/application_key/<str:api_key_id>',
views.ApplicationKey.Operate.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/export', views.Application.Export.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/work_flow_version',

View File

@ -31,7 +31,7 @@ class AccessToken(APIView):
request=ApplicationAccessTokenAPI.get_request(),
tags=[_('Application')] # type: ignore
)
@has_permissions(PermissionConstants.APPLICATION_OVERVIEW_ACCESS.get_workspace_permission())
@has_permissions(PermissionConstants.APPLICATION_OVERVIEW_ACCESS.get_workspace_application_permission())
def put(self, request: Request, workspace_id: str, application_id: str):
return result.success(
AccessTokenSerializer(data={'application_id': application_id}).edit(
@ -45,6 +45,6 @@ class AccessToken(APIView):
parameters=ApplicationAccessTokenAPI.get_parameters(),
tags=[_('Application')] # type: ignore
)
@has_permissions(PermissionConstants.APPLICATION_READ.get_workspace_permission())
@has_permissions(PermissionConstants.APPLICATION_READ.get_workspace_application_permission())
def get(self, request: Request, workspace_id: str, application_id: str):
return result.success(AccessTokenSerializer(data={'application_id': application_id}).one())

View File

@ -1,17 +1,17 @@
from django.db.models import QuerySet
from django.utils.translation import gettext_lazy as _
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.api.application_api_key import ApplicationKeyAPI
from application.models import ApplicationApiKey
from application.serializers.application_api_key import ApplicationKeySerializer
from common.auth import TokenAuth
from common.auth.authentication import has_permissions
from common.constants.permission_constants import PermissionConstants
from common.log.log import log
from common.result import result, success
from common.result import result, success, DefaultResultSerializer
def get_application_operation_object(application_api_key_id):
@ -31,7 +31,9 @@ class ApplicationKey(APIView):
description=_('Create application ApiKey'),
summary=_('Create application ApiKey'),
operation_id=_('Create application ApiKey'), # type: ignore
parameters=ApplicationKeyCreateAPI.get_parameters(),
parameters=ApplicationKeyAPI.get_parameters(),
request=None,
responses=ApplicationKeyAPI.get_response(),
tags=[_('Application Api Key')] # type: ignore
)
@log(menu='Application', operate="Add ApiKey",
@ -47,26 +49,50 @@ class ApplicationKey(APIView):
description=_('GET application ApiKey List'),
summary=_('Create application ApiKey List'),
operation_id=_('Create application ApiKey List'), # type: ignore
parameters=ApplicationKeyCreateAPI.get_parameters(),
parameters=ApplicationKeyAPI.get_parameters(),
responses=ApplicationKeyAPI.List.get_response(),
tags=[_('Application Api Key')] # type: ignore
)
@has_permissions(PermissionConstants.APPLICATION_OVERVIEW_API_KEY.get_workspace_application_permission())
def get(self, request: Request, workspace_id: str, application_id: str):
return result, success(ApplicationKeySerializer(
data={'application_id': application_id, 'user_id': request.user.id,
return result.success(ApplicationKeySerializer(
data={'application_id': application_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(),
methods=['PUT'],
description=_('Modify application API_KEY'),
summary=_('Modify application API_KEY'),
operation_id=_('Modify application API_KEY'), # type: ignore
parameters=ApplicationKeyAPI.Operate.get_parameters(),
request=ApplicationKeyAPI.Operate.get_request(),
responses=DefaultResultSerializer,
tags=[_('Application Api Key')] # type: ignore
)
@has_permissions(PermissionConstants.APPLICATION_OVERVIEW_API_KEY.get_workspace_application_permission())
def put(self, request: Request, application_id: str, workspace_id: str):
return result.success(ApplicationKeySerializer.Operate())
def put(self, request: Request, workspace_id: str, application_id: str, api_key_id: str):
return result.success(
ApplicationKeySerializer.Operate(
data={'workspace_id': workspace_id, 'application_id': application_id,
'api_key_id': api_key_id}).edit(
request.data))
@extend_schema(
methods=['DELETE'],
description=_('Delete Application API_KEY'),
summary=_('Delete Application API_KEY'),
operation_id=_('Delete Application API_KEY'), # type: ignore
parameters=ApplicationKeyAPI.Operate.get_parameters(),
request=ApplicationKeyAPI.Operate.get_request(),
responses=DefaultResultSerializer,
tags=[_('Application Api Key')] # type: ignore
)
@has_permissions(PermissionConstants.APPLICATION_OVERVIEW_API_KEY.get_workspace_application_permission())
def delete(self, request: Request, workspace_id: str, application_id: str, api_key_id: str):
return result.success(
ApplicationKeySerializer.Operate(
data={'workspace_id': workspace_id, 'application_id': application_id,
'api_key_id': api_key_id}).delete())

View File

@ -0,0 +1,28 @@
# coding=utf-8
"""
@project: MaxKB
@Author
@file application_api_key_cache.py
@date2024/7/25 11:30
@desc:
"""
from django.core.cache import cache
from django.db.models import QuerySet
from application.models import ApplicationApiKey
from common.constants.cache_version import Cache_Version
from common.utils.cache_util import get_cache
@get_cache(cache_key=Cache_Version.APPLICATION_API_KEY.get_key_func(),
use_get_data=lambda secret_key, use_get_data: use_get_data,
version=Cache_Version.APPLICATION_API_KEY.get_version())
def get_application_api_key(secret_key, use_get_data):
application_api_key = QuerySet(ApplicationApiKey).filter(secret_key=secret_key).first()
return {'allow_cross_domain': application_api_key.allow_cross_domain,
'cross_domain_list': application_api_key.cross_domain_list}
def del_application_api_key(secret_key):
cache.delete(Cache_Version.APPLICATION_API_KEY.get_key(secret_key=secret_key, use_get_data=True),
version=Cache_Version.APPLICATION_API_KEY.get_version())

View File

@ -29,6 +29,8 @@ class Cache_Version(Enum):
# 对话
CHAT = "CHAT", lambda key: key
# 应用API KEY
APPLICATION_API_KEY = "APPLICATION_API_KEY", lambda secret_key, use_get_data: secret_key
def get_version(self):
return self.value[0]

View File

@ -0,0 +1,66 @@
# coding=utf-8
"""
@project: MaxKB
@Author
@file cache_util.py
@date2024/7/24 19:23
@desc:
"""
from django.core.cache import cache
def get_data_by_default_cache(key: str, get_data, cache_instance=cache, version=None, kwargs=None):
"""
获取数据, 先从缓存中获取,如果获取不到再调用get_data 获取数据
@param kwargs: get_data所需参数
@param key: key
@param get_data: 获取数据函数
@param cache_instance: cache实例
@param version: 版本用于隔离
@return:
"""
if kwargs is None:
kwargs = {}
if cache_instance.has_key(key, version=version):
return cache_instance.get(key, version=version)
data = get_data(**kwargs)
cache_instance.add(key, data, version=version)
return data
def set_data_by_default_cache(key: str, get_data, cache_instance=cache, version=None):
data = get_data()
cache_instance.set(key, data, version=version)
return data
def get_cache(cache_key, use_get_data: any = True, cache_instance=cache, version=None):
def inner(get_data):
def run(*args, **kwargs):
key = cache_key(*args, **kwargs) if callable(cache_key) else cache_key
is_use_get_data = use_get_data(*args, **kwargs) if callable(use_get_data) else use_get_data
if is_use_get_data:
if cache_instance.has_key(key, version=version):
return cache_instance.get(key, version=version)
data = get_data(*args, **kwargs)
cache_instance.add(key, data, timeout=None, version=version)
return data
data = get_data(*args, **kwargs)
cache_instance.set(key, data, timeout=None, version=version)
return data
return run
return inner
def del_cache(cache_key, cache_instance=cache, version=None):
def inner(func):
def run(*args, **kwargs):
key = cache_key(*args, **kwargs) if callable(cache_key) else cache_key
func(*args, **kwargs)
cache_instance.delete(key, version=version)
return run
return inner