@yongdall/user 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/index.mjs +2 -0
- package/assets/index.mjs.map +1 -0
- package/assets.yongdall.mjs +3 -0
- package/hooks.yongdall.mjs +35 -0
- package/hooks.yongdall.mjs.map +1 -0
- package/index.d.mts +74 -0
- package/index.mjs +3 -0
- package/package.json +28 -0
- package/routers.yongdall.mjs +31 -0
- package/routers.yongdall.mjs.map +1 -0
- package/user-KPkkrnN4.mjs +249 -0
- package/user-KPkkrnN4.mjs.map +1 -0
package/assets/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{Store as e,createStoreField as t,renderStore as n,single as r}from"@yongdall/web";import i from"@yongdall/web/plugin";var a=Object.defineProperty,o=(e,t)=>{let n={};for(var r in e)a(n,r,{get:e[r],enumerable:!0});return t||a(n,Symbol.toStringTag,{value:`Module`}),n};const s=[/[a-z]/,/[A-Z]/,/[0-9]/,/[~!@#$%^&*()_+{}|<>?,./:";'\\\[\]-]/];function*c(e){e.length<8&&(yield`密码不足 8 位`),s.filter(t=>t.test(e)).length<3&&(yield`密码至少包含小写字母、大写字母、数字、符号中的 3 种`),e.toLowerCase().includes(`admin`)&&(yield`密码不能包含admin`),[...e].find((e,t,n)=>n[t-1]===e&&n[t+1]===e)&&(yield`密码不能包含三位以上的相同的字母或数字`),[...e].find((e,t,n)=>{let r=n[t-1],i=n[t+1];if(!r||!i)return!1;let a=r.charCodeAt(0),o=e.charCodeAt(0),s=i.charCodeAt(0),c=a-o,l=o-s;if(c===l&&(l===1||l===-1))return!0})&&(yield`密码不能包含三位以上的连续的字母或数字`)}var l=o({user:()=>f});const u={password:{type:`password`,label:`您的密码`,hidden:r,validators:{blur(e){if(r)return;let t=e.value;if(!t||typeof t!=`string`)return`请填写您的密码`}}},newPassword1:{type:`password`,label:`新密码`,validators:{blur(e){let t=e.value;return!t||typeof t!=`string`?`请填写新密码`:[...c(t)]}}},newPassword2:{type:`password`,label:`重复密码`,validators:{blur(e){let t=e.value;if(!t)return`请填写重复密码`;if(t!==e.parent?.child(`newPassword1`)?.value)return`重复密码与新密码不一致`}}}},d={fields:[{field:`password`},{field:`newPassword1`},{field:`newPassword2`}]},f={documentScripts:{setPassword(r){let a=r.document?.id;if(!a)return;let o=document.createElement(`dialog`),s=document.createElement(`form`),c=s.appendChild(document.createElement(`h2`));c.textContent=`设置用户密码`,o.appendChild(s);let l=new AbortController;o.addEventListener(`close`,()=>{o.remove(),l.abort()});let f=e.create(u);f.reset();let p=n(f,(e,n)=>t(e,{},n),s,d,{editable:!0});l.signal.addEventListener(`abort`,p,{once:!0});let m=s.appendChild(document.createElement(`button`));m.type=`submit`,m.textContent=`修改密码`,s.addEventListener(`submit`,async e=>{if(e.preventDefault(),!m.disabled)try{m.disabled=!0;let{password:e,newPassword1:t}=f.value;if((await f.validate())?.length)return;let n=await i.request.clone().put(`password`).body({userId:a,password:e,newPassword:t}).result();if(!n||n===200||typeof n==`object`){o.close(),alert(`密码修改完成`);return}if(n===404){alert(`新旧密码不能相同`);return}if(n===403){alert(`密码错误`);return}if(n===400){alert(`不能修改自己的密码`);return}if(n===402){alert(`您无操作权限`);return}}catch(e){e instanceof Response&&e.status===403&&alert(`密码错误`)}finally{m.disabled=!1}}),document.body.appendChild(o),o.showModal()}}},p={password:{type:`password`,label:`当前密码`,validators:{blur(e){let t=e.value;if(!t||typeof t!=`string`)return`请填写密码`}}},newPassword1:{type:`password`,label:`新密码`,validators:{blur(e){let t=e.value;return!t||typeof t!=`string`?`请填写新密码`:[...c(t)]}}},newPassword2:{type:`password`,label:`重复密码`,validators:{blur(e){let t=e.value;if(!t)return`请填写重复密码`;if(t!==e.parent?.child(`newPassword1`)?.value)return`重复密码与新密码不一致`}}}},m={fields:[{field:`password`},{field:`newPassword1`},{field:`newPassword2`}]};function h(r){r.loading=!1,r.panel=!0,r.title=`修改密码`;let a=r.container;a.style.margin=`auto`,a.style.maxInlineSize=`300px`;let o=document.createElement(`form`),s=o.appendChild(document.createElement(`h2`));s.textContent=`修改密码`,a.appendChild(o);let c=e.create(p);if(c.reset(),r.signal.aborted)return null;let l=n(c,(e,n)=>t(e,{},n),o,m,{editable:!0});r.signal.addEventListener(`abort`,l,{once:!0});let u=o.appendChild(document.createElement(`button`));u.type=`submit`,u.textContent=`修改密码`,o.addEventListener(`submit`,async e=>{if(e.preventDefault(),!u.disabled)try{u.disabled=!0;let{password:e,newPassword1:t}=c.value;if((await c.validate())?.length)return;let n=await i.request.clone().put(`password`).body({password:e,newPassword:t}).result();if(!n||n===200||typeof n==`object`){c.reset(),alert(`密码修改完成`);return}if(n===403){alert(`密码错误`);return}}catch(e){e instanceof Response&&e.status===403&&alert(`密码错误`)}finally{u.disabled=!1}})}export{l as models,h as password,c as verifyNewPassword};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["schema","StoreLayout"],"sources":["../../../plugins/user/common/verifyNewPassword.mjs","../../../plugins/user/assets/models.mjs","../../../plugins/user/assets/password.mjs"],"sourcesContent":["const signTypeRegex = [\n\t/[a-z]/,\n\t/[A-Z]/,\n\t/[0-9]/,\n\t/[~!@#$%^&*()_+{}|<>?,./:\";'\\\\\\[\\]-]/,\n]\n/**\n * \n * @param {string} password \n */\nexport function* verifyNewPassword(password) {\n\tif (password.length < 8) { yield '密码不足 8 位'; }\n\t\n\tif (signTypeRegex.filter(r => r.test(password)).length < 3) {\n\t\tyield `密码至少包含小写字母、大写字母、数字、符号中的 3 种`;\n\t}\n\tif (password.toLowerCase().includes('admin')) {\n\t\tyield `密码不能包含admin`;\n\t}\n\tif ([...password].find((a,index, list) => list[index - 1] === a && list[index + 1] === a)) {\n\t\tyield `密码不能包含三位以上的相同的字母或数字`;\n\t}\n\tif ([...password].find((a,index, list) => {\n\t\tconst p = list[index - 1];\n\t\tconst n = list[index + 1];\n\t\tif (!p || !n) { return false; }\n\t\tconst x = p.charCodeAt(0);\n\t\tconst y = a.charCodeAt(0);\n\t\tconst z = n.charCodeAt(0);\n\t\tconst u = x - y;\n\t\tconst v = y - z;\n\t\tif (u === v && (v === 1 || v === -1)) { return true}\n\t})) {\n\t\tyield `密码不能包含三位以上的连续的字母或数字`;\n\t}\n}\n","/** @import { ModelScript } from '@yongdall/web' */\n/** @import { Schema, StoreLayout } from '@yongdall/web' */\n\nimport { createStoreField, renderStore, single, Store } from '@yongdall/web';\nimport Plugin from '@yongdall/web/plugin';\nimport { verifyNewPassword } from './common/verifyNewPassword.mjs';\n\n/** @type {Schema} */\nconst schema = {\n\n\tpassword: {\n\t\ttype: 'password', label: '您的密码',\n\t\thidden: single,\n\t\tvalidators: {\n\t\t\tblur(store) {\n\t\t\t\tif (single) { return; }\n\t\t\t\tconst value = store.value;\n\t\t\t\tif (!value || typeof value !== 'string') { return '请填写您的密码'; }\n\t\t\t},\n\t\t}\n\t},\n\tnewPassword1: {\n\t\ttype: 'password', label: '新密码',\n\t\tvalidators: {\n\t\t\tblur(store) {\n\t\t\t\tconst value = store.value;\n\t\t\t\tif (!value || typeof value !== 'string') { return '请填写新密码'; }\n\t\t\t\treturn [...verifyNewPassword(value)];\n\t\t\t},\n\t\t},\n\t},\n\tnewPassword2: {\n\t\ttype: 'password', label: '重复密码',\n\t\tvalidators: {\n\t\t\tblur(store) {\n\t\t\t\tconst value = store.value;\n\t\t\t\tif (!value) { return '请填写重复密码'; }\n\t\t\t\tif (value !== store.parent?.child('newPassword1')?.value) {\n\t\t\t\t\treturn '重复密码与新密码不一致';\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n};\n\n/** @type {StoreLayout} */\nconst StoreLayout = {\n\tfields: [\n\t\t{ field: 'password' },\n\t\t{ field: 'newPassword1' },\n\t\t{ field: 'newPassword2' },\n\t],\n};\n/** @type {ModelScript} */\nexport const user = {\n\tdocumentScripts: {\n\t\tsetPassword(params) {\n\t\t\tconst userId = params.document?.id;\n\t\t\tif (!userId) { return; }\n\t\t\tconst dialog = document.createElement('dialog');\n\t\t\tconst form = document.createElement('form');\n\t\t\tconst title = form.appendChild(document.createElement('h2'));\n\t\t\ttitle.textContent = '设置用户密码';\n\t\t\tdialog.appendChild(form);\n\t\t\tconst ac = new AbortController();\n\t\t\tdialog.addEventListener('close', () => {\n\t\t\t\tdialog.remove();\n\t\t\t\tac.abort();\n\t\t\t});\n\t\t\tconst store = Store.create(schema);\n\t\t\tstore.reset();\n\t\t\tconst destroy = renderStore(\n\t\t\t\tstore,\n\t\t\t\t(store, options) => createStoreField(store, {}, options),\n\t\t\t\tform,\n\t\t\t\tStoreLayout,\n\t\t\t\t{ editable: true },\n\t\t\t);\n\t\t\tac.signal.addEventListener('abort', destroy, { once: true });\n\n\t\t\tconst submitButton = form.appendChild(document.createElement('button'));\n\t\t\tsubmitButton.type = 'submit';\n\t\t\tsubmitButton.textContent = '修改密码';\n\n\t\t\tform.addEventListener('submit', async event => {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tif (submitButton.disabled) { return; }\n\t\t\t\ttry {\n\t\t\t\t\tsubmitButton.disabled = true;\n\t\t\t\t\tconst { password, newPassword1 } = store.value;\n\t\t\t\t\tconst r = await store.validate();\n\t\t\t\t\tif (r?.length) { return; }\n\t\t\t\t\tconst result = await Plugin.request.clone().put('password').body({\n\t\t\t\t\t\tuserId, password, newPassword: newPassword1,\n\t\t\t\t\t}).result();\n\t\t\t\t\tif (!result || result === 200 || typeof result === 'object') {\n\t\t\t\t\t\tdialog.close();\n\t\t\t\t\t\talert('密码修改完成');\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (result === 404) {\n\t\t\t\t\t\talert('新旧密码不能相同');\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (result === 403) {\n\t\t\t\t\t\talert('密码错误');\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (result === 400) {\n\t\t\t\t\t\talert('不能修改自己的密码');\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (result === 402) {\n\t\t\t\t\t\talert('您无操作权限');\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t} catch (e) {\n\t\t\t\t\tif (e instanceof Response) {\n\t\t\t\t\t\tif (e.status === 403) {\n\t\t\t\t\t\t\talert('密码错误');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tsubmitButton.disabled = false;\n\t\t\t\t}\n\t\t\t});\n\t\t\tdocument.body.appendChild(dialog);\n\t\t\tdialog.showModal();\n\t\t}\n\t}\n};\n","/** @import { PageContext, Schema, StoreLayout } from '@yongdall/web' */\n\nimport { createStoreField, renderStore, Store } from '@yongdall/web';\nimport Plugin from '@yongdall/web/plugin';\nimport { verifyNewPassword } from './common/verifyNewPassword.mjs';\n\n/** @type {Schema} */\nconst schema = {\n\n\tpassword: {\n\t\ttype: 'password', label: '当前密码',\n\t\tvalidators: {\n\t\t\tblur(store) {\n\t\t\t\tconst value = store.value;\n\t\t\t\tif (!value || typeof value !== 'string') { return '请填写密码'; }\n\t\t\t},\n\t\t}\n\t},\n\tnewPassword1: {\n\t\ttype: 'password', label: '新密码',\n\t\tvalidators: {\n\t\t\tblur(store) {\n\t\t\t\tconst value = store.value;\n\t\t\t\tif (!value || typeof value !== 'string') { return '请填写新密码'; }\n\t\t\t\treturn [...verifyNewPassword(value)];\n\t\t\t},\n\t\t},\n\t},\n\tnewPassword2: {\n\t\ttype: 'password', label: '重复密码',\n\t\tvalidators: {\n\t\t\tblur(store) {\n\t\t\t\tconst value = store.value;\n\t\t\t\tif (!value) { return '请填写重复密码'; }\n\t\t\t\tif (value !== store.parent?.child('newPassword1')?.value) {\n\t\t\t\t\treturn '重复密码与新密码不一致';\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n};\n\n/** @type {StoreLayout} */\nconst StoreLayout = {\n\tfields: [\n\t\t{ field: 'password' },\n\t\t{ field: 'newPassword1' },\n\t\t{ field: 'newPassword2' },\n\t],\n};\n/**\n * \n * @param {PageContext} ctx \n */\nexport default function(ctx) {\n\tctx.loading = false;\n\tctx.panel = true;\n\tctx.title = '修改密码';\n\tconst container = ctx.container;\n\tcontainer.style.margin = 'auto';\n\tcontainer.style.maxInlineSize = '300px';\n\n\tconst form = document.createElement('form');\n\tconst title = form.appendChild(document.createElement('h2'));\n\ttitle.textContent = '修改密码';\n\tcontainer.appendChild(form);\n\n\tconst store = Store.create(schema);\n\tstore.reset();\n\tif (ctx.signal.aborted) {\n\t\treturn null;\n\t}\n\n\n\tconst destroy = renderStore(\n\t\tstore,\n\t\t(store, options) => createStoreField(store, {}, options),\n\t\tform,\n\t\tStoreLayout,\n\t\t{ editable: true },\n\t);\n\tctx.signal.addEventListener('abort', destroy, { once: true });\n\n\n\tconst submitButton = form.appendChild(document.createElement('button'));\n\tsubmitButton.type = 'submit';\n\tsubmitButton.textContent = '修改密码';\n\n\tform.addEventListener('submit', async event => {\n\t\tevent.preventDefault();\n\t\tif (submitButton.disabled) { return; }\n\t\ttry {\n\t\t\tsubmitButton.disabled = true;\n\t\t\tconst { password, newPassword1 } = store.value;\n\t\t\tconst r = await store.validate();\n\t\t\tif (r?.length) { return; }\n\t\t\tconst result = await Plugin.request.clone().put('password').body({\n\t\t\t\tpassword, newPassword: newPassword1,\n\t\t\t}).result();\n\t\t\tif (!result || result === 200 || typeof result === 'object') {\n\t\t\t\tstore.reset();\n\t\t\t\talert('密码修改完成');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (result === 403) {\n\t\t\t\talert('密码错误');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t} catch (e) {\n\t\t\tif (e instanceof Response) {\n\t\t\t\tif (e.status === 403) {\n\t\t\t\t\talert('密码错误');\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tsubmitButton.disabled = false;\n\t\t}\n\t});\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { a as verifyPassword, c as User_default, i as setPassword, l as UserAccount_default, r as loadUserInfo, s as UserPassword_default, t as findUser } from "./user-KPkkrnN4.mjs";
|
|
2
|
+
import { getUser } from "@yongdall/core";
|
|
3
|
+
|
|
4
|
+
//#region plugins/user/hooks.yongdall.mjs
|
|
5
|
+
/** @type {Hooks.Define['models']} */
|
|
6
|
+
const models = { user: User_default };
|
|
7
|
+
/** @type {Hooks.Define['migrationModels']} */
|
|
8
|
+
const migrationModels = [
|
|
9
|
+
User_default,
|
|
10
|
+
UserAccount_default,
|
|
11
|
+
UserPassword_default
|
|
12
|
+
];
|
|
13
|
+
/** @type {Partial<Hooks['userManager']>} */
|
|
14
|
+
const userManager = {
|
|
15
|
+
find(account, types) {
|
|
16
|
+
if (typeof account !== "string") return "";
|
|
17
|
+
return findUser(account, types);
|
|
18
|
+
},
|
|
19
|
+
verify: { password(userId, password) {
|
|
20
|
+
return verifyPassword(userId, password);
|
|
21
|
+
} },
|
|
22
|
+
setVerify: { password(userId, password, deadline) {
|
|
23
|
+
return setPassword(userId, password, deadline);
|
|
24
|
+
} },
|
|
25
|
+
async load(userId) {
|
|
26
|
+
return await loadUserInfo(userId) || {};
|
|
27
|
+
},
|
|
28
|
+
async exist(userId, login) {
|
|
29
|
+
return Boolean(await loadUserInfo(userId).then((u) => login ? u?.enabled : u));
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
//#endregion
|
|
34
|
+
export { migrationModels, models, userManager };
|
|
35
|
+
//# sourceMappingURL=hooks.yongdall.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.yongdall.mjs","names":["User","UserAccount","UserPassword"],"sources":["../../plugins/user/hooks.yongdall.mjs"],"sourcesContent":["/** @import { Hooks } from '@yongdall/core' */\nimport { getUser } from '@yongdall/core';\nimport { User, UserAccount, UserPassword, loadUserInfo, findUser, verifyPassword, getPasswordHash, setPassword } from './index.mjs';\n\n/** @type {Hooks.Define['models']} */\nexport const models = {\n\tuser: User,\n};\n\n/** @type {Hooks.Define['migrationModels']} */\nexport const migrationModels = [\n\tUser,\n\tUserAccount,\n\tUserPassword,\n];\n\n/** @type {Partial<Hooks['userManager']>} */\nexport const userManager = {\n\tfind(account, types) {\n\t\tif (typeof account !== 'string') { return ''; }\n\t\treturn findUser(account, types);\n\t},\n\tverify: {\n\t\tpassword(userId, password) {\n\t\t\treturn verifyPassword(userId, password);\n\t\t}\n\t},\n\tsetVerify: {\n\t\tpassword(userId, password, deadline) {\n\t\t\treturn setPassword(userId, password, deadline);\n\t\t}\n\n\t},\n\tasync load(userId) {\n\t\treturn await loadUserInfo(userId) || {};\n\t},\n\tasync exist(userId, login) {\n\t\treturn Boolean(await loadUserInfo(userId).then(u => login ? u?.enabled : u));\n\t}\n};\n"],"mappings":";;;;;AAKA,MAAa,SAAS,EACrB,MAAMA,cACN;;AAGD,MAAa,kBAAkB;CAC9BA;CACAC;CACAC;CACA;;AAGD,MAAa,cAAc;CAC1B,KAAK,SAAS,OAAO;AACpB,MAAI,OAAO,YAAY,SAAY,QAAO;AAC1C,SAAO,SAAS,SAAS,MAAM;;CAEhC,QAAQ,EACP,SAAS,QAAQ,UAAU;AAC1B,SAAO,eAAe,QAAQ,SAAS;IAExC;CACD,WAAW,EACV,SAAS,QAAQ,UAAU,UAAU;AACpC,SAAO,YAAY,QAAQ,UAAU,SAAS;IAG/C;CACD,MAAM,KAAK,QAAQ;AAClB,SAAO,MAAM,aAAa,OAAO,IAAI,EAAE;;CAExC,MAAM,MAAM,QAAQ,OAAO;AAC1B,SAAO,QAAQ,MAAM,aAAa,OAAO,CAAC,MAAK,MAAK,QAAQ,GAAG,UAAU,EAAE,CAAC;;CAE7E"}
|
package/index.d.mts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as imodel3 from "imodel";
|
|
2
|
+
import * as _yongdall_model0 from "@yongdall/model";
|
|
3
|
+
|
|
4
|
+
//#region plugins/user/models/User.d.mts
|
|
5
|
+
declare const _default: _yongdall_model0.ModelTable<{
|
|
6
|
+
username: imodel3.FieldDefine<_yongdall_model0.ModelTable<{
|
|
7
|
+
account: imodel3.FieldDefine<"string", false, false>;
|
|
8
|
+
userId: imodel3.FieldDefine<"uuid", false, false>;
|
|
9
|
+
type: imodel3.FieldDefine<"string", false, false>;
|
|
10
|
+
}, "user.account">, false, false>;
|
|
11
|
+
id: imodel3.FieldDefine<"uuid", false, false>;
|
|
12
|
+
name: imodel3.FieldDefine<"string", false, false>;
|
|
13
|
+
enabled: imodel3.FieldDefine<"bool", false, false>;
|
|
14
|
+
createdAt: imodel3.FieldDefine<"timestamp", false, false>;
|
|
15
|
+
updatedAt: imodel3.FieldDefine<"timestamp", false, false>;
|
|
16
|
+
}, "user">;
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region plugins/user/models/UserAccount.d.mts
|
|
19
|
+
declare const _default$1: _yongdall_model0.ModelTable<{
|
|
20
|
+
account: imodel3.FieldDefine<"string", false, false>;
|
|
21
|
+
userId: imodel3.FieldDefine<"uuid", false, false>;
|
|
22
|
+
type: imodel3.FieldDefine<"string", false, false>;
|
|
23
|
+
}, "user.account">;
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region plugins/user/models/UserPassword.d.mts
|
|
26
|
+
declare const _default$2: _yongdall_model0.ModelTable<{
|
|
27
|
+
userId: imodel3.FieldDefine<"uuid", false, false>;
|
|
28
|
+
password: imodel3.FieldDefine<"string", false, false>;
|
|
29
|
+
deadline: imodel3.FieldDefine<"timestamp", false, true>;
|
|
30
|
+
updatedAt: imodel3.FieldDefine<"timestamp", false, false>;
|
|
31
|
+
}, "user.password">;
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region plugins/user/common/verifyNewPassword.d.mts
|
|
34
|
+
/**
|
|
35
|
+
*
|
|
36
|
+
* @param {string} password
|
|
37
|
+
*/
|
|
38
|
+
declare function verifyNewPassword(password: string): Generator<"密码不足 8 位" | "密码至少包含小写字母、大写字母、数字、符号中的 3 种" | "密码不能包含admin" | "密码不能包含三位以上的相同的字母或数字" | "密码不能包含三位以上的连续的字母或数字", void, unknown>;
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region plugins/user/index.d.mts
|
|
41
|
+
/**
|
|
42
|
+
*
|
|
43
|
+
* @param {string} userId
|
|
44
|
+
*/
|
|
45
|
+
declare function loadUserInfo(userId: string): Promise<any>;
|
|
46
|
+
/**
|
|
47
|
+
*
|
|
48
|
+
* @param {string} account
|
|
49
|
+
* @param {string | string[] | Set<string> | null} [types]
|
|
50
|
+
*/
|
|
51
|
+
declare function findUser(account: string, types?: string | string[] | Set<string> | null): Promise<any>;
|
|
52
|
+
/**
|
|
53
|
+
*
|
|
54
|
+
* @param {string} password
|
|
55
|
+
* @param {string} userId
|
|
56
|
+
*/
|
|
57
|
+
declare function getPasswordHash(password: string, userId: string): Promise<string>;
|
|
58
|
+
/**
|
|
59
|
+
*
|
|
60
|
+
* @param {string} userId
|
|
61
|
+
* @param {string} password
|
|
62
|
+
* @returns {Promise<-2 | -1 | 1 | 2>}
|
|
63
|
+
*/
|
|
64
|
+
declare function verifyPassword(userId: string, password: string): Promise<-2 | -1 | 1 | 2>;
|
|
65
|
+
/**
|
|
66
|
+
*
|
|
67
|
+
* @param {string} userId
|
|
68
|
+
* @param {string?} password
|
|
69
|
+
* @param {Date?} [deadline]
|
|
70
|
+
* @returns {Promise<boolean>}
|
|
71
|
+
*/
|
|
72
|
+
declare function setPassword(userId: string, password: string | null, deadline?: Date | null): Promise<boolean>;
|
|
73
|
+
//#endregion
|
|
74
|
+
export { _default as User, _default$1 as UserAccount, _default$2 as UserPassword, findUser, getPasswordHash, loadUserInfo, setPassword, verifyNewPassword, verifyPassword };
|
package/index.mjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { a as verifyPassword, c as User_default, i as setPassword, l as UserAccount_default, n as getPasswordHash, o as verifyNewPassword, r as loadUserInfo, s as UserPassword_default, t as findUser } from "./user-KPkkrnN4.mjs";
|
|
2
|
+
|
|
3
|
+
export { User_default as User, UserAccount_default as UserAccount, UserPassword_default as UserPassword, findUser, getPasswordHash, loadUserInfo, setPassword, verifyNewPassword, verifyPassword };
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yongdall/user",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"main": "./index.mjs",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": "./index.mjs",
|
|
7
|
+
"./assets": "./assets/index.mjs"
|
|
8
|
+
},
|
|
9
|
+
"version": "0.1.0",
|
|
10
|
+
"description": "",
|
|
11
|
+
"keywords": [],
|
|
12
|
+
"author": "",
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@yongdall/connection": "^0.1.0",
|
|
16
|
+
"@yongdall/context": "^0.1.0",
|
|
17
|
+
"@yongdall/model": "^0.1.0",
|
|
18
|
+
"@yongdall/core": "^0.1.0",
|
|
19
|
+
"@yongdall/http": "^0.1.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@yongdall/web": "^0.1.0",
|
|
23
|
+
"@yongdall/types": "^0.1.0"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"@yongdall/web": "^0.1.0"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { i as setPassword, o as verifyNewPassword } from "./user-KPkkrnN4.mjs";
|
|
2
|
+
import { getUser, getUserPermissions, isSingleUser, verifyUser } from "@yongdall/core";
|
|
3
|
+
import { ApiRouter, useBody } from "@yongdall/http";
|
|
4
|
+
|
|
5
|
+
//#region plugins/user/routers.yongdall.mjs
|
|
6
|
+
const plugin = new ApiRouter();
|
|
7
|
+
plugin.put`password`(async (ctx) => {
|
|
8
|
+
const { password, newPassword, userId } = await useBody() || {};
|
|
9
|
+
if (!newPassword || typeof newPassword !== "string") return 404;
|
|
10
|
+
const loggedUser = await getUser();
|
|
11
|
+
const singleUser = await isSingleUser();
|
|
12
|
+
if (!singleUser && !loggedUser) return 404;
|
|
13
|
+
const targetUser = userId || loggedUser;
|
|
14
|
+
if (!targetUser) return 404;
|
|
15
|
+
if (userId === loggedUser || !singleUser) {
|
|
16
|
+
if (!password || typeof password !== "string") return 404;
|
|
17
|
+
}
|
|
18
|
+
if (userId === loggedUser) {
|
|
19
|
+
if (newPassword === password) return 404;
|
|
20
|
+
} else if (!singleUser) {
|
|
21
|
+
if (!(await getUserPermissions()).has("system:user:password")) return 402;
|
|
22
|
+
}
|
|
23
|
+
if (loggedUser && password && !await verifyUser(loggedUser, password, "password")) return 403;
|
|
24
|
+
if ([...verifyNewPassword(newPassword)].length) return 408;
|
|
25
|
+
await setPassword(targetUser, newPassword, userId === loggedUser ? null : /* @__PURE__ */ new Date());
|
|
26
|
+
return 200;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
export { plugin };
|
|
31
|
+
//# sourceMappingURL=routers.yongdall.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routers.yongdall.mjs","names":[],"sources":["../../plugins/user/routers.yongdall.mjs"],"sourcesContent":["import { ApiRouter, useBody } from '@yongdall/http';\nimport { getUser, getUserPermissions, isSingleUser, verifyUser } from '@yongdall/core';\nimport { verifyNewPassword } from './common/verifyNewPassword.mjs';\nimport { setPassword } from './index.mjs';\n\nexport const plugin = new ApiRouter();\n\nplugin.put`password`(async ctx => {\n\tconst { password, newPassword, userId } = await useBody() || {};\n\tif (!newPassword || typeof newPassword !== 'string') { return 404; }\n\tconst loggedUser = await getUser();\n\tconst singleUser = await isSingleUser();\n\tif (!singleUser && !loggedUser) { return 404; }\n\tconst targetUser = userId || loggedUser;\n\tif (!targetUser) { return 404; }\n\tif (userId === loggedUser || !singleUser) {\n\t\tif (!password || typeof password !== 'string') { return 404; }\n\t}\n\tif (userId === loggedUser) {\n\t\tif (newPassword === password) { return 404; }\n\t} else if (!singleUser) {\n\t\tconst permissions = await getUserPermissions();\n\t\tif (!permissions.has('system:user:password')) { return 402; }\n\n\t}\n\tif (loggedUser && password && !await verifyUser(loggedUser, password, 'password')) { return 403; }\n\tif ([...verifyNewPassword(newPassword)].length) { return 408; }\n\tawait setPassword(targetUser, newPassword, userId === loggedUser ? null : new Date);\n\treturn 200;\n});\n"],"mappings":";;;;;AAKA,MAAa,SAAS,IAAI,WAAW;AAErC,OAAO,GAAG,WAAW,OAAM,QAAO;CACjC,MAAM,EAAE,UAAU,aAAa,WAAW,MAAM,SAAS,IAAI,EAAE;AAC/D,KAAI,CAAC,eAAe,OAAO,gBAAgB,SAAY,QAAO;CAC9D,MAAM,aAAa,MAAM,SAAS;CAClC,MAAM,aAAa,MAAM,cAAc;AACvC,KAAI,CAAC,cAAc,CAAC,WAAc,QAAO;CACzC,MAAM,aAAa,UAAU;AAC7B,KAAI,CAAC,WAAc,QAAO;AAC1B,KAAI,WAAW,cAAc,CAAC,YAC7B;MAAI,CAAC,YAAY,OAAO,aAAa,SAAY,QAAO;;AAEzD,KAAI,WAAW,YACd;MAAI,gBAAgB,SAAY,QAAO;YAC7B,CAAC,YAEX;MAAI,EADgB,MAAM,oBAAoB,EAC7B,IAAI,uBAAuB,CAAI,QAAO;;AAGxD,KAAI,cAAc,YAAY,CAAC,MAAM,WAAW,YAAY,UAAU,WAAW,CAAI,QAAO;AAC5F,KAAI,CAAC,GAAG,kBAAkB,YAAY,CAAC,CAAC,OAAU,QAAO;AACzD,OAAM,YAAY,YAAY,aAAa,WAAW,aAAa,uBAAO,IAAI,MAAI,CAAC;AACnF,QAAO;EACN"}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { useTenant } from "@yongdall/core";
|
|
2
|
+
import { connect } from "@yongdall/connection";
|
|
3
|
+
import { Query, Where, createField, createModel, now, uuid } from "@yongdall/model";
|
|
4
|
+
|
|
5
|
+
//#region plugins/user/models/UserAccount.mjs
|
|
6
|
+
var UserAccount_default = createModel("user.account", {
|
|
7
|
+
account: createField("string", {
|
|
8
|
+
nullable: false,
|
|
9
|
+
label: "帐号",
|
|
10
|
+
unique: true
|
|
11
|
+
}),
|
|
12
|
+
userId: createField("uuid", {
|
|
13
|
+
nullable: false,
|
|
14
|
+
label: "用户",
|
|
15
|
+
primary: 1
|
|
16
|
+
}),
|
|
17
|
+
type: createField("string", {
|
|
18
|
+
nullable: false,
|
|
19
|
+
default: "",
|
|
20
|
+
primary: 2,
|
|
21
|
+
label: "类型"
|
|
22
|
+
})
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region plugins/user/models/User.mjs
|
|
27
|
+
var User_default = createModel("user", {
|
|
28
|
+
username: createField(UserAccount_default, {
|
|
29
|
+
label: "登陆账号",
|
|
30
|
+
constraints: {
|
|
31
|
+
userId: { field: "id" },
|
|
32
|
+
type: { value: "username" }
|
|
33
|
+
}
|
|
34
|
+
}),
|
|
35
|
+
id: createField("uuid", {
|
|
36
|
+
nullable: false,
|
|
37
|
+
default: uuid,
|
|
38
|
+
uncreatable: true,
|
|
39
|
+
primary: 1
|
|
40
|
+
}),
|
|
41
|
+
name: createField("string", {
|
|
42
|
+
nullable: false,
|
|
43
|
+
default: "",
|
|
44
|
+
label: "名称",
|
|
45
|
+
layout: {
|
|
46
|
+
colSpan: 6,
|
|
47
|
+
head: 2
|
|
48
|
+
}
|
|
49
|
+
}),
|
|
50
|
+
enabled: createField("bool", {
|
|
51
|
+
nullable: false,
|
|
52
|
+
default: true,
|
|
53
|
+
label: "是否启用帐号"
|
|
54
|
+
}),
|
|
55
|
+
createdAt: createField("timestamp", {
|
|
56
|
+
nullable: false,
|
|
57
|
+
creating: now,
|
|
58
|
+
label: "创建日期"
|
|
59
|
+
}),
|
|
60
|
+
updatedAt: createField("timestamp", {
|
|
61
|
+
nullable: false,
|
|
62
|
+
creating: now,
|
|
63
|
+
updating: now,
|
|
64
|
+
label: "最后更新日期"
|
|
65
|
+
})
|
|
66
|
+
}, {
|
|
67
|
+
label: "用户",
|
|
68
|
+
labelField: "name",
|
|
69
|
+
permissions: [{
|
|
70
|
+
permission: "system:user",
|
|
71
|
+
fields: "*",
|
|
72
|
+
authorizations: [
|
|
73
|
+
"query",
|
|
74
|
+
"read",
|
|
75
|
+
"create",
|
|
76
|
+
"update",
|
|
77
|
+
"destroy",
|
|
78
|
+
"add",
|
|
79
|
+
"remove"
|
|
80
|
+
]
|
|
81
|
+
}],
|
|
82
|
+
scripts: ["@yongdall/user#models.user"]
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region plugins/user/models/UserPassword.mjs
|
|
87
|
+
var UserPassword_default = createModel("user.password", {
|
|
88
|
+
userId: createField("uuid", {
|
|
89
|
+
nullable: false,
|
|
90
|
+
label: "用户",
|
|
91
|
+
primary: 1
|
|
92
|
+
}),
|
|
93
|
+
password: createField("string", {
|
|
94
|
+
nullable: false,
|
|
95
|
+
default: "",
|
|
96
|
+
label: "新密码",
|
|
97
|
+
renderer: "password",
|
|
98
|
+
layout: { colSpan: 6 }
|
|
99
|
+
}),
|
|
100
|
+
deadline: createField("timestamp", {
|
|
101
|
+
nullable: true,
|
|
102
|
+
label: "有效期",
|
|
103
|
+
layout: { colSpan: 6 }
|
|
104
|
+
}),
|
|
105
|
+
updatedAt: createField("timestamp", {
|
|
106
|
+
nullable: false,
|
|
107
|
+
creating: now,
|
|
108
|
+
updating: now,
|
|
109
|
+
label: "最后更新日期",
|
|
110
|
+
layout: { colSpan: 6 }
|
|
111
|
+
})
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region plugins/user/common/verifyNewPassword.mjs
|
|
116
|
+
const signTypeRegex = [
|
|
117
|
+
/[a-z]/,
|
|
118
|
+
/[A-Z]/,
|
|
119
|
+
/[0-9]/,
|
|
120
|
+
/[~!@#$%^&*()_+{}|<>?,./:";'\\\[\]-]/
|
|
121
|
+
];
|
|
122
|
+
/**
|
|
123
|
+
*
|
|
124
|
+
* @param {string} password
|
|
125
|
+
*/
|
|
126
|
+
function* verifyNewPassword(password) {
|
|
127
|
+
if (password.length < 8) yield "密码不足 8 位";
|
|
128
|
+
if (signTypeRegex.filter((r) => r.test(password)).length < 3) yield `密码至少包含小写字母、大写字母、数字、符号中的 3 种`;
|
|
129
|
+
if (password.toLowerCase().includes("admin")) yield `密码不能包含admin`;
|
|
130
|
+
if ([...password].find((a, index, list) => list[index - 1] === a && list[index + 1] === a)) yield `密码不能包含三位以上的相同的字母或数字`;
|
|
131
|
+
if ([...password].find((a, index, list) => {
|
|
132
|
+
const p = list[index - 1];
|
|
133
|
+
const n = list[index + 1];
|
|
134
|
+
if (!p || !n) return false;
|
|
135
|
+
const x = p.charCodeAt(0);
|
|
136
|
+
const y = a.charCodeAt(0);
|
|
137
|
+
const z = n.charCodeAt(0);
|
|
138
|
+
const u = x - y;
|
|
139
|
+
const v = y - z;
|
|
140
|
+
if (u === v && (v === 1 || v === -1)) return true;
|
|
141
|
+
})) yield `密码不能包含三位以上的连续的字母或数字`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region plugins/user/index.mjs
|
|
146
|
+
const USER_INFO_KEY = "user:info";
|
|
147
|
+
/**
|
|
148
|
+
*
|
|
149
|
+
* @param {string} userId
|
|
150
|
+
*/
|
|
151
|
+
async function loadUserInfo(userId) {
|
|
152
|
+
return connect("cache").memoize(USER_INFO_KEY, userId, async () => {
|
|
153
|
+
return (await connect("rdb").select(new Query(User_default).where({ id: userId }).limit(1)))[0];
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
const allTypes = [
|
|
157
|
+
"username",
|
|
158
|
+
"phone",
|
|
159
|
+
"email"
|
|
160
|
+
];
|
|
161
|
+
/**
|
|
162
|
+
*
|
|
163
|
+
* @param {string} account
|
|
164
|
+
* @param {string | string[] | Set<string> | null} [types]
|
|
165
|
+
*/
|
|
166
|
+
async function findUser(account, types) {
|
|
167
|
+
const typeSet = typeof types === "string" ? new Set(types) : new Set(types);
|
|
168
|
+
const t = typeSet.size ? allTypes.filter((v) => typeSet.has(v)) : allTypes;
|
|
169
|
+
if (!t.length) return "";
|
|
170
|
+
return (await connect("rdb").select(new Query(UserAccount_default).where("account", account.toLowerCase()).where("type", "in", t).limit(1)))[0]?.userId || "";
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* 使用 HMAC-SHA256 对一个或多个值进行哈希,使用 salt 作为密钥。
|
|
174
|
+
* 输出为 base64url 格式。
|
|
175
|
+
*
|
|
176
|
+
* @param {string | ArrayBuffer | ArrayBufferView<ArrayBuffer>} salt
|
|
177
|
+
* @param {string | number} value
|
|
178
|
+
* @param {...(string | number)} values
|
|
179
|
+
* @returns {Promise<string>}
|
|
180
|
+
*/
|
|
181
|
+
async function hash(salt, value, ...values) {
|
|
182
|
+
/** @type {ArrayBuffer | ArrayBufferView<ArrayBuffer>} */
|
|
183
|
+
let keyBuffer;
|
|
184
|
+
if (salt instanceof ArrayBuffer || ArrayBuffer.isView(salt)) keyBuffer = salt;
|
|
185
|
+
else if (typeof salt === "string") keyBuffer = new TextEncoder().encode(salt);
|
|
186
|
+
else throw new Error("`salt` 参数必须为字符串或 Uint8Array");
|
|
187
|
+
const cryptoKey = await crypto.subtle.importKey("raw", keyBuffer, {
|
|
188
|
+
name: "HMAC",
|
|
189
|
+
hash: "SHA-256"
|
|
190
|
+
}, false, ["sign"]);
|
|
191
|
+
/** @type {string[]} */
|
|
192
|
+
let data = [];
|
|
193
|
+
if (typeof value === "number") data.push(String(value));
|
|
194
|
+
else if (typeof value === "string") data.push(JSON.stringify(value).slice(1, -1));
|
|
195
|
+
else throw new Error("`value` 参数必须为字符串或数字");
|
|
196
|
+
for (const v of values) if (typeof v === "number") data.push(String(v));
|
|
197
|
+
else if (typeof v === "string") data.push(JSON.stringify(v).slice(1, -1));
|
|
198
|
+
const messageBuffer = new TextEncoder().encode(data.join("\n"));
|
|
199
|
+
const signature = await crypto.subtle.sign("HMAC", cryptoKey, messageBuffer);
|
|
200
|
+
return btoa(String.fromCharCode(...new Uint8Array(signature))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
*
|
|
204
|
+
* @param {string} password
|
|
205
|
+
* @param {string} userId
|
|
206
|
+
*/
|
|
207
|
+
async function getPasswordHash(password, userId) {
|
|
208
|
+
return await hash((await useTenant()).salt, password, userId);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
*
|
|
212
|
+
* @param {string} userId
|
|
213
|
+
* @param {string} password
|
|
214
|
+
* @returns {Promise<-2 | -1 | 1 | 2>}
|
|
215
|
+
*/
|
|
216
|
+
async function verifyPassword(userId, password) {
|
|
217
|
+
const item = await connect("rdb").first(new Query(UserPassword_default).select("password", "deadline").where({ userId }));
|
|
218
|
+
if (!item) return -2;
|
|
219
|
+
if (item.password !== await getPasswordHash(password, userId)) return -1;
|
|
220
|
+
if (!item.deadline) return 1;
|
|
221
|
+
if (Number(item.deadline) < Date.now()) return 2;
|
|
222
|
+
return 1;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
*
|
|
226
|
+
* @param {string} userId
|
|
227
|
+
* @param {string?} password
|
|
228
|
+
* @param {Date?} [deadline]
|
|
229
|
+
* @returns {Promise<boolean>}
|
|
230
|
+
*/
|
|
231
|
+
async function setPassword(userId, password, deadline) {
|
|
232
|
+
if (!password) return false;
|
|
233
|
+
const value = await getPasswordHash(password, userId);
|
|
234
|
+
const connection = connect("rdb");
|
|
235
|
+
if (await connection.first(new Query(UserPassword_default).select("password").where({ userId }))) connection.update(new Query(UserPassword_default), {
|
|
236
|
+
password: value,
|
|
237
|
+
deadline: deadline instanceof Date && deadline.valueOf() ? deadline : null
|
|
238
|
+
}, Where.and("userId", userId));
|
|
239
|
+
else connection.insert(new Query(UserPassword_default), {
|
|
240
|
+
userId,
|
|
241
|
+
password: value,
|
|
242
|
+
deadline: deadline instanceof Date && deadline.valueOf() ? deadline : null
|
|
243
|
+
});
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
//#endregion
|
|
248
|
+
export { verifyPassword as a, User_default as c, setPassword as i, UserAccount_default as l, getPasswordHash as n, verifyNewPassword as o, loadUserInfo as r, UserPassword_default as s, findUser as t };
|
|
249
|
+
//# sourceMappingURL=user-KPkkrnN4.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-KPkkrnN4.mjs","names":["UserAccount","User","UserAccount","UserPassword"],"sources":["../../plugins/user/models/UserAccount.mjs","../../plugins/user/models/User.mjs","../../plugins/user/models/UserPassword.mjs","../../plugins/user/common/verifyNewPassword.mjs","../../plugins/user/index.mjs"],"sourcesContent":["import { createModel, createField, now } from '@yongdall/model';\n\n\nexport default createModel('user.account', {\n\taccount: createField('string', { nullable: false, label: '帐号', unique: true }),\n\tuserId: createField('uuid', { nullable: false, label: '用户', primary: 1 }),\n\ttype: createField('string', { nullable: false, default: '', primary: 2, label: '类型' }),\n});\n","import { createField, createModel, now, uuid } from '@yongdall/model';\nimport UserAccount from './UserAccount.mjs';\n\nexport default createModel('user', {\n\tusername: createField(UserAccount, { label: '登陆账号', constraints: {userId: {field: 'id'}, type: {value: 'username'}}}),\n\tid: createField('uuid', { nullable: false, default: uuid, uncreatable: true, primary: 1 }),\n\tname: createField('string', { nullable: false, default: '', label: '名称', layout: { colSpan: 6, head: 2 } }),\n\tenabled: createField('bool', { nullable: false, default: true, label: '是否启用帐号' }),\n\tcreatedAt: createField('timestamp', { nullable: false, creating: now, label: '创建日期' }),\n\tupdatedAt: createField('timestamp', { nullable: false, creating: now, updating: now, label: '最后更新日期' }),\n}, {\n\tlabel: '用户',\n\tlabelField: 'name',\n\n\tpermissions: [\n\t\t{ permission: 'system:user', fields: '*', authorizations: ['query', 'read', 'create', 'update', 'destroy', 'add', 'remove'] },\n\t],\n\tscripts: ['@yongdall/user#models.user'],\n});\n","import { createField, createModel, now } from '@yongdall/model';\n\n\nexport default createModel('user.password', {\n\tuserId: createField('uuid', { nullable: false, label: '用户', primary: 1 }),\n\tpassword: createField('string', { nullable: false, default: '', label: '新密码', renderer: 'password', layout: {colSpan: 6} }),\n\tdeadline: createField('timestamp', { nullable: true, label: '有效期', layout: {colSpan: 6} }),\n\tupdatedAt: createField('timestamp', { nullable: false, creating: now, updating: now, label: '最后更新日期', layout: {colSpan: 6} }),\n})\n","const signTypeRegex = [\n\t/[a-z]/,\n\t/[A-Z]/,\n\t/[0-9]/,\n\t/[~!@#$%^&*()_+{}|<>?,./:\";'\\\\\\[\\]-]/,\n]\n/**\n * \n * @param {string} password \n */\nexport function* verifyNewPassword(password) {\n\tif (password.length < 8) { yield '密码不足 8 位'; }\n\t\n\tif (signTypeRegex.filter(r => r.test(password)).length < 3) {\n\t\tyield `密码至少包含小写字母、大写字母、数字、符号中的 3 种`;\n\t}\n\tif (password.toLowerCase().includes('admin')) {\n\t\tyield `密码不能包含admin`;\n\t}\n\tif ([...password].find((a,index, list) => list[index - 1] === a && list[index + 1] === a)) {\n\t\tyield `密码不能包含三位以上的相同的字母或数字`;\n\t}\n\tif ([...password].find((a,index, list) => {\n\t\tconst p = list[index - 1];\n\t\tconst n = list[index + 1];\n\t\tif (!p || !n) { return false; }\n\t\tconst x = p.charCodeAt(0);\n\t\tconst y = a.charCodeAt(0);\n\t\tconst z = n.charCodeAt(0);\n\t\tconst u = x - y;\n\t\tconst v = y - z;\n\t\tif (u === v && (v === 1 || v === -1)) { return true}\n\t})) {\n\t\tyield `密码不能包含三位以上的连续的字母或数字`;\n\t}\n}\n","import { connect } from '@yongdall/connection';\nimport { Query, Where } from '@yongdall/model';\nimport { useTenant } from '@yongdall/core';\nimport { User, UserAccount, UserPassword } from './models/index.mjs';\n\nexport * from './models/index.mjs';\nexport { verifyNewPassword } from './common/verifyNewPassword.mjs'\n\nconst USER_INFO_KEY = 'user:info';\n\n/**\n * \n * @param {string} userId \n */\nexport async function loadUserInfo(userId) {\n\n\treturn connect('cache').memoize(USER_INFO_KEY, userId, async () => {\n\t\tconst rdb = connect('rdb');\n\t\tconst info = await rdb.select(new Query(User).where({ id: userId }).limit(1));\n\t\treturn info[0];\n\t});\n}\n\nconst allTypes = ['username', 'phone', 'email'];\n\n/**\n * \n * @param {string} account \n * @param {string | string[] | Set<string> | null} [types] \n */\nexport async function findUser(account, types){\n\tconst typeSet = typeof types === 'string' ? new Set(types) : new Set(types);\n\tconst t = typeSet.size ? allTypes.filter(v => typeSet.has(v)) : allTypes;\n\tif (!t.length) { return ''; }\n\tconst rdb = connect('rdb');\n\tconst info = await rdb.select(\n\t\tnew Query(UserAccount)\n\t\t\t.where('account', account.toLowerCase())\n\t\t\t.where('type', 'in', t)\n\t\t\t.limit(1),\n\t);\n\treturn info[0]?.userId || '';\n}\n/**\n * 使用 HMAC-SHA256 对一个或多个值进行哈希,使用 salt 作为密钥。\n * 输出为 base64url 格式。\n *\n * @param {string | ArrayBuffer | ArrayBufferView<ArrayBuffer>} salt\n * @param {string | number} value\n * @param {...(string | number)} values\n * @returns {Promise<string>}\n */\nasync function hash(salt, value, ...values) {\n\t// 1. 准备密钥(salt)\n\t/** @type {ArrayBuffer | ArrayBufferView<ArrayBuffer>} */\n\tlet keyBuffer;\n\tif (salt instanceof ArrayBuffer || ArrayBuffer.isView(salt)) {\n\t\tkeyBuffer = salt;\n\t} else if (typeof salt === 'string') {\n\t\tkeyBuffer = new TextEncoder().encode(salt);\n\t} else {\n\t\tthrow new Error('`salt` 参数必须为字符串或 Uint8Array');\n\t}\n\n\t// 2. 导入 HMAC 密钥\n\tconst cryptoKey = await crypto.subtle.importKey(\n\t\t'raw',\n\t\tkeyBuffer,\n\t\t{ name: 'HMAC', hash: 'SHA-256' },\n\t\tfalse,\n\t\t['sign']\n\t);\n\n\t// 3. 构造输入数据\n\t/** @type {string[]} */\n\tlet data = [];\n\n\t// 处理第一个 value\n\tif (typeof value === 'number') {\n\t\tdata.push(String(value));\n\t} else if (typeof value === 'string') {\n\t\t// JSON.stringify(value).slice(1, -1) 相当于去掉引号,等价于直接编码字符串内容\n\t\tdata.push(JSON.stringify(value).slice(1, -1));\n\t} else {\n\t\tthrow new Error('`value` 参数必须为字符串或数字');\n\t}\n\n\t// 处理后续 values\n\tfor (const v of values) {\n\t\tif (typeof v === 'number') {\n\t\t\tdata.push(String(v));\n\t\t} else if (typeof v === 'string') {\n\t\t\tdata.push(JSON.stringify(v).slice(1, -1));\n\t\t}\n\t}\n\n\t// 合并所有数据\n\tconst messageBuffer = new TextEncoder().encode(data.join('\\n'))\n\n\t// 4. 生成 HMAC\n\tconst signature = await crypto.subtle.sign('HMAC', cryptoKey, messageBuffer);\n\n\t// 5. 转为 base64url 格式\n\tconst base64 = btoa(String.fromCharCode(...new Uint8Array(signature)));\n\treturn base64\n\t\t.replace(/\\+/g, '-')\n\t\t.replace(/\\//g, '_')\n\t\t.replace(/=+$/, ''); // 移除填充\n}\n/**\n * \n * @param {string} password \n * @param {string} userId \n */\nexport async function getPasswordHash(password, userId) {\n\tconst tenant = await useTenant();\n\treturn await hash(tenant.salt, password, userId)\n\n}\n/**\n * \n * @param {string} userId \n * @param {string} password \n * @returns {Promise<-2 | -1 | 1 | 2>}\n */\nexport async function verifyPassword(userId, password) {\n\tconst item = await connect('rdb').first(\n\t\tnew Query(UserPassword)\n\t\t.select('password', 'deadline')\n\t\t.where({ userId })\n\t);\n\tif (!item) { return -2; }\n\tif (item.password !== await getPasswordHash(password, userId)) { return -1; }\n\tif (!item.deadline) { return 1; }\n\tif (Number(item.deadline) < Date.now()) { return 2; }\n\treturn 1;\n}\n/**\n * \n * @param {string} userId \n * @param {string?} password \n * @param {Date?} [deadline] \n * @returns {Promise<boolean>}\n */\n\nexport async function setPassword(userId, password, deadline) {\n\tif (!password) { return false; }\n\tconst value = await getPasswordHash(password, userId);\n\t\n\n\tconst connection = connect('rdb');\n\tconst item = await connection.first(\n\t\tnew Query(UserPassword)\n\t\t.select('password')\n\t\t.where({ userId })\n\t);\n\tif (item) {\n\t\tconnection.update(new Query(UserPassword), {\n\t\t\tpassword: value,\n\t\t\tdeadline: deadline instanceof Date && deadline.valueOf() ? deadline : null,\n\t\t}, Where.and('userId', userId));\n\t} else {\n\t\tconnection.insert(new Query(UserPassword), {\n\t\t\tuserId,\n\t\t\tpassword: value,\n\t\t\tdeadline: deadline instanceof Date && deadline.valueOf() ? deadline : null,\n\t\t});\n\n\t}\n\treturn true;\n\t\n}\n"],"mappings":"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"}
|