@tachybase/module-auth 1.6.1 → 1.6.3

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.
@@ -1,14 +1,14 @@
1
1
  module.exports = {
2
2
  "react": "18.3.1",
3
- "@tachybase/client": "1.6.1",
3
+ "@tachybase/client": "1.6.3",
4
4
  "react-router-dom": "6.28.1",
5
- "@tego/client": "1.6.0-alpha.9",
5
+ "@tego/client": "1.6.1",
6
6
  "axios": "1.13.0",
7
7
  "lodash": "4.17.21",
8
- "@tego/server": "1.6.0-alpha.9",
9
- "@tachybase/test": "1.6.0-alpha.9",
8
+ "@tego/server": "1.6.1",
9
+ "@tachybase/test": "1.6.1",
10
10
  "antd": "5.22.5",
11
- "@tachybase/schema": "1.6.0-alpha.9",
11
+ "@tachybase/schema": "1.6.1",
12
12
  "react-i18next": "16.2.1",
13
13
  "@ant-design/icons": "6.1.0"
14
14
  };
@@ -12,6 +12,7 @@
12
12
  "Expired token refresh limit": "Expired token refresh limit",
13
13
  "Hours": "Hours",
14
14
  "In configuration mode, the entire column becomes transparent. In non-configuration mode, the entire column will be hidden. Even if the entire column is hidden, its configured default values and other settings will still take effect.": "In configuration mode, the entire column becomes transparent. In non-configuration mode, the entire column will be hidden. Even if the entire column is hidden, its configured default values and other settings will still take effect.",
15
+ "Invalid status": "Invalid status",
15
16
  "Minutes": "Minutes",
16
17
  "No authentication methods available.": "No authentication methods available.",
17
18
  "Not a valid cellphone number, please re-enter": "Not a valid cellphone number, please re-enter",
@@ -28,6 +29,8 @@
28
29
  "Sign in via email": "Sign in via email",
29
30
  "Sign in via password": "Sign in via password",
30
31
  "Sign-in": "Sign-in",
32
+ "Status expired, auto restored": "Status expired, auto restored",
33
+ "System error, please contact administrator": "System error, please contact administrator",
31
34
  "The authentication allows users to sign in via username or email.": "The authentication allows users to sign in via username or email.",
32
35
  "The maximum time limit allowed for refreshing a Token after it expires. After this time limit, the token cannot be automatically renewed, and the user needs to log in again.": "The maximum time limit allowed for refreshing a Token after it expires. After this time limit, the token cannot be automatically renewed, and the user needs to log in again.",
33
36
  "The maximum valid time for each user login. During the session validity, the Token will be automatically updated. After the timeout, the user is required to log in again.": "The maximum valid time for each user login. During the session validity, the Token will be automatically updated. After the timeout, the user is required to log in again.",
@@ -42,12 +45,16 @@
42
45
  "Token validity period must be less than session validity period!": "Token validity period must be less than session validity period!",
43
46
  "Unauthenticated. Please sign in to continue.": "Unauthenticated. Please sign in to continue.",
44
47
  "Unbind": "Unbind",
48
+ "Unknown status": "Unknown status",
45
49
  "User can bind or unbind the sign in type": "User can bind or unbind the sign in type",
46
50
  "User not found. Please sign in again to continue.": "User not found. Please sign in again to continue.",
51
+ "User status does not allow login": "User status does not allow login",
52
+ "User status is invalid, please contact administrator": "User status is invalid, please contact administrator",
47
53
  "Username/Email": "Username/Email",
48
54
  "Your account has been disabled, please contact administrator if you have any questions": "Your account has been disabled, please contact administrator if you have any questions",
49
55
  "Your account has been locked due to multiple failed login attempts. Please try again later.": "Your account has been locked due to multiple failed login attempts. Please try again later.",
50
56
  "Your account is under review, please wait for administrator approval": "Your account is under review, please wait for administrator approval",
57
+ "Your account status has changed. Please sign in again.": "Your account status has changed. Please sign in again.",
51
58
  "Your session has expired. Please sign in again.": "Your session has expired. Please sign in again.",
52
59
  "gitea": "Gitea",
53
60
  "sms": "SMS",
@@ -5,6 +5,7 @@
5
5
  "Auth UID": "인증 UID",
6
6
  "Authentication": "인증",
7
7
  "Authenticators": "인증기",
8
+ "Invalid status": "상태 오류",
8
9
  "No authentication methods available.": "사용 가능한 인증 방법이 없습니다.",
9
10
  "Not a valid cellphone number, please re-enter": "유효하지 않은 휴대폰 번호입니다. 다시 입력하세요.",
10
11
  "Not allowed to sign up": "가입할 수 없음",
@@ -16,14 +17,20 @@
16
17
  "Sign in via email": "이메일로 로그인",
17
18
  "Sign in via password": "비밀번호로 로그인",
18
19
  "Sign-in": "로그인",
20
+ "Status expired, auto restored": "상태가 만료되었습니다. 자동으로 복구되었습니다.",
21
+ "System error, please contact administrator": "시스템 오류입니다. 관리자에게 문의하세요.",
19
22
  "The authentication allows users to sign in via username or email.": "이 인증 방식을 사용하면 사용자가 사용자 이름 또는 이메일로 로그인할 수 있습니다.",
20
23
  "The password is inconsistent, please re-enter": "비밀번호가 일치하지 않습니다. 다시 입력하세요.",
21
24
  "The password is incorrect, please re-enter": "비밀번호가 잘못되었습니다. 다시 입력하세요.",
22
25
  "The phone number has been registered, please login directly": "전화번호가 이미 등록되어 있습니다. 직접 로그인하세요.",
23
26
  "The phone number is not registered, please register first": "전화번호가 등록되어 있지 않습니다. 먼저 등록하세요.",
24
27
  "The username or email is incorrect, please re-enter": "사용자 이름 또는 이메일이 잘못되었습니다. 다시 입력하세요.",
28
+ "Unknown status": "알 수 없는 상태",
29
+ "User status does not allow login": "사용자 상태가 로그인을 허용하지 않습니다.",
30
+ "User status is invalid, please contact administrator": "사용자 상태가 오류입니다. 관리자에게 문의하세요.",
25
31
  "Username/Email": "사용자 이름/이메일",
26
32
  "Your account has been disabled, please contact administrator if you have any questions": "Your account has been disabled, please contact administrator if you have any questions",
27
33
  "Your account has been locked due to multiple failed login attempts. Please try again later.": "Your account has been locked due to multiple failed login attempts. Please try again later.",
28
- "Your account is under review, please wait for administrator approval": "Your account is under review, please wait for administrator approval"
34
+ "Your account is under review, please wait for administrator approval": "Your account is under review, please wait for administrator approval",
35
+ "Your account status has changed. Please sign in again.": "사용자 상태가 변경되었습니다. 다시 로그인하세요."
29
36
  }
@@ -11,6 +11,7 @@
11
11
  "Days": "天",
12
12
  "Expired token refresh limit": "过期 Token 刷新时限",
13
13
  "Hours": "小时",
14
+ "Invalid status": "状态异常",
14
15
  "Minutes": "分钟",
15
16
  "No authentication methods available.": "没有可用的认证方式。",
16
17
  "Not a valid cellphone number, please re-enter": "不是有效的手机号,请重新输入",
@@ -27,6 +28,8 @@
27
28
  "Sign in via email": "邮箱登录",
28
29
  "Sign in via password": "密码登录",
29
30
  "Sign-in": "登录",
31
+ "Status expired, auto restored": "状态已过期,自动恢复",
32
+ "System error, please contact administrator": "系统错误,请联系管理员",
30
33
  "The authentication allows users to sign in via username or email.": "该认证方式支持用户通过用户名或邮箱登录。",
