Skip to content

Commit e8886d5

Browse files
authored
feat: application api key (#3224)
1 parent dfba894 commit e8886d5

File tree

8 files changed

+214
-27
lines changed

8 files changed

+214
-27
lines changed

apps/application/api/application_api_key.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
11
from drf_spectacular.types import OpenApiTypes
22
from drf_spectacular.utils import OpenApiParameter
33

4+
from application.serializers.application_api_key import EditApplicationKeySerializer, ApplicationKeySerializerModel
45
from common.mixins.api_mixin import APIMixin
6+
from common.result import ResultSerializer
57

68

7-
class ApplicationKeyCreateAPI(APIMixin):
9+
class ApplicationKeyListResult(ResultSerializer):
10+
def get_data(self):
11+
return ApplicationKeySerializerModel(many=True)
12+
13+
14+
class ApplicationKeyResult(ResultSerializer):
15+
def get_data(self):
16+
return ApplicationKeySerializerModel()
17+
18+
19+
class ApplicationKeyAPI(APIMixin):
820
@staticmethod
921
def get_parameters():
1022
return [
@@ -24,10 +36,26 @@ def get_parameters():
2436
)
2537
]
2638

27-
# class Operate(APIMixin):
28-
# @staticmethod
29-
# def s():
30-
# pass
39+
@staticmethod
40+
def get_response():
41+
return ApplicationKeyResult
42+
43+
class List(APIMixin):
44+
@staticmethod
45+
def get_response():
46+
return ApplicationKeyListResult
47+
48+
class Operate(APIMixin):
49+
@staticmethod
50+
def get_parameters():
51+
return [*ApplicationKeyAPI.get_parameters(), OpenApiParameter(
52+
name="api_key_id",
53+
description="ApiKeyId",
54+
type=OpenApiTypes.STR,
55+
location='path',
56+
required=True,
57+
)]
3158

32-
# def get_response():
33-
# return ApplicationKeyCreateResponse
59+
@staticmethod
60+
def get_request():
61+
return EditApplicationKeySerializer

apps/application/serializers/application_api_key.py

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from application.models import Application
99
from application.models.application_api_key import ApplicationApiKey
10+
from common.cache_data.application_api_key_cache import get_application_api_key, del_application_api_key
1011
from common.exception.app_exception import AppApiException
1112

1213

@@ -16,12 +17,19 @@ class Meta:
1617
fields = "__all__"
1718

1819

19-
class Edit(serializers.Serializer):
20-
pass
20+
class EditApplicationKeySerializer(serializers.Serializer):
21+
is_active = serializers.BooleanField(required=False, label=_("Availability"))
22+
23+
allow_cross_domain = serializers.BooleanField(required=False,
24+
label=_("Is cross-domain allowed"))
25+
26+
cross_domain_list = serializers.ListSerializer(required=False,
27+
child=serializers.CharField(required=True,
28+
label=_("Cross-domain address")),
29+
label=_("Cross-domain list"))
2130

2231

2332
class ApplicationKeySerializer(serializers.Serializer):
24-
user_id = serializers.UUIDField(required=True, label=_('user id'))
2533
workspace_id = serializers.CharField(required=True, label=_('workspace id'))
2634
application_id = serializers.UUIDField(required=True, label=_('application id'))
2735

@@ -53,10 +61,37 @@ def list(self, with_valid=True):
5361
QuerySet(ApplicationApiKey).filter(application_id=application_id)]
5462

5563
class Operate(serializers.Serializer):
56-
user_id = serializers.UUIDField(required=True, label=_('user id'))
5764
workspace_id = serializers.CharField(required=True, label=_('workspace id'))
5865
application_id = serializers.UUIDField(required=True, label=_('application id'))
66+
api_key_id = serializers.UUIDField(required=True, label=_('ApiKeyId'))
67+
68+
def delete(self, with_valid=True):
69+
if with_valid:
70+
self.is_valid(raise_exception=True)
71+
api_key_id = self.data.get("api_key_id")
72+
application_id = self.data.get('application_id')
73+
application_api_key = QuerySet(ApplicationApiKey).filter(id=api_key_id,
74+
application_id=application_id).first()
75+
del_application_api_key(application_api_key.secret_key)
76+
application_api_key.delete()
5977

