@zhang_libo/resource-hub 1.0.2
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/LICENSE +21 -0
- package/README.en.md +80 -0
- package/README.ja.md +80 -0
- package/README.md +79 -0
- package/README.zh-TW.md +80 -0
- package/bin/cli.js +10 -0
- package/dist/app.d.ts +2 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +59 -0
- package/dist/app.js.map +1 -0
- package/dist/db/index.js +12 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrate.d.ts +3 -0
- package/dist/db/migrate.d.ts.map +1 -0
- package/dist/db/migrate.js +169 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/schema.d.ts +743 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +88 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/i18n.js +309 -0
- package/dist/i18n.js.map +1 -0
- package/dist/plugins/admin.d.ts +4 -0
- package/dist/plugins/admin.d.ts.map +1 -0
- package/dist/plugins/admin.js +19 -0
- package/dist/plugins/admin.js.map +1 -0
- package/dist/plugins/auth.d.ts +4 -0
- package/dist/plugins/auth.d.ts.map +1 -0
- package/dist/plugins/auth.js +35 -0
- package/dist/plugins/auth.js.map +1 -0
- package/dist/routes/auth.d.ts +4 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/auth.js +352 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/categories.d.ts +4 -0
- package/dist/routes/categories.d.ts.map +1 -0
- package/dist/routes/categories.js +112 -0
- package/dist/routes/categories.js.map +1 -0
- package/dist/routes/config.d.ts +4 -0
- package/dist/routes/config.d.ts.map +1 -0
- package/dist/routes/config.js +227 -0
- package/dist/routes/config.js.map +1 -0
- package/dist/routes/resources.d.ts +4 -0
- package/dist/routes/resources.d.ts.map +1 -0
- package/dist/routes/resources.js +474 -0
- package/dist/routes/resources.js.map +1 -0
- package/dist/routes/tags.d.ts +4 -0
- package/dist/routes/tags.d.ts.map +1 -0
- package/dist/routes/tags.js +37 -0
- package/dist/routes/tags.js.map +1 -0
- package/dist/routes/users.d.ts +4 -0
- package/dist/routes/users.d.ts.map +1 -0
- package/dist/routes/users.js +181 -0
- package/dist/routes/users.js.map +1 -0
- package/dist/services/crypto.js +49 -0
- package/dist/services/crypto.js.map +1 -0
- package/dist/services/mail.d.ts +16 -0
- package/dist/services/mail.d.ts.map +1 -0
- package/dist/services/mail.js +33 -0
- package/dist/services/mail.js.map +1 -0
- package/dist/services/rsa.js +49 -0
- package/dist/services/rsa.js.map +1 -0
- package/dist/services/token.d.ts +9 -0
- package/dist/services/token.d.ts.map +1 -0
- package/dist/services/token.js +29 -0
- package/dist/services/token.js.map +1 -0
- package/dist/types.d.ts +80 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +73 -0
- package/public/admin/AdminCategories.jsx +310 -0
- package/public/admin/AdminConfig.jsx +254 -0
- package/public/admin/AdminEmail.jsx +279 -0
- package/public/admin/AdminTags.jsx +263 -0
- package/public/admin/AdminUsers.jsx +452 -0
- package/public/app.jsx +186 -0
- package/public/components/ConfirmDialog.jsx +78 -0
- package/public/components/DropdownSelect.jsx +281 -0
- package/public/components/EmailPreviewModal.jsx +104 -0
- package/public/components/EmptyState.jsx +50 -0
- package/public/components/Modal.jsx +127 -0
- package/public/components/PasswordStrength.jsx +45 -0
- package/public/components/Skeleton.jsx +68 -0
- package/public/components/Toast.jsx +80 -0
- package/public/components/TooltipIconButton.jsx +55 -0
- package/public/context/AppContext.jsx +314 -0
- package/public/features/BatchResourceModal.jsx +606 -0
- package/public/features/ChangePasswordModal.jsx +187 -0
- package/public/features/ProfileModal.jsx +170 -0
- package/public/features/ResourceCard.jsx +422 -0
- package/public/features/ResourceFormModal.jsx +915 -0
- package/public/features/ResourceRow.jsx +287 -0
- package/public/features/ResourceTimeline.jsx +472 -0
- package/public/hooks/useApi.jsx +26 -0
- package/public/hooks/useRouter.jsx +35 -0
- package/public/index.html +258 -0
- package/public/layout/AdminLayout.jsx +167 -0
- package/public/layout/AppLayout.jsx +119 -0
- package/public/layout/AuthLayout.jsx +503 -0
- package/public/layout/Header.jsx +543 -0
- package/public/layout/Sidebar.jsx +175 -0
- package/public/pages/AdminPage.jsx +30 -0
- package/public/pages/ForgotPasswordPage.jsx +93 -0
- package/public/pages/HomePage.jsx +2297 -0
- package/public/pages/LoginPage.jsx +191 -0
- package/public/pages/RegisterPage.jsx +137 -0
- package/public/pages/ResetPasswordPage.jsx +169 -0
- package/public/pages/SetupPage.jsx +157 -0
- package/public/utils/helpers.jsx +152 -0
- package/public/utils/i18n.jsx +1374 -0
- package/public/utils/preferences.jsx +220 -0
- package/public/utils/security.jsx +88 -0
- package/public/utils/theme.jsx +24 -0
- package/public/vendor/babel.min.js +2 -0
- package/public/vendor/lucide-react.min.js +9 -0
- package/public/vendor/react-dom.development.js +29869 -0
- package/public/vendor/react.development.js +3342 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAShB,CAAA;AAEF,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAKrB,CAAA;AAEF,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAapB,CAAA;AAEF,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAG0C,CAAA;AAEnE,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAIgD,CAAA;AAEtE,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAKvB,CAAA;AAEF,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUvB,CAAA;AAEF,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAStB,CAAA;AAEF,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAKtB,CAAA;AAEF,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGtB,CAAA"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { sqliteTable, text, integer, primaryKey } from 'drizzle-orm/sqlite-core';
|
|
2
|
+
export const users = sqliteTable('users', {
|
|
3
|
+
id: text('id').primaryKey(),
|
|
4
|
+
username: text('username').notNull().unique(),
|
|
5
|
+
displayName: text('display_name').notNull(),
|
|
6
|
+
email: text('email').notNull().unique(),
|
|
7
|
+
role: text('role', { enum: ['admin', 'user'] }).notNull().default('user'),
|
|
8
|
+
status: text('status', { enum: ['active', 'disabled'] }).notNull().default('active'),
|
|
9
|
+
passwordHash: text('password_hash').notNull(),
|
|
10
|
+
createdAt: integer('created_at').notNull()
|
|
11
|
+
});
|
|
12
|
+
export const categories = sqliteTable('categories', {
|
|
13
|
+
id: text('id').primaryKey(),
|
|
14
|
+
name: text('name').notNull().unique(),
|
|
15
|
+
color: text('color').notNull().default('#0071E3'),
|
|
16
|
+
createdAt: integer('created_at').notNull()
|
|
17
|
+
});
|
|
18
|
+
export const resources = sqliteTable('resources', {
|
|
19
|
+
id: text('id').primaryKey(),
|
|
20
|
+
name: text('name').notNull(),
|
|
21
|
+
url: text('url').notNull(),
|
|
22
|
+
categoryId: text('category_id').references(() => categories.id, { onDelete: 'set null' }),
|
|
23
|
+
visibility: text('visibility', { enum: ['public', 'private'] }).notNull().default('public'),
|
|
24
|
+
logoUrl: text('logo_url').default(''),
|
|
25
|
+
description: text('description').default(''),
|
|
26
|
+
enabled: integer('enabled', { mode: 'boolean' }).notNull().default(true),
|
|
27
|
+
ownerId: text('owner_id').notNull().references(() => users.id),
|
|
28
|
+
visitCount: integer('visit_count').notNull().default(0),
|
|
29
|
+
createdAt: integer('created_at').notNull(),
|
|
30
|
+
updatedAt: integer('updated_at').notNull()
|
|
31
|
+
});
|
|
32
|
+
export const resourceTags = sqliteTable('resource_tags', {
|
|
33
|
+
resourceId: text('resource_id').notNull().references(() => resources.id, { onDelete: 'cascade' }),
|
|
34
|
+
tag: text('tag').notNull()
|
|
35
|
+
}, (t) => ({ pk: primaryKey({ columns: [t.resourceId, t.tag] }) }));
|
|
36
|
+
export const favorites = sqliteTable('favorites', {
|
|
37
|
+
userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
|
|
38
|
+
resourceId: text('resource_id').notNull().references(() => resources.id, { onDelete: 'cascade' }),
|
|
39
|
+
createdAt: integer('created_at').notNull()
|
|
40
|
+
}, (t) => ({ pk: primaryKey({ columns: [t.userId, t.resourceId] }) }));
|
|
41
|
+
export const visitHistory = sqliteTable('visit_history', {
|
|
42
|
+
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
43
|
+
userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
|
|
44
|
+
resourceId: text('resource_id').notNull().references(() => resources.id, { onDelete: 'cascade' }),
|
|
45
|
+
visitedAt: integer('visited_at').notNull()
|
|
46
|
+
});
|
|
47
|
+
export const visitHourly = sqliteTable('visit_hourly', {
|
|
48
|
+
visitHour: integer('visit_hour').primaryKey(),
|
|
49
|
+
visitCount: integer('visit_count').notNull().default(0)
|
|
50
|
+
});
|
|
51
|
+
export const systemConfig = sqliteTable('system_config', {
|
|
52
|
+
id: text('id').primaryKey().default('default'),
|
|
53
|
+
siteTitle: text('site_title').notNull().default('资源导航系统'),
|
|
54
|
+
siteSubtitle: text('site_subtitle').notNull().default('统一管理与访问你的资源'),
|
|
55
|
+
logoUrl: text('logo_url').default(''),
|
|
56
|
+
tokenExpiry: integer('token_expiry').notNull().default(60),
|
|
57
|
+
resetTokenExpiry: integer('reset_token_expiry').notNull().default(60),
|
|
58
|
+
enableRegister: integer('enable_register', { mode: 'boolean' }).notNull().default(true),
|
|
59
|
+
restrictEmailDomain: integer('restrict_email_domain', { mode: 'boolean' }).notNull().default(false),
|
|
60
|
+
emailDomainWhitelist: text('email_domain_whitelist').default('')
|
|
61
|
+
});
|
|
62
|
+
export const emailConfig = sqliteTable('email_config', {
|
|
63
|
+
id: text('id').primaryKey().default('default'),
|
|
64
|
+
smtpHost: text('smtp_host').default(''),
|
|
65
|
+
smtpPort: integer('smtp_port').default(465),
|
|
66
|
+
encryption: text('encryption', { enum: ['ssl', 'tls', 'none'] }).default('ssl'),
|
|
67
|
+
fromEmail: text('from_email').default(''),
|
|
68
|
+
fromName: text('from_name').default('资源导航系统'),
|
|
69
|
+
smtpUser: text('smtp_user').default(''),
|
|
70
|
+
smtpPassword: text('smtp_password').default('')
|
|
71
|
+
});
|
|
72
|
+
export const resetTokens = sqliteTable('reset_tokens', {
|
|
73
|
+
token: text('token').primaryKey(),
|
|
74
|
+
email: text('email').notNull(),
|
|
75
|
+
expiresAt: integer('expires_at').notNull(),
|
|
76
|
+
used: integer('used', { mode: 'boolean' }).notNull().default(false)
|
|
77
|
+
});
|
|
78
|
+
export const initialized = sqliteTable('initialized', {
|
|
79
|
+
id: text('id').primaryKey().default('default'),
|
|
80
|
+
done: integer('done', { mode: 'boolean' }).notNull().default(false)
|
|
81
|
+
});
|
|
82
|
+
export const rsaKeys = sqliteTable('rsa_keys', {
|
|
83
|
+
id: text('id').primaryKey().default('current'),
|
|
84
|
+
publicKey: text('public_key').notNull(),
|
|
85
|
+
privateKey: text('private_key').notNull(),
|
|
86
|
+
createdAt: integer('created_at').notNull(),
|
|
87
|
+
});
|
|
88
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/i18n.js
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
export const DEFAULT_LOCALE = 'zh-Hans';
|
|
2
|
+
const SUPPORTED_LOCALES = new Set(['zh-Hans', 'zh-Hant', 'en', 'ja']);
|
|
3
|
+
const STATIC_TRANSLATIONS = {
|
|
4
|
+
'zh-Hant': {
|
|
5
|
+
'权限不足': '權限不足',
|
|
6
|
+
'缺少 Authorization header': '缺少 Authorization header',
|
|
7
|
+
'token 无效或已过期': 'token 無效或已過期',
|
|
8
|
+
'账号已被禁用': '帳號已被停用',
|
|
9
|
+
'系统已初始化': '系統已初始化',
|
|
10
|
+
'用户名格式不正确(3-20字符,字母/数字/下划线,不能以数字开头)': '使用者名稱格式不正確(3-20 字元,字母/數字/底線,且不能以數字開頭)',
|
|
11
|
+
'显示名称须为1-30字符': '顯示名稱須為 1-30 字元',
|
|
12
|
+
'邮箱格式不正确': '信箱格式不正確',
|
|
13
|
+
'密码须为8-64字符,且同时包含字母和数字': '密碼須為 8-64 字元,且同時包含字母與數字',
|
|
14
|
+
'两次密码不一致': '兩次密碼不一致',
|
|
15
|
+
'请求参数校验失败': '請求參數驗證失敗',
|
|
16
|
+
'初始化完成': '初始化完成',
|
|
17
|
+
'用户名或密码错误': '使用者名稱或密碼錯誤',
|
|
18
|
+
'注册功能已关闭': '註冊功能已關閉',
|
|
19
|
+
'用户名格式不正确': '使用者名稱格式不正確',
|
|
20
|
+
'邮箱域名不在白名单': '信箱網域不在白名單內',
|
|
21
|
+
'用户名已被占用': '使用者名稱已被占用',
|
|
22
|
+
'邮箱已被注册': '信箱已被註冊',
|
|
23
|
+
'注册成功': '註冊成功',
|
|
24
|
+
'若邮箱已注册,重置链接已发送': '若信箱已註冊,重設連結已寄出',
|
|
25
|
+
'重置令牌无效': '重設令牌無效',
|
|
26
|
+
'密码已重置': '密碼已重設',
|
|
27
|
+
'token 无效': 'token 無效',
|
|
28
|
+
'当前密码错误': '目前密碼錯誤',
|
|
29
|
+
'新旧密码不能相同': '新舊密碼不能相同',
|
|
30
|
+
'密码已修改': '密碼已修改',
|
|
31
|
+
'类别名称已存在': '類別名稱已存在',
|
|
32
|
+
'类别不存在': '類別不存在',
|
|
33
|
+
'删除成功': '刪除成功',
|
|
34
|
+
'站点标题须为 1-50 字符': '站點標題須為 1-50 字元',
|
|
35
|
+
'站点副标题最多 100 字符': '站點副標題最多 100 字元',
|
|
36
|
+
'Logo URL 格式不正确': 'Logo URL 格式不正確',
|
|
37
|
+
'Token 有效期须为 5-43200 分钟的整数': 'Token 有效期須為 5-43200 分鐘的整數',
|
|
38
|
+
'重置链接有效期须为 5-43200 分钟的整数': '重設連結有效期須為 5-43200 分鐘的整數',
|
|
39
|
+
'邮箱域名白名单格式不正确': '信箱網域白名單格式不正確',
|
|
40
|
+
'启用邮箱域名限制时,白名单不能为空': '啟用信箱網域限制時,白名單不能為空',
|
|
41
|
+
'SMTP 端口须为 1-65535 的整数': 'SMTP 埠號須為 1-65535 的整數',
|
|
42
|
+
'加密方式必须为 ssl、tls 或 none': '加密方式必須為 ssl、tls 或 none',
|
|
43
|
+
'发件人邮箱格式不正确': '寄件者信箱格式不正確',
|
|
44
|
+
'发件人名称最多 50 字符': '寄件者名稱最多 50 字元',
|
|
45
|
+
'测试邮件已发送': '測試郵件已寄出',
|
|
46
|
+
'标签最多 10 个': '標籤最多 10 個',
|
|
47
|
+
'标签格式不正确(1-20字符,不可含空格)': '標籤格式不正確(1-20 字元,不可含空格)',
|
|
48
|
+
'资源不存在': '資源不存在',
|
|
49
|
+
'用户不存在': '使用者不存在',
|
|
50
|
+
'不能删除自身账号': '不能刪除自己的帳號',
|
|
51
|
+
},
|
|
52
|
+
en: {
|
|
53
|
+
'权限不足': 'Permission denied',
|
|
54
|
+
'缺少 Authorization header': 'Missing Authorization header',
|
|
55
|
+
'token 无效或已过期': 'Token is invalid or expired',
|
|
56
|
+
'账号已被禁用': 'Account is disabled',
|
|
57
|
+
'系统已初始化': 'System has already been initialized',
|
|
58
|
+
'用户名格式不正确(3-20字符,字母/数字/下划线,不能以数字开头)': 'Username format is invalid (3-20 characters, letters, numbers, underscores, not starting with a number)',
|
|
59
|
+
'显示名称须为1-30字符': 'Display name must be 1-30 characters',
|
|
60
|
+
'邮箱格式不正确': 'Email format is invalid',
|
|
61
|
+
'密码须为8-64字符,且同时包含字母和数字': 'Password must be 8-64 characters and include both letters and numbers',
|
|
62
|
+
'两次密码不一致': 'Passwords do not match',
|
|
63
|
+
'请求参数校验失败': 'Request validation failed',
|
|
64
|
+
'初始化完成': 'Initialization completed',
|
|
65
|
+
'用户名或密码错误': 'Incorrect username or password',
|
|
66
|
+
'注册功能已关闭': 'Registration is disabled',
|
|
67
|
+
'用户名格式不正确': 'Username format is invalid',
|
|
68
|
+
'邮箱域名不在白名单': 'Email domain is not allowed',
|
|
69
|
+
'用户名已被占用': 'Username is already taken',
|
|
70
|
+
'邮箱已被注册': 'Email is already registered',
|
|
71
|
+
'注册成功': 'Registration successful',
|
|
72
|
+
'若邮箱已注册,重置链接已发送': 'If the email is registered, a reset link has been sent',
|
|
73
|
+
'重置令牌无效': 'Reset token is invalid',
|
|
74
|
+
'密码已重置': 'Password has been reset',
|
|
75
|
+
'token 无效': 'Token is invalid',
|
|
76
|
+
'当前密码错误': 'Current password is incorrect',
|
|
77
|
+
'新旧密码不能相同': 'New password must differ from the current password',
|
|
78
|
+
'密码已修改': 'Password has been changed',
|
|
79
|
+
'类别名称已存在': 'Category name already exists',
|
|
80
|
+
'类别不存在': 'Category does not exist',
|
|
81
|
+
'删除成功': 'Deleted successfully',
|
|
82
|
+
'站点标题须为 1-50 字符': 'Site title must be 1-50 characters',
|
|
83
|
+
'站点副标题最多 100 字符': 'Site subtitle can be up to 100 characters',
|
|
84
|
+
'Logo URL 格式不正确': 'Logo URL format is invalid',
|
|
85
|
+
'Token 有效期须为 5-43200 分钟的整数': 'Token expiry must be an integer between 5 and 43200 minutes',
|
|
86
|
+
'重置链接有效期须为 5-43200 分钟的整数': 'Reset link expiry must be an integer between 5 and 43200 minutes',
|
|
87
|
+
'邮箱域名白名单格式不正确': 'Email domain allowlist format is invalid',
|
|
88
|
+
'启用邮箱域名限制时,白名单不能为空': 'Email domain allowlist cannot be empty when domain restriction is enabled',
|
|
89
|
+
'SMTP 端口须为 1-65535 的整数': 'SMTP port must be an integer between 1 and 65535',
|
|
90
|
+
'加密方式必须为 ssl、tls 或 none': 'Encryption must be ssl, tls, or none',
|
|
91
|
+
'发件人邮箱格式不正确': 'Sender email format is invalid',
|
|
92
|
+
'发件人名称最多 50 字符': 'Sender name can be up to 50 characters',
|
|
93
|
+
'测试邮件已发送': 'Test email sent',
|
|
94
|
+
'标签最多 10 个': 'You can use up to 10 tags',
|
|
95
|
+
'标签格式不正确(1-20字符,不可含空格)': 'Tag format is invalid (1-20 characters, no spaces)',
|
|
96
|
+
'资源不存在': 'Resource does not exist',
|
|
97
|
+
'用户不存在': 'User does not exist',
|
|
98
|
+
'不能删除自身账号': 'You cannot delete your own account',
|
|
99
|
+
},
|
|
100
|
+
ja: {
|
|
101
|
+
'权限不足': '権限がありません',
|
|
102
|
+
'缺少 Authorization header': 'Authorization ヘッダーがありません',
|
|
103
|
+
'token 无效或已过期': 'token が無効か期限切れです',
|
|
104
|
+
'账号已被禁用': 'アカウントは無効化されています',
|
|
105
|
+
'系统已初始化': 'システムはすでに初期化されています',
|
|
106
|
+
'用户名格式不正确(3-20字符,字母/数字/下划线,不能以数字开头)': 'ユーザー名の形式が正しくありません(3〜20 文字、英数字とアンダースコア、数字始まり不可)',
|
|
107
|
+
'显示名称须为1-30字符': '表示名は 1〜30 文字である必要があります',
|
|
108
|
+
'邮箱格式不正确': 'メール形式が正しくありません',
|
|
109
|
+
'密码须为8-64字符,且同时包含字母和数字': 'パスワードは 8〜64 文字で、英字と数字を含める必要があります',
|
|
110
|
+
'两次密码不一致': '確認用パスワードが一致しません',
|
|
111
|
+
'请求参数校验失败': 'リクエストの検証に失敗しました',
|
|
112
|
+
'初始化完成': '初期化が完了しました',
|
|
113
|
+
'用户名或密码错误': 'ユーザー名またはパスワードが正しくありません',
|
|
114
|
+
'注册功能已关闭': '登録機能は無効です',
|
|
115
|
+
'用户名格式不正确': 'ユーザー名の形式が正しくありません',
|
|
116
|
+
'邮箱域名不在白名单': 'メールドメインが許可リストにありません',
|
|
117
|
+
'用户名已被占用': 'ユーザー名はすでに使用されています',
|
|
118
|
+
'邮箱已被注册': 'メールアドレスはすでに登録されています',
|
|
119
|
+
'注册成功': '登録に成功しました',
|
|
120
|
+
'若邮箱已注册,重置链接已发送': '登録済みの場合、再設定リンクを送信しました',
|
|
121
|
+
'重置令牌无效': '再設定トークンが無効です',
|
|
122
|
+
'密码已重置': 'パスワードを再設定しました',
|
|
123
|
+
'token 无效': 'token が無効です',
|
|
124
|
+
'当前密码错误': '現在のパスワードが正しくありません',
|
|
125
|
+
'新旧密码不能相同': '新しいパスワードは現在のものと異なる必要があります',
|
|
126
|
+
'密码已修改': 'パスワードを変更しました',
|
|
127
|
+
'类别名称已存在': 'カテゴリ名はすでに存在します',
|
|
128
|
+
'类别不存在': 'カテゴリが存在しません',
|
|
129
|
+
'删除成功': '削除しました',
|
|
130
|
+
'站点标题须为 1-50 字符': 'サイトタイトルは 1〜50 文字である必要があります',
|
|
131
|
+
'站点副标题最多 100 字符': 'サイトサブタイトルは 100 文字以内である必要があります',
|
|
132
|
+
'Logo URL 格式不正确': 'Logo URL の形式が正しくありません',
|
|
133
|
+
'Token 有效期须为 5-43200 分钟的整数': 'Token の有効期限は 5〜43200 分の整数である必要があります',
|
|
134
|
+
'重置链接有效期须为 5-43200 分钟的整数': '再設定リンクの有効期限は 5〜43200 分の整数である必要があります',
|
|
135
|
+
'邮箱域名白名单格式不正确': 'メールドメイン許可リストの形式が正しくありません',
|
|
136
|
+
'启用邮箱域名限制时,白名单不能为空': 'ドメイン制限を有効にする場合、許可リストは空にできません',
|
|
137
|
+
'SMTP 端口须为 1-65535 的整数': 'SMTP ポートは 1〜65535 の整数である必要があります',
|
|
138
|
+
'加密方式必须为 ssl、tls 或 none': '暗号化方式は ssl、tls、none のいずれかである必要があります',
|
|
139
|
+
'发件人邮箱格式不正确': '送信者メールアドレスの形式が正しくありません',
|
|
140
|
+
'发件人名称最多 50 字符': '送信者名は 50 文字以内である必要があります',
|
|
141
|
+
'测试邮件已发送': 'テストメールを送信しました',
|
|
142
|
+
'标签最多 10 个': 'タグは最大 10 個までです',
|
|
143
|
+
'标签格式不正确(1-20字符,不可含空格)': 'タグ形式が正しくありません(1〜20 文字、空白不可)',
|
|
144
|
+
'资源不存在': 'リソースが存在しません',
|
|
145
|
+
'用户不存在': 'ユーザーが存在しません',
|
|
146
|
+
'不能删除自身账号': '自分自身のアカウントは削除できません',
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
const RESET_TOKEN_ERROR_MESSAGES = {
|
|
150
|
+
'zh-Hant': {
|
|
151
|
+
RESET_TOKEN_INVALID: '重設令牌無效',
|
|
152
|
+
RESET_TOKEN_USED: '重設令牌已使用',
|
|
153
|
+
RESET_TOKEN_EXPIRED: '重設令牌已過期',
|
|
154
|
+
},
|
|
155
|
+
en: {
|
|
156
|
+
RESET_TOKEN_INVALID: 'Reset token is invalid',
|
|
157
|
+
RESET_TOKEN_USED: 'Reset token has already been used',
|
|
158
|
+
RESET_TOKEN_EXPIRED: 'Reset token has expired',
|
|
159
|
+
},
|
|
160
|
+
ja: {
|
|
161
|
+
RESET_TOKEN_INVALID: '再設定トークンが無効です',
|
|
162
|
+
RESET_TOKEN_USED: '再設定トークンはすでに使用されています',
|
|
163
|
+
RESET_TOKEN_EXPIRED: '再設定トークンの有効期限が切れています',
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
export function normalizeLocale(candidate) {
|
|
167
|
+
if (!candidate)
|
|
168
|
+
return DEFAULT_LOCALE;
|
|
169
|
+
const normalized = String(candidate).trim();
|
|
170
|
+
if (!normalized)
|
|
171
|
+
return DEFAULT_LOCALE;
|
|
172
|
+
const firstToken = normalized.split(',')[0].trim();
|
|
173
|
+
const languageToken = firstToken.split(';')[0].trim().toLowerCase();
|
|
174
|
+
if (SUPPORTED_LOCALES.has(firstToken))
|
|
175
|
+
return firstToken;
|
|
176
|
+
if (SUPPORTED_LOCALES.has(normalized))
|
|
177
|
+
return normalized;
|
|
178
|
+
if (languageToken === 'zh-hant' || languageToken.startsWith('zh-tw') || languageToken.startsWith('zh-hk') || languageToken.startsWith('zh-mo'))
|
|
179
|
+
return 'zh-Hant';
|
|
180
|
+
if (languageToken === 'zh-hans' || languageToken.startsWith('zh-cn') || languageToken.startsWith('zh-sg') || languageToken === 'zh')
|
|
181
|
+
return 'zh-Hans';
|
|
182
|
+
if (languageToken.startsWith('en'))
|
|
183
|
+
return 'en';
|
|
184
|
+
if (languageToken.startsWith('ja'))
|
|
185
|
+
return 'ja';
|
|
186
|
+
return DEFAULT_LOCALE;
|
|
187
|
+
}
|
|
188
|
+
export function getRequestLocale(req) {
|
|
189
|
+
const headerValue = req.headers['accept-language'];
|
|
190
|
+
if (Array.isArray(headerValue))
|
|
191
|
+
return normalizeLocale(headerValue[0] ?? null);
|
|
192
|
+
return normalizeLocale(headerValue ?? null);
|
|
193
|
+
}
|
|
194
|
+
export function localizeText(locale, sourceText) {
|
|
195
|
+
if (locale === 'zh-Hans')
|
|
196
|
+
return sourceText;
|
|
197
|
+
return STATIC_TRANSLATIONS[locale][sourceText] ?? sourceText;
|
|
198
|
+
}
|
|
199
|
+
export function localizeFields(locale, fields) {
|
|
200
|
+
if (!fields)
|
|
201
|
+
return undefined;
|
|
202
|
+
const localized = {};
|
|
203
|
+
Object.entries(fields).forEach(([key, value]) => {
|
|
204
|
+
localized[key] = localizeText(locale, value);
|
|
205
|
+
});
|
|
206
|
+
return localized;
|
|
207
|
+
}
|
|
208
|
+
export function getResetTokenErrorMessage(locale, code) {
|
|
209
|
+
if (locale === 'zh-Hans')
|
|
210
|
+
return '重置令牌无效';
|
|
211
|
+
return RESET_TOKEN_ERROR_MESSAGES[locale][code] || localizeText(locale, '重置令牌无效');
|
|
212
|
+
}
|
|
213
|
+
export function getRegisterMail(locale, displayName, username, tempPassword) {
|
|
214
|
+
if (locale === 'zh-Hant') {
|
|
215
|
+
return {
|
|
216
|
+
subject: '歡迎註冊,您的初始密碼',
|
|
217
|
+
body: `您好 ${displayName},\n\n您的帳號已建立成功。\n\n使用者名稱:${username}\n初始密碼:${tempPassword}\n\n請登入後儘快修改密碼。`,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
if (locale === 'en') {
|
|
221
|
+
return {
|
|
222
|
+
subject: 'Welcome! Your initial password',
|
|
223
|
+
body: `Hello ${displayName},\n\nYour account has been created successfully.\n\nUsername: ${username}\nInitial password: ${tempPassword}\n\nPlease sign in and change it as soon as possible.`,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
if (locale === 'ja') {
|
|
227
|
+
return {
|
|
228
|
+
subject: '登録完了のお知らせと初期パスワード',
|
|
229
|
+
body: `こんにちは ${displayName} さん\n\nアカウントの作成が完了しました。\n\nユーザー名: ${username}\n初期パスワード: ${tempPassword}\n\nログイン後、できるだけ早くパスワードを変更してください。`,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
subject: '欢迎注册,您的初始密码',
|
|
234
|
+
body: `您好 ${displayName},\n\n您的账号已创建成功。\n\n用户名:${username}\n初始密码:${tempPassword}\n\n请登录后及时修改密码。`,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
export function getForgotPasswordMail(locale, displayName, resetLink, expiryMinutes) {
|
|
238
|
+
if (locale === 'zh-Hant') {
|
|
239
|
+
return {
|
|
240
|
+
subject: '密碼重設請求',
|
|
241
|
+
body: `您好 ${displayName},\n\n您請求重設密碼。請點擊以下連結完成重設(${expiryMinutes} 分鐘內有效):\n\n${resetLink}\n\n若非本人操作,請忽略此郵件。`,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
if (locale === 'en') {
|
|
245
|
+
return {
|
|
246
|
+
subject: 'Password reset request',
|
|
247
|
+
body: `Hello ${displayName},\n\nYou requested a password reset. Use the link below to continue (valid for ${expiryMinutes} minutes):\n\n${resetLink}\n\nIf this was not you, you can ignore this email.`,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
if (locale === 'ja') {
|
|
251
|
+
return {
|
|
252
|
+
subject: 'パスワード再設定のご案内',
|
|
253
|
+
body: `こんにちは ${displayName} さん\n\nパスワード再設定のリクエストを受け付けました。以下のリンクから続行してください(有効期限 ${expiryMinutes} 分)。\n\n${resetLink}\n\n心当たりがない場合は、このメールを無視してください。`,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
subject: '密码重置请求',
|
|
258
|
+
body: `您好 ${displayName},\n\n您请求重置密码。请点击以下链接完成重置(${expiryMinutes} 分钟内有效):\n\n${resetLink}\n\n若非本人操作,请忽略此邮件。`,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
export function getAdminResetPasswordMail(locale, displayName, newPassword) {
|
|
262
|
+
if (locale === 'zh-Hant') {
|
|
263
|
+
return {
|
|
264
|
+
subject: '管理員已重設您的密碼',
|
|
265
|
+
body: `您好 ${displayName},\n\n管理員已重設您的帳號密碼。\n\n新密碼:${newPassword}\n\n請登入後儘快修改密碼。`,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
if (locale === 'en') {
|
|
269
|
+
return {
|
|
270
|
+
subject: 'An administrator reset your password',
|
|
271
|
+
body: `Hello ${displayName},\n\nAn administrator has reset your account password.\n\nNew password: ${newPassword}\n\nPlease sign in and change it as soon as possible.`,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
if (locale === 'ja') {
|
|
275
|
+
return {
|
|
276
|
+
subject: '管理者がパスワードを再設定しました',
|
|
277
|
+
body: `こんにちは ${displayName} さん\n\n管理者があなたのアカウントのパスワードを再設定しました。\n\n新しいパスワード: ${newPassword}\n\nログイン後、できるだけ早く変更してください。`,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
subject: '管理员已重置您的密码',
|
|
282
|
+
body: `您好 ${displayName},\n\n管理员已重置您的账号密码。\n\n新密码:${newPassword}\n\n请登录后及时修改密码。`,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
export function getTestMail(locale, displayName) {
|
|
286
|
+
if (locale === 'zh-Hant') {
|
|
287
|
+
return {
|
|
288
|
+
subject: '郵件服務測試',
|
|
289
|
+
body: `您好 ${displayName},\n\n這是一封測試郵件,用來確認郵件服務配置正常。`,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
if (locale === 'en') {
|
|
293
|
+
return {
|
|
294
|
+
subject: 'Email service test',
|
|
295
|
+
body: `Hello ${displayName},\n\nThis is a test email to confirm the email service is configured correctly.`,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
if (locale === 'ja') {
|
|
299
|
+
return {
|
|
300
|
+
subject: 'メールサービスのテスト',
|
|
301
|
+
body: `こんにちは ${displayName} さん\n\nこれはメールサービスの設定確認用テストメールです。`,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
return {
|
|
305
|
+
subject: '邮件服务测试',
|
|
306
|
+
body: `您好 ${displayName},\n\n这是一封测试邮件,确认邮件服务配置正常。`,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=i18n.js.map
|
package/dist/i18n.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,cAAc,GAAc,SAAS,CAAA;AAElD,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAY,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;AAEhF,MAAM,mBAAmB,GAAkE;IACzF,SAAS,EAAE;QACT,MAAM,EAAE,MAAM;QACd,yBAAyB,EAAE,yBAAyB;QACpD,cAAc,EAAE,cAAc;QAC9B,QAAQ,EAAE,QAAQ;QAClB,QAAQ,EAAE,QAAQ;QAClB,oCAAoC,EAAE,uCAAuC;QAC7E,cAAc,EAAE,gBAAgB;QAChC,SAAS,EAAE,SAAS;QACpB,uBAAuB,EAAE,yBAAyB;QAClD,SAAS,EAAE,SAAS;QACpB,UAAU,EAAE,UAAU;QACtB,OAAO,EAAE,OAAO;QAChB,UAAU,EAAE,YAAY;QACxB,SAAS,EAAE,SAAS;QACpB,UAAU,EAAE,YAAY;QACxB,WAAW,EAAE,YAAY;QACzB,SAAS,EAAE,WAAW;QACtB,QAAQ,EAAE,QAAQ;QAClB,MAAM,EAAE,MAAM;QACd,gBAAgB,EAAE,gBAAgB;QAClC,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,OAAO;QAChB,UAAU,EAAE,UAAU;QACtB,QAAQ,EAAE,QAAQ;QAClB,UAAU,EAAE,UAAU;QACtB,OAAO,EAAE,OAAO;QAChB,SAAS,EAAE,SAAS;QACpB,OAAO,EAAE,OAAO;QAChB,MAAM,EAAE,MAAM;QACd,gBAAgB,EAAE,gBAAgB;QAClC,gBAAgB,EAAE,gBAAgB;QAClC,gBAAgB,EAAE,gBAAgB;QAClC,2BAA2B,EAAE,2BAA2B;QACxD,yBAAyB,EAAE,yBAAyB;QACpD,cAAc,EAAE,cAAc;QAC9B,mBAAmB,EAAE,mBAAmB;QACxC,uBAAuB,EAAE,uBAAuB;QAChD,wBAAwB,EAAE,wBAAwB;QAClD,YAAY,EAAE,YAAY;QAC1B,eAAe,EAAE,eAAe;QAChC,SAAS,EAAE,SAAS;QACpB,WAAW,EAAE,WAAW;QACxB,uBAAuB,EAAE,wBAAwB;QACjD,OAAO,EAAE,OAAO;QAChB,OAAO,EAAE,QAAQ;QACjB,UAAU,EAAE,WAAW;KACxB;IACD,EAAE,EAAE;QACF,MAAM,EAAE,mBAAmB;QAC3B,yBAAyB,EAAE,8BAA8B;QACzD,cAAc,EAAE,6BAA6B;QAC7C,QAAQ,EAAE,qBAAqB;QAC/B,QAAQ,EAAE,qCAAqC;QAC/C,oCAAoC,EAAE,yGAAyG;QAC/I,cAAc,EAAE,sCAAsC;QACtD,SAAS,EAAE,yBAAyB;QACpC,uBAAuB,EAAE,uEAAuE;QAChG,SAAS,EAAE,wBAAwB;QACnC,UAAU,EAAE,2BAA2B;QACvC,OAAO,EAAE,0BAA0B;QACnC,UAAU,EAAE,gCAAgC;QAC5C,SAAS,EAAE,0BAA0B;QACrC,UAAU,EAAE,4BAA4B;QACxC,WAAW,EAAE,6BAA6B;QAC1C,SAAS,EAAE,2BAA2B;QACtC,QAAQ,EAAE,6BAA6B;QACvC,MAAM,EAAE,yBAAyB;QACjC,gBAAgB,EAAE,wDAAwD;QAC1E,QAAQ,EAAE,wBAAwB;QAClC,OAAO,EAAE,yBAAyB;QAClC,UAAU,EAAE,kBAAkB;QAC9B,QAAQ,EAAE,+BAA+B;QACzC,UAAU,EAAE,oDAAoD;QAChE,OAAO,EAAE,2BAA2B;QACpC,SAAS,EAAE,8BAA8B;QACzC,OAAO,EAAE,yBAAyB;QAClC,MAAM,EAAE,sBAAsB;QAC9B,gBAAgB,EAAE,oCAAoC;QACtD,gBAAgB,EAAE,2CAA2C;QAC7D,gBAAgB,EAAE,4BAA4B;QAC9C,2BAA2B,EAAE,6DAA6D;QAC1F,yBAAyB,EAAE,kEAAkE;QAC7F,cAAc,EAAE,0CAA0C;QAC1D,mBAAmB,EAAE,2EAA2E;QAChG,uBAAuB,EAAE,kDAAkD;QAC3E,wBAAwB,EAAE,sCAAsC;QAChE,YAAY,EAAE,gCAAgC;QAC9C,eAAe,EAAE,wCAAwC;QACzD,SAAS,EAAE,iBAAiB;QAC5B,WAAW,EAAE,2BAA2B;QACxC,uBAAuB,EAAE,oDAAoD;QAC7E,OAAO,EAAE,yBAAyB;QAClC,OAAO,EAAE,qBAAqB;QAC9B,UAAU,EAAE,oCAAoC;KACjD;IACD,EAAE,EAAE;QACF,MAAM,EAAE,UAAU;QAClB,yBAAyB,EAAE,0BAA0B;QACrD,cAAc,EAAE,kBAAkB;QAClC,QAAQ,EAAE,iBAAiB;QAC3B,QAAQ,EAAE,mBAAmB;QAC7B,oCAAoC,EAAE,gDAAgD;QACtF,cAAc,EAAE,wBAAwB;QACxC,SAAS,EAAE,gBAAgB;QAC3B,uBAAuB,EAAE,kCAAkC;QAC3D,SAAS,EAAE,iBAAiB;QAC5B,UAAU,EAAE,iBAAiB;QAC7B,OAAO,EAAE,YAAY;QACrB,UAAU,EAAE,wBAAwB;QACpC,SAAS,EAAE,WAAW;QACtB,UAAU,EAAE,mBAAmB;QAC/B,WAAW,EAAE,qBAAqB;QAClC,SAAS,EAAE,mBAAmB;QAC9B,QAAQ,EAAE,qBAAqB;QAC/B,MAAM,EAAE,WAAW;QACnB,gBAAgB,EAAE,uBAAuB;QACzC,QAAQ,EAAE,cAAc;QACxB,OAAO,EAAE,eAAe;QACxB,UAAU,EAAE,aAAa;QACzB,QAAQ,EAAE,mBAAmB;QAC7B,UAAU,EAAE,2BAA2B;QACvC,OAAO,EAAE,cAAc;QACvB,SAAS,EAAE,gBAAgB;QAC3B,OAAO,EAAE,aAAa;QACtB,MAAM,EAAE,QAAQ;QAChB,gBAAgB,EAAE,4BAA4B;QAC9C,gBAAgB,EAAE,+BAA+B;QACjD,gBAAgB,EAAE,uBAAuB;QACzC,2BAA2B,EAAE,qCAAqC;QAClE,yBAAyB,EAAE,qCAAqC;QAChE,cAAc,EAAE,0BAA0B;QAC1C,mBAAmB,EAAE,8BAA8B;QACnD,uBAAuB,EAAE,iCAAiC;QAC1D,wBAAwB,EAAE,qCAAqC;QAC/D,YAAY,EAAE,wBAAwB;QACtC,eAAe,EAAE,yBAAyB;QAC1C,SAAS,EAAE,eAAe;QAC1B,WAAW,EAAE,gBAAgB;QAC7B,uBAAuB,EAAE,6BAA6B;QACtD,OAAO,EAAE,aAAa;QACtB,OAAO,EAAE,aAAa;QACtB,UAAU,EAAE,oBAAoB;KACjC;CACF,CAAA;AAED,MAAM,0BAA0B,GAAkE;IAChG,SAAS,EAAE;QACT,mBAAmB,EAAE,QAAQ;QAC7B,gBAAgB,EAAE,SAAS;QAC3B,mBAAmB,EAAE,SAAS;KAC/B;IACD,EAAE,EAAE;QACF,mBAAmB,EAAE,wBAAwB;QAC7C,gBAAgB,EAAE,mCAAmC;QACrD,mBAAmB,EAAE,yBAAyB;KAC/C;IACD,EAAE,EAAE;QACF,mBAAmB,EAAE,cAAc;QACnC,gBAAgB,EAAE,qBAAqB;QACvC,mBAAmB,EAAE,qBAAqB;KAC3C;CACF,CAAA;AAED,MAAM,UAAU,eAAe,CAAC,SAAyB;IACvD,IAAI,CAAC,SAAS;QAAE,OAAO,cAAc,CAAA;IACrC,MAAM,UAAU,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,CAAA;IAC3C,IAAI,CAAC,UAAU;QAAE,OAAO,cAAc,CAAA;IAEtC,MAAM,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAA;IACnD,MAAM,aAAa,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IAEpE,IAAI,iBAAiB,CAAC,GAAG,CAAC,UAAuB,CAAC;QAAE,OAAO,UAAuB,CAAA;IAClF,IAAI,iBAAiB,CAAC,GAAG,CAAC,UAAuB,CAAC;QAAE,OAAO,UAAuB,CAAA;IAClF,IAAI,aAAa,KAAK,SAAS,IAAI,aAAa,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,aAAa,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,aAAa,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,SAAS,CAAA;IAChK,IAAI,aAAa,KAAK,SAAS,IAAI,aAAa,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,aAAa,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,aAAa,KAAK,IAAI;QAAE,OAAO,SAAS,CAAA;IACrJ,IAAI,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAC/C,IAAI,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAC/C,OAAO,cAAc,CAAA;AACvB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAoC;IACnE,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAClD,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC;QAAE,OAAO,eAAe,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAA;IAC9E,OAAO,eAAe,CAAC,WAAW,IAAI,IAAI,CAAC,CAAA;AAC7C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAiB,EAAE,UAAkB;IAChE,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,UAAU,CAAA;IAC3C,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,IAAI,UAAU,CAAA;AAC9D,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAiB,EAAE,MAA+B;IAC/E,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAA;IAC7B,MAAM,SAAS,GAA2B,EAAE,CAAA;IAC5C,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QAC9C,SAAS,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;IACF,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,MAAiB,EAAE,IAAY;IACvE,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAA;IACzC,OAAO,0BAA0B,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;AACnF,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAiB,EAAE,WAAmB,EAAE,QAAgB,EAAE,YAAoB;IAC5G,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO;YACL,OAAO,EAAE,aAAa;YACtB,IAAI,EAAE,MAAM,WAAW,4BAA4B,QAAQ,UAAU,YAAY,iBAAiB;SACnG,CAAA;IACH,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO;YACL,OAAO,EAAE,gCAAgC;YACzC,IAAI,EAAE,SAAS,WAAW,iEAAiE,QAAQ,uBAAuB,YAAY,uDAAuD;SAC9L,CAAA;IACH,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO;YACL,OAAO,EAAE,mBAAmB;YAC5B,IAAI,EAAE,SAAS,WAAW,qCAAqC,QAAQ,cAAc,YAAY,kCAAkC;SACpI,CAAA;IACH,CAAC;IACD,OAAO;QACL,OAAO,EAAE,aAAa;QACtB,IAAI,EAAE,MAAM,WAAW,0BAA0B,QAAQ,UAAU,YAAY,iBAAiB;KACjG,CAAA;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAAiB,EAAE,WAAmB,EAAE,SAAiB,EAAE,aAAqB;IACpH,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO;YACL,OAAO,EAAE,QAAQ;YACjB,IAAI,EAAE,MAAM,WAAW,4BAA4B,aAAa,eAAe,SAAS,oBAAoB;SAC7G,CAAA;IACH,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO;YACL,OAAO,EAAE,wBAAwB;YACjC,IAAI,EAAE,SAAS,WAAW,kFAAkF,aAAa,iBAAiB,SAAS,qDAAqD;SACzM,CAAA;IACH,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO;YACL,OAAO,EAAE,cAAc;YACvB,IAAI,EAAE,SAAS,WAAW,uDAAuD,aAAa,WAAW,SAAS,gCAAgC;SACnJ,CAAA;IACH,CAAC;IACD,OAAO;QACL,OAAO,EAAE,QAAQ;QACjB,IAAI,EAAE,MAAM,WAAW,4BAA4B,aAAa,eAAe,SAAS,oBAAoB;KAC7G,CAAA;AACH,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,MAAiB,EAAE,WAAmB,EAAE,WAAmB;IACnG,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO;YACL,OAAO,EAAE,YAAY;YACrB,IAAI,EAAE,MAAM,WAAW,6BAA6B,WAAW,iBAAiB;SACjF,CAAA;IACH,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO;YACL,OAAO,EAAE,sCAAsC;YAC/C,IAAI,EAAE,SAAS,WAAW,2EAA2E,WAAW,uDAAuD;SACxK,CAAA;IACH,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO;YACL,OAAO,EAAE,mBAAmB;YAC5B,IAAI,EAAE,SAAS,WAAW,oDAAoD,WAAW,4BAA4B;SACtH,CAAA;IACH,CAAC;IACD,OAAO;QACL,OAAO,EAAE,YAAY;QACrB,IAAI,EAAE,MAAM,WAAW,6BAA6B,WAAW,iBAAiB;KACjF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAiB,EAAE,WAAmB;IAChE,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO;YACL,OAAO,EAAE,QAAQ;YACjB,IAAI,EAAE,MAAM,WAAW,6BAA6B;SACrD,CAAA;IACH,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO;YACL,OAAO,EAAE,oBAAoB;YAC7B,IAAI,EAAE,SAAS,WAAW,iFAAiF;SAC5G,CAAA;IACH,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO;YACL,OAAO,EAAE,aAAa;YACtB,IAAI,EAAE,SAAS,WAAW,kCAAkC;SAC7D,CAAA;IACH,CAAC;IACD,OAAO;QACL,OAAO,EAAE,QAAQ;QACjB,IAAI,EAAE,MAAM,WAAW,2BAA2B;KACnD,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/plugins/admin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAgC,MAAM,SAAS,CAAA;;AAa/E,wBAA8B"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import fp from 'fastify-plugin';
|
|
2
|
+
import { getRequestLocale, localizeText } from '../i18n.js';
|
|
3
|
+
const adminPlugin = async (fastify) => {
|
|
4
|
+
fastify.decorate('requireAdmin', async (request, reply) => {
|
|
5
|
+
await fastify.authenticate(request, reply);
|
|
6
|
+
if (reply.sent)
|
|
7
|
+
return;
|
|
8
|
+
if (request.user.role !== 'admin') {
|
|
9
|
+
const locale = getRequestLocale(request);
|
|
10
|
+
reply.code(403).send({
|
|
11
|
+
success: false,
|
|
12
|
+
error: localizeText(locale, '权限不足'),
|
|
13
|
+
code: 'PERMISSION_DENIED',
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
export default fp(adminPlugin);
|
|
19
|
+
//# sourceMappingURL=admin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/plugins/admin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAE/B,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE3D,MAAM,WAAW,GAAuB,KAAK,EAAE,OAAO,EAAE,EAAE;IACxD,OAAO,CAAC,QAAQ,CAAC,cAAc,EAAE,KAAK,EAAE,OAAuB,EAAE,KAAmB,EAAE,EAAE;QACtF,MAAM,OAAO,CAAC,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAC1C,IAAI,KAAK,CAAC,IAAI;YAAE,OAAM;QAEtB,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;YACxC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC;gBACnC,IAAI,EAAE,mBAAmB;aAC1B,CAAC,CAAA;QACJ,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,eAAe,EAAE,CAAC,WAAW,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/plugins/auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAgC,MAAM,SAAS,CAAA;;AAiC/E,wBAA6B"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import fp from 'fastify-plugin';
|
|
2
|
+
import { db } from '../db/index.js';
|
|
3
|
+
import { users } from '../db/schema.js';
|
|
4
|
+
import { eq } from 'drizzle-orm';
|
|
5
|
+
import { getRequestLocale, localizeText } from '../i18n.js';
|
|
6
|
+
const authPlugin = async (fastify) => {
|
|
7
|
+
fastify.decorate('authenticate', async (request, reply) => {
|
|
8
|
+
const locale = getRequestLocale(request);
|
|
9
|
+
const authHeader = request.headers.authorization;
|
|
10
|
+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
11
|
+
reply.code(401).send({ success: false, error: localizeText(locale, '缺少 Authorization header'), code: 'TOKEN_MISSING' });
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const token = authHeader.slice(7);
|
|
15
|
+
try {
|
|
16
|
+
const payload = fastify.jwt.verify(token);
|
|
17
|
+
// Check user is still active
|
|
18
|
+
const user = db.select().from(users).where(eq(users.id, payload.userId)).get();
|
|
19
|
+
if (!user) {
|
|
20
|
+
reply.code(401).send({ success: false, error: localizeText(locale, 'token 无效或已过期'), code: 'TOKEN_INVALID' });
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (user.status === 'disabled') {
|
|
24
|
+
reply.code(403).send({ success: false, error: localizeText(locale, '账号已被禁用'), code: 'ACCOUNT_DISABLED' });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
request.user = { userId: payload.userId, role: payload.role };
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
reply.code(401).send({ success: false, error: localizeText(locale, 'token 无效或已过期'), code: 'TOKEN_INVALID' });
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
export default fp(authPlugin);
|
|
35
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/plugins/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAE/B,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAA;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AACvC,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAChC,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE3D,MAAM,UAAU,GAAuB,KAAK,EAAE,OAAO,EAAE,EAAE;IACvD,OAAO,CAAC,QAAQ,CAAC,cAAc,EAAE,KAAK,EAAE,OAAuB,EAAE,KAAmB,EAAE,EAAE;QACtF,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;QACxC,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAA;QAChD,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACrD,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,EAAE,yBAAyB,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAA;YACvH,OAAM;QACR,CAAC;QAED,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QACjC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAA6C,KAAK,CAAC,CAAA;YACrF,6BAA6B;YAC7B,MAAM,IAAI,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,CAAA;YAC9E,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAA;gBAC5G,OAAM;YACR,CAAC;YACD,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;gBAC/B,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC,CAAA;gBACzG,OAAM;YACR,CAAC;YACD,OAAO,CAAC,IAAI,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAA;QAC/D,CAAC;QAAC,MAAM,CAAC;YACP,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAA;QAC9G,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,eAAe,EAAE,CAAC,UAAU,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAgB,MAAM,SAAS,CAAA;AAwE/D,QAAA,MAAM,UAAU,EAAE,kBAiRjB,CAAA;AAED,eAAe,UAAU,CAAA"}
|