31
34
  "The maximum time limit allowed for refreshing a Token after it expires. After this time limit, the token cannot be automatically renewed, and the user needs to log in again.": "Token 过期后允许刷新的最大时限,超过此时限后,Token 无法自动更新,用户需重新登录。",
32
35
  "The maximum valid time for each user login. During the session validity, the Token will be automatically updated. After the timeout, the user is required to log in again.": "用户每次登录的最长有效时间,在会话有效期内,Token 会自动更新,超时后要求用户重新登录。",
@@ -41,13 +44,17 @@
41
44
  "Token validity period must be less than session validity period!": "Token 有效期必须小于会话有效期!",
42
45
  "Unauthenticated. Please sign in to continue.": "未认证。请登录以继续。",
43
46
  "Unbind": "解绑",
47
+ "Unknown status": "未知状态",
44
48
  "User can bind or unbind the sign in type": "用户可以绑定或解绑登录方式",
45
49
  "User not found. Please sign in again to continue.": "用户不存在。请重新登录以继续。",
50
+ "User status does not allow login": "用户状态不允许登录",
51
+ "User status is invalid, please contact administrator": "用户状态异常,请联系管理员",
46
52
  "Username/Email": "用户名/邮箱",
47
53
  "WeChat": "微信",
48
54
  "Your account has been disabled, please contact administrator if you have any questions": "您的账户已被停用,如有疑问请联系管理员",
49
55
  "Your account has been locked due to multiple failed login attempts. Please try again later.": "您的账户因多次登录失败已被锁定,请稍后再试",
50
56
  "Your account is under review, please wait for administrator approval": "您的账户正在审核中,请等待管理员审核通过",
57
+ "Your account status has changed. Please sign in again.": "您的账号状态已变更,请重新登录",
51
58
  "Your session has expired. Please sign in again.": "您的会话已过期,请重新登录。",
52
59
  "gitea": "Gitea 验证登录",
53
60
  "sms": "短信验证",