6078
def edit(self, instance, with_valid=True):
6179
if with_valid:
6280
self.is_valid(raise_exception=True)
81+
EditApplicationKeySerializer(data=instance).is_valid(raise_exception=True)
82+
api_key_id = self.data.get("api_key_id")
83+
application_id = self.data.get('application_id')
84+
application_api_key = QuerySet(ApplicationApiKey).filter(id=api_key_id,
85+
application_id=application_id).first()
86+
if application_api_key is None:
87+
raise AppApiException(500, _('APIKey does not exist'))
88+
if 'is_active' in instance and instance.get('is_active') is not None:
89+
application_api_key.is_active = instance.get('is_active')
90+
if 'allow_cross_domain' in instance and instance.get('allow_cross_domain') is not None:
91+
application_api_key.allow_cross_domain = instance.get('allow_cross_domain')
92+
if 'cross_domain_list' in instance and instance.get('cross_domain_list') is not None:
93+
application_api_key.cross_domain_list = instance.get('cross_domain_list')
94+
application_api_key.save()
95+
# 写入缓存
96+
get_application_api_key(application_api_key.secret_key, False)
97+
return True

apps/application/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
path('workspace/<str:workspace_id>/application/<str:application_id>', views.Application.Operate.as_view()),
1414
path('workspace/<str:workspace_id>/application/<str:application_id>/application_key',
1515
views.ApplicationKey.as_view()),
16+
path('workspace/<str:workspace_id>/application/<str:application_id>/application_key/<str:api_key_id>',
17+
views.ApplicationKey.Operate.as_view()),
1618
path('workspace/<str:workspace_id>/application/<str:application_id>/export', views.Application.Export.as_view()),
1719

