first commit
This commit is contained in:
commit
b46426abe0
3
json/build.sh
Executable file
3
json/build.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
xls2ui -m ../models -o ../wwwroot rbac *.json
|
||||
51
json/organization.json
Normal file
51
json/organization.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"models_dir": "${HOME}$/py/rbac/models",
|
||||
"output_dir": "${HOME}$/py/sage/wwwroot/_a/organization",
|
||||
"dbname": "sage",
|
||||
"tblname": "organization",
|
||||
"title":"Organization",
|
||||
"params": {
|
||||
"sortby":"orgname",
|
||||
"editor":{
|
||||
"binds":[
|
||||
{
|
||||
"wid":"province_id",
|
||||
"event":"changed",
|
||||
"actiontype":"script",
|
||||
"target":"city_id",
|
||||
"script":"this.loadData({cond:'parentid=\\''+params.province_id+'\\''})"
|
||||
},
|
||||
{
|
||||
"wid":"city_id",
|
||||
"event":"changed",
|
||||
"actiontype":"script",
|
||||
"target":"distinct_id",
|
||||
"script":"this.loadData({cond:'parentid=\\'' + params.city_id+ '\\''})"
|
||||
}
|
||||
]
|
||||
},
|
||||
"browserfields": {
|
||||
"exclouded": ["id"],
|
||||
"alters": {
|
||||
}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id"
|
||||
],
|
||||
"subtables":[
|
||||
{
|
||||
"field":"orgid",
|
||||
"title":"Org. type",
|
||||
"url":"../orgtypes",
|
||||
"subtable":"orgtypes"
|
||||
},
|
||||
{
|
||||
"field":"orgid",
|
||||
"title":"Users",
|
||||
"url":"../users",
|
||||
"subtable":"users"
|
||||
}
|
||||
],
|
||||
"record_toolbar": null
|
||||
}
|
||||
}
|
||||
18
json/orgtypes.json
Normal file
18
json/orgtypes.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"models_dir": "${HOME}$/py/rbac/models",
|
||||
"output_dir": "${HOME}$/py/sage/wwwroot/_a/orgtypes",
|
||||
"dbname": "sage",
|
||||
"tblname": "orgtypes",
|
||||
"title":"Org. type",
|
||||
"params": {
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "orgid"],
|
||||
"cwidth": {}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id",
|
||||
"orgid"
|
||||
],
|
||||
"record_toolbar": null
|
||||
}
|
||||
}
|
||||
21
json/permission.json
Normal file
21
json/permission.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"tblname": "permission",
|
||||
"uitype":"tree",
|
||||
"title":"权限",
|
||||
"params":{
|
||||
"idField":"id",
|
||||
"textField":"path",
|
||||
"sortby":"path",
|
||||
"editable":true,
|
||||
"browserfields":{
|
||||
"alters":{}
|
||||
},
|
||||
"edit_exclouded_fields":[],
|
||||
"parentField":"parentid",
|
||||
"toolbar":{
|
||||
},
|
||||
"binds":[
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
31
json/role.json
Normal file
31
json/role.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"models_dir": "${HOME}$/py/rbac/models",
|
||||
"output_dir": "${HOME}$/py/sage/wwwroot/_a/role",
|
||||
"dbname": "sage",
|
||||
"tblname": "role",
|
||||
"title":"角色",
|
||||
"params": {
|
||||
"sortby":"name",
|
||||
"browserfields": {
|
||||
"exclouded": ["id"],
|
||||
"cwidth": {}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id"
|
||||
],
|
||||
"subtables":[
|
||||
{
|
||||
"field":"roleid",
|
||||
"title":"角色权限",
|
||||
"url":"../rolepermission",
|
||||
"subtable":"rolepermission"
|
||||
},
|
||||
{
|
||||
"field":"roleid",
|
||||
"title":"用户",
|
||||
"url":"../users",
|
||||
"subtable":"users"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
26
json/rolepermission.json
Normal file
26
json/rolepermission.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"models_dir": "${HOME}$/py/rbac/models",
|
||||
"output_dir": "${HOME}$/py/sage/wwwroot/_a/rolepermission",
|
||||
"dbname": "sage",
|
||||
"tblname": "rolepermission",
|
||||
"title":"用户",
|
||||
"params": {
|
||||
"relation":{
|
||||
"outter_field":"permid",
|
||||
"param_field":"roleid"
|
||||
},
|
||||
"noedit":true,
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "roleid"],
|
||||
"alters":{
|
||||
"permid":{
|
||||
"cwidth":60
|
||||
}
|
||||
}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id", "roleid"
|
||||
],
|
||||
"record_toolbar": null
|
||||
}
|
||||
}
|
||||
16
json/userapp.json
Normal file
16
json/userapp.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"tblname": "userapp",
|
||||
"title":"用户",
|
||||
"params": {
|
||||
"confidential_fields":["apikey"],
|
||||
"sortby":"appname",
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "userid"],
|
||||
"alters": {}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id", "userid"
|
||||
],
|
||||
"record_toolbar": null
|
||||
}
|
||||
}
|
||||
17
json/userdepartment.json
Normal file
17
json/userdepartment.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"models_dir": "${HOME}$/py/rbac/models",
|
||||
"output_dir": "${HOME}$/py/sage/wwwroot/_a/userdepartment",
|
||||
"dbname": "sage",
|
||||
"tblname": "userdepartment",
|
||||
"title":"用户",
|
||||
"params": {
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "userid"],
|
||||
"cwidth": {}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id", "userid"
|
||||
],
|
||||
"record_toolbar": null
|
||||
}
|
||||
}
|
||||
14
json/userrole.json
Normal file
14
json/userrole.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"tblname": "userrole",
|
||||
"title":"用户角色管理",
|
||||
"params": {
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "orgid"],
|
||||
"alters": {}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id",
|
||||
"userid"
|
||||
]
|
||||
}
|
||||
}
|
||||
28
json/users.json
Normal file
28
json/users.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"tblname": "users",
|
||||
"title":"用户",
|
||||
"params": {
|
||||
"sortby":"username",
|
||||
"confidential_fields":["password"],
|
||||
"logined_userorgid":"orgid",
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "password", "orgid", "nick_name" ],
|
||||
"cwidth": {}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id", "nick_name", "orgid"
|
||||
],
|
||||
"subtables": [
|
||||
{
|
||||
"field":"userid",
|
||||
"title":"用户角色",
|
||||
"subtable":"userrole"
|
||||
},
|
||||
{
|
||||
"field":"userid",
|
||||
"title":"APIKEY",
|
||||
"subtable":"userapp"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
BIN
models/audit_log.xlsx
Normal file
BIN
models/audit_log.xlsx
Normal file
Binary file not shown.
BIN
models/organization.xlsx
Normal file
BIN
models/organization.xlsx
Normal file
Binary file not shown.
BIN
models/orgtypes.xlsx
Normal file
BIN
models/orgtypes.xlsx
Normal file
Binary file not shown.
BIN
models/permission.xlsx
Normal file
BIN
models/permission.xlsx
Normal file
Binary file not shown.
BIN
models/role.xlsx
Normal file
BIN
models/role.xlsx
Normal file
Binary file not shown.
BIN
models/rolepermission.xlsx
Normal file
BIN
models/rolepermission.xlsx
Normal file
Binary file not shown.
BIN
models/userapp.xlsx
Normal file
BIN
models/userapp.xlsx
Normal file
Binary file not shown.
BIN
models/userdepartment.xlsx
Normal file
BIN
models/userdepartment.xlsx
Normal file
Binary file not shown.
BIN
models/userrole.xlsx
Normal file
BIN
models/userrole.xlsx
Normal file
Binary file not shown.
BIN
models/users.xlsx
Normal file
BIN
models/users.xlsx
Normal file
Binary file not shown.
4
pyproject.toml
Normal file
4
pyproject.toml
Normal file
@ -0,0 +1,4 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=61", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
0
rbac/README.md
Normal file
0
rbac/README.md
Normal file
0
rbac/__init__.py
Normal file
0
rbac/__init__.py
Normal file
9
rbac/audit_log.py
Normal file
9
rbac/audit_log.py
Normal file
@ -0,0 +1,9 @@
|
||||
from sqlor.dbpools import DBPools
|
||||
from appPublic.dictObject import DictObject
|
||||
from appPublic.uniqueID import getID
|
||||
import json
|
||||
|
||||
def write_audit_log(sor, request):
|
||||
id = getID()
|
||||
params_kw = get_params_kw(request)
|
||||
|
||||
176
rbac/check_perm.py
Normal file
176
rbac/check_perm.py
Normal file
@ -0,0 +1,176 @@
|
||||
import time
|
||||
|
||||
from aiohttp import BasicAuth
|
||||
from sqlor.dbpools import DBPools
|
||||
from appPublic.registerfunction import RegisterFunction
|
||||
from appPublic.rc4 import password, unpassword
|
||||
from appPublic.jsonConfig import getConfig
|
||||
from appPublic.log import debug, exception
|
||||
from appPublic.dictObject import DictObject
|
||||
from appPublic.timeUtils import curDateString
|
||||
from appPublic.uniqueID import getID
|
||||
from ahserver.auth_api import AuthAPI, user_login
|
||||
from ahserver.globalEnv import password_encode
|
||||
from ahserver.serverenv import ServerEnv, get_serverenv, set_serverenv
|
||||
|
||||
async def get_user_roles(userid):
|
||||
sql = "select concat(b.orgtypeid, '.', b.name) as name from userrole a, role b where a.userid=${userid}$ and a.roleid = b.id"
|
||||
db = DBPools()
|
||||
roles = []
|
||||
dbname = await get_dbname()
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
recs = await sor.sqlExe(sql, {'userid':userid})
|
||||
if len(recs) < 1:
|
||||
return roles
|
||||
for r in recs:
|
||||
roles.append(r.name)
|
||||
return roles
|
||||
|
||||
async def create_org(sor, ns, orgtypes=[]):
|
||||
await sor.C('organization', ns)
|
||||
if orgtypes == []:
|
||||
orgtypes = ['customer']
|
||||
if 'customer' not in orgtypes:
|
||||
orgtypes.append('customer')
|
||||
for ot in orgtypes:
|
||||
otns = {
|
||||
'id':getID(),
|
||||
'orgid':ns.id,
|
||||
'orgtypeid':ot
|
||||
}
|
||||
await sor.C('orgtypes', otns)
|
||||
|
||||
async def create_user(sor, ns, roles=[]):
|
||||
"""
|
||||
role format:
|
||||
{
|
||||
orgtypeid: rr,
|
||||
roles: ['ee', 'bb']
|
||||
}
|
||||
"""
|
||||
await sor.C('users', ns)
|
||||
if roles == []:
|
||||
roles = [
|
||||
{
|
||||
'orgtypeid': 'customer',
|
||||
'roles': [ 'customer']
|
||||
}
|
||||
]
|
||||
for rt in roles:
|
||||
sql = "select * from role where orgtypeid = ${otid}$ and name in ${roles}$)"
|
||||
recs = await sor.sqlExe(sql, {
|
||||
'otid': rt['orgtypeid'],
|
||||
'roles': rt['roles']
|
||||
})
|
||||
for r in recs:
|
||||
await sor.C('userrole', {
|
||||
'id':getID(),
|
||||
'userid':ns.id,
|
||||
'roleid':r.id
|
||||
})
|
||||
|
||||
async def register_user(sor, ns):
|
||||
if ns.password != ns.cfm_password:
|
||||
debug('password not match')
|
||||
return False
|
||||
ns.password = password_encode(ns.password)
|
||||
id = getID()
|
||||
ns.id = id
|
||||
ns.orgid = id
|
||||
ns1 = DictObject(id=id, orgname=ns.username)
|
||||
await create_org(sor, ns1)
|
||||
await create_user(sor, ns)
|
||||
return id
|
||||
|
||||
async def get_dbname():
|
||||
rf = RegisterFunction()
|
||||
dbname = await rf.exe('get_module_dbname', 'rbac')
|
||||
return dbname
|
||||
|
||||
async def checkUserPassword(request, username, password):
|
||||
db = DBPools()
|
||||
dbname = await get_dbname()
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
sql = "select * from users where username=${username}$ and password=${password}$"
|
||||
recs = await sor.sqlExe(sql, {'username':username, 'password':password})
|
||||
if len(recs) < 1:
|
||||
return False
|
||||
await user_login(request, recs[0].id,
|
||||
username=recs[0].username,
|
||||
userorgid=recs[0].orgid)
|
||||
return True
|
||||
return False
|
||||
|
||||
async def basic_auth(sor, auth):
|
||||
auther = BasicAuth('x')
|
||||
m = auther.decode(auth)
|
||||
username = m.login
|
||||
password = password_encode(m.password)
|
||||
sql = "select * from users where username=${username}$ and password=${password}$"
|
||||
recs = await sor.sqlExe(sql, {'username':username,'password':password})
|
||||
if len(recs) < 1:
|
||||
return None
|
||||
return recs[0].id
|
||||
|
||||
async def bearer_auth(sor, auth):
|
||||
# apikey = get_apikey_from_token(auth[7:])
|
||||
apikey = auth[7:]
|
||||
if apikey is None:
|
||||
return None
|
||||
sql = "select * from userapp where apikey=${apikey}$ and expired_date > ${today}$"
|
||||
recs = await sor.sqlExe(sql, {"apikey":apikey, 'today': curDateString()})
|
||||
if len(recs) < 1:
|
||||
return None
|
||||
return recs[0].userid
|
||||
|
||||
async def getAuthenticationUserid(sor, request):
|
||||
auth = request.headers.get('Authentication')
|
||||
if auth is None:
|
||||
return None
|
||||
for h,f in registered_auth_methods.items():
|
||||
if auth.startswith(h):
|
||||
return await f(auth)
|
||||
return None
|
||||
|
||||
async def objcheckperm(obj, request, userid, path):
|
||||
debug(f'check permission: {userid=}, {path=}')
|
||||
sql = """select distinct a.*, c.userid from
|
||||
(select id, path from permission where path=${path}$) a
|
||||
right join
|
||||
rolepermission b on a.id = b.permid
|
||||
right join userrole c on b.roleid = c.roleid
|
||||
where c.userid = ${userid}$
|
||||
"""
|
||||
|
||||
dbname = await get_dbname()
|
||||
db = DBPools()
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
if userid is None:
|
||||
userid = await getAuthenticationUserid(sor, request)
|
||||
perms = await sor.R('permission', {'path':path})
|
||||
if len(perms) == 0:
|
||||
debug(f'{path=} not found in permission, can access')
|
||||
return True
|
||||
if userid is None:
|
||||
debug(f'{userid=} is None, can not access {path=}')
|
||||
return False
|
||||
|
||||
recs = await sor.sqlExe(sql, {'path':path, 'userid':userid})
|
||||
for r in recs:
|
||||
id = r['id']
|
||||
if id is not None:
|
||||
debug(f'{userid=} can access {path=}')
|
||||
return True
|
||||
debug(f'{userid=} has not permission to call {path=}')
|
||||
return False
|
||||
debug(f'error happened {userid}, {path}')
|
||||
return False
|
||||
|
||||
registered_auth_methods = {
|
||||
"Basic ": basic_auth,
|
||||
"Bearer ": bearer_auth
|
||||
}
|
||||
|
||||
def register_auth_method(heading, func):
|
||||
registered_auth_methods[heading] = func
|
||||
|
||||
18
rbac/init.py
Normal file
18
rbac/init.py
Normal file
@ -0,0 +1,18 @@
|
||||
from ahserver.auth_api import AuthAPI
|
||||
from ahserver.serverenv import ServerEnv
|
||||
from rbac.check_perm import objcheckperm, get_user_roles, checkUserPassword, register_user, register_auth_method, create_org, create_user
|
||||
from rbac.set_role_perms import set_role_perm, set_role_perms
|
||||
|
||||
def load_rbac():
|
||||
AuthAPI.checkUserPermission = objcheckperm
|
||||
env = ServerEnv()
|
||||
env.create_org = create_org
|
||||
env.create_user = create_user
|
||||
env.get_user_roles = get_user_roles
|
||||
env.check_user_password = checkUserPassword
|
||||
env.register_user = register_user
|
||||
env.set_role_perm = set_role_perm
|
||||
env.set_role_perms = set_role_perms
|
||||
env.register_auth_method = register_auth_method
|
||||
|
||||
|
||||
71
rbac/set_role_perms.py
Normal file
71
rbac/set_role_perms.py
Normal file
@ -0,0 +1,71 @@
|
||||
import sys
|
||||
import os
|
||||
import asyncio
|
||||
from sqlor.dbpools import DBPools
|
||||
from appPublic.jsonConfig import getConfig
|
||||
from appPublic.uniqueID import getID
|
||||
from appPublic.asynciorun import run
|
||||
|
||||
async def set_role_perm(dbname, module, orgtype, role, tblname):
|
||||
db = DBPools()
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
if '/' in dbname:
|
||||
path = [f'/{module}/{dbname}']
|
||||
else:
|
||||
paths = [
|
||||
f'/{module}/{tblname}',
|
||||
f'/{module}/{tblname}/index.ui',
|
||||
f'/{module}/{tblname}/get_{tblname}.dspy',
|
||||
f'/{module}/{tblname}/add_{tblname}.dspy',
|
||||
f'/{module}/{tblname}/delete_{tblname}.dspy',
|
||||
f'/{module}/{tblname}/update_{tblname}.dspy'
|
||||
]
|
||||
for pat in paths:
|
||||
recs = await sor.R('permission', {'path': pat})
|
||||
if len(recs) == 0:
|
||||
permid = getID()
|
||||
await sor.C('permission', {'id':permid, 'path':pat})
|
||||
else:
|
||||
permid = recs[0].id
|
||||
recs = await sor.R('role', {'orgtypeid':orgtype, 'name':role})
|
||||
if len(recs) == 0:
|
||||
roleid = getID()
|
||||
await sor.C('role', {
|
||||
'id':roleid,
|
||||
'name':role,
|
||||
'orgtypeid':orgtype
|
||||
})
|
||||
else:
|
||||
roleid = recs[0].id
|
||||
await sor.C('rolepermission', {
|
||||
'id':getID(),
|
||||
'roleid':roleid,
|
||||
'permid':permid
|
||||
})
|
||||
print(f'{orgtype=}, {role=}, {tblname=} permission configured')
|
||||
|
||||
async def set_role_perms(dbname, module, orgtype, role, items):
|
||||
for tblname in items:
|
||||
await set_role_perm(dbname, module, orgtype, role, tblname)
|
||||
|
||||
if __name__ == '__main__':
|
||||
async def main():
|
||||
if len(sys.argv) < 6:
|
||||
print(f'{sys.argv[0]} dbname module orgtype role tblname ...\n')
|
||||
sys.exit(1)
|
||||
dbname = sys.argv[1]
|
||||
module = sys.argv[2]
|
||||
orgtype = sys.argv[3]
|
||||
role = sys.argv[4]
|
||||
await set_role_perms(dbname, module, orgtype, role, sys.argv[5:])
|
||||
|
||||
def run(coro):
|
||||
p = '.'
|
||||
config = getConfig(p, {'woridir':p})
|
||||
DBPools(config.databases)
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
loop.run_until_complete(coro())
|
||||
|
||||
run(main)
|
||||
|
||||
1
rbac/version.py
Normal file
1
rbac/version.py
Normal file
@ -0,0 +1 @@
|
||||
__version__ = '0.1.0'
|
||||
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
aiohttp
|
||||
PyJWT
|
||||
191
script/init.py
Normal file
191
script/init.py
Normal file
@ -0,0 +1,191 @@
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
from traceback import format_exc
|
||||
import asyncio
|
||||
from appPublic.uniqueID import getID
|
||||
from appPublic.timeUtils import days_between, strdate_add
|
||||
from appPublic.jsonConfig import getConfig
|
||||
from random import randint, random
|
||||
from appPublic.folderUtils import listFile
|
||||
from appPublic.registerfunction import RegisterFunction
|
||||
|
||||
from sqlor.dbpools import DBPools
|
||||
from rbac.check_perm import mypassword
|
||||
databases = {
|
||||
"sage":{
|
||||
"driver":"aiomysql",
|
||||
"async_mode":True,
|
||||
"coding":"utf8",
|
||||
"maxconn":100,
|
||||
"dbname":"sage",
|
||||
"kwargs":{
|
||||
"user":"test",
|
||||
"db":"sage",
|
||||
"password":"QUZVcXg5V1p1STMybG5Ia6mX9D0v7+g=",
|
||||
"host":"localhost"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async def insert_perm(path):
|
||||
db = DBPools()
|
||||
async with db.sqlorContext('sage') as sor:
|
||||
ns = {
|
||||
'id':getID(),
|
||||
'path':path
|
||||
}
|
||||
await sor.C('permission', ns)
|
||||
|
||||
av_folders = [
|
||||
'/index.ui',
|
||||
'/user.ui',
|
||||
'/menu.ui',
|
||||
'/get_code.dspy',
|
||||
'/top.ui',
|
||||
'/center',
|
||||
'/bottom.ui',
|
||||
'/app_panel.ui',
|
||||
'/bricks',
|
||||
'/imgs',
|
||||
'/login.ui',
|
||||
'/gen_code.dspy',
|
||||
'/code_login.dspy',
|
||||
'/up_login.dspy',
|
||||
'/wechat_login.ui',
|
||||
'/public',
|
||||
'/debug/index.dspy',
|
||||
'/rbac/userpassword_login.ui',
|
||||
'/rbac/userpassword_login.dspy',
|
||||
'/tmp'
|
||||
]
|
||||
|
||||
|
||||
orgtypes = ['owner', 'reseller']
|
||||
type_roles = {
|
||||
'owner':['superuser', 'customer'],
|
||||
'reseller':['admin','operator', 'sale', 'maintainer', 'accountant'],
|
||||
'customer':['admin', 'customer']
|
||||
}
|
||||
|
||||
rbac_tables = [
|
||||
"organization", "orgtypes", "role", "users", "userrole", "rolepermission", "permission",
|
||||
"userapp", "userdepartment" ]
|
||||
|
||||
role_perms = {
|
||||
'superuser': [
|
||||
'/get_code.dspy',
|
||||
'/rbac/add_adminuser.ui',
|
||||
'/rbac/add_adminuser.dspy',
|
||||
'/rbac/role/index.ui',
|
||||
'/rbac/role/get_role.dspy',
|
||||
'/rbac/role/update_role.dspy',
|
||||
'/rbac/role/delete_role.dspy',
|
||||
'/rbac/role/add_role.dspy',
|
||||
'/rbac/permission/add_permission.dspy',
|
||||
'/rbac/permission/delete_permission.dspy',
|
||||
'/rbac/permission/update_permission.dspy',
|
||||
'/rbac/permission/get_permission.dspy',
|
||||
'/rbac/permission/index.ui',
|
||||
'/rbac/rolepermission/add_rolepermission.dspy',
|
||||
'/rbac/rolepermission/delete_rolepermission.dspy',
|
||||
'/rbac/rolepermission/update_rolepermission.dspy',
|
||||
'/rbac/rolepermission/get_rolepermission.dspy',
|
||||
'/rbac/rolepermission/index.ui'
|
||||
],
|
||||
'admin':[
|
||||
'/rbac/users/add_users.dspy',
|
||||
'/rbac/users/delete_users.dspy',
|
||||
'/rbac/users/update_users.dspy',
|
||||
'/rbac/users/get_users.dspy',
|
||||
'/rbac/users/index.ui',
|
||||
'/rbac/userrole/add_userrole.dspy',
|
||||
'/rbac/userrole/delete_userrole.dspy',
|
||||
'/rbac/userrole/update_userrole.dspy',
|
||||
'/rbac/userrole/get_userrole.dspy',
|
||||
'/rbac/userrole/index.ui'
|
||||
]
|
||||
}
|
||||
|
||||
async def init_perms():
|
||||
db = DBPools()
|
||||
async with db.sqlorContext('sage') as sor:
|
||||
for rn, pths in role_perms.items():
|
||||
roles = await sor.sqlExe('select * from role where name=${rn}$', {'rn':rn})
|
||||
if len(roles) == 0:
|
||||
print(f'{rn=} not exists')
|
||||
continue
|
||||
for p in pths:
|
||||
print(f'{rn=} set {p=}')
|
||||
perms = await sor.sqlExe('select * from permission where path=${p}$', {'p':p})
|
||||
if len(perms):
|
||||
await sor.C('rolepermission', {'id':getID(),
|
||||
'roleid':roles[0].id,
|
||||
'permid':perms[0].id})
|
||||
else:
|
||||
print(f'{p} is not exists')
|
||||
|
||||
async def init_rbac(passwd):
|
||||
db = DBPools()
|
||||
async with db.sqlorContext('sage') as sor:
|
||||
recs = await sor.R('organization', {'id':'0'})
|
||||
if len(recs) > 0:
|
||||
return
|
||||
await sor.C('organization',{'id':'0'})
|
||||
await sor.C('orgtypes', {'id':getID(), 'orgid':'0', 'orgtypeid':'owner'})
|
||||
await sor.C('orgtypes', {'id':getID(), 'orgid':'0', 'orgtypeid':'reseller'})
|
||||
roleid = ''
|
||||
for orgtype, roles in type_roles.items():
|
||||
for r in roles:
|
||||
id = getID()
|
||||
await sor.C('role', {'id':id,'orgtypeid':orgtype,'name':r})
|
||||
if orgtype == 'owner' and r == 'superuser':
|
||||
roleid = id
|
||||
ns = {
|
||||
'id':getID(),
|
||||
'username':'superuser',
|
||||
'orgid':'0',
|
||||
'password':passwd
|
||||
}
|
||||
await sor.C('users', ns)
|
||||
ns1 = {
|
||||
'id':getID(),
|
||||
'userid':ns['id'],
|
||||
'roleid':roleid
|
||||
}
|
||||
await sor.C('userrole', ns1)
|
||||
|
||||
async def add_permissions(workdir):
|
||||
root = os.path.join(workdir, 'wwwroot')
|
||||
for f in listFile(root, rescursive=True):
|
||||
cnt = len(root)
|
||||
pth = f[cnt:]
|
||||
print(f'{f=},{root=},{pth=}, {cnt=}')
|
||||
act = True
|
||||
for f in av_folders:
|
||||
if pth.startswith(f):
|
||||
act = False
|
||||
break
|
||||
if act:
|
||||
await insert_perm(pth)
|
||||
|
||||
async def main(workdir, passwd):
|
||||
await add_permissions(workdir)
|
||||
await init_rbac(passwd)
|
||||
await init_perms()
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(prog='RBAC init')
|
||||
parser.add_argument('-w', '--workdir')
|
||||
parser.add_argument('password')
|
||||
args = parser.parse_args()
|
||||
if args.password is None:
|
||||
parser.usage()
|
||||
sys.exit(1)
|
||||
print(f'{args=}')
|
||||
config = getConfig(args.workdir, {'workdir':args.workdir})
|
||||
supassword = mypassword(args.password)
|
||||
DBPools(config.databases)
|
||||
asyncio.get_event_loop().run_until_complete(main(args.workdir, supassword))
|
||||
|
||||
7
script/setroleperms.sh
Normal file
7
script/setroleperms.sh
Normal file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
python -m rbac.set_role_perms sage rbac owner superuser role permission rolepermission user user role organizationorgtypes
|
||||
python -m rbac.set_role_perms sage rbac owner admin user userrole
|
||||
python -m rbac.set_role_perms sage rbac customer admin user userrole
|
||||
python -m rbac.set_role_perms sage rbac reseller admin user userrole
|
||||
|
||||
16
setup.cfg
Normal file
16
setup.cfg
Normal file
@ -0,0 +1,16 @@
|
||||
[metadata]
|
||||
name=rbac
|
||||
version = 1.0.0
|
||||
description = a RBAC user authenticate module
|
||||
author = "yu moqing"
|
||||
author_email = "yumoqing@gmail.com"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
[options]
|
||||
packages = find:
|
||||
requires_python = ">=3.8"
|
||||
install_requires =
|
||||
apppublic
|
||||
sqlor
|
||||
ahserver
|
||||
|
||||
23
wwwroot/add_adminuser.dspy
Normal file
23
wwwroot/add_adminuser.dspy
Normal file
@ -0,0 +1,23 @@
|
||||
if params_kw.get('password') != params_kw.get('chkpassword'):
|
||||
return Error(title='add user error', message='password not match')
|
||||
|
||||
ns = params_kw.copy()
|
||||
ns['id'] = uuid()
|
||||
await rfexe('passowrd', ns)
|
||||
user_orgid = await get_userorgid()
|
||||
ns['orgid'] = user_orgid
|
||||
dbname = await rfexe('get_module_dbname','rbac')
|
||||
db = DBPools()
|
||||
debug(f'{dbname=}')
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
await sor.C('users',ns.copy())
|
||||
uid = ns['id']
|
||||
ns = {
|
||||
'id':uuid(),
|
||||
'userid':uid,
|
||||
'roleid':'admin'
|
||||
}
|
||||
await sor.C('userrole', ns.copy())
|
||||
return UiMesage(title='Success', message='admin user added')
|
||||
return UiError(title='Error', message='Error happened when add admin user')
|
||||
|
||||
36
wwwroot/add_adminuser.ui
Normal file
36
wwwroot/add_adminuser.ui
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"widgettype":"ModalForm",
|
||||
"options":{
|
||||
"cwidth":20,
|
||||
"cheight":30,
|
||||
"title":"增加管理员",
|
||||
"fields":[
|
||||
{
|
||||
"name":"username",
|
||||
"label":"用户名",
|
||||
"uitype":"str"
|
||||
},
|
||||
{
|
||||
"name":"password",
|
||||
"label":"密码",
|
||||
"uitype":"password"
|
||||
},
|
||||
{
|
||||
"name":"chkpassword",
|
||||
"label":"确认密码",
|
||||
"uitype":"password"
|
||||
}
|
||||
]
|
||||
},
|
||||
"binds":[
|
||||
{
|
||||
"wid":"self",
|
||||
"event":"submit",
|
||||
"actiontype":"urlwidget",
|
||||
"target":"self",
|
||||
"options":{
|
||||
"url":"{{entire_url('./add_adminuser.dspy')}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
36
wwwroot/add_provider.dspy
Normal file
36
wwwroot/add_provider.dspy
Normal file
@ -0,0 +1,36 @@
|
||||
ns = params_kw.copy()
|
||||
id = params_kw.id
|
||||
if not id or len(id) > 32:
|
||||
id = uuid()
|
||||
ns['id'] = id
|
||||
db = DBPools()
|
||||
dbname = get_module_dbname('rbac')
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
ownerid = await get_userorgid()
|
||||
await create_org(sor, ns, orgtypes=['customer', 'provider'])
|
||||
if openCustomerAccounts:
|
||||
await openCustomerAccounts(sor, ownerid, orgid)
|
||||
if openProviderAccounts:
|
||||
await openProviderAccounts(sor, ownerid, id)
|
||||
return {
|
||||
"widgettype":"Message",
|
||||
"options":{
|
||||
"user_data":ns,
|
||||
"cwidth":16,
|
||||
"cheight":9,
|
||||
"title":"Add Success",
|
||||
"timeout":3,
|
||||
"message":"ok"
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
"widgettype":"Error",
|
||||
"options":{
|
||||
"title":"Add Error",
|
||||
"cwidth":16,
|
||||
"cheight":9,
|
||||
"timeout":3,
|
||||
"message":"failed"
|
||||
}
|
||||
}
|
||||
26
wwwroot/add_reseller.dspy
Normal file
26
wwwroot/add_reseller.dspy
Normal file
@ -0,0 +1,26 @@
|
||||
ns = params_kw.copy()
|
||||
id = params_kw.id
|
||||
if not id or len(id) > 32:
|
||||
id = uuid()
|
||||
ns['id'] = id
|
||||
db = DBPools()
|
||||
dbname = get_module_dbname('rbac')
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
ownerid = await get_userorgid()
|
||||
await create_org(sor, ns, orgtypes=['customer', 'reseller'])
|
||||
if openCustomerAccounts:
|
||||
await openCustomerAccounts(sor, ownerid, orgid)
|
||||
if openResellerAccounts:
|
||||
await openResellerAccounts(sor, ownerid, id)
|
||||
return UiMessage(title="Success", message="add reseller success")
|
||||
|
||||
return {
|
||||
"widgettype":"Error",
|
||||
"options":{
|
||||
"title":"Add reseller Error",
|
||||
"cwidth":16,
|
||||
"cheight":9,
|
||||
"timeout":3,
|
||||
"message":"failed"
|
||||
}
|
||||
}
|
||||
7
wwwroot/admin_menu.ui
Normal file
7
wwwroot/admin_menu.ui
Normal file
@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"name":"users",
|
||||
"label":"用户管理"
|
||||
"url":"{{entire_url('/rbac/users')}}"
|
||||
}
|
||||
]
|
||||
151
wwwroot/get_provider.dspy
Normal file
151
wwwroot/get_provider.dspy
Normal file
@ -0,0 +1,151 @@
|
||||
ns = params_kw.copy()
|
||||
debug(f'get_organization.dspy:{ns=}')
|
||||
if not ns.get('page'):
|
||||
ns['page'] = 1
|
||||
if not ns.get('sort'):
|
||||
ns['sort'] = 'orgname'
|
||||
|
||||
sql = '''select a.*, b.province_id_text, c.city_id_text, d.distinct_id_text
|
||||
from (select x.* from (select * from organization where 1=1 [[filterstr]]) x, orgtypes y where x.id = y.orgid and y.orgtypeid='provider') a left join (select k as province_id,
|
||||
v as province_id_text from appcodes_kv where parentid='chnaddr') b on a.province_id = b.province_id left join (select k as city_id,
|
||||
v as city_id_text from appcodes_kv where parentid='city') c on a.city_id = c.city_id left join (select k as distinct_id,
|
||||
v as distinct_id_text from appcodes_kv where parentid='distinct') d on a.distinct_id = d.distinct_id'''
|
||||
|
||||
filterjson = params_kw.get('data_filter')
|
||||
if not filterjson:
|
||||
fields = [ f['name'] for f in [
|
||||
{
|
||||
"name": "id",
|
||||
"title": "机构编码",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "orgname",
|
||||
"title": "机构名称",
|
||||
"type": "str",
|
||||
"length": 100
|
||||
},
|
||||
{
|
||||
"name": "alias_name",
|
||||
"title": "机构别名",
|
||||
"type": "str",
|
||||
"length": 100
|
||||
},
|
||||
{
|
||||
"name": "contactor",
|
||||
"title": "联系人",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "contactor_phone",
|
||||
"title": "联系人电话",
|
||||
"type": "str",
|
||||
"length": 100
|
||||
},
|
||||
{
|
||||
"name": "province_id",
|
||||
"title": "所在省id",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "city_id",
|
||||
"title": "所在城市id",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "distinct_id",
|
||||
"title": "所在地区id",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "emailaddress",
|
||||
"title": "邮箱",
|
||||
"type": "str",
|
||||
"length": 256
|
||||
},
|
||||
{
|
||||
"name": "address",
|
||||
"title": "地址",
|
||||
"type": "str",
|
||||
"length": 400
|
||||
},
|
||||
{
|
||||
"name": "main_business",
|
||||
"title": "主营业务描述",
|
||||
"type": "str",
|
||||
"length": 1000
|
||||
},
|
||||
{
|
||||
"name": "orgcode",
|
||||
"title": "组织结构代码",
|
||||
"type": "str",
|
||||
"length": 100,
|
||||
"comments": "个人客户存身份证"
|
||||
},
|
||||
{
|
||||
"name": "license_img",
|
||||
"title": "营业执照",
|
||||
"type": "str",
|
||||
"length": 400,
|
||||
"comments": "个人客户存身份证照片"
|
||||
},
|
||||
{
|
||||
"name": "id_img",
|
||||
"title": "身份证",
|
||||
"type": "str",
|
||||
"length": 400,
|
||||
"comments": "个人客户存身份证背面照片"
|
||||
},
|
||||
{
|
||||
"name": "parentid",
|
||||
"title": "父机构id",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "org_type",
|
||||
"title": "机构类型",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"comments": "0:业主机构;1:分销商;2:公司客户;3:个人客户;4:供应商"
|
||||
},
|
||||
{
|
||||
"name": "accountid",
|
||||
"title": "账号",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
}
|
||||
] ]
|
||||
filterjson = default_filterjson(fields, ns)
|
||||
filterdic = ns.copy()
|
||||
filterdic['filterstr'] = ''
|
||||
filterdic['userorgid'] = '${userorgid}$'
|
||||
filterdic['userid'] = '${userid}$'
|
||||
if filterjson:
|
||||
dbf = DBFilter(filterjson)
|
||||
conds = dbf.gen(ns)
|
||||
if conds:
|
||||
ns.update(dbf.consts)
|
||||
conds = f' and {conds}'
|
||||
filterdic['filterstr'] = conds
|
||||
ac = ArgsConvert('[[', ']]')
|
||||
vars = ac.findAllVariables(sql)
|
||||
NameSpace = {v:'${' + v + '}$' for v in vars if v != 'filterstr' }
|
||||
filterdic.update(NameSpace)
|
||||
sql = ac.convert(sql, filterdic)
|
||||
|
||||
debug(f'{sql=}')
|
||||
db = DBPools()
|
||||
dbname = get_module_dbname('rbac')
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
r = await sor.sqlPaging(sql, ns)
|
||||
return r
|
||||
return {
|
||||
"total":0,
|
||||
"rows":[]
|
||||
}
|
||||
151
wwwroot/get_reseller.dspy
Normal file
151
wwwroot/get_reseller.dspy
Normal file
@ -0,0 +1,151 @@
|
||||
ns = params_kw.copy()
|
||||
debug(f'get_organization.dspy:{ns=}')
|
||||
if not ns.get('page'):
|
||||
ns['page'] = 1
|
||||
if not ns.get('sort'):
|
||||
ns['sort'] = 'orgname'
|
||||
|
||||
sql = '''select a.*, b.province_id_text, c.city_id_text, d.distinct_id_text
|
||||
from (select x.* from (select * from organization where 1=1 [[filterstr]]) x, orgtypes y where x.id = y.orgid and y.orgtypeid='reseller') a left join (select k as province_id,
|
||||
v as province_id_text from appcodes_kv where parentid='chnaddr') b on a.province_id = b.province_id left join (select k as city_id,
|
||||
v as city_id_text from appcodes_kv where parentid='city') c on a.city_id = c.city_id left join (select k as distinct_id,
|
||||
v as distinct_id_text from appcodes_kv where parentid='distinct') d on a.distinct_id = d.distinct_id'''
|
||||
|
||||
filterjson = params_kw.get('data_filter')
|
||||
if not filterjson:
|
||||
fields = [ f['name'] for f in [
|
||||
{
|
||||
"name": "id",
|
||||
"title": "机构编码",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "orgname",
|
||||
"title": "机构名称",
|
||||
"type": "str",
|
||||
"length": 100
|
||||
},
|
||||
{
|
||||
"name": "alias_name",
|
||||
"title": "机构别名",
|
||||
"type": "str",
|
||||
"length": 100
|
||||
},
|
||||
{
|
||||
"name": "contactor",
|
||||
"title": "联系人",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "contactor_phone",
|
||||
"title": "联系人电话",
|
||||
"type": "str",
|
||||
"length": 100
|
||||
},
|
||||
{
|
||||
"name": "province_id",
|
||||
"title": "所在省id",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "city_id",
|
||||
"title": "所在城市id",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "distinct_id",
|
||||
"title": "所在地区id",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "emailaddress",
|
||||
"title": "邮箱",
|
||||
"type": "str",
|
||||
"length": 256
|
||||
},
|
||||
{
|
||||
"name": "address",
|
||||
"title": "地址",
|
||||
"type": "str",
|
||||
"length": 400
|
||||
},
|
||||
{
|
||||
"name": "main_business",
|
||||
"title": "主营业务描述",
|
||||
"type": "str",
|
||||
"length": 1000
|
||||
},
|
||||
{
|
||||
"name": "orgcode",
|
||||
"title": "组织结构代码",
|
||||
"type": "str",
|
||||
"length": 100,
|
||||
"comments": "个人客户存身份证"
|
||||
},
|
||||
{
|
||||
"name": "license_img",
|
||||
"title": "营业执照",
|
||||
"type": "str",
|
||||
"length": 400,
|
||||
"comments": "个人客户存身份证照片"
|
||||
},
|
||||
{
|
||||
"name": "id_img",
|
||||
"title": "身份证",
|
||||
"type": "str",
|
||||
"length": 400,
|
||||
"comments": "个人客户存身份证背面照片"
|
||||
},
|
||||
{
|
||||
"name": "parentid",
|
||||
"title": "父机构id",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "org_type",
|
||||
"title": "机构类型",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"comments": "0:业主机构;1:分销商;2:公司客户;3:个人客户;4:供应商"
|
||||
},
|
||||
{
|
||||
"name": "accountid",
|
||||
"title": "账号",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
}
|
||||
] ]
|
||||
filterjson = default_filterjson(fields, ns)
|
||||
filterdic = ns.copy()
|
||||
filterdic['filterstr'] = ''
|
||||
filterdic['userorgid'] = '${userorgid}$'
|
||||
filterdic['userid'] = '${userid}$'
|
||||
if filterjson:
|
||||
dbf = DBFilter(filterjson)
|
||||
conds = dbf.gen(ns)
|
||||
if conds:
|
||||
ns.update(dbf.consts)
|
||||
conds = f' and {conds}'
|
||||
filterdic['filterstr'] = conds
|
||||
ac = ArgsConvert('[[', ']]')
|
||||
vars = ac.findAllVariables(sql)
|
||||
NameSpace = {v:'${' + v + '}$' for v in vars if v != 'filterstr' }
|
||||
filterdic.update(NameSpace)
|
||||
sql = ac.convert(sql, filterdic)
|
||||
|
||||
debug(f'{sql=}')
|
||||
db = DBPools()
|
||||
dbname = get_module_dbname('rbac')
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
r = await sor.sqlPaging(sql, ns)
|
||||
return r
|
||||
return {
|
||||
"total":0,
|
||||
"rows":[]
|
||||
}
|
||||
121
wwwroot/user/login.ui
Normal file
121
wwwroot/user/login.ui
Normal file
@ -0,0 +1,121 @@
|
||||
{
|
||||
"id":"login_window",
|
||||
"widgettype":"PopupWindow",
|
||||
"options":{
|
||||
"auto_open":true,
|
||||
"anthor":"cc",
|
||||
"cwidth":22,
|
||||
"cheight":19
|
||||
},
|
||||
"subwidgets":[
|
||||
{
|
||||
"widgettype":"TabPanel",
|
||||
"options":{
|
||||
"tab_wide":"auto",
|
||||
"height":"100%",
|
||||
"width":"100%",
|
||||
"tab_pos":"top",
|
||||
"items":[
|
||||
{
|
||||
"name":"userpasswd",
|
||||
"label":"用户密码",
|
||||
"content":{
|
||||
"widgettype":"Form",
|
||||
"options":{
|
||||
"cols":1,
|
||||
"fields":[
|
||||
{
|
||||
"name":"username",
|
||||
"label":"用户名",
|
||||
"uitype":"str"
|
||||
},
|
||||
{
|
||||
"name":"password",
|
||||
"label":"密码",
|
||||
"uitype":"password"
|
||||
}
|
||||
]
|
||||
},
|
||||
"binds":[
|
||||
{
|
||||
"wid":"self",
|
||||
"event":"submit",
|
||||
"actiontype":"urlwidget",
|
||||
"target":"self",
|
||||
"options":{
|
||||
"method":"POST",
|
||||
"url":"{{entire_url('up_login.dspy')}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name":"checkcode",
|
||||
"label":"手机验证码",
|
||||
"content":{
|
||||
"widgettype":"Form",
|
||||
"options":{
|
||||
"toolbar":{
|
||||
"tools":[
|
||||
{
|
||||
"name":"gen_code",
|
||||
"label":"发送验证码"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description":"限中国国内手机",
|
||||
"fields":[
|
||||
{
|
||||
"name":"cell_no",
|
||||
"label":"手机号",
|
||||
"uitype":"str"
|
||||
},{
|
||||
"name":"codeid",
|
||||
"uitype":"hide",
|
||||
"value":"{{uuid()}}"
|
||||
},{
|
||||
"name":"check_code",
|
||||
"uitype":"str"
|
||||
}
|
||||
]
|
||||
},
|
||||
"binds":[
|
||||
{
|
||||
"wid":"self",
|
||||
"event":"gen_code",
|
||||
"actiontype":"urlwidget",
|
||||
"datawidget":"self",
|
||||
"datamethod":"getValue",
|
||||
"target":"self",
|
||||
"options":{
|
||||
"url":"{{entire_url('gen_code.dspy')}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"wid":"self",
|
||||
"event":"submit",
|
||||
"actiontype":"urlwidget",
|
||||
"target":"self",
|
||||
"options":{
|
||||
"url":"{{entire_url('code_login.dspy')}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name":"wechat",
|
||||
"label":"微信",
|
||||
"content":{
|
||||
"widgettype":"urlwidget",
|
||||
"options":{
|
||||
"url":"{{entire_url('wechat_login.ui')}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
23
wwwroot/user/logout.dspy
Normal file
23
wwwroot/user/logout.dspy
Normal file
@ -0,0 +1,23 @@
|
||||
await forget_user()
|
||||
return {
|
||||
"widgettype":"Message",
|
||||
"options":{
|
||||
"title":"Message",
|
||||
"message":"logout success",
|
||||
"auto_open":true,
|
||||
"anthor":"cc",
|
||||
"timeout":3
|
||||
},
|
||||
"binds":[
|
||||
{
|
||||
"wid":"self",
|
||||
"event":"dismissed",
|
||||
"actiontype":"urlwidget",
|
||||
"target":"window",
|
||||
"options":{
|
||||
"url":entire_url('/index.ui')
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
27
wwwroot/user/myrole.ui
Normal file
27
wwwroot/user/myrole.ui
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"widgettype":"VBox",
|
||||
"options":{
|
||||
"height":"100%"
|
||||
},
|
||||
"subwidgets":[
|
||||
{
|
||||
"widgettype":"Title4",
|
||||
"options":{
|
||||
"otext":"我的角色",
|
||||
"i18n":true
|
||||
}
|
||||
},
|
||||
{% set roles = get_user_roles(get_user()) %}
|
||||
{% for role in roles %}
|
||||
{
|
||||
"widgettype":"Text",
|
||||
"options":{
|
||||
"text":"{{role}}"
|
||||
}
|
||||
},
|
||||
{% endfor %}
|
||||
{
|
||||
"oops":true
|
||||
}
|
||||
]
|
||||
}
|
||||
10
wwwroot/user/register.dspy
Normal file
10
wwwroot/user/register.dspy
Normal file
@ -0,0 +1,10 @@
|
||||
debug(f'{params_kw=}')
|
||||
db = DBPools()
|
||||
dbname = await rfexe('get_module_dbname', 'sage')
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
orgid = await register_user(sor, params_kw)
|
||||
if get_owner_orgid and openCustomerAccounts:
|
||||
ownerid = await get_owner_orgid(sor, orgid)
|
||||
await openCustomerAccounts(sor, ownerid, orgid)
|
||||
return UiMessage(title="Success", message="register success")
|
||||
return UiError(title='Error', message="register failed")
|
||||
80
wwwroot/user/register.ui
Normal file
80
wwwroot/user/register.ui
Normal file
@ -0,0 +1,80 @@
|
||||
{
|
||||
"widgettype":"PopupWindow",
|
||||
"options":{
|
||||
"cwidth":22,
|
||||
"height":"75%",
|
||||
"archor":"cc",
|
||||
"auto_open":true
|
||||
},
|
||||
"subwidgets":[
|
||||
{
|
||||
"widgettype":"Form",
|
||||
"options":{
|
||||
"title":"user register",
|
||||
"description":"base info we need is username, password and a cell phone number",
|
||||
|
||||
"fields":[
|
||||
{
|
||||
"name": "username",
|
||||
"title": "\u7528\u6237\u540d",
|
||||
"type": "str",
|
||||
"length": 255,
|
||||
"uitype": "str",
|
||||
"datatype": "str",
|
||||
"required":true,
|
||||
"label": "\u7528\u6237\u540d"
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"type": "str",
|
||||
"length": 255,
|
||||
"uitype": "password",
|
||||
"datatype": "str",
|
||||
"required":true,
|
||||
"label": "\u5bc6\u7801"
|
||||
},
|
||||
{
|
||||
"name": "cfm_password",
|
||||
"type": "str",
|
||||
"length": 255,
|
||||
"uitype": "password",
|
||||
"datatype": "str",
|
||||
"required":true,
|
||||
"label": "\u5bc6\u7801"
|
||||
},
|
||||
{
|
||||
"name": "email",
|
||||
"title": "\u90ae\u4ef6\u5730\u5740",
|
||||
"type": "str",
|
||||
"length": 255,
|
||||
"uitype": "str",
|
||||
"datatype": "str",
|
||||
"required":true,
|
||||
"label": "\u90ae\u4ef6\u5730\u5740"
|
||||
},
|
||||
{
|
||||
"name": "mobile",
|
||||
"title": "\u624b\u673a",
|
||||
"type": "str",
|
||||
"length": 255,
|
||||
"uitype": "str",
|
||||
"datatype": "str",
|
||||
"required":true,
|
||||
"label": "\u624b\u673a"
|
||||
}
|
||||
]
|
||||
},
|
||||
"binds":[
|
||||
{
|
||||
"wid":"self",
|
||||
"event":"submit",
|
||||
"actiontype":"urlwidget",
|
||||
"target":"self",
|
||||
"options":{
|
||||
"url":"{{entire_url('register.dspy')}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
49
wwwroot/user/reset_password/index.ui
Normal file
49
wwwroot/user/reset_password/index.ui
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"widgettype":"PopupWindow",
|
||||
"options":{
|
||||
"cwidth":22,
|
||||
"height":"75%",
|
||||
"archor":"cc",
|
||||
"auto_open":true
|
||||
},
|
||||
"subwidgets":[
|
||||
{
|
||||
"widgettype":"Form",
|
||||
"options":{
|
||||
"title":"Reset Password",
|
||||
"description":"reset yourself password",
|
||||
"fields":[
|
||||
{
|
||||
"name": "password",
|
||||
"type": "str",
|
||||
"length": 255,
|
||||
"uitype": "password",
|
||||
"datatype": "str",
|
||||
"required":true,
|
||||
"label": "\u5bc6\u7801"
|
||||
},
|
||||
{
|
||||
"name": "cfm_password",
|
||||
"type": "str",
|
||||
"length": 255,
|
||||
"uitype": "password",
|
||||
"datatype": "str",
|
||||
"required":true,
|
||||
"label": "\u5bc6\u7801"
|
||||
}
|
||||
]
|
||||
},
|
||||
"binds":[
|
||||
{
|
||||
"wid":"self",
|
||||
"event":"submit",
|
||||
"actiontype":"urlwidget",
|
||||
"target":"self",
|
||||
"options":{
|
||||
"url":"{{entire_url('reset_password.dspy')}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
17
wwwroot/user/reset_password/reset_password.dspy
Normal file
17
wwwroot/user/reset_password/reset_password.dspy
Normal file
@ -0,0 +1,17 @@
|
||||
if params_kw.password != params_kw.cfm_password:
|
||||
return UiError(title='Error', message='Password not match')
|
||||
|
||||
userid = await get_user()
|
||||
if userid is None:
|
||||
return UiError(title='Error', message='You need login first')
|
||||
|
||||
ns = {
|
||||
'id':userid,
|
||||
'password':params_kw.password
|
||||
}
|
||||
db = DBPools()
|
||||
dbname = await rfexe('get_module_dbname', 'rbac')
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
await sor.U('users', ns)
|
||||
return UiMessage(title='Success', message='Password reset success')
|
||||
return UiError(title='Error', message='Reset password failed')
|
||||
58
wwwroot/user/up_login.dspy
Normal file
58
wwwroot/user/up_login.dspy
Normal file
@ -0,0 +1,58 @@
|
||||
|
||||
debug(f'{params_kw=}, {password=}')
|
||||
ns = {
|
||||
"username":params_kw.username,
|
||||
"password":password_encode(params_kw.password)
|
||||
}
|
||||
|
||||
info(f'{ns=}')
|
||||
db = DBPools()
|
||||
dbname = await rfexe('get_module_dbname', 'rbac')
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
r = await sor.sqlExe('select * from users where username=${username}$ and password=${password}$', ns.copy())
|
||||
if len(r) == 0:
|
||||
return {
|
||||
"widgettype":"Error",
|
||||
"options":{
|
||||
"timeout":3,
|
||||
"title":"Login Error",
|
||||
"message":"user name or password error"
|
||||
}
|
||||
}
|
||||
await remember_user(r[0].id, username=r[0].username, userorgid=r[0].orgid)
|
||||
return {
|
||||
"widgettype":"Message",
|
||||
"options":{
|
||||
"timeout":3,
|
||||
"auto_open":true,
|
||||
"title":"Login",
|
||||
"message":"Welcome back"
|
||||
},
|
||||
"binds":[
|
||||
{
|
||||
"wid":"self",
|
||||
"event":"dismissed",
|
||||
"actiontype":"urlwidget",
|
||||
"target":"window",
|
||||
"options":{
|
||||
"url":entire_url('/index.ui')
|
||||
}
|
||||
},
|
||||
{
|
||||
"wid":"self",
|
||||
"event":"opened",
|
||||
"actiontype":"script",
|
||||
"target":"window.login_window",
|
||||
"script":"this.destroy()"
|
||||
}
|
||||
]
|
||||
}
|
||||
return {
|
||||
"widgettype":"Error",
|
||||
"options":{
|
||||
"timeout":3,
|
||||
"title":"Login Error",
|
||||
"message":"system error"
|
||||
}
|
||||
}
|
||||
|
||||
80
wwwroot/user/user.ui
Normal file
80
wwwroot/user/user.ui
Normal file
@ -0,0 +1,80 @@
|
||||
{% if get_user() %}
|
||||
{
|
||||
"widgettype":"HBox",
|
||||
"options":{
|
||||
"css":"clickable",
|
||||
"tip":"用户功能",
|
||||
"cwidth":6
|
||||
},
|
||||
"binds":[
|
||||
{
|
||||
"wid":"self",
|
||||
"event":"click",
|
||||
"actiontype":"urlwidget",
|
||||
"popup_options":{
|
||||
"eventpos":true,
|
||||
"dismiss_events":["command"]
|
||||
},
|
||||
"target":"Popup",
|
||||
"options":{
|
||||
"url":"{{entire_url('user_menu.ui')}}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"subwidgets":[
|
||||
{
|
||||
"widgettype":"Svg",
|
||||
"options":{
|
||||
"url":"{{entire_url('/bricks/imgs/user.svg')}}",
|
||||
"rate":1.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype":"Text",
|
||||
"options":{
|
||||
"cwidth": 4,
|
||||
"text":"{{get_username()}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
{% else %}
|
||||
{
|
||||
"widgettype":"IconBar",
|
||||
"options":{
|
||||
"css":"filler",
|
||||
"tools":[
|
||||
{
|
||||
"name":"login",
|
||||
"tip":"点击登录",
|
||||
"icon":"{{entire_url('/bricks/imgs/login.svg')}}"
|
||||
},
|
||||
{
|
||||
"name":"register",
|
||||
"tip":"点击注册",
|
||||
"icon":"{{entire_url('/bricks/imgs/register.svg')}}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"binds":[
|
||||
{
|
||||
"wid":"self",
|
||||
"event":"login",
|
||||
"actiontype":"urlwidget",
|
||||
"target":"self",
|
||||
"options":{
|
||||
"url":"{{entire_url('login.ui')}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"wid":"self",
|
||||
"event":"register",
|
||||
"actiontype":"urlwidget",
|
||||
"target":"self",
|
||||
"options":{
|
||||
"url":"{{entire_url('register.ui')}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
{% endif %}
|
||||
55
wwwroot/user/user_menu.ui
Normal file
55
wwwroot/user/user_menu.ui
Normal file
@ -0,0 +1,55 @@
|
||||
{
|
||||
"widgettype":"Menu",
|
||||
"options":{
|
||||
"cwidth":10,
|
||||
"target":"PopupWindow",
|
||||
"popup_options":{
|
||||
"height":"70%",
|
||||
"width":"70%"
|
||||
},
|
||||
"items":[
|
||||
{% if 'customer.customer' in get_user_roles(get_user()) %}
|
||||
{
|
||||
"name":"recharge",
|
||||
"label":"充值",
|
||||
"url":"{{entire_url('/platformbiz/recharge.ui')}}"
|
||||
},
|
||||
{
|
||||
"name":"myacc",
|
||||
"label":"我的账户",
|
||||
"url":"{{entire_url('myaccount')}}"
|
||||
},
|
||||
{
|
||||
"name":"mybill",
|
||||
"label":"我的账单",
|
||||
"url":"{{entire_url('mybill')}}"
|
||||
},
|
||||
{
|
||||
"name":"myorder",
|
||||
"label":"我的订单",
|
||||
"url":"{{entire_url('myorder')}}"
|
||||
},
|
||||
{
|
||||
"name":"myresource",
|
||||
"label":"我的资源",
|
||||
"url":"{{entire_url('myresource')}}"
|
||||
},
|
||||
{% endif %}
|
||||
{
|
||||
"name":"resetpwd",
|
||||
"label":"重置密码",
|
||||
"url":"{{entire_url('reset_password')}}"
|
||||
},
|
||||
{
|
||||
"name":"myrole",
|
||||
"label":"我的角色",
|
||||
"url":"{{entire_url('myrole.ui')}}"
|
||||
},
|
||||
{
|
||||
"name":"logout",
|
||||
"label":"签退",
|
||||
"url":"{{entire_url('logout.dspy')}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
15
wwwroot/user/user_panel.ui
Normal file
15
wwwroot/user/user_panel.ui
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"id":"user_panel",
|
||||
"widgettype":"VBox",
|
||||
"options":{
|
||||
"width":"auto"
|
||||
},
|
||||
"subwidgets":[
|
||||
{
|
||||
"widgettype":"urlwidget",
|
||||
"options":{
|
||||
"url":"{{entire_url('user.ui')}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
25
wwwroot/user/userapikey/add_userapikey.dspy
Normal file
25
wwwroot/user/userapikey/add_userapikey.dspy
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
ns = params_kw.copy()
|
||||
id = params_kw.id
|
||||
if not id or len(id) > 32:
|
||||
id = uuid()
|
||||
ns['id'] = id
|
||||
db = DBPools()
|
||||
async with db.sqlorContext('sage') as sor:
|
||||
r = await sor.C('userapikey', ns.copy())
|
||||
return {
|
||||
"widgettype":"Message",
|
||||
"options":{
|
||||
"user_data":ns,
|
||||
"title":"Add Success",
|
||||
"message":"ok"
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
"widgettype":"Error",
|
||||
"options":{
|
||||
"title":"Add Error",
|
||||
"message":"failed"
|
||||
}
|
||||
}
|
||||
24
wwwroot/user/userapikey/delete_userapikey.dspy
Normal file
24
wwwroot/user/userapikey/delete_userapikey.dspy
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
ns = {
|
||||
'id':params_kw['id'],
|
||||
}
|
||||
db = DBPools()
|
||||
async with db.sqlorContext('sage') as sor:
|
||||
r = await sor.D('userapikey', ns)
|
||||
print('delete success');
|
||||
return {
|
||||
"widgettype":"Message",
|
||||
"options":{
|
||||
"title":"Delete Success",
|
||||
"message":"ok"
|
||||
}
|
||||
}
|
||||
|
||||
print('Delete failed');
|
||||
return {
|
||||
"widgettype":"Error",
|
||||
"options":{
|
||||
"title":"Delete Error",
|
||||
"message":"failed"
|
||||
}
|
||||
}
|
||||
74
wwwroot/user/userapikey/get_userapikey.dspy
Normal file
74
wwwroot/user/userapikey/get_userapikey.dspy
Normal file
@ -0,0 +1,74 @@
|
||||
|
||||
ns = params_kw.copy()
|
||||
print(f'get_userapikey.dspy:{ns=}')
|
||||
if not ns.get('page'):
|
||||
ns['page'] = 1
|
||||
if not ns.get('sort'):
|
||||
|
||||
ns['sort'] = 'id'
|
||||
|
||||
filterjson = params_kw.get('data_filter')
|
||||
userid = await get_user()
|
||||
ns['userid'] = userid
|
||||
if not filterjson:
|
||||
fields = [ f['name'] for f in [
|
||||
{
|
||||
"name": "id",
|
||||
"title": "id",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "providerid",
|
||||
"title": "\u4f9b\u5e94\u5546id",
|
||||
"type": "str",
|
||||
"length": 200
|
||||
},
|
||||
{
|
||||
"name": "customerid",
|
||||
"title": "\u7528\u6237id",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"default": "0"
|
||||
},
|
||||
{
|
||||
"name": "apikey",
|
||||
"title": "api\u5bc6\u94a5",
|
||||
"type": "str",
|
||||
"length": 4000,
|
||||
"default": "0"
|
||||
},
|
||||
{
|
||||
"name": "secretkey",
|
||||
"title": "\u9644\u5c5e\u5bc6\u94a5",
|
||||
"type": "str",
|
||||
"length": 4000
|
||||
},
|
||||
{
|
||||
"name": "rfname",
|
||||
"title": "\u51fd\u6570\u540d",
|
||||
"type": "str",
|
||||
"length": 400
|
||||
}
|
||||
] ]
|
||||
filterjson = default_filterjson(fields, ns)
|
||||
sql = '''select a.*, b.providerid_text, c.customerid_text
|
||||
from (select y.* from users x, userapikey y where x.id=${userid}$ and x.orgid = y.customerid) a left join (select id as providerid,
|
||||
orgname as providerid_text from organization where 1 = 1) b on a.providerid = b.providerid left join (select id as customerid,
|
||||
orgname as customerid_text from organization where 1 = 1) c on a.customerid = c.customerid'''
|
||||
|
||||
if filterjson:
|
||||
dbf = DBFilter(filterjson)
|
||||
conds = dbf.gen(ns)
|
||||
if conds:
|
||||
ns.update(dbf.consts)
|
||||
sql += ' and ' + conds
|
||||
info(f'{ns=},{sql=}')
|
||||
db = DBPools()
|
||||
async with db.sqlorContext('sage') as sor:
|
||||
r = await sor.sqlPaging(sql, ns)
|
||||
return r
|
||||
return {
|
||||
"total":0,
|
||||
"rows":[]
|
||||
}
|
||||
126
wwwroot/user/userapikey/index.ui
Normal file
126
wwwroot/user/userapikey/index.ui
Normal file
@ -0,0 +1,126 @@
|
||||
|
||||
{
|
||||
"widgettype":"Tabular",
|
||||
"options":{
|
||||
|
||||
|
||||
"editable":{
|
||||
"new_data_url":"{{entire_url('add_userapikey.dspy')}}",
|
||||
"delete_data_url":"{{entire_url('delete_userapikey.dspy')}}",
|
||||
"update_data_url":"{{entire_url('update_userapikey.dspy')}}"
|
||||
},
|
||||
|
||||
"data_url":"{{entire_url('./get_userapikey.dspy')}}",
|
||||
"data_method":"GET",
|
||||
"data_params":{{json.dumps(params_kw, indent=4)}},
|
||||
"row_options":{
|
||||
|
||||
|
||||
|
||||
|
||||
"browserfields":{
|
||||
"excloud": [],
|
||||
"cwidth": {}
|
||||
},
|
||||
|
||||
|
||||
"editexclouded":[
|
||||
"id"
|
||||
],
|
||||
|
||||
"fields":[
|
||||
{
|
||||
"name": "id",
|
||||
"title": "id",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"cwidth": 18,
|
||||
"uitype": "str",
|
||||
"datatype": "str",
|
||||
"label": "id"
|
||||
},
|
||||
{
|
||||
"name": "providerid",
|
||||
"title": "\u4f9b\u5e94\u5546id",
|
||||
"type": "str",
|
||||
"length": 200,
|
||||
"label": "\u4f9b\u5e94\u5546id",
|
||||
"cwidth":16,
|
||||
"uitype": "code",
|
||||
"cwidth":18,
|
||||
"valueField": "providerid",
|
||||
"textField": "providerid_text",
|
||||
"params": {
|
||||
"dbname": "sage",
|
||||
"table": "organization",
|
||||
"tblvalue": "id",
|
||||
"tbltext": "orgname",
|
||||
"valueField": "providerid",
|
||||
"textField": "providerid_text"
|
||||
},
|
||||
"dataurl": "\/get_code.dspy"
|
||||
},
|
||||
{
|
||||
"name": "customerid",
|
||||
"title": "\u7528\u6237id",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"cwidth":18,
|
||||
"default": "0",
|
||||
"label": "\u7528\u6237id",
|
||||
"cwidth":16,
|
||||
"uitype": "code",
|
||||
"valueField": "customerid",
|
||||
"textField": "customerid_text",
|
||||
"params": {
|
||||
"dbname": "sage",
|
||||
"table": "organization",
|
||||
"tblvalue": "id",
|
||||
"tbltext": "orgname",
|
||||
"valueField": "customerid",
|
||||
"textField": "customerid_text"
|
||||
},
|
||||
"dataurl": "\/get_code.dspy"
|
||||
},
|
||||
{
|
||||
"name": "apikey",
|
||||
"title": "api\u5bc6\u94a5",
|
||||
"type": "str",
|
||||
"length": 4000,
|
||||
"default": "0",
|
||||
"cwidth": 18,
|
||||
"uitype": "text",
|
||||
"rows": 4,
|
||||
"datatype": "str",
|
||||
"label": "api\u5bc6\u94a5"
|
||||
},
|
||||
{
|
||||
"name": "secretkey",
|
||||
"title": "\u9644\u5c5e\u5bc6\u94a5",
|
||||
"type": "str",
|
||||
"length": 4000,
|
||||
"cwidth": 18,
|
||||
"uitype": "text",
|
||||
"rows": 4,
|
||||
"datatype": "str",
|
||||
"label": "\u9644\u5c5e\u5bc6\u94a5"
|
||||
},
|
||||
{
|
||||
"name": "rfname",
|
||||
"title": "\u51fd\u6570\u540d",
|
||||
"type": "str",
|
||||
"length": 400,
|
||||
"cwidth": 18,
|
||||
"uitype": "text",
|
||||
"rows": 4,
|
||||
"datatype": "str",
|
||||
"label": "\u51fd\u6570\u540d"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"page_rows":160,
|
||||
"cache_limit":5
|
||||
}
|
||||
|
||||
}
|
||||
22
wwwroot/user/userapikey/update_userapikey.dspy
Normal file
22
wwwroot/user/userapikey/update_userapikey.dspy
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
ns = params_kw.copy()
|
||||
db = DBPools()
|
||||
async with db.sqlorContext('sage') as sor:
|
||||
r = await sor.U('userapikey', ns)
|
||||
print('update success');
|
||||
return {
|
||||
"widgettype":"Message",
|
||||
"options":{
|
||||
"title":"Update Success",
|
||||
"message":"ok"
|
||||
}
|
||||
}
|
||||
|
||||
print('update failed');
|
||||
return {
|
||||
"widgettype":"Error",
|
||||
"options":{
|
||||
"title":"Update Error",
|
||||
"message":"failed"
|
||||
}
|
||||
}
|
||||
5
wwwroot/user/wechat_login.ui
Normal file
5
wwwroot/user/wechat_login.ui
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"widgettype":"VBox",
|
||||
"options":{
|
||||
}
|
||||
}
|
||||
11
wwwroot/userpassword_login.dspy
Normal file
11
wwwroot/userpassword_login.dspy
Normal file
@ -0,0 +1,11 @@
|
||||
username = params_kw.get('username')
|
||||
passwd = params_kw.get('passwd')
|
||||
if not passwd:
|
||||
return UiError(title='Login failed', message='Password is required')
|
||||
passwd = password(passwd)
|
||||
rzt = await check_user_password(request, username, passwd)
|
||||
if rzt:
|
||||
return UiMessage(title='Logined', message=f'Welcome back ')
|
||||
return UiError(title='login failed', message='user and password mismatch')
|
||||
|
||||
|
||||
43
wwwroot/userpassword_login.ui
Normal file
43
wwwroot/userpassword_login.ui
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"id":"login_window",
|
||||
"widgettype":"PopupWindow",
|
||||
"options":{
|
||||
"auto_open":true,
|
||||
"anthor":"cc",
|
||||
"cwidth":20,
|
||||
"cheight":"14"
|
||||
},
|
||||
"subwidgets":[
|
||||
{
|
||||
"widgettype":"Form",
|
||||
"id":"userpasswd",
|
||||
"options":{
|
||||
"cols":1,
|
||||
"fields":[
|
||||
{
|
||||
"name":"username",
|
||||
"label":"用户名",
|
||||
"uitype":"str"
|
||||
},
|
||||
{
|
||||
"name":"passwd",
|
||||
"label":"密码",
|
||||
"uitype":"password"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"binds":[
|
||||
{
|
||||
"wid":"userpasswd",
|
||||
"event":"submit",
|
||||
"actiontype":"urlwidget",
|
||||
"target":"self",
|
||||
"options":{
|
||||
"url":"{{entire_url('userpassword_login.dspy')}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user