Skip to content

Commit

Permalink
Merge pull request #775 from longguikeji/loopbing_2.5
Browse files Browse the repository at this point in the history
更新了app创建逻辑
  • Loading branch information
hanbinloop authored Apr 19, 2022
2 parents b65b982 + 202885e commit c92835c
Show file tree
Hide file tree
Showing 79 changed files with 5,730 additions and 129 deletions.
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

0 comments on commit c92835c

Please sign in to comment.