1820
path('workspace/<str:workspace_id>/application/<str:application_id>/work_flow_version',

apps/application/views/application_access_token.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class AccessToken(APIView):
3131
request=ApplicationAccessTokenAPI.get_request(),
3232
tags=[_('Application')] # type: ignore
3333
)
34-
@has_permissions(PermissionConstants.APPLICATION_OVERVIEW_ACCESS.get_workspace_permission())
34+
@has_permissions(PermissionConstants.APPLICATION_OVERVIEW_ACCESS.get_workspace_application_permission())
3535
def put(self, request: Request, workspace_id: str, application_id: str):
3636
return result.success(
3737
AccessTokenSerializer(data={'application_id': application_id}).edit(
@@ -45,6 +45,6 @@ def put(self, request: Request, workspace_id: str, application_id: str):
4545
parameters=ApplicationAccessTokenAPI.get_parameters(),
4646
tags=[_('Application')] # type: ignore
4747
)
48-
@has_permissions(PermissionConstants.APPLICATION_READ.get_workspace_permission())
48+
@has_permissions(PermissionConstants.APPLICATION_READ.get_workspace_application_permission())
4949
def get(self, request: Request, workspace_id: str, application_id: str):
5050
return result.success(AccessTokenSerializer(data={'application_id': application_id}).one())

apps/application/views/application_api_key.py

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
from django.db.models import QuerySet
2+
from django.utils.translation import gettext_lazy as _
23
from drf_spectacular.utils import extend_schema
34
from rest_framework.request import Request
45
from rest_framework.views import APIView
5-
from django.utils.translation import gettext_lazy as _
66

7-
from application.api.application_api_key import ApplicationKeyCreateAPI
7+
from application.api.application_api_key import ApplicationKeyAPI
88
from application.models import ApplicationApiKey
99
from application.serializers.application_api_key import ApplicationKeySerializer
1010
from common.auth import TokenAuth
1111
from common.auth.authentication import has_permissions
1212
from common.constants.permission_constants import PermissionConstants
1313
from common.log.log import log
14-
from common.result import result, success
14+
from common.result import result, success, DefaultResultSerializer
1515

1616

1717
def get_application_operation_object(application_api_key_id):
@@ -31,7 +31,9 @@ class ApplicationKey(APIView):
3131
description=_('Create application ApiKey'),
3232
summary=_('Create application ApiKey'),
3333
operation_id=_('Create application ApiKey'), # type: ignore
34-
parameters=ApplicationKeyCreateAPI.get_parameters(),
34+
parameters=ApplicationKeyAPI.get_parameters(),
35+
request=None,
36+
responses=ApplicationKeyAPI.get_response(),
3537
tags=[_('Application Api Key')] # type: ignore
3638
)
3739
@log(menu='Application', operate="Add ApiKey",
@@ -47,26 +49,50 @@ def post(self, request: Request, workspace_id: str, application_id: str):
4749
description=_('GET application ApiKey List'),
4850
summary=_('Create application ApiKey List'),
4951
operation_id=_('Create application ApiKey List'), # type: ignore
50-
parameters=ApplicationKeyCreateAPI.get_parameters(),
52+
parameters=ApplicationKeyAPI.get_parameters(),
53+
responses=ApplicationKeyAPI.List.get_response(),
5154
tags=[_('Application Api Key')] # type: ignore
5255
)
5356
@has_permissions(PermissionConstants.APPLICATION_OVERVIEW_API_KEY.get_workspace_application_permission())
5457
def get(self, request: Request, workspace_id: str, application_id: str):
55-
return result, success(ApplicationKeySerializer(
56-
data={'application_id': application_id, 'user_id': request.user.id,
58+
return result.success(ApplicationKeySerializer(
59+
data={'application_id': application_id,
5760
'workspace_id': workspace_id}).list())
5861

5962
class Operate(APIView):
6063
authentication_classes = [TokenAuth]
6164

6265
@extend_schema(
63-
methods=['GET'],
64-
description=_('GET application ApiKey List'),
65-
summary=_('Create application ApiKey List'),
66-
operation_id=_('Create application ApiKey List'), # type: ignore
67-
parameters=ApplicationKeyCreateAPI.get_parameters(),
66+
methods=['PUT'],
67+
description=_('Modify application API_KEY'),
68+
summary=_('Modify application API_KEY'),
69+
operation_id=_('Modify application API_KEY'), # type: ignore
70+
parameters=ApplicationKeyAPI.Operate.get_parameters(),
71+
request=ApplicationKeyAPI.Operate.get_request(),
72+
responses=DefaultResultSerializer,
73+
tags=[_('Application Api Key')] # type: ignore
74+
)
75+
@has_permissions(PermissionConstants.APPLICATION_OVERVIEW_API_KEY.get_workspace_application_permission())
76+
def put(self, request: Request, workspace_id: str, application_id: str, api_key_id: str):
77+
return result.success(
78+
ApplicationKeySerializer.Operate(
79+
data={'workspace_id': workspace_id, 'application_id': application_id,
80+
'api_key_id': api_key_id}).edit(
81+
request.data))
82+
83+
@extend_schema(
84+
methods=['DELETE'],
85+
description=_('Delete Application API_KEY'),
86+
summary=_('Delete Application API_KEY'),
87+
operation_id=_('Delete Application API_KEY'), # type: ignore
88+
parameters=ApplicationKeyAPI.Operate.get_parameters(),
89+
request=ApplicationKeyAPI.Operate.get_request(),
90+
responses=DefaultResultSerializer,
6891
tags=[_('Application Api Key')] # type: ignore
6992
)
7093
@has_permissions(PermissionConstants.APPLICATION_OVERVIEW_API_KEY.get_workspace_application_permission())
71-
def put(self, request: Request, application_id: str, workspace_id: str):
72-
return result.success(ApplicationKeySerializer.Operate())
94+
def delete(self, request: Request, workspace_id: str, application_id: str, api_key_id: str):
95+
return result.success(
96+
ApplicationKeySerializer.Operate(
97+
data={'workspace_id': workspace_id, 'application_id': application_id,
98+
'api_key_id': api_key_id}).delete())
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎
5+
@file: application_api_key_cache.py
6+
@date:2024/7/25 11:30
7+
@desc:
8+
"""
9+
from django.core.cache import cache
10+
from django.db.models import QuerySet
11+
12+
from application.models import ApplicationApiKey
13+
from common.constants.cache_version import Cache_Version
14+
from common.utils.cache_util import get_cache
15+
16+
17+
@get_cache(cache_key=Cache_Version.APPLICATION_API_KEY.get_key_func(),
18+
use_get_data=lambda secret_key, use_get_data: use_get_data,
19+
version=Cache_Version.APPLICATION_API_KEY.get_version())
20+
def get_application_api_key(secret_key, use_get_data):
21+
application_api_key = QuerySet(ApplicationApiKey).filter(secret_key=secret_key).first()
22+
return {'allow_cross_domain': application_api_key.allow_cross_domain,
23+
'cross_domain_list': application_api_key.cross_domain_list}
24+
25+
26+
def del_application_api_key(secret_key):
27+
cache.delete(Cache_Version.APPLICATION_API_KEY.get_key(secret_key=secret_key, use_get_data=True),
28+
version=Cache_Version.APPLICATION_API_KEY.get_version())

apps/common/constants/cache_version.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ class Cache_Version(Enum):
2929

3030
# 对话
3131
CHAT = "CHAT", lambda key: key
32+
# 应用API KEY
33+
APPLICATION_API_KEY = "APPLICATION_API_KEY", lambda secret_key, use_get_data: secret_key
3234

3335
def get_version(self):
3436
return self.value[0]

apps/common/utils/cache_util.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎
5+
@file: cache_util.py
6+
@date:2024/7/24 19:23
7+
@desc:
8+
"""
9+
from django.core.cache import cache
10+
11+
12+
def get_data_by_default_cache(key: str, get_data, cache_instance=cache, version=None, kwargs=None):
13+
"""
14+
获取数据, 先从缓存中获取,如果获取不到再调用get_data 获取数据
15+
@param kwargs: get_data所需参数
16+
@param key: key
17+
@param get_data: 获取数据函数
18+
@param cache_instance: cache实例
19+
@param version: 版本用于隔离
20+
@return:
21+
"""
22+
if kwargs is None:
23+
kwargs = {}
24+
if cache_instance.has_key(key, version=version):
25+
return cache_instance.get(key, version=version)
26+
data = get_data(**kwargs)
27+
cache_instance.add(key, data, version=version)
28+
return data
29+
30+
31+
def set_data_by_default_cache(key: str, get_data, cache_instance=cache, version=None):
32+
data = get_data()
33+
cache_instance.set(key, data, version=version)
34+
return data
35+
36+
37+
def get_cache(cache_key, use_get_data: any = True, cache_instance=cache, version=None):
38+
def inner(get_data):
39+
def run(*args, **kwargs):
40+
key = cache_key(*args, **kwargs) if callable(cache_key) else cache_key
41+
is_use_get_data = use_get_data(*args, **kwargs) if callable(use_get_data) else use_get_data
42+
if is_use_get_data:
43+
if cache_instance.has_key(key, version=version):
44+
return cache_instance.get(key, version=version)
45+
data = get_data(*args, **kwargs)
46+
cache_instance.add(key, data, timeout=None, version=version)
47+
return data
48+
data = get_data(*args, **kwargs)
49+
cache_instance.set(key, data, timeout=None, version=version)
50+
return data
51+
52+
return run
53+
54+
return inner
55+
56+
57+
def del_cache(cache_key, cache_instance=cache, version=None):
58+
def inner(func):
59+
def run(*args, **kwargs):
60+
key = cache_key(*args, **kwargs) if callable(cache_key) else cache_key
61+
func(*args, **kwargs)
62+
cache_instance.delete(key, version=version)
63+
64+
return run
65+
66+
return inner

0 commit comments

Comments
 (0)