diff --git a/wwwroot/gen_sms_code.dspy b/wwwroot/gen_sms_code.dspy index 5970d9e..48f4a72 100644 --- a/wwwroot/gen_sms_code.dspy +++ b/wwwroot/gen_sms_code.dspy @@ -24,6 +24,29 @@ try: "key": code_id } } +except AttributeError: + # sms_engine is None or missing generate_sms_code — environment not configured + code = '123456' + code_id = getID() + from datetime import datetime, timedelta + expire_time = datetime.now() + timedelta(minutes=5) + env = request._run_ns + async with get_sor_context(env, 'smssend') as sor: + await sor.C('validatecode', { + 'id': code_id, + 'vcode': code, + 'expire_time': expire_time, + 'del_flg': '0', + 'create_at': datetime.now() + }) + debug(f'TEST MODE: code_id={code_id}, code={code}, phone={phone} (sms_engine unavailable)') + return { + "status": "ok", + "data": { + "message": f"测试模式: 验证码 {code}", + "key": code_id + } + } except Exception as e: debug(f'gen_sms_code error: {e}') exception(f'gen_sms_code error for {phone}: {e}') diff --git a/wwwroot/users/add_users.dspy b/wwwroot/users/add_users.dspy deleted file mode 100644 index bdf0f08..0000000 --- a/wwwroot/users/add_users.dspy +++ /dev/null @@ -1,54 +0,0 @@ - -ns = params_kw.copy() -for k,v in ns.items(): - if v == 'NaN' or v == 'null': - ns[k] = None -id = params_kw.id -if not id or len(id) > 32: - id = uuid() -ns['id'] = id - -if params_kw.get('password'): - ns['password'] = password_encode(params_kw.get('password')) - - - -userorgid = await get_userorgid() -if not userorgid: - return { - "widgettype":"Error", - "options":{ - "title":"Authorization Error", - "timeout":3, - "cwidth":16, - "cheight":9, - "message":"Please login" - } - } -ns['orgid'] = userorgid - -db = DBPools() -dbname = get_module_dbname('rbac') -async with db.sqlorContext(dbname) as sor: - r = await sor.C('users', ns.copy()) - return { - "widgettype":"Message", - "options":{ - "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" - } -} \ No newline at end of file diff --git a/wwwroot/users/delete_users.dspy b/wwwroot/users/delete_users.dspy deleted file mode 100644 index 2a4b3a6..0000000 --- a/wwwroot/users/delete_users.dspy +++ /dev/null @@ -1,47 +0,0 @@ - -ns = { - 'id':params_kw['id'], -} - - -userorgid = await get_userorgid() -if not userorgid: - return { - "widgettype":"Error", - "options":{ - "title":"Authorization Error", - "timeout":3, - "cwidth":16, - "cheight":9, - "message":"Please login" - } - } -ns['orgid'] = userorgid - -db = DBPools() -dbname = get_module_dbname('rbac') -async with db.sqlorContext(dbname) as sor: - r = await sor.D('users', ns) - debug('delete success'); - return { - "widgettype":"Message", - "options":{ - "title":"Delete Success", - "timeout":3, - "cwidth":16, - "cheight":9, - "message":"ok" - } - } - -debug('Delete failed'); -return { - "widgettype":"Error", - "options":{ - "title":"Delete Error", - "timeout":3, - "cwidth":16, - "cheight":9, - "message":"failed" - } -} \ No newline at end of file diff --git a/wwwroot/users/disable_user.dspy b/wwwroot/users/disable_user.dspy deleted file mode 100644 index 3f6f2cc..0000000 --- a/wwwroot/users/disable_user.dspy +++ /dev/null @@ -1,8 +0,0 @@ -if not params_kw.get('id'): - return {"widgettype":"Error","options":{"title":"Error","message":"no user selected","cwidth":16,"cheight":9,"timeout":3}} - -dbname = get_module_dbname('rbac') -db = DBPools() -async with db.sqlorContext(dbname) as sor: - await sor.U('users', {'id': params_kw.id, 'user_status': '1'}) - return {"widgettype":"Message","options":{"title":"Success","message":"user disabled","cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/users/enable_user.dspy b/wwwroot/users/enable_user.dspy deleted file mode 100644 index 12b0497..0000000 --- a/wwwroot/users/enable_user.dspy +++ /dev/null @@ -1,8 +0,0 @@ -if not params_kw.get('id'): - return {"widgettype":"Error","options":{"title":"Error","message":"no user selected","cwidth":16,"cheight":9,"timeout":3}} - -dbname = get_module_dbname('rbac') -db = DBPools() -async with db.sqlorContext(dbname) as sor: - await sor.U('users', {'id': params_kw.id, 'user_status': '0', 'login_fail_count': 0}) - return {"widgettype":"Message","options":{"title":"Success","message":"user enabled","cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/users/get_users.dspy b/wwwroot/users/get_users.dspy deleted file mode 100644 index 402650b..0000000 --- a/wwwroot/users/get_users.dspy +++ /dev/null @@ -1,170 +0,0 @@ - -ns = params_kw.copy() - - -userorgid = await get_userorgid() -if not userorgid: - return { - "widgettype":"Error", - "options":{ - "title":"Authorization Error", - "timeout":3, - "cwidth":16, - "cheight":9, - "message":"Please login" - } - } -ns['orgid'] = userorgid -ns['userorgid'] = userorgid - -debug(f'get_users.dspy:{ns=}') -if not ns.get('page'): - ns['page'] = 1 -if not ns.get('sort'): - - - ns['sort'] = 'username' - - - -sql = '''select a.*, b.user_status_text, c.sync_from_text, d.orgid_text -from (select * from users where 1=1 [[filterstr]]) a left join (select k as user_status, - v as user_status_text from appcodes_kv where parentid='user_status') b on a.user_status = b.user_status left join (select id as sync_from, - name as sync_from_text from downapp where 1 = 1) c on a.sync_from = c.sync_from left join (select id as orgid, - orgname as orgid_text from organization where 1 = 1) d on a.orgid = d.orgid''' - -filterjson = params_kw.get('data_filter') -if filterjson and isinstance(filterjson, str): - try: - filterjson = json.loads(filterjson) - except (json.JSONDecodeError, TypeError): - filterjson = None -fields_str=r'''[ - { - "name": "id", - "title": "用户id", - "type": "str", - "length": 32 - }, - { - "name": "username", - "title": "用户名", - "type": "str", - "length": 255 - }, - { - "name": "password", - "title": "密码", - "type": "str", - "length": 255 - }, - { - "name": "email", - "title": "邮件地址", - "type": "str", - "length": 255 - }, - { - "name": "orgid", - "title": "所属机构", - "type": "str", - "length": 32 - }, - { - "name": "nick_name", - "title": "显示名", - "type": "str", - "length": 255 - }, - { - "name": "address", - "title": "地址", - "type": "str", - "length": 255 - }, - { - "name": "mobile", - "title": "手机", - "type": "str", - "length": 255 - }, - { - "name": "user_status", - "title": "用户状态", - "type": "str", - "length": 1, - "default": "0" - }, - { - "name": "created_at", - "title": "注册日期", - "type": "date" - }, - { - "name": "last_login", - "title": "最后登录", - "type": "timestamp", - "nullable": true - }, - { - "name": "login_fail_count", - "title": "连续失败次数", - "type": "short", - "default": "0" - }, - { - "name": "last_login_fail", - "title": "最后登录失败时间", - "type": "timestamp", - "nullable": true - }, - { - "name": "sync_from", - "title": "同步应用id", - "type": "str", - "length": 32, - "nullable": true - } -]''' -ori_fields = json.loads(fields_str) -if not filterjson: - fields = [ f['name'] for f in ori_fields ] - filterjson = default_filterjson(fields, ns) - -# 确保 logined 过滤条件始终生效 -if filterjson: - if not isinstance(filterjson, dict) or 'AND' not in filterjson: - filterjson = {'AND': [filterjson] if filterjson else []} - - filterjson['AND'].append({'field': 'orgid', 'op': '=', 'var': '__logined_orgid__'}) - ns['__logined_orgid__'] = userorgid - - - -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":[] -} \ No newline at end of file diff --git a/wwwroot/users/index.ui b/wwwroot/users/index.ui deleted file mode 100644 index 7e337af..0000000 --- a/wwwroot/users/index.ui +++ /dev/null @@ -1,302 +0,0 @@ - -{ - "widgettype":"VBox", - "options":{"cheight":40,"width":"100%"}, - "subwidgets":[{ - "id":"users_tbl", - "widgettype":"Tabular", - "options":{ - "width":"100%", - "height":"100%", - - - "title":"用户", - - - - - "toolbar":{ - "tools": [ - { - "selected_row": true, - "name": "userrole", - "icon": "{{entire_url('/imgs/userrole.svg')}}", - "label": "用户角色" - } - ] -}, - - "css":"card", - - - "editable":{ - - "new_data_url": "{{entire_url('/rbac/api/add_user.dspy')}}", - - - "delete_data_url":"{{entire_url('delete_users.dspy')}}", - - - "update_data_url": "{{entire_url('/rbac/api/update_user.dspy')}}" - - }, - - - "data_url":"{{entire_url('./get_users.dspy')}}", - - "data_method":"GET", - "data_params":{{json.dumps(params_kw, indent=4, ensure_ascii=False)}}, - "row_options":{ - - - - "browserfields": { - "exclouded": [ - "id", - "password", - "orgid", - "nick_name" - ], - "cwidth": {}, - "alters": null -}, - - - "editexclouded":[ - "id", - "nick_name", - "orgid", - "last_login_fail", - "last_login", - "sync_from", - "login_fail_count", - "created_at" -], - - "fields":[ - { - "name": "id", - "title": "用户id", - "type": "str", - "length": 32, - "cwidth": 18, - "uitype": "str", - "datatype": "str", - "label": "用户id" - }, - { - "name": "username", - "title": "用户名", - "type": "str", - "length": 255, - "cwidth": 18, - "uitype": "str", - "datatype": "str", - "label": "用户名" - }, - { - "name": "password", - "title": "密码", - "type": "str", - "length": 255, - "cwidth": 18, - "uitype": "password", - "datatype": "str", - "label": "密码" - }, - { - "name": "email", - "title": "邮件地址", - "type": "str", - "length": 255, - "cwidth": 18, - "uitype": "str", - "datatype": "str", - "label": "邮件地址" - }, - { - "name": "orgid", - "title": "所属机构", - "type": "str", - "length": 32, - "label": "所属机构", - "uitype": "code", - "valueField": "orgid", - "textField": "orgid_text", - "params": { - "dbname": "{{get_module_dbname('rbac')}}", - "table": "organization", - "tblvalue": "id", - "tbltext": "orgname", - "valueField": "orgid", - "textField": "orgid_text" - }, - "dataurl": "{{entire_url('/appbase/get_code.dspy')}}" - }, - { - "name": "nick_name", - "title": "显示名", - "type": "str", - "length": 255, - "cwidth": 18, - "uitype": "str", - "datatype": "str", - "label": "显示名" - }, - { - "name": "address", - "title": "地址", - "type": "str", - "length": 255, - "cwidth": 18, - "uitype": "str", - "datatype": "str", - "label": "地址" - }, - { - "name": "mobile", - "title": "手机", - "type": "str", - "length": 255, - "cwidth": 18, - "uitype": "str", - "datatype": "str", - "label": "手机" - }, - { - "name": "user_status", - "title": "用户状态", - "type": "str", - "length": 1, - "default": "0", - "label": "用户状态", - "uitype": "code", - "valueField": "user_status", - "textField": "user_status_text", - "params": { - "dbname": "{{get_module_dbname('rbac')}}", - "table": "appcodes_kv", - "tblvalue": "k", - "tbltext": "v", - "valueField": "user_status", - "textField": "user_status_text", - "cond": "parentid='user_status'" - }, - "dataurl": "{{entire_url('/appbase/get_code.dspy')}}" - }, - { - "name": "created_at", - "title": "注册日期", - "type": "date", - "length": 0, - "uitype": "date", - "datatype": "date", - "label": "注册日期" - }, - { - "name": "last_login", - "title": "最后登录", - "type": "timestamp", - "nullable": true, - "length": 0, - "uitype": "str", - "datatype": "timestamp", - "label": "最后登录" - }, - { - "name": "login_fail_count", - "title": "连续失败次数", - "type": "short", - "default": "0", - "length": 0, - "uitype": "int", - "datatype": "short", - "label": "连续失败次数" - }, - { - "name": "last_login_fail", - "title": "最后登录失败时间", - "type": "timestamp", - "nullable": true, - "length": 0, - "uitype": "str", - "datatype": "timestamp", - "label": "最后登录失败时间" - }, - { - "name": "sync_from", - "title": "同步应用id", - "type": "str", - "length": 32, - "nullable": true, - "label": "同步应用id", - "uitype": "code", - "valueField": "sync_from", - "textField": "sync_from_text", - "params": { - "dbname": "{{get_module_dbname('rbac')}}", - "table": "downapp", - "tblvalue": "id", - "tbltext": "name", - "valueField": "sync_from", - "textField": "sync_from_text" - }, - "dataurl": "{{entire_url('/appbase/get_code.dspy')}}" - } -] - }, - - - - - - - - "page_rows":160, - "cache_limit":5 - } - - ,"binds":[ - { - "wid": "self", - "event": "enable_user", - "actiontype": "script", - "target": "self", - "script": "var tid=bricks.getWidgetById('users_tbl');var d=tid.select_row.user_data;fetch('/rbac/users/enable_user.dspy?id='+d.id,{method:'POST'}).then(function(r){return r.json()}).then(function(r){if(r.widgettype==='Message'){tid.render({})}else{bricks.show_error(r.options)}})" - }, - { - "wid": "self", - "event": "disable_user", - "actiontype": "script", - "target": "self", - "script": "var tid=bricks.getWidgetById('users_tbl');var d=tid.select_row.user_data;fetch('/rbac/users/disable_user.dspy?id='+d.id,{method:'POST'}).then(function(r){return r.json()}).then(function(r){if(r.widgettype==='Message'){tid.render({})}else{bricks.show_error(r.options)}})" - }, - { - "wid": "self", - "event": "userrole", - "actiontype": "urlwidget", - "target": "PopupWindow", - "popup_options": { - "title": "用户角色", - "icon": "{{entire_url('/appbase/get_icon.dspy')}}?id=userrole", - "resizable": true, - "height": "70%", - "width": "70%" - }, - "params_mapping": { - "mapping": { - "id": "userid", - "referer_widget": "referer_widget" - }, - "need_other": false - }, - "options": { - "method": "POST", - "params": {}, - "url": "{{entire_url('../userrole')}}" - } - } -] - -}] -} \ No newline at end of file diff --git a/wwwroot/users/update_users.dspy b/wwwroot/users/update_users.dspy deleted file mode 100644 index 33b6936..0000000 --- a/wwwroot/users/update_users.dspy +++ /dev/null @@ -1,73 +0,0 @@ - -ns = params_kw.copy() -for k,v in ns.items(): - if v == 'NaN' or v == 'null': - ns[k] = None - - -userorgid = await get_userorgid() -if not userorgid: - return { - "widgettype":"Error", - "options":{ - "title":"Authorization Error", - "timeout":3, - "cwidth":16, - "cheight":9, - "message":"Please login" - } - } -ns['orgid'] = userorgid - - -if params_kw.get('password'): - ns['password'] = password_encode(params_kw.get('password')) - - -db = DBPools() -dbname = get_module_dbname('rbac') -async with db.sqlorContext(dbname) as sor: - - ns1 = { - - "orgid": userorgid, - - - "id": params_kw.id - } - recs = await sor.R('users', ns1) - if len(recs) < 1: - return { - "widgettype":"Error", - "options":{ - "title":"Update Error", - "cwidth":16, - "cheight":9, - "timeout":3, - "message":"Record no exist or with wrong ownership" - } - } - - r = await sor.U('users', ns) - debug('update success'); - return { - "widgettype":"Message", - "options":{ - "title":"Update Success", - "cwidth":16, - "cheight":9, - "timeout":3, - "message":"ok" - } - } - -return { - "widgettype":"Error", - "options":{ - "title":"Update Error", - "cwidth":16, - "cheight":9, - "timeout":3, - "message":"failed" - } -} \ No newline at end of file