@@ -1 +1 @@
1
- {"name":"cron","description":"Cron jobs for your node","version":"3.3.1","author":"Nick Campbell <nicholas.j.campbell@gmail.com> (https://github.com/ncb000gt)","bugs":{"url":"https://github.com/kelektiv/node-cron/issues"},"repository":{"type":"git","url":"https://github.com/kelektiv/node-cron.git"},"main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc -b tsconfig.build.json","lint:eslint":"eslint src/ tests/","lint:prettier":"prettier ./**/*.{json,md,yml} --check","lint":"npm run lint:eslint && npm run lint:prettier","lint:fix":"npm run lint:eslint -- --fix && npm run lint:prettier -- --write","test":"jest --coverage","test:watch":"jest --watch --coverage","test:fuzz":"jest --testMatch='**/*.fuzz.ts' --coverage=false --testTimeout=120000","prepare":"husky"},"dependencies":{"@types/luxon":"~3.4.0","luxon":"~3.5.0"},"devDependencies":{"@commitlint/cli":"19.6.0","@eslint/js":"^9.14.0","@fast-check/jest":"2.0.3","@insurgent/commitlint-config":"20.0.0","@insurgent/conventional-changelog-preset":"10.0.0","@semantic-release/changelog":"6.0.3","@semantic-release/commit-analyzer":"13.0.0","@semantic-release/git":"10.0.1","@semantic-release/github":"11.0.1","@semantic-release/npm":"12.0.1","@semantic-release/release-notes-generator":"14.0.1","@types/jest":"29.5.14","@types/node":"20.17.9","@types/sinon":"17.0.3","chai":"4.5.0","eslint":"8.57.1","eslint-config-prettier":"9.1.0","eslint-plugin-jest":"27.9.0","eslint-plugin-prettier":"5.2.1","husky":"9.1.7","jest":"29.7.0","lint-staged":"15.2.10","prettier":"3.3.3","semantic-release":"24.2.0","sinon":"17.0.2","ts-jest":"29.2.5","typescript":"5.7.2","typescript-eslint":"^7.2.0"},"keywords":["cron","node cron","node-cron","schedule","scheduler","cronjob","cron job"],"license":"MIT","contributors":["Brandon der Blätter <https://interlucid.com/contact/> (https://github.com/intcreator)","Pierre Cavin <me@sherlox.io> (https://github.com/sheerlox)","Romain Beauxis <toots@rastageeks.org> (https://github.com/toots)","James Padolsey <> (https://github.com/jamespadolsey)","Finn Herpich <fh@three-heads.de> (https://github.com/ErrorProne)","Clifton Cunningham <clifton.cunningham@gmail.com> (https://github.com/cliftonc)","Eric Abouaf <eric.abouaf@gmail.com> (https://github.com/neyric)","humanchimp <morphcham@gmail.com> (https://github.com/humanchimp)","Craig Condon <craig@spiceapps.com> (https://github.com/spiceapps)","Dan Bear <daniel@hulu.com> (https://github.com/danhbear)","Vadim Baryshev <vadimbaryshev@gmail.com> (https://github.com/baryshev)","Leandro Ferrari <lfthomaz@gmail.com> (https://github.com/lfthomaz)","Gregg Zigler <greggzigler@gmail.com> (https://github.com/greggzigler)","Jordan Abderrachid <jabderrachid@gmail.com> (https://github.com/jordanabderrachid)","Masakazu Matsushita <matsukaz@gmail.com> (matsukaz)","Christopher Lunt <me@kirisu.co.uk> (https://github.com/kirisu)"],"files":["dist/**/*.js","dist/**/*.d.ts","CHANGELOG.md","LICENSE","README.md"],"lint-staged":{"*.ts":"eslint src/ tests/ --fix","*.{json,md,yml}":"prettier ./**/*.{json,md,yml} --check --write"},"_lastModified":"2025-12-05T07:09:26.013Z"}
1
+ {"name":"cron","description":"Cron jobs for your node","version":"3.3.1","author":"Nick Campbell <nicholas.j.campbell@gmail.com> (https://github.com/ncb000gt)","bugs":{"url":"https://github.com/kelektiv/node-cron/issues"},"repository":{"type":"git","url":"https://github.com/kelektiv/node-cron.git"},"main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc -b tsconfig.build.json","lint:eslint":"eslint src/ tests/","lint:prettier":"prettier ./**/*.{json,md,yml} --check","lint":"npm run lint:eslint && npm run lint:prettier","lint:fix":"npm run lint:eslint -- --fix && npm run lint:prettier -- --write","test":"jest --coverage","test:watch":"jest --watch --coverage","test:fuzz":"jest --testMatch='**/*.fuzz.ts' --coverage=false --testTimeout=120000","prepare":"husky"},"dependencies":{"@types/luxon":"~3.4.0","luxon":"~3.5.0"},"devDependencies":{"@commitlint/cli":"19.6.0","@eslint/js":"^9.14.0","@fast-check/jest":"2.0.3","@insurgent/commitlint-config":"20.0.0","@insurgent/conventional-changelog-preset":"10.0.0","@semantic-release/changelog":"6.0.3","@semantic-release/commit-analyzer":"13.0.0","@semantic-release/git":"10.0.1","@semantic-release/github":"11.0.1","@semantic-release/npm":"12.0.1","@semantic-release/release-notes-generator":"14.0.1","@types/jest":"29.5.14","@types/node":"20.17.9","@types/sinon":"17.0.3","chai":"4.5.0","eslint":"8.57.1","eslint-config-prettier":"9.1.0","eslint-plugin-jest":"27.9.0","eslint-plugin-prettier":"5.2.1","husky":"9.1.7","jest":"29.7.0","lint-staged":"15.2.10","prettier":"3.3.3","semantic-release":"24.2.0","sinon":"17.0.2","ts-jest":"29.2.5","typescript":"5.7.2","typescript-eslint":"^7.2.0"},"keywords":["cron","node cron","node-cron","schedule","scheduler","cronjob","cron job"],"license":"MIT","contributors":["Brandon der Blätter <https://interlucid.com/contact/> (https://github.com/intcreator)","Pierre Cavin <me@sherlox.io> (https://github.com/sheerlox)","Romain Beauxis <toots@rastageeks.org> (https://github.com/toots)","James Padolsey <> (https://github.com/jamespadolsey)","Finn Herpich <fh@three-heads.de> (https://github.com/ErrorProne)","Clifton Cunningham <clifton.cunningham@gmail.com> (https://github.com/cliftonc)","Eric Abouaf <eric.abouaf@gmail.com> (https://github.com/neyric)","humanchimp <morphcham@gmail.com> (https://github.com/humanchimp)","Craig Condon <craig@spiceapps.com> (https://github.com/spiceapps)","Dan Bear <daniel@hulu.com> (https://github.com/danhbear)","Vadim Baryshev <vadimbaryshev@gmail.com> (https://github.com/baryshev)","Leandro Ferrari <lfthomaz@gmail.com> (https://github.com/lfthomaz)","Gregg Zigler <greggzigler@gmail.com> (https://github.com/greggzigler)","Jordan Abderrachid <jabderrachid@gmail.com> (https://github.com/jordanabderrachid)","Masakazu Matsushita <matsukaz@gmail.com> (matsukaz)","Christopher Lunt <me@kirisu.co.uk> (https://github.com/kirisu)"],"files":["dist/**/*.js","dist/**/*.d.ts","CHANGELOG.md","LICENSE","README.md"],"lint-staged":{"*.ts":"eslint src/ tests/ --fix","*.{json,md,yml}":"prettier ./**/*.{json,md,yml} --check --write"},"_lastModified":"2025-12-26T04:40:08.648Z"}
@@ -1 +1 @@
1
- {"name":"ms","version":"2.1.3","description":"Tiny millisecond conversion utility","repository":"vercel/ms","main":"./index","files":["index.js"],"scripts":{"precommit":"lint-staged","lint":"eslint lib/* bin/*","test":"mocha tests.js"},"eslintConfig":{"extends":"eslint:recommended","env":{"node":true,"es6":true}},"lint-staged":{"*.js":["npm run lint","prettier --single-quote --write","git add"]},"license":"MIT","devDependencies":{"eslint":"4.18.2","expect.js":"0.3.1","husky":"0.14.3","lint-staged":"5.0.0","mocha":"4.0.1","prettier":"2.0.5"},"_lastModified":"2025-12-05T07:09:26.116Z"}
1
+ {"name":"ms","version":"2.1.3","description":"Tiny millisecond conversion utility","repository":"vercel/ms","main":"./index","files":["index.js"],"scripts":{"precommit":"lint-staged","lint":"eslint lib/* bin/*","test":"mocha tests.js"},"eslintConfig":{"extends":"eslint:recommended","env":{"node":true,"es6":true}},"lint-staged":{"*.js":["npm run lint","prettier --single-quote --write","git add"]},"license":"MIT","devDependencies":{"eslint":"4.18.2","expect.js":"0.3.1","husky":"0.14.3","lint-staged":"5.0.0","mocha":"4.0.1","prettier":"2.0.5"},"_lastModified":"2025-12-26T04:40:08.750Z"}
@@ -36,7 +36,8 @@ var import_preset = require("../preset");
36
36
  class BasicAuth extends import_server.BaseAuth {
37
37
  constructor(config) {
38
38
  const userCollection = config.ctx.db.getCollection("users");
39
- super({ ...config, userCollection });
39
+ const userStatusCollection = config.ctx.db.getCollection("userStatuses");
40
+ super({ ...config, userCollection, userStatusCollection });
40
41
  }
41
42
  async validate() {
42
43
  const ctx = this.ctx;
@@ -6,13 +6,5 @@ declare const _default: {
6
6
  'The phone number has been registered, please login directly': string;
7
7
  'The phone number is not registered, please register first': string;
8
8
  'The username, email or password is incorrect, please re-enter': string;
9
- 'Invalid status': string;
10
- 'User status is invalid, please contact administrator': string;
11
- 'User status does not allow login': string;
12
- 'Your account is under review, please wait for administrator approval': string;
13
- 'Your account has been disabled, please contact administrator if you have any questions': string;
14
- 'Unknown status': string;
15
- 'System error, please contact administrator': string;
16
- 'Your account status has changed. Please sign in again.': string;
17
9
  };
18
10
  export default _default;
@@ -27,13 +27,5 @@ var en_US_default = {
27
27
  "Not a valid cellphone number, please re-enter": "Not a valid cellphone number, please re-enter",
28
28
  "The phone number has been registered, please login directly": "The phone number has been registered, please login directly",
29
29
  "The phone number is not registered, please register first": "The phone number is not registered, please register first",
30
- "The username, email or password is incorrect, please re-enter": "The username, email or password is incorrect, please re-enter",
31
- "Invalid status": "Invalid status",
32
- "User status is invalid, please contact administrator": "User status is invalid, please contact administrator",
33
- "User status does not allow login": "User status does not allow login",
34
- "Your account is under review, please wait for administrator approval": "Your account is under review, please wait for administrator approval",
35
- "Your account has been disabled, please contact administrator if you have any questions": "Your account has been disabled, please contact administrator if you have any questions",
36
- "Unknown status": "Unknown status",
37
- "System error, please contact administrator": "System error, please contact administrator",
38
- "Your account status has changed. Please sign in again.": "Your account status has changed. Please sign in again."
30
+ "The username, email or password is incorrect, please re-enter": "The username, email or password is incorrect, please re-enter"
39
31
  };
@@ -8,13 +8,5 @@ declare const _default: {
8
8
  'Please enter your username or email': string;
9
9
  'Please enter a valid username': string;
10
10
  'The username, email or password is incorrect, please re-enter': string;
11
- 'Invalid status': string;
12
- 'User status is invalid, please contact administrator': string;
13
- 'User status does not allow login': string;
14
- 'Your account is under review, please wait for administrator approval': string;
15
- 'Your account has been disabled, please contact administrator if you have any questions': string;
16
- 'Unknown status': string;
17
- 'System error, please contact administrator': string;
18
- 'Your account status has changed. Please sign in again.': string;
19
11
  };
20
12
  export default _default;
@@ -29,13 +29,5 @@ var zh_CN_default = {
29
29
  "Please keep and enable at least one authenticator": "\u8BF7\u81F3\u5C11\u4FDD\u7559\u5E76\u542F\u7528\u4E00\u4E2A\u8BA4\u8BC1\u5668",
30
30
  "Please enter your username or email": "\u8BF7\u8F93\u5165\u7528\u6237\u540D\u6216\u90AE\u7BB1",
31
31
  "Please enter a valid username": "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u7528\u6237\u540D",
32
- "The username, email or password is incorrect, please re-enter": "\u7528\u6237\u540D\u90AE\u7BB1\u6216\u8005\u5BC6\u7801\u6709\u8BEF,\u8BF7\u91CD\u65B0\u8F93\u5165",
33
- "Invalid status": "\u72B6\u6001\u5F02\u5E38",
34
- "User status is invalid, please contact administrator": "\u7528\u6237\u72B6\u6001\u5F02\u5E38\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458",
35
- "User status does not allow login": "\u7528\u6237\u72B6\u6001\u4E0D\u5141\u8BB8\u767B\u5F55",
36
- "Your account is under review, please wait for administrator approval": "\u60A8\u7684\u8D26\u53F7\u6B63\u5728\u5BA1\u6838\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u7BA1\u7406\u5458\u6279\u51C6",
37
- "Your account has been disabled, please contact administrator if you have any questions": "\u60A8\u7684\u8D26\u53F7\u5DF2\u88AB\u7981\u7528\uFF0C\u5982\u6709\u7591\u95EE\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458",
38
- "Unknown status": "\u672A\u77E5\u72B6\u6001",
39
- "System error, please contact administrator": "\u7CFB\u7EDF\u9519\u8BEF\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458",
40
- "Your account status has changed. Please sign in again.": "\u60A8\u7684\u8D26\u53F7\u72B6\u6001\u5DF2\u53D8\u66F4\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55"
32
+ "The username, email or password is incorrect, please re-enter": "\u7528\u6237\u540D\u90AE\u7BB1\u6216\u8005\u5BC6\u7801\u6709\u8BEF,\u8BF7\u91CD\u65B0\u8F93\u5165"
41
33
  };
@@ -46,23 +46,27 @@ var import_user_status = require("./user-status");
46
46
  class PluginAuthServer extends import_server.Plugin {
47
47
  afterAdd() {
48
48
  this.app.on("afterLoad", async () => {
49
- if (this.app.authManager.tokenController) {
50
- return;
51
- }
52
- const cache = await this.app.cacheManager.createCache({
53
- name: "auth-token-controller",
54
- prefix: "auth-token-controller"
55
- });
56
- const tokenController = new import_token_controller.TokenController({ cache, app: this.app, logger: this.app.logger });
57
- this.app.authManager.setTokenControlService(tokenController);
58
- const tokenPolicyRepo = this.app.db.getRepository(import_constants.tokenPolicyCollectionName);
59
- try {
60
- const res = await tokenPolicyRepo.findOne({ filterByTk: import_constants.tokenPolicyRecordKey });
61
- if (res) {
62
- this.app.authManager.tokenController.setConfig(res.config);
49
+ if (!this.app.authManager.tokenController) {
50
+ const cache = await this.app.cacheManager.createCache({
51
+ name: "auth-token-controller",
52
+ prefix: "auth-token-controller"
53
+ });
54
+ const tokenController = new import_token_controller.TokenController({ cache, app: this.app, logger: this.app.logger });
55
+ this.app.authManager.setTokenControlService(tokenController);
56
+ const tokenPolicyRepo = this.app.db.getRepository(import_constants.tokenPolicyCollectionName);
57
+ try {
58
+ const res = await tokenPolicyRepo.findOne({ filterByTk: import_constants.tokenPolicyRecordKey });
59
+ if (res) {
60
+ this.app.authManager.tokenController.setConfig(res.config);
61
+ }
62
+ } catch (error) {
63
+ this.app.logger.warn("access control config not exist, use default value");
63
64
  }
64
- } catch (error) {
65
- this.app.logger.warn("access control config not exist, use default value");
65
+ }
66
+ if (!this.app.authManager.userStatusService) {
67
+ this.app.authManager.setUserStatusService(
68
+ new import_user_status.UserStatusService({ cache: this.cache, app: this.app, logger: this.app.logger })
69
+ );
66
70
  }
67
71
  });
68
72
  }
@@ -72,10 +76,6 @@ class PluginAuthServer extends import_server.Plugin {
72
76
  this.app.db.registerModels({ AuthModel: import_authenticator.AuthModel });
73
77
  }
74
78
  async load() {
75
- this.userStatusService = new import_user_status.UserStatusService(this.app);
76
- this.app.userStatusService = this.userStatusService;
77
- this.userStatusService.injectLoginCheck();
78
- this.userStatusService.registerStatusChangeInterceptor();
79
79
  this.cache = await this.app.cacheManager.createCache({
80
80
  name: "auth",
81
81
  prefix: "auth",
@@ -1,106 +1,45 @@
1
- import { Application } from '@tego/server';
1
+ import { Application, Cache, Collection, SystemLogger, type IUserStatusService, type UserStatusCache, type UserStatusCheckResult } from '@tego/server';
2
2
  /**
3
- * 用户状态检查结果
3
+ * 基础用户状态服务实现
4
4
  */
5
- export interface UserStatusCheckResult {
6
- allowed: boolean;
7
- status: string;
8
- statusInfo?: {
9
- title: string;
10
- color: string;
11
- allowLogin: boolean;
12
- loginErrorMessage?: string;
13
- };
14
- errorMessage?: string;
15
- isExpired?: boolean;
16
- }
17
- /**
18
- * 修改用户状态的选项
19
- */
20
- export interface ChangeUserStatusOptions {
21
- expireAt?: Date;
22
- reason?: string;
23
- operationType: 'manual' | 'auto' | 'system';
24
- operatorId?: number;
25
- }
26
- /**
27
- * 状态注册配置
28
- */
29
- export interface StatusConfig {
30
- key: string;
31
- title: string;
32
- color?: string;
33
- allowLogin: boolean;
34
- loginErrorMessage?: string;
35
- packageName: string;
36
- description?: string;
37
- sort?: number;
38
- config?: Record<string, any>;
39
- }
40
- declare module '@tego/server' {
41
- interface BaseAuth {
42
- checkUserStatusInContext?(userId: number): Promise<UserStatusCheckResult>;
43
- }
44
- }
45
- /**
46
- * 用户状态管理服务
47
- * 负责用户状态的检查、变更、恢复等核心业务逻辑
48
- */
49
- export declare class UserStatusService {
50
- private db;
51
- private app;
52
- private cache;
53
- private logger;
54
- constructor(app: Application);
55
- /**
56
- * 获取用户状态缓存键
57
- */
58
- private getUserStatusCacheKey;
59
- /**
60
- * 从缓存获取用户状态
61
- */
62
- private getUserStatusFromCache;
63
- /**
64
- * 设置用户状态缓存
65
- */
66
- private setUserStatusCache;
67
- /**
68
- * 清除用户状态缓存
69
- */
70
- private clearUserStatusCache;
71
- /**
72
- * 检查用户状态是否允许登录
73
- * @param userId 用户ID
74
- * @returns 检查结果
75
- */
5
+ export declare class UserStatusService implements IUserStatusService {
6
+ protected cache: Cache;
7
+ protected app: Application;
8
+ protected logger: SystemLogger;
9
+ protected userCollection: Collection;
10
+ protected userStatusCollection: Collection;
11
+ protected userStatusHistoryCollection: Collection;
12
+ constructor({ cache, app, logger }: {
13
+ cache: Cache;
14
+ app: Application;
15
+ logger: SystemLogger;
16
+ });
17
+ get userRepository(): import("@tego/server").Repository<any, any>;
18
+ get userStatusRepository(): import("@tego/server").Repository<any, any>;
19
+ get userStatusHistoryRepository(): import("@tego/server").Repository<any, any>;
20
+ /**
21
+ * 翻译消息
22
+ */
23
+ private t;
76
24
  checkUserStatus(userId: number): Promise<UserStatusCheckResult>;
77
- /**
78
- * 记录状态变更历史(如果不存在相同记录)
79
- * @param params 状态变更参数
80
- */
81
- private recordStatusHistoryIfNotExists;
82
- /**
83
- * 恢复过期的用户状态
84
- * @param userId 用户ID
85
- */
25
+ setUserStatusCache(userId: number, data: UserStatusCache): Promise<void>;
26
+ getUserStatusFromCache(userId: number): Promise<UserStatusCache | null>;
27
+ getUserStatusCacheKey(userId: number): string;
86
28
  restoreUserStatus(userId: number): Promise<void>;
87
- /**
88
- * 注册新的用户状态(供插件使用)
89
- * @param config 状态配置
90
- */
91
- registerStatus(config: StatusConfig): Promise<void>;
92
- /**
93
- * 获取每个状态的用户数量统计
94
- */
95
- getStatusStatistics(): Promise<Record<string, number>>;
96
- /**
97
- * 注入登录检查
98
- */
99
- injectLoginCheck(): void;
29
+ clearUserStatusCache(userId: number): Promise<void>;
30
+ recordStatusHistoryIfNotExists(params: {
31
+ userId: number;
32
+ fromStatus: string;
33
+ toStatus: string;
34
+ reason: string | null;
35
+ expireAt: Date | null;
36
+ operationType: 'manual' | 'auto' | 'system';
37
+ createdBy: number | null;
38
+ transaction?: any;
39
+ }): Promise<void>;
100
40
  /**
101
41
  * 注册用户状态变更拦截器
102
42
  * 拦截 users:update 请求,自动记录状态变更历史
103
43
  */
104
- registerStatusChangeInterceptor(): void;
44
+ private registerStatusChangeInterceptor;
105
45
  }
106
- export default UserStatusService;
@@ -17,79 +17,57 @@ var __copyProps = (to, from, except, desc) => {
17
17
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
18
  var user_status_exports = {};
19
19
  __export(user_status_exports, {
20
- UserStatusService: () => UserStatusService,
21
- default: () => user_status_default
20
+ UserStatusService: () => UserStatusService
22
21
  });
23
22
  module.exports = __toCommonJS(user_status_exports);
24
- var import_server = require("@tego/server");
25
23
  var import_preset = require("../preset");
24
+ const localeNamespace = import_preset.namespace;
26
25
  class UserStatusService {
27
- constructor(app) {
26
+ constructor({ cache, app, logger }) {
27
+ this.cache = cache;
28
28
  this.app = app;
29
- this.db = app.db;
30
- this.cache = app.cache;
31
- this.logger = app.logger;
29
+ this.logger = logger;
30
+ this.userCollection = app.db.getCollection("users");
31
+ this.userStatusCollection = app.db.getCollection("userStatuses");
32
+ this.userStatusHistoryCollection = app.db.getCollection("userStatusHistories");
33
+ this.registerStatusChangeInterceptor();
32
34
  }
33
- /**
34
- * 获取用户状态缓存键
35
- */
36
- getUserStatusCacheKey(userId) {
37
- return `userStatus:${userId}`;
35
+ get userRepository() {
36
+ return this.userCollection.repository;
38
37
  }
39
- /**
40
- * 从缓存获取用户状态
41
- */
42
- async getUserStatusFromCache(userId) {
43
- try {
44
- const cacheKey = this.getUserStatusCacheKey(userId);
45
- const cached = await this.cache.get(cacheKey);
46
- return cached ? JSON.parse(cached) : null;
47
- } catch (error) {
48
- this.logger.error("Error getting user status from cache:", error);
49
- return null;
50
- }
38
+ get userStatusRepository() {
39
+ return this.userStatusCollection.repository;
51
40
  }
52
- /**
53
- * 设置用户状态缓存
54
- */
55
- async setUserStatusCache(userId, data) {
56
- try {
57
- const cacheKey = this.getUserStatusCacheKey(userId);
58
- await this.cache.set(cacheKey, JSON.stringify(data), 300 * 1e3);
59
- } catch (error) {
60
- this.logger.error("Error setting user status cache:", error);
61
- }
41
+ get userStatusHistoryRepository() {
42
+ return this.userStatusHistoryCollection.repository;
62
43
  }
63
44
  /**
64
- * 清除用户状态缓存
45
+ * 翻译消息
65
46
  */
66
- async clearUserStatusCache(userId) {
67
- try {
68
- const cacheKey = this.getUserStatusCacheKey(userId);
69
- await this.cache.del(cacheKey);
70
- } catch (error) {
71
- this.logger.error("Error clearing user status cache:", error);
72
- }
47
+ t(key, options) {
48
+ var _a, _b;
49
+ const language = (options == null ? void 0 : options.lng) || ((_a = this.app.i18n) == null ? void 0 : _a.language) || "en-US";
50
+ return ((_b = this.app.i18n) == null ? void 0 : _b.t(key, { ...options, lng: language })) || key;
73
51
  }
74
- /**
75
- * 检查用户状态是否允许登录
76
- * @param userId 用户ID
77
- * @returns 检查结果
78
- */
79
52
  async checkUserStatus(userId) {
80
53
  try {
81
54
  let cached = await this.getUserStatusFromCache(userId);
82
55
  if (!cached) {
83
- const userRepo = this.db.getRepository("users");
84
- const user = await userRepo.findOne({
85
- filterByTk: userId,
56
+ const user = await this.userRepository.findOne({
57
+ filter: { id: userId },
86
58
  fields: ["id", "status", "statusExpireAt", "previousStatus"]
87
59
  });
88
60
  if (!user) {
89
61
  return {
90
62
  allowed: false,
91
63
  status: "unknown",
92
- errorMessage: this.app.i18n.t("User not found")
64
+ statusInfo: {
65
+ title: this.t("User not found. Please sign in again to continue.", { ns: localeNamespace }),
66
+ color: "red",
67
+ allowLogin: false
68
+ },
69
+ errorMessage: this.t("User not found. Please sign in again to continue.", { ns: localeNamespace }),
70
+ isExpired: false
93
71
  };
94
72
  }
95
73
  cached = {
@@ -103,9 +81,8 @@ class UserStatusService {
103
81
  }
104
82
  if (cached.expireAt && new Date(cached.expireAt) <= /* @__PURE__ */ new Date()) {
105
83
  await this.restoreUserStatus(userId);
106
- const userRepo = this.db.getRepository("users");
107
- const user = await userRepo.findOne({
108
- filterByTk: userId,
84
+ const user = await this.userRepository.findOne({
85
+ filter: { id: userId },
109
86
  fields: ["status"]
110
87
  });
111
88
  cached.status = user.status || "active";
@@ -113,9 +90,8 @@ class UserStatusService {
113
90
  cached.previousStatus = null;
114
91
  await this.setUserStatusCache(userId, cached);
115
92
  }
116
- const statusRepo = this.db.getRepository("userStatuses");
117
- const statusInfo = await statusRepo.findOne({
118
- filterByTk: cached.status
93
+ const statusInfo = await this.userStatusRepository.findOne({
94
+ filter: { key: cached.status }
119
95
  });
120
96
  if (!statusInfo) {
121
97
  this.logger.warn(`Status definition not found: ${cached.status}`);
@@ -123,36 +99,34 @@ class UserStatusService {
123
99
  allowed: false,
124
100
  status: cached.status,
125
101
  statusInfo: {
126
- title: this.app.i18n.t("Invalid status", { ns: import_preset.namespace }),
102
+ title: this.t("Invalid status", { ns: localeNamespace }),
127
103
  color: "red",
128
- allowLogin: false,
129
- loginErrorMessage: this.app.i18n.t("User status is invalid, please contact administrator", {
130
- ns: import_preset.namespace
131
- })
104
+ allowLogin: false
132
105
  },
133
- errorMessage: this.app.i18n.t("User status is invalid, please contact administrator", { ns: import_preset.namespace })
106
+ errorMessage: this.t("User status is invalid, please contact administrator", { ns: localeNamespace }),
107
+ isExpired: true
134
108
  };
135
109
  }
136
110
  const translateMessage = (message) => {
137
111
  if (!message) return "";
138
112
  const match = message.match(/\{\{t\("([^"]+)"\)\}\}/);
139
113
  if (match && match[1]) {
140
- return this.app.i18n.t(match[1], { ns: import_preset.namespace });
114
+ return this.t(match[1], { ns: localeNamespace });
141
115
  }
142
116
  return message;
143
117
  };
144
118
  const translatedTitle = translateMessage(statusInfo.title);
145
- const translatedLoginErrorMessage = translateMessage(statusInfo.loginErrorMessage);
119
+ const translatedLoginErrorMessage = translateMessage(statusInfo.loginErrorMessage) || this.t("User status does not allow login", { ns: localeNamespace });
146
120
  return {
147
121
  allowed: statusInfo.allowLogin,
148
122
  status: cached.status,
149
123
  statusInfo: {
150
124
  title: translatedTitle,
151
125
  color: statusInfo.color,
152
- allowLogin: statusInfo.allowLogin,
153
- loginErrorMessage: translatedLoginErrorMessage
126
+ allowLogin: statusInfo.allowLogin
154
127
  },
155
- errorMessage: !statusInfo.allowLogin ? translatedLoginErrorMessage || this.app.i18n.t("User status does not allow login", { ns: import_preset.namespace }) : void 0
128
+ errorMessage: statusInfo.allowLogin ? null : translatedLoginErrorMessage || null,
129
+ isExpired: false
156
130
  };
157
131
  } catch (error) {
158
132
  this.logger.error(`Error checking user status for userId=${userId}: ${error}`);
@@ -160,106 +134,73 @@ class UserStatusService {
160
134
  allowed: false,
161
135
  status: "unknown",
162
136
  statusInfo: {
163
- title: this.app.i18n.t("Unknown status", { ns: import_preset.namespace }),
137
+ title: this.t("Unknown status", { ns: localeNamespace }),
164
138
  color: "red",
165
- allowLogin: false,
166
- loginErrorMessage: this.app.i18n.t("System error, please contact administrator", { ns: import_preset.namespace })
139
+ allowLogin: false
167
140
  },
168
- errorMessage: this.app.i18n.t("System error, please contact administrator", { ns: import_preset.namespace })
141
+ errorMessage: this.t("System error, please contact administrator", { ns: localeNamespace }),
142
+ isExpired: false
169
143
  };
170
144
  }
171
145
  }
172
- /**
173
- * 记录状态变更历史(如果不存在相同记录)
174
- * @param params 状态变更参数
175
- */
176
- async recordStatusHistoryIfNotExists(params) {
177
- const { userId, fromStatus, toStatus, reason, expireAt, operationType, createdBy, transaction } = params;
146
+ async setUserStatusCache(userId, data) {
178
147
  try {
179
- const historyRepo = this.db.getRepository("userStatusHistories");
180
- const fiveSecondsAgo = new Date(Date.now() - 5e3);
181
- const existing = await historyRepo.findOne({
182
- filter: {
183
- userId,
184
- fromStatus,
185
- toStatus,
186
- createdAt: {
187
- $gte: fiveSecondsAgo
188
- }
189
- },
190
- sort: ["-createdAt"],
191
- transaction
192
- });
193
- if (existing) {
194
- this.logger.warn(
195
- `Skipping duplicate history record: userId=${userId}, ${fromStatus} \u2192 ${toStatus}, operationType=${operationType}`
196
- );
197
- return;
198
- }
199
- await historyRepo.create({
200
- values: {
201
- userId,
202
- fromStatus,
203
- toStatus,
204
- reason,
205
- expireAt,
206
- operationType,
207
- createdBy
208
- },
209
- transaction
210
- });
211
- this.logger.debug(
212
- `History recorded: userId=${userId}, ${fromStatus} \u2192 ${toStatus}, operationType=${operationType}`
213
- );
214
- await this.clearUserStatusCache(userId);
215
- this.logger.debug(`Cache cleared for userId=${userId}`);
148
+ const cacheKey = this.getUserStatusCacheKey(userId);
149
+ await this.cache.set(cacheKey, JSON.stringify(data), 300 * 1e3);
216
150
  } catch (error) {
217
- this.logger.error("Failed to record status history:", error);
218
- throw error;
151
+ this.logger.error("Error setting user status cache:", error);
219
152
  }
220
153
  }
221
- /**
222
- * 恢复过期的用户状态
223
- * @param userId 用户ID
224
- */
154
+ async getUserStatusFromCache(userId) {
155
+ try {
156
+ const cacheKey = this.getUserStatusCacheKey(userId);
157
+ const cached = await this.cache.get(cacheKey);
158
+ return cached ? JSON.parse(cached) : null;
159
+ } catch (error) {
160
+ this.logger.error("Error getting user status from cache:", error);
161
+ return null;
162
+ }
163
+ }
164
+ getUserStatusCacheKey(userId) {
165
+ return `userStatus:${userId}`;
166
+ }
225
167
  async restoreUserStatus(userId) {
226
168
  try {
227
- const userRepo = this.db.getRepository("users");
228
- const user = await userRepo.findOne({
229
- filterByTk: userId,
169
+ const user = await this.userRepository.findOne({
170
+ filter: { id: userId },
230
171
  fields: ["id", "status", "statusExpireAt", "previousStatus"]
231
172
  });
232
173
  if (!user) {
233
- throw new Error(this.app.i18n.t("User not found"));
174
+ throw new Error(this.t("User not found. Please sign in again to continue.", { ns: localeNamespace }));
234
175
  }
235
176
  if (!user.statusExpireAt || new Date(user.statusExpireAt) > /* @__PURE__ */ new Date()) {
236
177
  return;
237
178
  }
238
179
  const oldStatus = user.status;
239
180
  const restoreToStatus = user.previousStatus || "active";
240
- await this.db.sequelize.transaction(async (transaction) => {
181
+ await this.app.db.sequelize.transaction(async (transaction) => {
241
182
  await this.recordStatusHistoryIfNotExists({
242
183
  userId,
243
184
  fromStatus: oldStatus,
244
185
  toStatus: restoreToStatus,
245
- reason: this.app.i18n.t("Status expired, auto restored"),
186
+ reason: this.t("Status expired, auto restored", { ns: localeNamespace }),
246
187
  operationType: "auto",
247
188
  createdBy: null,
248
189
  expireAt: null,
249
190
  transaction
250
191
  });
251
- await userRepo.update({
252
- filterByTk: userId,
192
+ await this.userRepository.update({
193
+ filter: { id: userId },
253
194
  values: {
254
195
  status: restoreToStatus,
255
196
  statusExpireAt: null,
256
197
  previousStatus: null,
257
- statusReason: this.app.i18n.t("Status expired, auto restored")
198
+ statusReason: this.t("Status expired, auto restored", { ns: localeNamespace })
258
199
  },
259
200
  transaction
260
201
  });
261
202
  });
262
- this.app.emitAsync("user:statusRestored", {
203
+ await this.app.emitAsync("user:statusRestored", {
263
204
  userId,
264
205
  fromStatus: oldStatus,
265
206
  toStatus: restoreToStatus
@@ -270,321 +211,57 @@ class UserStatusService {
270
211
  throw error;
271
212
  }
272
213
  }
273
- /**
274
- * 注册新的用户状态(供插件使用)
275
- * @param config 状态配置
276
- */
277
- async registerStatus(config) {
214
+ async clearUserStatusCache(userId) {
278
215
  try {
279
- const statusRepo = this.db.getRepository("userStatuses");
280
- const existing = await statusRepo.findOne({
281
- filterByTk: config.key
282
- });
283
- if (existing) {
284
- if (existing.packageName === config.packageName) {
285
- await statusRepo.update({
286
- filterByTk: config.key,
287
- values: {
288
- title: config.title,
289
- color: config.color || "default",
290
- allowLogin: config.allowLogin,
291
- loginErrorMessage: config.loginErrorMessage || null,
292
- description: config.description || null,
293
- sort: config.sort || 0,
294
- config: config.config || {}
295
- }
296
- });
297
- this.logger.info(`Updated status: ${config.key} by ${config.packageName}`);
298
- } else {
299
- this.logger.warn(`Status ${config.key} already registered by ${existing.packageName}, skipping`);
300
- }
301
- return;
302
- }
303
- await statusRepo.create({
304
- values: {
305
- key: config.key,
306
- title: config.title,
307
- color: config.color || "default",
308
- allowLogin: config.allowLogin,
309
- loginErrorMessage: config.loginErrorMessage || null,
310
- isSystemDefined: false,
311
- // 插件注册的状态不是系统内置
312
- packageName: config.packageName,
313
- description: config.description || null,
314
- sort: config.sort || 0,
315
- config: config.config || {}
316
- }
317
- });
318
- this.logger.info(`Registered new status: ${config.key} by ${config.packageName}`);
216
+ const cacheKey = this.getUserStatusCacheKey(userId);
217
+ await this.cache.del(cacheKey);
319
218
  } catch (error) {
320
- this.logger.error("Error registering status:", error);
321
- throw error;
219
+ this.logger.error("Error clearing user status cache:", error);
322
220
  }
323
221
  }
324
- /**
325
- * 获取每个状态的用户数量统计
326
- */
327
- async getStatusStatistics() {
222
+ async recordStatusHistoryIfNotExists(params) {
223
+ const { userId, fromStatus, toStatus, reason, expireAt, operationType, createdBy, transaction } = params;
328
224
  try {
329
- const userRepo = this.db.getRepository("users");
330
- const result = await userRepo.find({
331
- attributes: ["status", [this.db.sequelize.fn("COUNT", "id"), "count"]],
332
- group: ["status"],
333
- raw: true
225
+ const fiveSecondsAgo = new Date(Date.now() - 5e3);
226
+ const existing = await this.userStatusHistoryRepository.findOne({
227
+ filter: {
228
+ userId,
229
+ fromStatus,
230
+ toStatus,
231
+ createdAt: {
232
+ $gte: fiveSecondsAgo
233
+ }
234
+ },
235
+ sort: ["-createdAt"],
236
+ transaction
334
237
  });
335
- const statistics = {};
336
- for (const row of result) {
337
- statistics[row.status] = parseInt(row.count, 10);
238
+ if (existing) {
239
+ this.logger.warn(
240
+ `Skipping duplicate history record: userId=${userId}, ${fromStatus} \u2192 ${toStatus}, operationType=${operationType}`
241
+ );
242
+ return;
338
243
  }
339
- return statistics;
340
- } catch (error) {
341
- this.logger.error("Error getting status statistics:", error);
342
- return {};
343
- }
344
- }
345
- /**
346
- * 注入登录检查
347
- */
348
- injectLoginCheck() {
349
- const userStatusService = this;
350
- import_server.BaseAuth.prototype.signNewToken = async function(userId, options) {
351
- const user = await this.userRepository.findOne({
352
- filter: { id: userId },
353
- fields: ["id", "status"]
354
- });
355
- const userStatus = (user == null ? void 0 : user.status) || "active";
356
- const tokenInfo = await this.tokenController.add({ userId });
357
- const expiresIn = Math.floor((await this.tokenController.getConfig()).tokenExpirationTime / 1e3);
358
- const token = this.jwt.sign(
359
- {
244
+ await this.userStatusHistoryRepository.create({
245
+ values: {
360
246
  userId,
361
- userStatus,
362
- // 将用户状态写入 JWT payload
363
- temp: true,
364
- iat: Math.floor(tokenInfo.issuedTime / 1e3),
365
- signInTime: tokenInfo.signInTime
247
+ fromStatus,
248
+ toStatus,
249
+ reason,
250
+ expireAt,
251
+ operationType,
252
+ createdBy
366
253
  },
367
- {
368
- jwtid: tokenInfo.jti,
369
- expiresIn
370
- }
254
+ transaction
255
+ });
256
+ this.logger.debug(
257
+ `History recorded: userId=${userId}, ${fromStatus} \u2192 ${toStatus}, operationType=${operationType}`
371
258
  );
372
- userStatusService.logger.debug(`[signNewToken] userId=${userId}, userStatus=${userStatus}, jti=${tokenInfo.jti}`);
373
- return token;
374
- };
375
- import_server.BaseAuth.prototype.signIn = async function() {
376
- let user;
377
- try {
378
- user = await this.validate();
379
- } catch (err) {
380
- this.ctx.throw(err.status || 401, err.message, {
381
- ...err
382
- });
383
- }
384
- if (!user) {
385
- this.ctx.throw(401, {
386
- message: this.ctx.t("User not found. Please sign in again to continue.", { ns: import_preset.namespace }),
387
- code: import_server.AuthErrorCode.NOT_EXIST_USER
388
- });
389
- }
390
- const statusCheckResult = await userStatusService.checkUserStatus(user.id);
391
- if (!statusCheckResult.allowed) {
392
- this.ctx.throw(403, {
393
- message: this.ctx.t(statusCheckResult.statusInfo.loginErrorMessage, { ns: import_preset.namespace }),
394
- code: import_server.AuthErrorCode.INVALID_TOKEN
395
- });
396
- }
397
- const token = await this.signNewToken(user.id);
398
- return {
399
- user,
400
- token
401
- };
402
- };
403
- const originalCheck = import_server.BaseAuth.prototype.check;
404
- import_server.BaseAuth.prototype.check = async function() {
405
- const user = await originalCheck.call(this);
406
- if (!user) {
407
- return null;
408
- }
409
- const token = this.ctx.getBearerToken();
410
- if (!token) {
411
- return user;
412
- }
413
- try {
414
- const decoded = await this.jwt.decode(token);
415
- const tokenUserStatus = decoded.userStatus || "active";
416
- const currentUserStatus = user.status || "active";
417
- if (tokenUserStatus !== currentUserStatus) {
418
- userStatusService.logger.warn(
419
- `[check] Status mismatch, userId=${user.id}, token=${tokenUserStatus}, current=${currentUserStatus}`
420
- );
421
- this.ctx.throw(401, {
422
- message: this.ctx.t("Your account status has changed. Please sign in again.", { ns: import_preset.namespace }),
423
- code: import_server.AuthErrorCode.INVALID_TOKEN
424
- });
425
- }
426
- const statusCheckResult = await this.checkUserStatusInContext(user.id);
427
- if (!statusCheckResult.allowed) {
428
- userStatusService.logger.warn(`[check] Status not allowed, userId=${user.id}, status=${currentUserStatus}`);
429
- this.ctx.throw(403, {
430
- message: this.ctx.t(statusCheckResult.statusInfo.loginErrorMessage, { ns: import_preset.namespace }),
431
- code: import_server.AuthErrorCode.INVALID_TOKEN
432
- });
433
- }
434
- userStatusService.logger.debug(`[check] Passed, userId=${user.id}, status=${currentUserStatus}`);
435
- } catch (err) {
436
- if (err.status) {
437
- throw err;
438
- }
439
- userStatusService.logger.error("[check] Token validation error:", err);
440
- throw err;
441
- }
442
- return user;
443
- };
444
- import_server.BaseAuth.prototype.checkUserStatusInContext = async function(userId) {
445
- try {
446
- if (!userStatusService) {
447
- throw new Error("userStatusService is undefined");
448
- }
449
- if (!userStatusService.app) {
450
- throw new Error("userStatusService.app is undefined");
451
- }
452
- if (!userStatusService.app.db) {
453
- throw new Error("userStatusService.app.db is undefined");
454
- }
455
- if (!userStatusService.app.cache) {
456
- throw new Error("userStatusService.app.cache is undefined");
457
- }
458
- const contextDb = userStatusService.app.db;
459
- const contextCache = userStatusService.app.cache;
460
- let cached = null;
461
- try {
462
- const cacheKey = `userStatus:${userId}`;
463
- const cachedData = await contextCache.get(cacheKey);
464
- cached = cachedData ? JSON.parse(cachedData) : null;
465
- } catch (error) {
466
- userStatusService.logger.error("Error getting user status from cache:", error);
467
- cached = null;
468
- }
469
- if (!cached) {
470
- const userRepo = contextDb.getRepository("users");
471
- const user = await userRepo.findOne({
472
- filterByTk: userId,
473
- fields: ["id", "status", "statusExpireAt", "previousStatus"]
474
- });
475
- if (!user) {
476
- return {
477
- allowed: false,
478
- status: "unknown",
479
- errorMessage: userStatusService.app.i18n.t("User not found")
480
- };
481
- }
482
- cached = {
483
- userId: user.id,
484
- status: user.status || "active",
485
- expireAt: user.statusExpireAt,
486
- previousStatus: user.previousStatus,
487
- lastChecked: /* @__PURE__ */ new Date()
488
- };
489
- try {
490
- const cacheKey = `userStatus:${userId}`;
491
- await contextCache.set(cacheKey, JSON.stringify(cached), 300 * 1e3);
492
- } catch (error) {
493
- userStatusService.logger.error("Error setting user status cache:", error);
494
- }
495
- }
496
- if (cached.expireAt && new Date(cached.expireAt) <= /* @__PURE__ */ new Date()) {
497
- const userRepo = contextDb.getRepository("users");
498
- const user = await userRepo.findOne({
499
- filterByTk: userId,
500
- fields: ["id", "status", "statusExpireAt", "previousStatus"]
501
- });
502
- if (!user) {
503
- return {
504
- allowed: false,
505
- status: "unknown",
506
- errorMessage: userStatusService.app.i18n.t("User not found")
507
- };
508
- }
509
- if (!user.statusExpireAt || new Date(user.statusExpireAt) > /* @__PURE__ */ new Date()) {
510
- return userStatusService.checkUserStatus(userId);
511
- }
512
- await userStatusService.restoreUserStatus(userId);
513
- const restoredUser = await userRepo.findOne({
514
- filterByTk: userId,
515
- fields: ["status"]
516
- });
517
- cached.status = restoredUser.status || "active";
518
- cached.expireAt = null;
519
- cached.previousStatus = null;
520
- try {
521
- const cacheKey = `userStatus:${userId}`;
522
- await contextCache.set(cacheKey, JSON.stringify(cached), 300 * 1e3);
523
- } catch (error) {
524
- userStatusService.logger.error("Error setting user status cache:", error);
525
- }
526
- }
527
- const statusRepo = contextDb.getRepository("userStatuses");
528
- const statusInfo = await statusRepo.findOne({
529
- filterByTk: cached.status
530
- });
531
- if (!statusInfo) {
532
- userStatusService.logger.warn(`Status definition not found: ${cached.status}`);
533
- return {
534
- allowed: false,
535
- status: cached.status,
536
- statusInfo: {
537
- title: userStatusService.app.i18n.t("Invalid status", { ns: import_preset.namespace }),
538
- color: "red",
539
- allowLogin: false,
540
- loginErrorMessage: userStatusService.app.i18n.t("User status is invalid, please contact administrator", {
541
- ns: import_preset.namespace
542
- })
543
- },
544
- errorMessage: userStatusService.app.i18n.t("User status is invalid, please contact administrator", {
545
- ns: import_preset.namespace
546
- })
547
- };
548
- }
549
- const translateMessage = (message) => {
550
- if (!message) return "";
551
- const match = message.match(/\{\{t\("([^"]+)"\)\}\}/);
552
- if (match && match[1]) {
553
- return userStatusService.app.i18n.t(match[1], { ns: import_preset.namespace });
554
- }
555
- return message;
556
- };
557
- const translatedTitle = translateMessage(statusInfo.title);
558
- const translatedLoginErrorMessage = translateMessage(statusInfo.loginErrorMessage);
559
- return {
560
- allowed: statusInfo.allowLogin,
561
- status: cached.status,
562
- statusInfo: {
563
- title: translatedTitle,
564
- color: statusInfo.color,
565
- allowLogin: statusInfo.allowLogin,
566
- loginErrorMessage: translatedLoginErrorMessage
567
- },
568
- errorMessage: !statusInfo.allowLogin ? translatedLoginErrorMessage || userStatusService.app.i18n.t("User status does not allow login", { ns: import_preset.namespace }) : void 0
569
- };
570
- } catch (error) {
571
- userStatusService.logger.error(`Error checking user status for userId=${userId}: ${error}`);
572
- return {
573
- allowed: false,
574
- status: "unknown",
575
- statusInfo: {
576
- title: userStatusService.app.i18n.t("Unknown status", { ns: import_preset.namespace }),
577
- color: "red",
578
- allowLogin: false,
579
- loginErrorMessage: userStatusService.app.i18n.t("System error, please contact administrator", {
580
- ns: import_preset.namespace
581
- })
582
- },
583
- errorMessage: userStatusService.app.i18n.t("System error, please contact administrator", { ns: import_preset.namespace })
584
- };
585
- }
586
- };
587
- this.logger.info("signIn, signNewToken and check methods injected with UserStatusService integration");
259
+ await this.clearUserStatusCache(userId);
260
+ this.logger.debug(`Cache cleared for userId=${userId}`);
261
+ } catch (error) {
262
+ this.logger.error("Failed to record status history:", error);
263
+ throw error;
264
+ }
588
265
  }
589
266
  /**
590
267
  * 注册用户状态变更拦截器
@@ -592,7 +269,7 @@ class UserStatusService {
592
269
  */
593
270
  registerStatusChangeInterceptor() {
594
271
  const userStatusService = this;
595
- this.db.on("users.beforeUpdate", async (model, options) => {
272
+ this.app.db.on("users.beforeUpdate", async (model, options) => {
596
273
  if (model.changed("status")) {
597
274
  const oldStatus = model._previousDataValues.status || "active";
598
275
  const newStatus = model.status;
@@ -615,7 +292,7 @@ class UserStatusService {
615
292
  }
616
293
  }
617
294
  });
618
- this.db.on("users.afterUpdate", async (model, options) => {
295
+ this.app.db.on("users.afterUpdate", async (model, options) => {
619
296
  var _a, _b, _c, _d, _e;
620
297
  const statusChange = (_b = (_a = options == null ? void 0 : options.transaction) == null ? void 0 : _a.__statusChange) == null ? void 0 : _b[model.id];
621
298
  if (statusChange) {
@@ -640,7 +317,6 @@ class UserStatusService {
640
317
  this.logger.info("User status change interceptor registered");
641
318
  }
642
319
  }
643
- var user_status_default = UserStatusService;
644
320
  // Annotate the CommonJS export names for ESM import in node:
645
321
  0 && (module.exports = {
646
322
  UserStatusService
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tachybase/module-auth",
3
3
  "displayName": "Authentication",
4
- "version": "1.6.1",
4
+ "version": "1.6.3",
5
5
  "description": "User authentication management, including password, SMS, and support for Single Sign-On (SSO) protocols, with extensibility.",
6
6
  "keywords": [
7
7
  "Authentication",
@@ -11,10 +11,10 @@
11
11
  "dependencies": {},
12
12
  "devDependencies": {
13
13
  "@ant-design/icons": "^6.1.0",
14
- "@tachybase/schema": "1.6.0-alpha.9",
15
- "@tachybase/test": "1.6.0-alpha.9",
16
- "@tego/client": "1.6.0-alpha.9",
17
- "@tego/server": "1.6.0-alpha.9",
14
+ "@tachybase/schema": "1.6.1",
15
+ "@tachybase/test": "1.6.1",
16
+ "@tego/client": "1.6.1",
17
+ "@tego/server": "1.6.1",
18
18
  "antd": "5.22.5",
19
19
  "axios": "1.13.0",
20
20
  "cron": "^3.3.1",
@@ -23,7 +23,7 @@
23
23
  "react": "18.3.1",
24
24
  "react-i18next": "16.2.1",
25
25
  "react-router-dom": "6.28.1",
26
- "@tachybase/client": "1.6.1"
26
+ "@tachybase/client": "1.6.3"
27
27
  },
28
28
  "description.zh-CN": "用户认证管理,包括基础的密码认证、短信认证、SSO 协议的认证等,可扩展。",
29
29
  "displayName.zh-CN": "用户认证",