Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

更新了app创建逻辑 #775

Merged
merged 13 commits into from
Apr 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ requests = "*"
celery = "*"
redis = "*"
mysqlclient = "*"
oauth2_provider = "*"
toml = "*"
aliyunsdkcore = "*"
jwcrypto = "*"
oauthlib = "*"
mkdocstrings = "*"
mkdocs = "*"
mkdocs-material = "*"
Expand Down
2 changes: 1 addition & 1 deletion api/v1/views/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from . import loginpage, auth, extension_config, register
from . import loginpage, auth, extension_config, register, app
49 changes: 49 additions & 0 deletions api/v1/views/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from ninja import Schema
from pydantic import Field
from arkid.core.api import api
from arkid.core.models import App
from django.db import transaction
from arkid.core.translation import gettext_default as _
from arkid.extension.models import TenantExtensionConfig
from arkid.core.event import Event, register_event, dispatch_event
from arkid.core.extension.app_protocol import create_app_protocol_extension_config_schema
from arkid.core.event import CREATE_APP, UPDATE_APP, DELETE_APP

register_event(CREATE_APP, _('create app','创建应用'))
register_event(UPDATE_APP, _('update app','修改应用'))
register_event(DELETE_APP, _('delete app','删除应用'))

class AppConfigSchemaIn(Schema):
pass

create_app_protocol_extension_config_schema(
AppConfigSchemaIn,
)


class AppConfigSchemaOut(Schema):
config_id: str


@transaction.atomic
@api.post("/{tenant_id}/app/", response=AppConfigSchemaOut, auth=None)
def create_app_config(request, tenant_id: str, data: AppConfigSchemaIn):
tenant = request.tenant
# 事件分发
results = dispatch_event(Event(tag=CREATE_APP, tenant=tenant, request=request, data=data))
for func, (result, extension) in results:
if result:
# 创建config
config = extension.create_tenant_config(tenant, data.config.data.dict())
# 创建app
app = App()
app.name = data.name
app.url = data.url
app.logo = data.logo
app.type = data.app_type
app.description = data.description
app.config = config
app.tenant_id = tenant_id
app.save()
break
return {"app_id": app.id.hex}
2 changes: 1 addition & 1 deletion api/v1/views/loginpage.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from arkid.core.event import Event, register_event, dispatch_event
from arkid.core.api import api, operation
from arkid.core.models import Tenant
from arkid.core.extension import AuthFactorExtension
from arkid.core.extension.auth_factor import AuthFactorExtension
from arkid.core.translation import gettext_default as _
from arkid.core.event import CREATE_LOGIN_PAGE_AUTH_FACTOR, CREATE_LOGIN_PAGE_RULES

Expand Down
16 changes: 15 additions & 1 deletion arkid/core/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
from django.contrib import admin
from .models import(
User, UserGroup,
App, AppGroup, Permission,
Approve, ExpiringToken, TenantConfig,
)

# admin.site.register(Tenant)
admin.site.register(User)
admin.site.register(UserGroup)
admin.site.register(App)
admin.site.register(AppGroup)
admin.site.register(Permission)
admin.site.register(Approve)
admin.site.register(ExpiringToken)
admin.site.register(TenantConfig)

# Register your models here.
3 changes: 3 additions & 0 deletions arkid/core/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,6 @@ def unlisten_event(tag, func, **kwargs):
# events
CREATE_LOGIN_PAGE_AUTH_FACTOR = 'CREATE_LOGIN_PAGE_AUTH_FACTOR'
CREATE_LOGIN_PAGE_RULES = 'CREATE_LOGIN_PAGE_RULES'
CREATE_APP = 'CREATE_APP'
UPDATE_APP = 'UPDATE_APP'
DELETE_APP = 'DELETE_APP'
126 changes: 4 additions & 122 deletions arkid/core/extension.py → arkid/core/extension/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ def register_front_pages(self, page):
core_page.register_front_pages(page)
self.front_pages.append(page)

