diff --git a/.gitignore b/.gitignore index a7ac21b4f..abc4bef2a 100644 --- a/.gitignore +++ b/.gitignore @@ -205,3 +205,4 @@ results.sqlite tasks/* ~/* tasks +docker-entrypoint.sh \ No newline at end of file diff --git a/api/v1/schema/app.py b/api/v1/schema/app.py index dba562f5f..5c25ff5b3 100644 --- a/api/v1/schema/app.py +++ b/api/v1/schema/app.py @@ -15,6 +15,12 @@ class Config: model_fields = ['id', 'name', 'url', 'logo', 'type'] +class AppListQueryIn(Schema): + + order: Optional[str] = Field(hidden=True, default=None) + category_id: Optional[str] = Field(hidden=True, default=None) + name: Optional[str] = Field(title=_("应用名称"), default=None) + class AppListOut(ResponseSchema): data: List[AppListItemOut] @@ -31,6 +37,11 @@ class AppListsOut(ResponseSchema): data: List[AppItemsOut] +class AppAllListsQueryIn(Schema): + + not_arkid: Optional[int]=Field(hidden=True,default=None) + + class AppItemOut(ModelSchema): id: UUID = Field(readonly=True) diff --git a/api/v1/schema/approve_request.py b/api/v1/schema/approve_request.py index cbae88a5b..8a9f556af 100644 --- a/api/v1/schema/approve_request.py +++ b/api/v1/schema/approve_request.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 - +from enum import Enum from typing import List from pydantic import Field from ninja import Schema, ModelSchema @@ -38,3 +38,13 @@ def resolve_status(obj): class ApproveRequestListOut(ResponseSchema): data: List[ApproveRequestListItemOut] + +class PermissionCategory(str, Enum): + 否 = 'false' + 是 = 'true' + +class ApproveRequestListQueryIn(Schema): + + package: str = Field(title=_('Package', '包名'), hidden=True, default=None) + username: str = Field(title=_('Username', '用户名'), default=None) + is_approved: PermissionCategory = Field(title=_('Is Approved', '是否同意'), default=None) \ No newline at end of file diff --git a/api/v1/schema/auth_factor.py b/api/v1/schema/auth_factor.py index 7c4f00f20..67a46047f 100644 --- a/api/v1/schema/auth_factor.py +++ b/api/v1/schema/auth_factor.py @@ -14,6 +14,11 @@ class AuthFactorListItemOut(Schema): extension_name: str = Field(title=_("所属插件")) extension_package: str = Field(title=_("所属插件标识")) + +class FactorListQueryIn(Schema): + + order: str = Field(hidden=True, default=None) + class AuthFactorListOut(ResponseSchema): data: List[AuthFactorListItemOut] diff --git a/api/v1/schema/languages.py b/api/v1/schema/languages.py index 21ce8ca86..c676b58c0 100644 --- a/api/v1/schema/languages.py +++ b/api/v1/schema/languages.py @@ -45,7 +45,8 @@ class LanguageDataItemOut(Schema): ) translated:str = Field( - title=_("译词句") + title=_("译词句"), + notranslation=True ) class LanguageDataOut(ResponseSchema): @@ -60,7 +61,8 @@ class LanguageDataItemCreateIn(Schema): ) translated:str = Field( - title=_("译词句") + title=_("译词句"), + notranslation=True ) class LanguageDataItemCreateOut(ResponseSchema): diff --git a/api/v1/schema/mine.py b/api/v1/schema/mine.py index 50f0984ec..e3b0645b3 100644 --- a/api/v1/schema/mine.py +++ b/api/v1/schema/mine.py @@ -69,6 +69,21 @@ class MinePermissionListSchemaOut(Schema): # model_fields = ['id', 'name', 'category', 'is_system'] +class MinePermissionListQueryIn(Schema): + + app_id: Optional[str] = Field(hidden=True, default=None) + app_name: Optional[str] = Field(title=_("应用名称"), default=None) + category: Optional[str] = Field(title=_("分类"), default=None) + name: Optional[str] = Field(title=_("权限名称"), default=None) + operation_id: Optional[str] = Field(title=_("操作ID"), default=None) + + +class MineAppsGroupQueryIn(Schema): + + app_group_id: Optional[str] = Field(hidden=True, default=None) + order: Optional[str] = Field(hidden=True, default=None) + name: Optional[str] = Field(title=_("应用名称"), default=None) + class MineTenantListOut(ResponseSchema): data: List[MineTenantListItemOut] diff --git a/api/v1/schema/permission.py b/api/v1/schema/permission.py index f391e94c0..115531d1a 100644 --- a/api/v1/schema/permission.py +++ b/api/v1/schema/permission.py @@ -55,7 +55,7 @@ class UserAppLastPermissionsItemSchemaOut(Schema): app_name: str = Field(default=None, alias="app.name", title=_("应用")) sort_id: int = Field(hidden=True) # is_open: bool = Field(item_action={"path":"/api/v1/tenant/{tenant_id}/permission/{id}/toggle_open", "method":actions.FrontActionMethod.POST.value}, title=_("是否授权给其它租户")) - category: str = Field(title=_("分类应用名称")) + category: str = Field(title=_("分类名称"), notranslation=True) in_current: bool = Field(title=_("是否已拥有")) class UserAppLastPermissionsSchemaOut(ResponseSchema): @@ -80,6 +80,49 @@ class PermissionCreateItemSchemaIn(Schema): id:UUID = Field(hidden=True) name:str + +class PermissionListQueryIn(Schema): + + app_id: Optional[str] = Field(hidden=True, default=None) + select_user_id: Optional[str] = Field(hidden=True, default=None) + group_id: Optional[str] = Field(hidden=True, default=None) + app_name: Optional[str] = Field(title=_("应用名称"), default=None) + category: Optional[PermissionCategory] = Field(title=_("分类"), default=None) + name: Optional[str] = Field(title=_("权限名称"), default=None) + operation_id: Optional[str] = Field(title=_("操作ID"), default=None) + + +class AppPermissionListQueryIn(Schema): + + category: Optional[PermissionCategory] = Field(title=_("分类"), default=None) + name: Optional[str] = Field(title=_("权限名称"), default=None) + operation_id: Optional[str] = Field(title=_("操作ID"), default=None) + +class UserGroupLastPermQueryIn(Schema): + + usergroup_id: Optional[str] = Field(hidden=True, default=None) + app_name: Optional[str] = Field(title=_("应用名称"), default=None) + category: Optional[PermissionCategory] = Field(title=_("分类"), default=None) + name: Optional[str] = Field(title=_("权限名称"), default=None) + operation_id: Optional[str] = Field(title=_("操作ID"), default=None) + +class GroupPermissionListQueryIn(Schema): + + select_usergroup_id: Optional[str] = Field(hidden=True, default=None) + app_name: Optional[str] = Field(title=_("应用名称"), default=None) + category: Optional[PermissionCategory] = Field(title=_("分类"), default=None) + name: Optional[str] = Field(title=_("权限名称"), default=None) + operation_id: Optional[str] = Field(title=_("操作ID"), default=None) + +class UserPermissionLastQueryIn(Schema): + + app_id: Optional[str] = Field(hidden=True, default=None) + user_id: Optional[str] = Field(hidden=True, default=None) + app_name: Optional[str] = Field(title=_("应用名称"), default=None) + category: Optional[PermissionCategory] = Field(title=_("分类"), default=None) + name: Optional[str] = Field(title=_("权限名称"), default=None) + operation_id: Optional[str] = Field(title=_("操作ID"), default=None) + class PermissionCreateSchemaIn(ModelSchema): app: PermissionCreateItemSchemaIn = Field( diff --git a/api/v1/schema/permission_group.py b/api/v1/schema/permission_group.py index 21bb96160..b986d78af 100644 --- a/api/v1/schema/permission_group.py +++ b/api/v1/schema/permission_group.py @@ -92,17 +92,32 @@ class PermissionGroupEditSchemaIn(Schema): # model_fields = ['name'] class PermissionListSchemaOut(Schema): - category: str = Field(title=_("分类")) + category: str = Field(title=_("分类"),notranslation=True) operation_id: str = Field(default='', title=_("操作ID")) id: UUID = Field(title=_("id")) name: str = Field(title=_("名称")) is_open: bool = Field(item_action={"path":"/api/v1/tenant/{tenant_id}/permission/{id}/toggle_open", "method":actions.FrontActionMethod.POST.value}, title=_("是否授权给其它租户")) is_open_other_user: bool = Field(item_action={"path":"/api/v1/tenant/{tenant_id}/permission/{id}/toggle_other_user_open", "method":actions.FrontActionMethod.POST.value}, title=_("是否租户内所有人可见")) - is_system: bool = Field(title=_("是否是系统权限 ")) + is_system: bool = Field(title=_("是否是系统权限")) # class Config: # model = SystemPermission # model_fields = ['id', 'name', 'is_system'] +class PermissionGroupCategory(str, Enum): + entry = 'entry' + api = 'api' + data = 'data' + group = 'group' + ui = 'ui' + other = 'other' + +class PermissionGroupListQueryIn(Schema): + + category: Optional[PermissionGroupCategory] = Field(title=_("分类"), default=None) + name: Optional[str] = Field(title=_("权限名称"), default=None) + app_name: Optional[str] = Field(title=_("应用名称"), default=None) + operation_id: Optional[str] = Field(title=_("操作ID"), default=None) + class PermissionListSelectSchemaOut(Schema): @@ -110,7 +125,7 @@ class PermissionListSelectSchemaOut(Schema): in_current: bool = Field(title=_("是否在当前分组里"), hidden=True) name: str category: str - is_system: bool + is_system: bool = Field(title=_("是否是系统权限")) class PermissionListDataSelectSchemaOut(ResponseSchema): data: List[PermissionListSelectSchemaOut] diff --git a/api/v1/schema/user.py b/api/v1/schema/user.py index 0a46a4a37..b8db4ae12 100644 --- a/api/v1/schema/user.py +++ b/api/v1/schema/user.py @@ -10,6 +10,7 @@ class UserListQueryIn(Schema): order:str = Field( default=None, title=_("排序字段"), + hidden=True, notranslation=True ) username:str = Field( @@ -18,6 +19,21 @@ class UserListQueryIn(Schema): notranslation=True ) + nickname:str = Field( + default=None, + title=_("昵称"), + ) + + mobile:str = Field( + default=None, + title=_("电话"), + ) + + email:str = Field( + default=None, + title=_("邮箱"), + ) + class UserListItemOut(ModelSchema): class Config: diff --git a/api/v1/views/app.py b/api/v1/views/app.py index 308bbad58..f661e5c1c 100644 --- a/api/v1/views/app.py +++ b/api/v1/views/app.py @@ -3,7 +3,7 @@ from ninja import ModelSchema from django.db.models import Q from arkid.core.models import App - +from ninja import Query from arkid.core.api import api, operation from django.db import transaction from ninja.pagination import paginate @@ -28,10 +28,14 @@ @api.get("/tenant/{tenant_id}/apps/", response=List[AppListItemOut], tags=['应用']) @operation(AppListOut, roles=[TENANT_ADMIN, PLATFORM_ADMIN]) @paginate(CustomPagination) -def list_apps(request, tenant_id: str,order:str=None, category_id:str=None): +def list_apps(request, tenant_id: str, query_data: AppListQueryIn=Query(...)): ''' app列表 ''' + category_id = query_data.category_id + name = query_data.name + order = query_data.order + apps = App.expand_objects.filter( tenant_id=tenant_id, is_active=True, @@ -42,6 +46,9 @@ def list_apps(request, tenant_id: str,order:str=None, category_id:str=None): apps = apps.filter(arkstore_category_id=category_id) elif category_id == "-1": apps = apps.filter(arkstore_category_id=None, arkstore_app_id=None) + if name: + name = name.strip() + apps = apps.filter(name__icontains=name) if order: apps = apps.order_by(order) else: @@ -85,7 +92,7 @@ def list_all_apps(request, tenant_id: str): @api.get("/tenant/{tenant_id}/all_apps_in_arkid/", response=AppListsOut, tags=['应用']) @operation(AppListOut, roles=[NORMAL_USER, TENANT_ADMIN, PLATFORM_ADMIN]) -def all_apps_in_arkid(request, tenant_id: str, not_arkid: int=None): +def all_apps_in_arkid(request, tenant_id: str, query_data:AppAllListsQueryIn=Query(...)): ''' 所有app列表(含arkid) ''' @@ -93,7 +100,7 @@ def all_apps_in_arkid(request, tenant_id: str, not_arkid: int=None): Q(entry_permission__is_open=True)|Q(tenant_id=tenant_id) ) items = [] - if not_arkid is None: + if query_data.not_arkid is None: items.append({ 'id': 'arkid', 'name': 'arkid', diff --git a/api/v1/views/approve_request.py b/api/v1/views/approve_request.py index f4ff783ee..3e0777caa 100644 --- a/api/v1/views/approve_request.py +++ b/api/v1/views/approve_request.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +from ninja import Query from arkid.core.api import api, operation from arkid.core.constants import * from arkid.core.translation import gettext_default as _ @@ -17,6 +18,7 @@ from api.v1.schema.approve_request import ( ApproveRequestListItemOut, ApproveRequestListOut, + ApproveRequestListQueryIn, ) @@ -28,14 +30,16 @@ @operation(List[ApproveRequestListItemOut], roles=[TENANT_ADMIN, PLATFORM_ADMIN]) @paginate(CustomPagination) def approve_request_list( - request, tenant_id: str, package: str = "", is_approved: str = "" + request, tenant_id: str, query_data:ApproveRequestListQueryIn=Query(...) ): tenant = request.tenant requests = ApproveRequest.valid_objects.filter(tenant=tenant) - if package: - requests = requests.filter(action__extension__package=package) - if is_approved == "true": + if query_data.package: + requests = requests.filter(action__extension__package=query_data.package) + if query_data.username: + requests = requests.filter(user__username__icontains=query_data.username) + if query_data.is_approved == "true": requests = requests.exclude(status="wait") - elif is_approved == "false": + elif query_data.is_approved == "false": requests = requests.filter(status="wait") return requests diff --git a/api/v1/views/auth.py b/api/v1/views/auth.py index ac490f8d6..f446f622f 100644 --- a/api/v1/views/auth.py +++ b/api/v1/views/auth.py @@ -6,6 +6,7 @@ from arkid.core.error import ErrorCode, ErrorDict from api.v1.schema.auth import * from django.http import HttpResponse, JsonResponse +from arkid.core.tasks.celery import dispatch_task @api.post("/tenant/{tenant_id}/auth/", response=AuthOut, tags=['登录与注册'], auth=None) @operation(AuthOut, use_id=True) @@ -22,10 +23,21 @@ def auth(request, tenant_id: str, event_tag: str, data: AuthIn): # 生成 token token = refresh_token(user) + dispatch_task.delay('async_get_arkstore_access_token', tenant.id.hex, token) netloc = request.get_host().split(':')[0] + + is_ip_addr = False + ip_list = netloc.split('.') + if len(ip_list) == 4 and ''.join(ip_list).isdigit(): + is_ip_addr = True + domain = ('.'.join(netloc.split('.')[-2:])) response = JsonResponse({'error': ErrorCode.OK.value, 'data': {'user': {"id": user.id.hex, "username": user.username}, 'token': token}}) - response.set_cookie("arkid_token", token, domain=domain, httponly=True) + if is_ip_addr: + response.set_cookie("arkid_token", token, httponly=True) + else: + response.set_cookie("arkid_token", token, domain=domain, httponly=True) + return response @api.post("/tenant/{tenant_id}/reset_password/", response=ResetPasswordOut, tags=['登录与注册'],auth=None) diff --git a/api/v1/views/auth_factor.py b/api/v1/views/auth_factor.py index 552e77182..df59800a6 100644 --- a/api/v1/views/auth_factor.py +++ b/api/v1/views/auth_factor.py @@ -1,6 +1,7 @@ from distutils.command.build_ext import extension_name_re from distutils.command.config import config from typing import List +from ninja import Query from ninja import Field, ModelSchema, Schema from ninja.pagination import paginate from arkid.core.api import api, operation @@ -9,13 +10,13 @@ from arkid.core.extension.auth_factor import AuthFactorExtension from arkid.extension.models import Extension, TenantExtensionConfig from arkid.core.error import ErrorCode, ErrorDict -from api.v1.schema.auth_factor import AuthFactorCreateIn, AuthFactorCreateOut, AuthFactorDeleteOut, AuthFactorListItemOut, AuthFactorListOut, AuthFactorOut, AuthFactorUpdateIn, AuthFactorUpdateOut +from api.v1.schema.auth_factor import * from arkid.core.pagenation import CustomPagination @api.get("/tenant/{tenant_id}/auth_factors/", response=List[AuthFactorListItemOut], tags=[_("认证因素")]) @operation(List[AuthFactorListItemOut], roles=[TENANT_ADMIN, PLATFORM_ADMIN]) @paginate(CustomPagination) -def get_auth_factors(request, tenant_id: str,order:str=None): +def get_auth_factors(request, tenant_id: str, query_data: FactorListQueryIn=Query(...)): """ 认证因素列表 """ extensions = Extension.active_objects.filter( @@ -23,7 +24,7 @@ def get_auth_factors(request, tenant_id: str,order:str=None): configs = TenantExtensionConfig.active_objects.filter( tenant__id=tenant_id, extension__in=extensions) - if order: + if query_data.order: configs = configs.order_by(order) configs = configs.all() diff --git a/api/v1/views/child_manager.py b/api/v1/views/child_manager.py index f9b629653..61d2e0492 100644 --- a/api/v1/views/child_manager.py +++ b/api/v1/views/child_manager.py @@ -16,14 +16,14 @@ @api.get("/tenant/{tenant_id}/child_managers/", response=List[ChildManagerListOut], tags=["子管理员"]) @operation(roles=[TENANT_ADMIN, PLATFORM_ADMIN]) @paginate(CustomPagination) -def get_child_managers(request, tenant_id: str): +def get_child_managers(request, tenant_id: str, username: str=None): """ 子管理员列表 """ from arkid.core.perm.permission_data import PermissionData tenant = request.tenant users = User.valid_objects.filter(tenant=tenant) permissiondata = PermissionData() - child_mans = permissiondata.get_child_mans(users, tenant) + child_mans = permissiondata.get_child_mans(users, tenant, username) return child_mans @api.get("/tenant/{tenant_id}/child_managers/{id}/", response=ChildManagerDeatilBaseOut, tags=["子管理员"]) diff --git a/api/v1/views/event_list.py b/api/v1/views/event_list.py index 55152c721..48c9a6789 100644 --- a/api/v1/views/event_list.py +++ b/api/v1/views/event_list.py @@ -1,4 +1,5 @@ from typing import List +from ninja import Query from arkid.core.api import api, operation from arkid.core.constants import * from arkid.core.translation import gettext_default as _ @@ -14,18 +15,46 @@ class GetEventListOutItem(Schema): description:str = Field(title=_("描述")) url:str = Field(title=_("文档链接")) +class EventListQueryIn(Schema): + tag:str = Field(title=_("标签"), default=None) + name:str = Field(title=_("名称"), default=None) + @api.get("/tenant/{tenant_id}/event_list/", response=List[GetEventListOutItem], tags=["事件列表"]) @operation(List[GetEventListOutItem], roles=[TENANT_ADMIN, PLATFORM_ADMIN]) @paginate(CustomPagination) -def get_event_list(request, tenant_id: str): +def get_event_list(request, tenant_id: str, query_data: EventListQueryIn=Query(...)): """ 事件列表 """ events = [] host = get_app_config().get_host() + name = query_data.name + tag = query_data.tag for event_type in event.tag_map_event_type.values(): - events.append({ - 'tag':event_type.tag, - 'name':event_type.name, - 'description':event_type.description, - 'url': f'{host}/arkid/%20%20%20用户指南/用户手册/%20租户管理员/扩展能力/事件列表/#{event_type.name}' - }) + if name and tag and name in event_type.description and tag in event_type.tag: + events.append({ + 'tag':event_type.tag, + 'name':event_type.name, + 'description':event_type.description, + 'url': f'{host}/arkid/%20%20%20用户指南/用户手册/%20租户管理员/扩展能力/事件列表/#{event_type.name}' + }) + elif name and name in event_type.description: + events.append({ + 'tag':event_type.tag, + 'name':event_type.name, + 'description':event_type.description, + 'url': f'{host}/arkid/%20%20%20用户指南/用户手册/%20租户管理员/扩展能力/事件列表/#{event_type.name}' + }) + elif tag and tag in event_type.tag: + events.append({ + 'tag':event_type.tag, + 'name':event_type.name, + 'description':event_type.description, + 'url': f'{host}/arkid/%20%20%20用户指南/用户手册/%20租户管理员/扩展能力/事件列表/#{event_type.name}' + }) + elif not name and not tag: + events.append({ + 'tag':event_type.tag, + 'name':event_type.name, + 'description':event_type.description, + 'url': f'{host}/arkid/%20%20%20用户指南/用户手册/%20租户管理员/扩展能力/事件列表/#{event_type.name}' + }) return events \ No newline at end of file diff --git a/api/v1/views/extension.py b/api/v1/views/extension.py index 1a40b0913..f23f078ad 100644 --- a/api/v1/views/extension.py +++ b/api/v1/views/extension.py @@ -1,4 +1,5 @@ from uuid import UUID +from ninja import Query from datetime import datetime from ninja import Schema, ModelSchema from arkid.core import actions, extension @@ -42,6 +43,12 @@ class ExtensionConfigSchemaOut(Schema): class ExtensionProfileGetSchemaResponse(ResponseSchema): data: ExtensionProfileGetSchemaOut +class ExtensionListQueryIn(Schema): + + name: str = Field(title=_("插件名称"), default=None) + package: str = Field(title=_("标识"), default=None) + category_id: str = Field(hidden=True, default=None) + @api.get("/extensions/{id}/profile/", response=ExtensionProfileGetSchemaResponse, tags=['平台插件']) @operation(roles=[PLATFORM_ADMIN]) def get_extension_profile(request, id: str): @@ -107,13 +114,17 @@ class Config: @api.get("/extensions/", response=List[ExtensionListOut], tags=['平台插件']) @operation(roles=[PLATFORM_ADMIN]) @paginate(CustomPagination) -def list_extensions(request, status: str = None, category_id: str = None): +def list_extensions(request, query_data: ExtensionListQueryIn=Query(...)): """ 获取平台插件列表""" - if not status: + if not query_data.name: qs = ExtensionModel.valid_objects.filter() else: - qs = ExtensionModel.valid_objects.filter(status=status) + qs = ExtensionModel.valid_objects.filter(name__icontains=query_data.name) + + if query_data.package: + qs = qs.filter(package__icontains=query_data.package) + category_id = query_data.category_id if category_id and category_id != "" and category_id != "0": qs = qs.filter(category_id=int(category_id)) diff --git a/api/v1/views/loginpage.py b/api/v1/views/loginpage.py index 5e6eefad2..77b7f6d3e 100644 --- a/api/v1/views/loginpage.py +++ b/api/v1/views/loginpage.py @@ -41,6 +41,7 @@ class ButtonSchema(Schema): http: Optional[ButtonHttpSchema] = Field(title=_('http', 'http请求')) delay: Optional[int] = Field(title=_('delay', '点击后延时(单位:秒)')) agreement: Optional[ButtonAgreementSchema] = Field(title=_('agreement', '隐私声明')) + action: Optional[str] = Field(title=_('action', '点击执行函数')) class LOGIN_FORM_ITEM_TYPES(str, Enum): @@ -59,11 +60,15 @@ class LoginFormItemSchema(Schema): http: Optional[ButtonHttpSchema] = Field(title=_('http', 'http请求')) content: Optional[str] = Field(title=_('content', '内容')) +class ScriptSchema(Schema): + src: str + globals: Optional[List[str]] class LoginFormSchema(Schema): label: str = Field(title=_('label', '表单名')) items: List[LoginFormItemSchema] = Field(title=_('items', '表单项')) submit: ButtonSchema = Field(title=_('submit', '表单提交')) + scripts: Optional[List[ScriptSchema]] = Field(title=_('scripts', '自定义表单脚本')) class LoginPageExtendSchema(Schema): @@ -190,6 +195,12 @@ def login_page(request, tenant_id: str): tenant_expanded = Tenant.expand_objects.get(id=tenant.id) tenant_expanded["is_platform_tenant"] = tenant.is_platform_tenant + + + if not request.session.exists(request.session.session_key): + request.session.create() + request.session.set_expiry(0) + return { 'tenant': tenant_expanded, 'data': data, diff --git a/api/v1/views/mine.py b/api/v1/views/mine.py index 6ae7cfb91..41a748039 100644 --- a/api/v1/views/mine.py +++ b/api/v1/views/mine.py @@ -1,4 +1,5 @@ from typing import List +from ninja import Query from django.urls import reverse from django.shortcuts import render from arkid.config import get_app_config @@ -67,10 +68,7 @@ def update_mine_profile(request, tenant_id: str, data: ProfileSchemaIn): def get_mine_permissions( request, tenant_id: str, - app_id: str = None, - app_name: str = None, - category: str = None, - operation_id: str = None, + query_data:MinePermissionListQueryIn=Query(...) ): """我的权限列表""" login_user = request.user @@ -78,7 +76,7 @@ def get_mine_permissions( permissiondata = PermissionData() items = permissiondata.get_permissions_by_mine_search( - tenant_id, app_id, None, None, login_user, app_name=app_name, category=category, operation_id=operation_id, + tenant_id, query_data.app_id, None, None, login_user, app_name=query_data.app_name, category=query_data.category, operation_id=query_data.operation_id, name=query_data.name, ) return items @@ -396,9 +394,13 @@ def get_mine_app_groups(request, tenant_id: str, parent_id=None): @api.get("/mine/tenant/{tenant_id}/mine_group_apps/", response=List[MineAppListItemOut], tags=["我的"]) @operation(MineAppListOut,roles=[NORMAL_USER, TENANT_ADMIN, PLATFORM_ADMIN]) @paginate(CustomPagination) -def get_mine_apps_with_group(request, tenant_id: str, app_group_id:str=None, order:str=None): +def get_mine_apps_with_group(request, tenant_id: str, query_data:MineAppsGroupQueryIn=Query(...)): """获取我的分组应用 """ + app_group_id = query_data.app_group_id + order = query_data.order + name = query_data.name + apps = [] if app_group_id in [None,"","0",0]: apps = App.active_objects.filter( @@ -418,12 +420,45 @@ def get_mine_apps_with_group(request, tenant_id: str, app_group_id:str=None, ord apps = apps.filter(id__in=app_ids) else: apps = apps.filter(id=None) + if name: + name = name.strip() + apps = apps.filter(name__icontains=name) if order: apps = apps.order_by(order) apps = apps.all() return list(apps) if apps else [] +@api.get("/mine/tenant/{tenant_id}/mine_group_apps_all/", response=MineAppListOut, tags=["我的"]) +@operation(MineAppListOut,roles=[NORMAL_USER, TENANT_ADMIN, PLATFORM_ADMIN]) +def get_mine_apps_with_group_all(request, tenant_id: str, app_group_id:str=None, order:str=None): + """获取我的分组应用 + """ + apps = [] + if app_group_id in [None,"","0",0]: + apps = App.active_objects.filter( + Q(tenant=request.tenant) | Q(entry_permission__is_open=True) + ) + else: + app_group = AppGroup.active_objects.get( + tenant =request.tenant, + id=app_group_id + ) + apps = app_group.apps.filter(Q(tenant=request.tenant) | Q(entry_permission__is_open=True)) + # 需要甄别有入口权限的应用 + from arkid.core.perm.permission_data import PermissionData + pd = PermissionData() + app_ids = pd.get_entry_apps(request.user, tenant_id, apps) + if app_ids: + apps = apps.filter(id__in=app_ids) + else: + apps = apps.filter(id=None) + if order: + apps = apps.order_by(order) + apps = apps.all() + + return SuccessDict(data=list(apps) if apps else []) + @api.get("/mine/unread_messages/",response=List[MineMessageListItemOut],tags=["我的"],auth=GlobalAuth()) @operation(MineMessageListOut,roles=[NORMAL_USER, TENANT_ADMIN, PLATFORM_ADMIN]) @paginate(CustomPagination) diff --git a/api/v1/views/permission.py b/api/v1/views/permission.py index 020674b7f..8f67b1d93 100644 --- a/api/v1/views/permission.py +++ b/api/v1/views/permission.py @@ -2,6 +2,7 @@ from arkid.core.api import api, operation from typing import List, Optional from django.db import transaction +from ninja import Query from ninja.pagination import paginate from arkid.core.error import ErrorCode, ErrorDict from arkid.core.pagenation import CustomPagination @@ -48,38 +49,38 @@ def create_permission(request, tenant_id: str, data: PermissionCreateSchemaIn): @api.get("/tenant/{tenant_id}/permissions", response=List[PermissionsListSchemaOut], tags=['权限']) @operation(roles=[TENANT_ADMIN, PLATFORM_ADMIN]) @paginate(CustomPagination) -def list_permissions(request, tenant_id: str, app_id: str = None, select_user_id: str = None, group_id: str = None, app_name: str = None, category: str = None, operation_id: str = None): +def list_permissions(request, tenant_id: str, query_data:PermissionListQueryIn=Query(...)): ''' 权限列表 ''' login_user = request.user from arkid.core.perm.permission_data import PermissionData permissiondata = PermissionData() - return permissiondata.get_permissions_by_search(tenant_id, app_id, select_user_id, group_id, login_user, app_name=app_name, category=category, operation_id=operation_id) + return permissiondata.get_permissions_by_search(tenant_id, query_data.app_id, query_data.select_user_id, query_data.group_id, login_user, app_name=query_data.app_name, category=query_data.category, operation_id=query_data.operation_id, name=query_data.name) @api.get("/tenant/{tenant_id}/apps/{id}/permissions", response=List[AppPermissionsItemSchemaOut], tags=['权限']) @operation(AppPermissionsListSchemaOut, roles=[TENANT_ADMIN, PLATFORM_ADMIN]) @paginate(CustomPagination) -def app_list_permissions(request, tenant_id: str, id: str, category: str = None, operation_id: str = None): +def app_list_permissions(request, tenant_id: str, id: str, query_data:AppPermissionListQueryIn=Query(...)): ''' 应用权限列表 ''' from arkid.core.perm.permission_data import PermissionData permissiondata = PermissionData() - return permissiondata.get_app_permissions_by_search(tenant_id, id, category, operation_id) + return permissiondata.get_app_permissions_by_search(tenant_id, id, query_data.category, query_data.operation_id, query_data.name) @api.get("/tenant/{tenant_id}/user_app_last_permissions", response=List[UserAppLastPermissionsItemSchemaOut], tags=['权限']) @operation(UserAppLastPermissionsSchemaOut, roles=[TENANT_ADMIN, PLATFORM_ADMIN]) @paginate(CustomPagination) -def user_app_last_permissions(request, tenant_id: str, user_id: str = None, app_id: str = None, category: str = None, operation_id: str = None): +def user_app_last_permissions(request, tenant_id: str, query_data:UserPermissionLastQueryIn=Query(...)): ''' 用户最终结果权限列表 ''' login_user = request.user from arkid.core.perm.permission_data import PermissionData permissiondata = PermissionData() - return permissiondata.get_user_app_last_permissions(tenant_id, app_id, user_id, category, operation_id) + return permissiondata.get_user_app_last_permissions(tenant_id, query_data.app_id, query_data.user_id, query_data.category, query_data.operation_id, query_data.name, query_data.app_name) @api.get("/tenant/{tenant_id}/childmanager_permissions", response=List[PermissionsListSchemaOut], tags=['权限']) @operation(roles=[TENANT_ADMIN, PLATFORM_ADMIN]) @@ -275,7 +276,6 @@ def usergroup_remove_permission(request, tenant_id: str, select_usergroup_id: st # from arkid.core.perm.permission_data import PermissionData # pd = PermissionData() if isinstance(permission, SystemPermission): - pass dispatch_event(Event(tag=REMOVE_USERGROUP_SYSTEM_PERMISSION, tenant=request.tenant, request=request, data=permission)) else: # pd.remove_app_permission_to_usergroup( @@ -288,24 +288,24 @@ def usergroup_remove_permission(request, tenant_id: str, select_usergroup_id: st @api.get("/tenant/{tenant_id}/group_permissions", response=List[PermissionsListSchemaOut], tags=['权限']) @operation(roles=[TENANT_ADMIN, PLATFORM_ADMIN]) @paginate(CustomPagination) -def list_group_permissions(request, tenant_id: str, select_usergroup_id: str = None, app_name: str = None, category: str = None, operation_id: str = None): +def list_group_permissions(request, tenant_id: str, query_data:GroupPermissionListQueryIn=Query(...)): ''' 分组权限列表 ''' from arkid.core.perm.permission_data import PermissionData permissiondata = PermissionData() - return permissiondata.get_group_permissions_by_search(tenant_id, select_usergroup_id, app_name, category, operation_id) + return permissiondata.get_group_permissions_by_search(tenant_id, query_data.select_usergroup_id, query_data.app_name, query_data.category, query_data.operation_id, query_data.name) @api.get("/tenant/{tenant_id}/user_group_last_permissions", response=List[UserAppLastPermissionsItemSchemaOut], tags=['权限']) @operation(UserAppLastPermissionsSchemaOut, roles=[TENANT_ADMIN, PLATFORM_ADMIN]) @paginate(CustomPagination) -def list_user_group_last_permissions(request, tenant_id: str, usergroup_id: str = None, category: str = None, operation_id: str = None): +def list_user_group_last_permissions(request, tenant_id: str, query_data:UserGroupLastPermQueryIn=Query(...)): ''' 分组权限最终列表 ''' from arkid.core.perm.permission_data import PermissionData permissiondata = PermissionData() - return permissiondata.get_user_group_last_permissions(tenant_id, usergroup_id, category, operation_id) + return permissiondata.get_user_group_last_permissions(tenant_id, query_data.usergroup_id, query_data.category, query_data.operation_id, query_data.name, query_data.app_name) # @api.post("/tenant/{tenant_id}/permission/{permission_id}/set_open", tags=['权限']) # @operation(roles=[TENANT_ADMIN, PLATFORM_ADMIN]) diff --git a/api/v1/views/permission_group.py b/api/v1/views/permission_group.py index ce4661ec2..2bed36d14 100644 --- a/api/v1/views/permission_group.py +++ b/api/v1/views/permission_group.py @@ -1,4 +1,5 @@ from uuid import UUID +from ninja import Query from typing import List from ninja import Field from ninja import Schema @@ -131,9 +132,13 @@ def delete_permission_group(request, tenant_id: str, id: str): @api.get("/tenant/{tenant_id}/permission_groups/{permission_group_id}/permissions/", response=List[PermissionListSchemaOut], tags=["权限分组"]) @operation(roles=[TENANT_ADMIN, PLATFORM_ADMIN]) @paginate(CustomPagination) -def get_permissions_from_group(request, tenant_id: str, permission_group_id: str, category: str = None, operation_id: str = None): +def get_permissions_from_group(request, tenant_id: str, permission_group_id: str, query_data:PermissionGroupListQueryIn=Query(...)): """ 获取当前分组的权限列表 """ + category = query_data.category + operation_id = query_data.operation_id + name = query_data.name + app_name = query_data.app_name from arkid.core.perm.permission_data import PermissionData if permission_group_id != 'arkid': permission = SystemPermission.valid_objects.filter(id=permission_group_id).first() @@ -147,6 +152,24 @@ def get_permissions_from_group(request, tenant_id: str, permission_group_id: str if operation_id: operation_id = operation_id.strip() result_items = result_items.filter(operation_id__icontains=operation_id) + if name: + name = name.strip() + result_items = result_items.filter(name__icontains=name) + if app_name: + app_name = app_name.strip() + if isinstance(permission, SystemPermission): + permissions = permissions.filter(app__name__icontains=app_name) + else: + filter_apps = App.valid_objects.filter( + name__icontains=app_name + ) + filter_ids = [] + for filter_app in filter_apps: + filter_ids.append(filter_app.entry_permission.id) + if filter_ids: + result_items = result_items.filter(id__in=filter_ids) + else: + result_items = result_items.filter(id__isnull=True) return result_items else: app = App.valid_objects.filter(id=permission_group_id).first() @@ -162,6 +185,18 @@ def get_permissions_from_group(request, tenant_id: str, permission_group_id: str #code__startswith='group_role' ) if group_permissions: + if category: + category = category.strip() + group_permissions = group_permissions.filter(category__icontains=category) + if operation_id: + operation_id = operation_id.strip() + group_permissions = group_permissions.filter(operation_id__icontains=operation_id) + if name: + name = name.strip() + group_permissions = group_permissions.filter(name__icontains=name) + if app_name: + app_name = app_name.strip() + group_permissions = group_permissions.filter(app__name__icontains=app_name) items.extend(group_permissions) for group_permission in group_permissions: for item in group_permission.container.all(): @@ -178,23 +213,59 @@ def get_permissions_from_group(request, tenant_id: str, permission_group_id: str if operation_id: operation_id = operation_id.strip() group_permission_details = group_permission_details.filter(operation_id__icontains=operation_id) + if name: + name = name.strip() + group_permission_details = group_permission_details.filter(name__icontains=name) + if app_name: + app_name = app_name.strip() + group_permissions = group_permissions.filter(app__name__icontains=app_name) items.extend(group_permission_details) entry_permission = None if app.entry_permission: - if category and category in app.entry_permission.category: - category = category.strip() + if category and operation_id and name and category in app.entry_permission.category and operation_id in app.entry_permission.operation_id and name in app.entry_permission.name: + entry_permission = app.entry_permission + elif category and operation_id and category in app.entry_permission.category and operation_id in app.entry_permission.operation_id: + entry_permission = app.entry_permission + elif category and name and category in app.entry_permission.category and name in app.entry_permission.name: + entry_permission = app.entry_permission + elif operation_id and name and operation_id in app.entry_permission.operation_id and name in app.entry_permission.name: + entry_permission = app.entry_permission + elif category and category in app.entry_permission.category: entry_permission = app.entry_permission elif operation_id and operation_id in app.entry_permission.operation_id: - operation_id = operation_id.strip() entry_permission = app.entry_permission - else: + elif name and name in app.entry_permission.name: entry_permission = app.entry_permission + + if app_name and app_name not in app.name: + entry_permission = None # 需要过滤展示 permissiondata = PermissionData() items = permissiondata.get_permissions_by_app_filter(tenant_id, app.id, items, entry_permission, request.user) return items else: permissions = SystemPermission.valid_objects.filter(category='group', is_system=True) + if category: + category = category.strip() + permissions = permissions.filter(category__icontains=category) + if operation_id: + operation_id = operation_id.strip() + permissions = permissions.filter(operation_id__icontains=operation_id) + if name: + name = name.strip() + permissions = permissions.filter(name__icontains=name) + if app_name: + app_name = app_name.strip() + filter_apps = App.valid_objects.filter( + name__icontains=app_name + ) + filter_ids = [] + for filter_app in filter_apps: + filter_ids.append(filter_app.entry_permission.id) + if filter_ids: + permissions = permissions.filter(id__in=filter_ids) + else: + permissions = permissions.filter(id__isnull=True) # 只能看到自己拥有的权限 permissiondata = PermissionData() return permissiondata.get_system_permission_by_filter(tenant_id, permissions, request.user) @@ -270,9 +341,12 @@ def update_permissions_from_group(request, tenant_id: str, permission_group_id: @api.get("/tenant/{tenant_id}/permission_groups/{permission_group_id}/select_permissions/", response=List[PermissionListSelectSchemaOut], tags=["权限分组"]) @operation(PermissionListDataSelectSchemaOut, roles=[TENANT_ADMIN, PLATFORM_ADMIN]) @paginate(CustomPagination) -def get_select_permissions(request, tenant_id: str, permission_group_id: str, category: str = None, operation_id: str = None): +def get_select_permissions(request, tenant_id: str, permission_group_id: str, query_data:PermissionGroupListQueryIn=Query(...)): """ 获取所有权限并附加是否在当前分组的状态 """ + category = query_data.category + operation_id = query_data.operation_id + name = query_data.name # 只允许非arkid的分组操作(如果用户直接在系统分组里加权限会有问题) if permission_group_id == 'arkid': return [] @@ -293,6 +367,9 @@ def get_select_permissions(request, tenant_id: str, permission_group_id: str, ca if operation_id: operation_id = operation_id.strip() permissions = permissions.filter(operation_id__icontains=operation_id) + if name: + name = name.strip() + permissions = permissions.filter(name__icontains=name) for permission in permissions: id_hex = permission.id.hex if id_hex in ids: @@ -312,6 +389,9 @@ def get_select_permissions(request, tenant_id: str, permission_group_id: str, ca if operation_id: operation_id = operation_id.strip() permissions = permissions.filter(operation_id__icontains=operation_id) + if name: + name = name.strip() + permissions = permissions.filter(name__icontains=name) for permission in permissions: id_hex = permission.id.hex if id_hex in ids: diff --git a/api/v1/views/tenant_extension.py b/api/v1/views/tenant_extension.py index 5c1f7a678..d2e2cc128 100644 --- a/api/v1/views/tenant_extension.py +++ b/api/v1/views/tenant_extension.py @@ -1,4 +1,5 @@ from uuid import UUID +from ninja import Query from datetime import datetime from ninja import Query, Schema, ModelSchema from arkid.core.api import api, operation @@ -42,6 +43,11 @@ class ExtensionConfigGetOut(ResponseSchema): class ExtensionConfigCreateSchemaOut(Schema): config_id: str +class TenantExtensionListQueryIn(Schema): + + name: str = Field(title=_("插件名称"), default=None) + package: str = Field(title=_("标识"), default=None) + category_id: str = Field(hidden=True, default=None) class TenantExtensionConfigOut(ModelSchema): @@ -256,14 +262,21 @@ def get_platform_extensions(request, tenant_id: str, category_id: str = None): @api.get("/tenant/{tenant_id}/tenant/extensions/", tags=["租户插件"],response=List[TenantRentedExtensionListOut]) @operation(List[TenantRentedExtensionListOut], roles=[TENANT_ADMIN, PLATFORM_ADMIN]) @paginate(CustomPagination) -def get_tenant_extensions(request, tenant_id: str, category_id: str = None): +def get_tenant_extensions(request, tenant_id: str, query_data: TenantExtensionListQueryIn=Query(...)): """ 租户插件列表 """ + category_id: str = query_data.category_id token = request.user.auth_token tenant = Tenant.objects.get(id=tenant_id) extension_ids = TenantExtension.valid_objects.filter(tenant_id=tenant_id, is_rented=True).values('extension_id') extensions = ExtensionModel.active_objects.filter(id__in = extension_ids) + if query_data.name: + extensions = extensions.filter(name__icontains=query_data.name) + + if query_data.package: + extensions = extensions.filter(package__icontains=query_data.package) + if category_id and category_id != "" and category_id != "0": extensions = extensions.filter(category_id=int(category_id)) diff --git a/api/v1/views/user.py b/api/v1/views/user.py index d005f3fe7..93512dabb 100644 --- a/api/v1/views/user.py +++ b/api/v1/views/user.py @@ -30,6 +30,12 @@ def user_list(request, tenant_id: str, query_data: UserListQueryIn=Query(...)): users = User.expand_objects.filter(tenant_id=tenant_id, is_del=False) if query_data.username: users = users.filter(username__icontains=query_data.username) + if query_data.nickname: + users = users.filter(nickname__icontains=query_data.nickname) + if query_data.mobile: + users = users.filter(mobile__icontains=query_data.mobile) + if query_data.email: + users = users.filter(email__icontains=query_data.email) if query_data.order: users = users.order_by(query_data.order) login_user = request.user @@ -43,7 +49,6 @@ def user_list(request, tenant_id: str, query_data: UserListQueryIn=Query(...)): return list(users) - @api.get("/tenant/{tenant_id}/user_no_super/",response=UserListOut, tags=['用户']) @operation(UserListOut,roles=[TENANT_ADMIN, PLATFORM_ADMIN]) # @paginate(CustomPagination) diff --git a/arkid/common/arkstore.py b/arkid/common/arkstore.py index ca0d2755f..132e812f7 100644 --- a/arkid/common/arkstore.py +++ b/arkid/common/arkstore.py @@ -16,18 +16,19 @@ from arkid.extension.utils import import_extension, unload_extension, load_extension_apps, restart_celery from pathlib import Path from arkid.common.logger import logger +from django.core.cache import cache -arkid_saas_token_cache = {} - def get_saas_token(tenant, token, use_cache=True): """ 获取saas平台token """ # 缓存 saas_token key = (str(tenant.id) + '_' + str(token)).replace('-', '') - if use_cache and key in arkid_saas_token_cache: - return arkid_saas_token_cache[key] + saas_cache = cache.get(key) + if use_cache and saas_cache: + return saas_cache + app = Application.objects.filter(name='arkid_saas', uuid = tenant.id).first() nonce = uuid.uuid4().hex params = { @@ -55,11 +56,12 @@ def get_saas_token(tenant, token, use_cache=True): resp = requests.get(resp.url) if resp.status_code != 200: - arkid_saas_token_cache.pop(key, None) + cache.delete(key) raise Exception(f'Error get_saas_token: {resp.status_code}') resp = resp.json() - arkid_saas_token_cache[key] = (resp['token'], resp['tenant_id'], resp['tenant_slug']) - return arkid_saas_token_cache[key] + result = (resp['token'], resp['tenant_id'], resp['tenant_slug']) + cache.set(key, result, timeout=60*60) + return result def get_arkstore_access_token(tenant, token, use_cache=True): @@ -77,7 +79,6 @@ def get_arkstore_access_token(tenant, token, use_cache=True): use_cache=False, local_tenant=tenant, local_token=token) -arkstore_access_token_saas_cache = {} def get_arkstore_access_token_with_saas_token(saas_tenant_slug, saas_tenant_id, saas_token, use_cache=True, local_tenant=None, local_token=None): @@ -86,19 +87,20 @@ def get_arkstore_access_token_with_saas_token(saas_tenant_slug, saas_tenant_id, """ # 缓存 idtoken key = (str(saas_tenant_id) + '_' + str(saas_token)).replace('-', '') - if use_cache and key in arkstore_access_token_saas_cache: + id_token = cache.get(key) + if use_cache and id_token: try: - payload = jwt.decode(arkstore_access_token_saas_cache[key], options={"verify_signature": False}) + payload = jwt.decode(id_token, options={"verify_signature": False}) except Exception: - arkstore_access_token_saas_cache.pop(key, None) + cache.delete(key) raise Exception("Unable to parse id_token") exp_dt = datetime.fromtimestamp(payload["exp"]) expire_time = timezone.make_aware(exp_dt, timezone.get_default_timezone()) now = timezone.localtime() if now <= expire_time: - return arkstore_access_token_saas_cache[key] + return id_token else: - arkstore_access_token_saas_cache.pop(key, None) + cache.delete(key) # id_token 过期后,重新获取saas_token和id_token if local_tenant and local_token: saas_token, saas_tenant_id, saas_tenant_slug = get_saas_token(local_tenant, local_token, use_cache=False) @@ -107,15 +109,16 @@ def get_arkstore_access_token_with_saas_token(saas_tenant_slug, saas_tenant_id, app_login_url = settings.ARKSTOER_URL + '/api/v1/login' resp = requests.get(app_login_url, params=params) if resp.status_code != 200: - arkstore_access_token_saas_cache.pop(key, None) + cache.delete(key) raise Exception(f'Error get_arkstore_access_token_with_saas_token: {resp.status_code}, url: {resp.url}') try: resp = resp.json() except: from urllib.parse import urlencode, unquote raise Exception(f'Error get_arkstore_access_token_with_saas_token: {resp.status_code}, url: {unquote(resp.url)}') - arkstore_access_token_saas_cache[key] = resp['access_token'] - return arkstore_access_token_saas_cache[key] + id_token = resp['access_token'] + cache.set(key, id_token, timeout=60*60) + return id_token def get_arkstore_extensions(access_token, purchased=None, rented=False, type=None, offset=0, limit=10, extra_params={}): @@ -605,6 +608,15 @@ def check_arkstore_purcahsed_extension_expired(tenant, token, package): if ext_info is None: return True extension_uuid = ext_info['uuid'] + + price_info = get_arkstore_extension_price(access_token, extension_uuid) + prices = price_info.get('prices') + if not prices: + return True + for price in prices: + if price.get('sale_price') == 0: + return True + order_url = settings.ARKSTOER_URL + f'/api/v1/arkstore/extensions/{extension_uuid}/purchased' headers = {'Authorization': f'Token {access_token}'} params = {} @@ -725,4 +737,24 @@ def get_app_config_from_arkstore(request, arkstore_app_id): if res['type'] == 'auto_form_fill': app = get_arkid_saas_app_detail(tenant, token, arkstore_app_id) return app.get('config', {}).get('config', {}) - return {} \ No newline at end of file + return {} + + +def get_admin_user_token(): + from django.core.cache import cache + + key = "ADMIN_USER_TOKEN" + value = refresh_admin_uesr_token + timeout = 60*30 + token = cache.get_or_set(key, value, timeout=timeout) + return token + + +def refresh_admin_uesr_token(): + from arkid.core.token import refresh_token + platform_tenant = Tenant.platform_tenant() + admin_user = User.objects.filter( + username='admin', tenant=platform_tenant + ).first() + token = refresh_token(admin_user) + return token diff --git a/arkid/core/actions.py b/arkid/core/actions.py index e81bd1757..479a9d064 100644 --- a/arkid/core/actions.py +++ b/arkid/core/actions.py @@ -1,9 +1,9 @@ from enum import Enum -from typing import Union, Tuple,List +from typing import Union, Tuple,List, Optional from arkid.common import DeepSN from arkid.common.utils import gen_tag from arkid.core.translation import gettext_default as _ - +from ninja import Schema class FrontActionType(Enum): """前端动作类型枚举类 @@ -35,6 +35,7 @@ class FrontActionType(Enum): NEXT_ACTION = 'next' ORDER_ACTION = 'order' ORDERSET_ACTION = 'orderset' + SCRIPT_ACTION = 'script' class FrontActionMethod(Enum): @@ -214,6 +215,16 @@ def __init__(self, path: str, order_parms:List[str], *args, **kwargs): self.order_parms = order_parms super().__init__(action_type=FrontActionType.ORDERSET_ACTION,*args, **kwargs) +class ScriptSchema(Schema): + src: str + globals: Optional[List[str]] + + +class ScriptAction(FrontAction): + def __init__(self, scripts: List[ScriptSchema], script_func:str, *args, **kwargs): + self.scripts = scripts + self.script_func = script_func + super().__init__(action_type=FrontActionType.SCRIPT_ACTION, *args, **kwargs) nav_actions = {} diff --git a/arkid/core/api.py b/arkid/core/api.py index 6f7233920..c87c335be 100644 --- a/arkid/core/api.py +++ b/arkid/core/api.py @@ -77,6 +77,7 @@ class HttpBaseBearer(HttpAuthBase, ABC): header: str = "Authorization" app_id: str = "APP_ID" app_secret: str = "APP_SECRET" + download_token = "DOWNLOAD_TOKEN" def __call__(self, request: HttpRequest) -> Optional[Any]: headers = get_headers(request) @@ -86,6 +87,8 @@ def __call__(self, request: HttpRequest) -> Optional[Any]: parts = auth_value.split(" ") if parts[0].lower() == self.openapi_scheme: token = " ".join(parts[1:]) + elif request.GET.get(self.download_token, None): + token = request.GET.get(self.download_token) app_id = headers.get(self.app_id, None) app_secret = headers.get(self.app_secret, None) @@ -107,6 +110,7 @@ def authenticate(self, request, token, app_id, app_secret): token = ExpiringToken.objects.filter(user=request.user).first() if not token: token = ExpiringToken.objects.create(user=request.user, token=generate_token()) + self.refresh_token_active_date(token) tenant = request.tenant # 获取操作id查询用户权限 operation_id = request.operation_id @@ -122,6 +126,7 @@ def authenticate(self, request, token, app_id, app_secret): if token: # 使用传统的token访问 token = ExpiringToken.objects.get(token=token) + self.refresh_token_active_date(token) if not token.user.is_active: raise HttpError(401, _('User inactive or deleted','用户无效或被删除')) tenant = request.tenant or Tenant.platform_tenant() @@ -172,6 +177,14 @@ def authenticate(self, request, token, app_id, app_secret): # logger.error(err) # return + @staticmethod + def refresh_token_active_date(token): + from django.utils import timezone + local_date = timezone.localdate() + if not token.active_date or token.active_date < local_date: + token.active_date = local_date + token.save() + class ArkidApi(NinjaAPI): def create_response(self, request, *args, **kwargs): diff --git a/arkid/core/migrations/0032_expiringtoken_active_date.py b/arkid/core/migrations/0032_expiringtoken_active_date.py new file mode 100644 index 000000000..541951d7a --- /dev/null +++ b/arkid/core/migrations/0032_expiringtoken_active_date.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.6 on 2022-10-26 13:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0031_alter_tenant_slug'), + ] + + operations = [ + migrations.AddField( + model_name='expiringtoken', + name='active_date', + field=models.DateField(blank=True, default=None, null=True, verbose_name='Active Date'), + ), + ] diff --git a/arkid/core/models.py b/arkid/core/models.py index 19a6d825f..ac3834b49 100644 --- a/arkid/core/models.py +++ b/arkid/core/models.py @@ -547,6 +547,7 @@ class Meta(object): default=generate_token, ) created = models.DateTimeField(_("Created", '创建时间'), auto_now=True) + active_date = models.DateField(_("Active Date", '活跃日期'), blank=True, null=True, default=None) def expired(self, tenant): """Return boolean indicating token expiration.""" diff --git a/arkid/core/perm/permission_data.py b/arkid/core/perm/permission_data.py index 3a8a275e0..d87d6f0b0 100644 --- a/arkid/core/perm/permission_data.py +++ b/arkid/core/perm/permission_data.py @@ -1451,7 +1451,7 @@ def update_tenant_all_user_permission(self, tenant): data_group_parent_child[parent_id_hex] = temp_data_group data_dict = collections.OrderedDict(sorted(data_dict.items(), key=lambda obj: obj[0])) - def get_app_permissions_by_search(self, tenant_id, app_id, category = None, operation_id = None): + def get_app_permissions_by_search(self, tenant_id, app_id, category = None, operation_id = None, name = None): ''' 根据应用查权限(根据应用权限字符串) ''' @@ -1481,6 +1481,9 @@ def get_app_permissions_by_search(self, tenant_id, app_id, category = None, oper if operation_id: operation_id = operation_id.strip() result_items = result_items.filter(operation_id__icontains=operation_id) + if name: + name = name.strip() + result_items = result_items.filter(name__icontains=name) result.extend(list(result_items.order_by('sort_id'))) else: result_items = SystemPermission.valid_objects.filter( @@ -1492,6 +1495,9 @@ def get_app_permissions_by_search(self, tenant_id, app_id, category = None, oper if operation_id: operation_id = operation_id.strip() result_items = result_items.filter(operation_id__icontains=operation_id) + if name: + name = name.strip() + result_items = result_items.filter(name__icontains=name) result.extend(list(result_items.order_by('sort_id'))) return result @@ -1566,7 +1572,7 @@ def get_permissions_by_app_filter(self, tenant_id, app_id, items, entry_permissi return result - def get_permissions_by_search(self, tenant_id, app_id, user_id, group_id, login_user, parent_id=None, is_only_show_group=False, app_name=None, category=None, operation_id=None): + def get_permissions_by_search(self, tenant_id, app_id, user_id, group_id, login_user, parent_id=None, is_only_show_group=False, app_name=None, category=None, operation_id=None, name=None): ''' 根据应用,用户,分组查权限(要根据用户身份显示正确的列表) ''' @@ -1600,11 +1606,24 @@ def get_permissions_by_search(self, tenant_id, app_id, user_id, group_id, login_ if app_name: app_name = app_name.strip() permissions = permissions.filter(app__name__icontains=app_name) - systempermissions = systempermissions.filter(id__isnull=True) + filter_apps = App.active_objects.filter(name__icontains=app_name) + entry_permission_ids = [] + for filter_app in filter_apps: + entry_permission = filter_app.entry_permission + if entry_permission: + entry_permission_ids.append(entry_permission.id) + if entry_permission_ids: + systempermissions = systempermissions.filter(id__in=entry_permission_ids) + else: + systempermissions = systempermissions.filter(id__isnull=True) if category: category = category.strip() permissions = permissions.filter(category__icontains=category) systempermissions = systempermissions.filter(category__icontains=category) + if name: + name = name.strip() + permissions = permissions.filter(name__icontains=name) + systempermissions = systempermissions.filter(name__icontains=name) if operation_id: operation_id = operation_id.strip() permissions = permissions.filter(operation_id__icontains=operation_id) @@ -1710,7 +1729,7 @@ def get_permissions_by_search(self, tenant_id, app_id, user_id, group_id, login_ systempermissions = systempermissions.filter(Q(tenant__isnull=True)|Q(tenant_id=tenant_id)) return list(systempermissions.all())+list(permissions.all()) - def get_user_app_last_permissions(self, tenant_id, app_id, user_id, category=None, operation_id=None): + def get_user_app_last_permissions(self, tenant_id, app_id, user_id, category=None, operation_id=None, name=None, app_name=None): ''' 获取用户指定应用的最终权限 ''' @@ -1757,6 +1776,24 @@ def get_user_app_last_permissions(self, tenant_id, app_id, user_id, category=Non if operation_id: operation_id = operation_id.strip() permissions = permissions.filter(operation_id__icontains=operation_id) + if name: + name = name.strip() + permissions = permissions.filter(name__icontains=name) + if app_name: + app_name = app_name.strip() + if app_id: + permissions = permissions.filter(app__name__icontains=app_name) + else: + filter_apps = App.valid_objects.filter( + name__icontains=app_name + ) + filter_ids = [] + for filter_app in filter_apps: + filter_ids.append(filter_app.entry_permission.id) + if filter_ids: + permissions = permissions.filter(id__in=filter_ids) + else: + permissions = permissions.filter(id__isnull=True) permissions = permissions.order_by('sort_id') for permission in permissions: @@ -1769,7 +1806,7 @@ def get_user_app_last_permissions(self, tenant_id, app_id, user_id, category=Non else: return [] - def get_user_group_last_permissions(self, tenant_id, usergroup_id, category=None, operation_id=None): + def get_user_group_last_permissions(self, tenant_id, usergroup_id, category=None, operation_id=None, name=None, app_name=None): ''' 获取用户分组的最终权限 ''' @@ -1789,7 +1826,23 @@ def get_user_group_last_permissions(self, tenant_id, usergroup_id, category=None operation_id = operation_id.strip() permissions = permissions.filter(operation_id__icontains=operation_id) systempermissions = systempermissions.filter(operation_id__icontains=operation_id) - + if name: + name = name.strip() + permissions = permissions.filter(name__icontains=name) + systempermissions = systempermissions.filter(name__icontains=name) + if app_name: + app_name = app_name.strip() + permissions = permissions.filter(app__name__icontains=app_name) + filter_apps = App.valid_objects.filter( + name__icontains=app_name + ) + filter_ids = [] + for filter_app in filter_apps: + filter_ids.append(filter_app.entry_permission.id) + if filter_ids: + systempermissions = systempermissions.filter(id__in=filter_ids) + else: + systempermissions = systempermissions.filter(id__isnull=True) compress = Compress() usergroup = UserGroup.valid_objects.filter( id=usergroup_id @@ -1854,7 +1907,7 @@ def get_user_group_last_permissions(self, tenant_id, usergroup_id, category=None return list(systempermissions)+list(permissions) - def get_permissions_by_mine_search(self, tenant_id, app_id, user_id, group_id, login_user, parent_id=None, is_only_show_group=False, app_name=None, category=None, operation_id=None): + def get_permissions_by_mine_search(self, tenant_id, app_id, user_id, group_id, login_user, parent_id=None, is_only_show_group=False, app_name=None, category=None, operation_id=None, name=None): ''' 根据应用,用户,分组查权限(要根据用户身份显示正确的列表) ''' @@ -1899,6 +1952,11 @@ def get_permissions_by_mine_search(self, tenant_id, app_id, user_id, group_id, l operation_id = operation_id.strip() permissions = permissions.filter(operation_id__icontains=operation_id) systempermissions = systempermissions.filter(operation_id__icontains=operation_id) + if name: + name = name.strip() + permissions = permissions.filter(name__icontains=name) + systempermissions = systempermissions.filter(name__icontains=name) + if app_id or user_id or group_id: if app_id: app = App.valid_objects.filter( @@ -2047,7 +2105,7 @@ def get_permissions_by_childmanager(self, tenant_id, login_user, only_show_group return systempermissions - def get_group_permissions_by_search(self, tenant_id, select_usergroup_id, app_name, category, operation_id=None): + def get_group_permissions_by_search(self, tenant_id, select_usergroup_id, app_name, category, operation_id=None, name=None): ''' 根据应用,用户分组,分类查权限(要根据分组身份显示正确的列表) ''' @@ -2071,6 +2129,10 @@ def get_group_permissions_by_search(self, tenant_id, select_usergroup_id, app_na operation_id = operation_id.strip() permissions = permissions.filter(operation_id__icontains=operation_id) systempermissions = systempermissions.filter(operation_id__icontains=operation_id) + if name: + name = name.strip() + permissions = permissions.filter(name__icontains=name) + systempermissions = systempermissions.filter(name__icontains=name) if select_usergroup_id: # 系统权限 usergroup_permissionresult = GroupPermissionResult.valid_objects.filter( @@ -2750,7 +2812,7 @@ def get_child_manager_info(self, tenant_id, select_user): }) return permissions, manager_scope, self_source_ids - def get_child_mans(self, auth_users, tenant): + def get_child_mans(self, auth_users, tenant, username): ''' 获取子管理员 ''' @@ -2802,7 +2864,11 @@ def get_child_mans(self, auth_users, tenant): auth_user.is_tenant_admin = False if ids: - return User.valid_objects.filter(id__in=ids) + users = User.valid_objects.filter(id__in=ids) + if username: + return users.filter(username__icontains=username) + else: + return users else: return [] diff --git a/arkid/extension/utils.py b/arkid/extension/utils.py index 7a7fd9598..93d7a3f98 100644 --- a/arkid/extension/utils.py +++ b/arkid/extension/utils.py @@ -152,7 +152,12 @@ def unload_extension(ext_dir: str) -> any: if not Path(ext_dir).exists(): return ext_name = f'{Path(ext_dir).parent}.{Path(ext_dir).name}' - ext = importlib.import_module(ext_name) + try: + ext = importlib.import_module(ext_name) + except Exception as e: + logger.error(f"import_module {ext_name} failed: {str(e)}") + return + if ext: ext.extension.stop() sys.modules.pop(ext_name, None) diff --git a/arkid/settings.py b/arkid/settings.py index cd9b1a1e3..6617b1017 100644 --- a/arkid/settings.py +++ b/arkid/settings.py @@ -175,12 +175,33 @@ # '/home/guancy/longgui/arkid/extension_root/com_longgui_international_en_us/locale' ] +REDIS_CONFIG = { + 'HOST': 'localhost', + 'PORT': 6379, + 'DB': 0, + 'PASSWORD': None, +} + +REDIS_URL = 'redis://{}:{}/{}'.format(REDIS_CONFIG['HOST'], REDIS_CONFIG['PORT'],\ + REDIS_CONFIG['DB']) if REDIS_CONFIG['PASSWORD'] is None \ + else 'redis://:{}@{}:{}/{}'.format(REDIS_CONFIG['PASSWORD'],\ + REDIS_CONFIG['HOST'], REDIS_CONFIG['PORT'], REDIS_CONFIG['DB']) + # Celery settings -CELERY_BROKER = 'redis://localhost:6379' -CELERY_BROKER_URL = 'redis://localhost:6379' +CELERY_BROKER = REDIS_URL +CELERY_BROKER_URL = REDIS_URL CELERY_TIMEZONE = 'Asia/Shanghai' CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers:DatabaseScheduler' +CACHES = { + "default": { + "BACKEND": "django.core.cache.backends.redis.RedisCache", + "LOCATION": f"{REDIS_URL}/1", + } +} +SESSION_ENGINE = "django.contrib.sessions.backends.cache" +SESSION_CACHE_ALIAS = "default" + #: Only add pickle to this list if your broker is secured #: from unwanted access (see userguide/security.html) # CELERY_ACCEPT_CONTENT = ['json'] @@ -244,4 +265,4 @@ exec(open(os.path.join(BASE_DIR, 'settings_local.py')).read()) -CSRF_TRUSTED_ORIGINS = [] \ No newline at end of file +CSRF_TRUSTED_ORIGINS = [] diff --git a/docker-compose/settings.py b/docker-compose/settings.py index d7d50e63b..dd31da081 100644 --- a/docker-compose/settings.py +++ b/docker-compose/settings.py @@ -30,3 +30,10 @@ # CELERY CELERY_BROKER_URL = REDIS_URL + +CACHES = { + "default": { + "BACKEND": "django.core.cache.backends.redis.RedisCache", + "LOCATION": f"{REDIS_URL}/1", + } +} \ No newline at end of file diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh old mode 100644 new mode 100755 diff --git "a/docs/ \345\225\206\344\270\232\345\220\210\344\275\234\346\214\207\345\215\227/ ArkID\345\274\200\345\217\221\345\225\206/ \346\217\222\344\273\266.md" "b/docs/ \345\225\206\344\270\232\345\220\210\344\275\234\346\214\207\345\215\227/ ArkID\345\274\200\345\217\221\345\225\206/ \346\217\222\344\273\266.md" new file mode 100644 index 000000000..805e5065a --- /dev/null +++ "b/docs/ \345\225\206\344\270\232\345\220\210\344\275\234\346\214\207\345\215\227/ ArkID\345\274\200\345\217\221\345\225\206/ \346\217\222\344\273\266.md" @@ -0,0 +1,46 @@ +# 插件接入 + +## 创建插件 + +### ArkID插件 + +1.在ArkID中点击应用管理-应用列表-本地应用,点击开发与代理,跳转到ArkStore + +[![Bbbgph.png](https://v1.ax1x.com/2022/10/24/Bbbgph.png)](https://x.imgtu.com/i/Bbbgph) + +2.在Arkstore中点击开发商-插件管理-仓库管理,上传插件所在的gitee仓库或者zip压缩包 + +[![Bbb5yZ.png](https://v1.ax1x.com/2022/10/24/Bbb5yZ.png)](https://x.imgtu.com/i/Bbb5yZ) + +!!! 注意 + 名称必填,仓库分支与上传文件任选其一 + +3.如果选择gitee方式来创建仓库,并且仓库为私有仓库,需要授权龙归来访问您的gitee仓库,公开gitee仓库和zip压缩包方式可以跳过此步
+点击授权 +[![BbbAUU.png](https://v1.ax1x.com/2022/10/24/BbbAUU.png)](https://x.imgtu.com/i/BbbAUU) + +跳转到gitee +[![BbbGoH.png](https://v1.ax1x.com/2022/10/24/BbbGoH.png)](https://x.imgtu.com/i/BbbGoH) + +同意授权 +[![BbbV2q.png](https://v1.ax1x.com/2022/10/24/BbbV2q.png)](https://x.imgtu.com/i/BbbV2q) + +4.导入插件, 插件导入后会展示在插件包管理中
+点击导入插件 +[![BbbAUU.png](https://v1.ax1x.com/2022/10/24/BbbAUU.png)](https://x.imgtu.com/i/BbbAUU) + +插件包管理 +[![BbbhG9.png](https://v1.ax1x.com/2022/10/24/BbbhG9.png)](https://x.imgtu.com/i/BbbhG9) + +5.设置价格,详情请见[**价格规则**](../#_2)
+点击设置价格 +[![Bbb8tY.png](https://v1.ax1x.com/2022/10/24/Bbb8tY.png)](https://x.imgtu.com/i/Bbb8tY) + +设置价格 +[![BbbKj4.png](https://v1.ax1x.com/2022/10/24/BbbKj4.png)](https://x.imgtu.com/i/BbbKj4) + +6.提交审核 + +[![Bbb8tY.png](https://v1.ax1x.com/2022/10/24/Bbb8tY.png)](https://x.imgtu.com/i/Bbb8tY) + +7.龙归审核通过后,插件上架完成,所有ArkID的用户在其插件商店中都能看到该插件。 diff --git "a/docs/ \345\225\206\344\270\232\345\220\210\344\275\234\346\214\207\345\215\227/ ArkID\345\274\200\345\217\221\345\225\206/index.md" "b/docs/ \345\225\206\344\270\232\345\220\210\344\275\234\346\214\207\345\215\227/ ArkID\345\274\200\345\217\221\345\225\206/index.md" new file mode 100644 index 000000000..57d39a779 --- /dev/null +++ "b/docs/ \345\225\206\344\270\232\345\220\210\344\275\234\346\214\207\345\215\227/ ArkID\345\274\200\345\217\221\345\225\206/index.md" @@ -0,0 +1,53 @@ +# ArkID开发商 + +如果您有开发能力,或者独立运营自己的SaaS产品,只通过ArkID账号访问ArkStore,即可成为ArkID开发商。 + +## 开发商条件 + +* 任何个人或企业 +* 有开发自研能力 +* 拥有ArkID账号 +* 访问ArkStore + +开发商可以向ArkID生态提供生产两类产品:[**ArkID插件**](./%20插件/) 与 [**SaaS应用**](./应用/)。 + +## 价格规则 + +### 商店支付 + +插件只能选择商店支付的方式,应用可以是商店支付或者应用内支付的方式。 + +无论是插件还是应用,开发商都可以设置价格。 + +价格分两类:**购买价格** 与 **租赁价格**,两种价格的组成是相同的。 + +价格包含两个元素:**市场指导价** 与 **成本折扣** + +市场指导价的单位为: x元/y人/z天 + +* 当x=0,则被视为免费 +* 当y=0,则被认为不限人数 +* 当z=0,则被认为不限天数 + +成本折扣可以为0-1之间的任何一个两位小数。 + +!!! 注意 + 每成交一个产品,开发商实际获得的收益为:市场指导价 * 成本折扣 + +价格可以设置多条,以满足不同的定价策略。 + +### 应用内支付 + +你的应用可以不通过商店支付,而是用户自行在你的应用内支付页面完成支付。 + +这种情况,需要你跟龙归签署相关合作协议,并商定好分成比例,定期与龙归结算即可。 + +## 订单记录 + +在ArkStore中,点击 费用中心-开发商订单 即可查看 + +## 提现 + +在ArkStore中,点击 费用中心-提现申请 即可发起申请,后台审核完后会打款。 + +微信分账功能正在接入。 \ No newline at end of file diff --git "a/docs/ \345\225\206\344\270\232\345\220\210\344\275\234\346\214\207\345\215\227/ ArkID\345\274\200\345\217\221\345\225\206/\345\272\224\347\224\250.md" "b/docs/ \345\225\206\344\270\232\345\220\210\344\275\234\346\214\207\345\215\227/ ArkID\345\274\200\345\217\221\345\225\206/\345\272\224\347\224\250.md" new file mode 100644 index 000000000..2ea5cd1e2 --- /dev/null +++ "b/docs/ \345\225\206\344\270\232\345\220\210\344\275\234\346\214\207\345\215\227/ ArkID\345\274\200\345\217\221\345\225\206/\345\272\224\347\224\250.md" @@ -0,0 +1,98 @@ +# 应用接入 + +## 创建应用 + +### SaaS应用 + +#### OIDC + +1.在ArkID中点击应用管理-应用列表-本地应用,点击开发与代理,跳转到ArkStore + +[![Bbbgph.png](https://v1.ax1x.com/2022/10/24/Bbbgph.png)](https://x.imgtu.com/i/Bbbgph) + +2.在ArkStore中点击开发商-应用管理-中心ArkID-应用管理-应用列表-本地应用-创建,来创建应用
+点击创建 +[![Bb8yEt.png](https://v1.ax1x.com/2022/10/24/Bb8yEt.png)](https://x.imgtu.com/i/Bb8yEt) + +创建应用 +[![Bb8NyJ.png](https://v1.ax1x.com/2022/10/24/Bb8NyJ.png)](https://x.imgtu.com/i/Bb8NyJ) + +3.对新创建的应用,点击协议配置,选择OIDC-platform协议,并完成应用接入 + +[![Bb8EoL.png](https://v1.ax1x.com/2022/10/24/Bb8EoL.png)](https://x.imgtu.com/i/Bb8EoL) + +!!! 提示 + OIDC-platform就是OIDC协议,只是该类型的应用可以被整个平台所有租户访问。你可以按照OIDC的接入流程接入即可,只是用户参数中会增加:租户标识。 + +4.点击开发商-SaaS应用-添加应用,接入方式选择OIDC,应用ID输入步骤2中创建的应用ID(可通过点击步骤2中创建应用的编辑按钮来获取)
+点击添加应用 +[![Bb83p7.png](https://v1.ax1x.com/2022/10/24/Bb83p7.png)](https://x.imgtu.com/i/Bb83p7) + +添加应用 +[![Bb8u2B.png](https://v1.ax1x.com/2022/10/24/Bb8u2B.png)](https://x.imgtu.com/i/Bb8u2B) + +获取应用ID +[![Bb8qUG.png](https://v1.ax1x.com/2022/10/24/Bb8qUG.png)](https://x.imgtu.com/i/Bb8qUG) + +5.设置价格,详情请见[**价格规则**](../#_2)
+点击设置价格 +[![Bb8l8I.png](https://v1.ax1x.com/2022/10/24/Bb8l8I.png)](https://x.imgtu.com/i/Bb8l8I) + +设置价格 +[![Bb87tV.png](https://v1.ax1x.com/2022/10/24/Bb87tV.png)](https://x.imgtu.com/i/Bb87tV) + +6.提交审核 + +[![Bb8l8I.png](https://v1.ax1x.com/2022/10/24/Bb8l8I.png)](https://x.imgtu.com/i/Bb8l8I) + + +7.龙归审核通过后,应用上架完成,所有ArkID的用户在其应用商店中都能看到该应用。 + +#### 自定义认证协议 + +如果你的应用使用的是其它非标准协议,请联系我们。 + +#### 账密代填 + +如果你的应用暂时无法通过OIDC接入,我们还推荐使用账密代填的方式。 + +创建账密代填应用与创建OIDC应用相似,只需在步骤3中协议配置选择AutoFormFill,同时在步骤4中选择表单代填即可。 + +账密代填并非单点登录的协议,只是会在浏览器中记录对应站点的账号密码,并自动填入并点击登录。 + +如果该网站使用了如验证码等额外的方式,则不适用账密代填。 + +#### 推广链接 + +如果当前应用不支持上述各类情况,你可以仅仅上传一个ArkID的专属推广链接,并记录下该用户后续的注册,登录,付费等行为,用来作为与ArkID分账的依据。 + +通常这种情况,支付会使用内付费的形式,你需要联系我们。 + + +### 私有化部署应用 + +1.在ArkID中点击应用管理-应用列表-本地应用,点击开发与代理,跳转到ArkStore + +[![Bbbgph.png](https://v1.ax1x.com/2022/10/24/Bbbgph.png)](https://x.imgtu.com/i/Bbbgph) + +2.在Arkstore中点击开发商-应用管理-私有部署应用-添加应用,上传Helm Charts的tgz格式压缩包
+点击创建 +[![Bb8gpP.png](https://v1.ax1x.com/2022/10/24/Bb8gpP.png)](https://x.imgtu.com/i/Bb8gpP) + +上传文件 +[![Bb8hGw.png](https://v1.ax1x.com/2022/10/24/Bb8hGw.png)](https://x.imgtu.com/i/Bb8hGw) + +5.设置价格,详情请见[**价格规则**](../#_2)
+点击设置价格 +[![Bb8Kje.png](https://v1.ax1x.com/2022/10/24/Bb8Kje.png)](https://x.imgtu.com/i/Bb8Kje) + +设置价格 +[![Bb87tV.png](https://v1.ax1x.com/2022/10/24/Bb87tV.png)](https://x.imgtu.com/i/Bb87tV) + +4.提交审核 + +[![Bb8Kje.png](https://v1.ax1x.com/2022/10/24/Bb8Kje.png)](https://x.imgtu.com/i/Bb8Kje) + +7.龙归审核通过后,应用上架完成,所有ArkID的用户在其应用商店中都能看到该应用。 + + diff --git "a/docs/ \345\225\206\344\270\232\345\220\210\344\275\234\346\214\207\345\215\227/ \345\274\200\345\217\221\345\225\206.md" "b/docs/ \345\225\206\344\270\232\345\220\210\344\275\234\346\214\207\345\215\227/ \345\274\200\345\217\221\345\225\206.md" deleted file mode 100644 index 8ef5b7c71..000000000 --- "a/docs/ \345\225\206\344\270\232\345\220\210\344\275\234\346\214\207\345\215\227/ \345\274\200\345\217\221\345\225\206.md" +++ /dev/null @@ -1,101 +0,0 @@ -# ArkID开发商 - -如果您有开发能力,或者独立运营自己的SaaS产品,只通过ArkID账号访问ArkStore,即可成为ArkID开发商。 - -## 开发商条件 - -* 任何个人或企业 -* 有开发自研能力 -* 拥有ArkID账号 -* 访问ArkStore - -开发商可以向ArkID生态提供生产两类产品:**ArkID插件** 与 **SaaS应用**。 - -## 创建插件或应用 - -### ArkID插件 - -1. 访问ArkStore -1. 点击开发商-仓库管理,上传插件所在的github仓库或者zip压缩包 - - !!! 注意 - ArkStore会通过解析插件下config.json文件,来生成插件记录 - -2. 设置价格,详情请见[价格规则](#_3) -3. 提交审批 -4. 通过即上架完成,所有ArkID的用户在其插件商店中都能看到该插件了。 - -### SaaS应用 - -#### OIDC - -1. 访问官方中心ArkID。 -1. 创建应用,选择OIDC-platform协议,并完成应用接入。 - - !!! 提示 - OIDC-platform就是OIDC协议,只是该类型的应用可以被整个平台所有租户访问。你可以按照OIDC的接入流程接入即可,只是用户参数中会增加:租户标识。 - -2. 访问ArkStore -3. 点击开发商-应用管理,创建应用,绑定在ArkID中创建的应用。 -4. 设置价格,详情请见[价格规则](#_3) -5. 提交审核 -6. 通过即上架完成,所有ArkID的用户在其应用商店中都能看到该应用了。 - -#### 自定义认证协议 - -如果你的应用使用的是其它非标准协议,请联系我们。 - -#### 账密代填 - -如果你的应用暂时无法通过OIDC接入,我们还推荐使用账密代填的方式。 - -账密代填并非单点登录的协议,只是会在浏览器中记录对应站点的账号密码,并自动填入并点击登录。 - -如果该网站使用了如验证码等额外的方式,则不适用账密代填。 - -#### 推广链接 - -如果当前应用不支持上述各类情况,你可以仅仅上传一个ArkID的专属推广链接,并记录下该用户后续的注册,登录,付费等行为,用来作为与ArkID分账的依据。 - -通常这种情况,支付会使用内付费的形式,你需要联系我们。 - -## 价格规则 - -### 商店支付 - -插件只能选择商店支付的方式,应用可以是商店支付或者应用内支付的方式。 - -无论是插件还是应用,开发商都可以设置价格。 - -价格分两类:**购买价格** 与 **租赁价格**,两种价格的组成是相同的。 - -价格包含两个元素:**市场指导价** 与 **成本折扣** - -市场指导价的单位为: x元/y人/z天 - -* 当x=0,则被视为免费 -* 当y=0,则被认为不限人数 -* 当z=0,则被认为不限天数 - -成本折扣可以为0-1之间的任何一个两位小数。 - -!!! 注意 - 每成交一个产品,开发商实际获得的收益为:市场指导价 * 成本折扣 - -价格可以设置多条,以满足不同的定价策略。 - -### 应用内支付 - -你的应用可以不通过商店支付,而是用户自行在你的应用内支付页面完成支付。 - -这种情况,需要你跟龙归签署相关合作协议,并商定好分成比例,定期与龙归结算即可。 - -## 订单记录 - -在ArkStore中,点击 费用中心-开发商订单 即可查看 - -## 提现 - -在ArkStore中,点击 费用中心-提现申请 即可发起申请,后台审核完后会打款。 - -微信分账功能正在接入。 \ No newline at end of file diff --git "a/docs/ \345\225\206\344\270\232\345\220\210\344\275\234\346\214\207\345\215\227/index.md" "b/docs/ \345\225\206\344\270\232\345\220\210\344\275\234\346\214\207\345\215\227/index.md" index 9a12d2718..23fda81f7 100644 --- "a/docs/ \345\225\206\344\270\232\345\220\210\344\275\234\346\214\207\345\215\227/index.md" +++ "b/docs/ \345\225\206\344\270\232\345\220\210\344\275\234\346\214\207\345\215\227/index.md" @@ -4,7 +4,7 @@ 您可以通过以下三种方式来参与并共建ArkID生态: -* 成为[**ArkID开发商**](./%20%20开发商/) +* 成为[**ArkID开发商**](./%20%20ArkID开发商/) * 成为[**ArkID代理商**](./%20代理商/) * 成为[**ArkID运营商**](./%20运营商/) diff --git a/extension_root/com_longgui_auto_form_fill/arkid_chrome_extension.zip b/extension_root/com_longgui_auto_form_fill/arkid_chrome_extension.zip index 565f89152..b3dba7065 100644 Binary files a/extension_root/com_longgui_auto_form_fill/arkid_chrome_extension.zip and b/extension_root/com_longgui_auto_form_fill/arkid_chrome_extension.zip differ