def register_config_schema(self, schema, package=None):
def register_config_schema(self, schema, package=None, **kwargs):
# class XxSchema(Schema):
# config: schema
# package: Literal[package or self.package] # type: ignore
Expand All @@ -207,7 +207,7 @@ def register_config_schema(self, schema, package=None):
fields=['id'],
custom_fields=[
("package", Literal[package or self.package], Field()), # type: ignore
("data", schema, Field())
("config", schema, Field())
],
)
config_schema_map[package or self.package] = new_schema
Expand All @@ -223,11 +223,11 @@ def get_config_by_id(self, id):
return TenantExtensionConfig.objects.get(id=id)

def update_tenant_config(self, id, config):
TenantExtensionConfig.objects.get(id=id).update(config=config)
return TenantExtensionConfig.objects.get(id=id).update(config=config)

def create_tenant_config(self, tenant, config):
ext = ExtensionModel.objects.filter(package=self.package, version=self.version).first()
TenantExtensionConfig.objects.create(tenant=tenant, extension=ext, config=config)
return TenantExtensionConfig.objects.create(tenant=tenant, extension=ext, config=config)

def load(self):
self.migrate_extension()
Expand All @@ -253,121 +253,3 @@ def unload(self):
if not core_translation.extension_lang_maps[self.lang_code]:
core_translation.extension_lang_maps.pop(self.lang_code)
core_translation.lang_maps = core_translation.reset_lang_maps()


class AuthFactorExtension(Extension):
LOGIN = 'login'
REGISTER = 'register'
RESET_PASSWORD = 'password'

def load(self):
super().load()
self.auth_event_tag = self.register_event('auth', '认证')
self.listen_event(self.auth_event_tag, self.authenticate)
self.register_event_tag = self.register_event('register', '注册')
self.listen_event(self.register_event_tag, self.register)
self.password_event_tag = self.register_event('password', '重置密码')
self.listen_event(self.password_event_tag, self.reset_password)
self.listen_event(core_event.CREATE_LOGIN_PAGE_AUTH_FACTOR, self.create_response)

@abstractmethod
def authenticate(self, event, **kwargs):
pass

def auth_success(self, user):
return user

def auth_failed(self, event, data):
core_event.remove_event_id(event)
core_event.break_event_loop(data)

@abstractmethod
def register(self, event, **kwargs):
pass

@abstractmethod
def reset_password(self, event, **kwargs):
pass

def create_response(self, event, **kwargs):
self.data = {
self.LOGIN: {
'forms':[],
'bottoms':[],
'expand':{},
},
self.REGISTER: {
'forms':[],
'bottoms':[],
'expand':{},
},
self.RESET_PASSWORD: {
'forms':[],
'bottoms':[],
'expand':{},
},
}
configs = self.get_tenant_configs(event.tenant)
for config in configs:
if config.config.get("login_enabled"):
self.create_login_page(event, config)
if config.config.get("register_enabled"):
self.create_register_page(event, config)
if config.config.get("reset_password_enabled"):
self.create_password_page(event, config)
self.create_other_page(event, config)
return self.data

def add_page_form(self, config, page_name, label, items, submit_url=None, submit_label=None):
default = {
"login": ("登录", f"/api/v1/auth/?tenant=tenant_id&event_tag={self.auth_event_tag}"),
"register": ("登录", f"/api/v1/register/?tenant=tenant_id&event_tag={self.register_event_tag}"),
"password": ("登录", f"/api/v1/reset_password/?tenant=tenant_id&event_tag={self.password_event_tag}"),
}
if not submit_label:
submit_label, useless = default.get(page_name)
if not submit_url:
useless, submit_url = default.get(page_name)

items.append({"type": "hidden", "name": "config_id", "value": config.id})
self.data[page_name]['forms'].append({
'label': label,
'items': items,
'submit': {'label': submit_label, 'http': {'url': submit_url, 'method': "post"}}
})

def add_page_bottoms(self, page_name, bottoms):
self.data[page_name]['bottoms'].append(bottoms)

def add_page_extend(self, page_name, buttons, title=None):
if not self.data[page_name].get('extend'):
self.data[page_name]['extend'] = {}

self.data[page_name]['extend']['title'] = title
self.data[page_name]['extend']['buttons'].append(buttons)

@abstractmethod
def create_login_page(self, event, config):
pass

@abstractmethod
def create_register_page(self, event, config):
pass

@abstractmethod
def create_password_page(self, event, config):
pass

@abstractmethod
def create_other_page(self, event, config):
pass

def get_current_config(self, event):
config_id = event.request.POST.get('config_id')
return self.get_config_by_id(config_id)


class BaseAuthFactorSchema(Schema):
login_enabled: bool = Field(default=True, title=_('login_enabled', '启用登录'))
register_enabled: bool = Field(default=True, title=_('register_enabled', '启用注册'))
reset_password_enabled: bool = Field(default=True, title=_('reset_password_enabled', '启用重置密码'))
91 changes: 91 additions & 0 deletions arkid/core/extension/app_protocol.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
from ninja import Schema
from pydantic import Field
from typing import Optional
from abc import abstractmethod
from typing import Union, Literal
from ninja.orm import create_schema
from arkid.core.extension import Extension
from arkid.extension.models import TenantExtensionConfig
from arkid.core.translation import gettext_default as _
from arkid.core.models import App
from arkid.core import api as core_api, event as core_event

app_protocol_schema_map = {}

def create_app_protocol_extension_config_schema(schema_cls, **field_definitions):
"""创建应用协议类插件配置的Schema

schema_cls只接受一个空定义的Schema
Examples:
>>> from ninja import Schema
>>> from pydantic import Field
>>> class ExampleExtensionConfigSchema(Schema):
>>> pass
>>> create_app_protocol_extension_config_schema(
>>> ExampleExtensionConfigSchema,
>>> field_name=( field_type, Field(...) )
>>> )

Args:
schema_cls (ninja.Schema): 需要创建的Schema class
field_definitions (Any): 任意数量的field,格式为: field_name=(field_type, Field(...))
"""
for schema in app_protocol_schema_map.values():
core_api.add_fields(schema, **field_definitions)
core_api.add_fields(schema_cls, __root__=(Union[tuple(app_protocol_schema_map.values())], Field(discriminator='app_type'))) # type: ignore


class AppProtocolExtension(Extension):

app_type_map = []

def load(self):
super().load()

self.listen_event(core_event.CREATE_APP, self.filter_event_handler)
self.listen_event(core_event.UPDATE_APP, self.filter_event_handler)
self.listen_event(core_event.DELETE_APP, self.filter_event_handler)


def register_config_schema(self, schema, app_type, package=None,**kwargs):
# 父类
super().register_config_schema(schema, package, **kwargs)

# app_type = kwargs.get('app_type', None)
# if app_type is None:
# raise Exception('')
new_schema = create_schema(App,
name=self.package+'_config',
exclude=['is_del', 'is_active', 'updated', 'created', 'tenant', 'secret'],
custom_fields=[
("app_type", Literal[app_type], Field()),
("config", schema, Field())
],
)
app_protocol_schema_map[app_type] = new_schema
self.app_type_map.append(app_type)
#

def filter_event_handler(self, event, **kwargs):
if event.data.app_type in self.app_type_map:
if event.tag == core_event.CREATE_APP:
return self.create_app(event, data.config)
elif event.tag == core_event.UPDATE_APP:
return self.update_app(event, data.config)
elif event.tag == core_event.DELETE_APP:
return self.delete_app(event, data.config)
return False


@abstractmethod
def create_app(self, event, config):
pass

@abstractmethod
def update_app(self, event, config):
pass

@abstractmethod
def delete_app(self, event, config):
pass

Loading