@h-ai/cli 0.1.0-alpha.56 → 0.1.0-alpha.58
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/README.en.md +239 -0
- package/README.ja.md +239 -0
- package/README.md +51 -1
- package/dist/chunk-2H7UOFLK.js +0 -0
- package/dist/chunk-F4IBPVZ5.js +355 -0
- package/dist/chunk-F4IBPVZ5.js.map +1 -0
- package/dist/{chunk-O3ANVICQ.js → chunk-WUSFJFW5.js} +13 -7
- package/dist/chunk-WUSFJFW5.js.map +1 -0
- package/dist/cli-config-templates-6XA7V6CC.js +5 -0
- package/dist/{cli-config-templates-OWBEGBZQ.js.map → cli-config-templates-6XA7V6CC.js.map} +1 -1
- package/dist/{cli-skill-templates-ZIWYMS4Y.js → cli-skill-templates-45CK4RQ2.js} +3 -3
- package/dist/{cli-skill-templates-ZIWYMS4Y.js.map → cli-skill-templates-45CK4RQ2.js.map} +1 -1
- package/dist/index.js +134 -125
- package/dist/index.js.map +1 -1
- package/dist/{src-TBIET7YU.js → src-DUGLX4YB.js} +7270 -2119
- package/dist/src-DUGLX4YB.js.map +1 -0
- package/package.json +17 -17
- package/templates/README.md +4 -4
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +7 -0
- package/templates/apps/admin/src/routes/+page.svelte.hbs +5 -4
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +2 -2
- package/templates/apps/api/.gitignore.hbs +13 -0
- package/templates/apps/api/.npmrc.hbs +1 -0
- package/templates/apps/api/Dockerfile.hbs +6 -5
- package/templates/apps/api/apps/{{projectName}}-contract/package.json.hbs +1 -1
- package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +2 -0
- package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +1 -1
- package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +8 -0
- package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -1
- package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +7 -8
- package/templates/apps/api/apps/{{projectName}}-service/src/index.ts.hbs +4 -3
- package/templates/apps/api/apps/{{projectName}}-service/src/lib/server/init.ts.hbs +0 -1
- package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/app-procedures.ts.hbs +4 -0
- package/templates/apps/api/apps/{{projectName}}-service/tests/service.test.ts.hbs +3 -1
- package/templates/apps/api/e2e/api-service.spec.ts.hbs +4 -2
- package/templates/apps/api/{eslint.config.js → eslint.config.js.hbs} +6 -8
- package/templates/apps/api/package.json.hbs +3 -3
- package/templates/apps/api/pnpm-workspace.yaml.hbs +26 -0
- package/templates/apps/api/tsconfig.base.json +0 -1
- package/templates/apps/fullstack/.dockerignore.hbs +3 -0
- package/templates/apps/fullstack/.gitignore.hbs +16 -0
- package/templates/apps/fullstack/.npmrc.hbs +1 -0
- package/templates/apps/fullstack/Dockerfile.hbs +6 -5
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +2 -2
- package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +5 -8
- package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +7 -2
- package/templates/apps/fullstack/apps/{{projectName}}-app/messages/en-US.json.hbs +1 -34
- package/templates/apps/fullstack/apps/{{projectName}}-app/messages/zh-CN.json.hbs +1 -34
- package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +2 -2
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +30 -170
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.d.ts.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +6 -5
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/main.ts.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +48 -22
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +3 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/eslint.config.js.hbs +5 -8
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/index.html.hbs +6 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/en-US.json.hbs +1 -28
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/zh-CN.json.hbs +1 -28
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +5 -4
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +10 -141
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/Cargo.toml.hbs +5 -5
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +3 -3
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +21 -2
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +2 -2
- package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +5 -8
- package/templates/apps/fullstack/apps/{{projectName}}-web/index.html.hbs +6 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/messages/en-US.json.hbs +1 -28
- package/templates/apps/fullstack/apps/{{projectName}}-web/messages/zh-CN.json.hbs +1 -28
- package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +2 -2
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/App.svelte.hbs +5 -143
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.d.ts.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +48 -22
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +207 -17
- package/templates/apps/fullstack/{eslint.config.js → eslint.config.js.hbs} +6 -8
- package/templates/apps/fullstack/package.json.hbs +3 -3
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +2 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/package.json.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-contract.ts.hbs +66 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +96 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/app-schemas.ts.hbs +3 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/index.ts.hbs +3 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +5 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/tests/contract.test.ts.hbs +3 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/tsup.config.ts.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +13 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_audit.yml.hbs +3 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_cache.yml.hbs +2 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +10 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_db.yml.hbs +3 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_iam.yml.hbs +21 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +10 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_storage.yml.hbs +3 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +10 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +63 -28
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/index.ts.hbs +26 -11
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +276 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/messages.ts.hbs +29 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +94 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/app-procedures.ts.hbs +6 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +5 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +98 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +446 -33
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/runtime.test.ts.hbs +48 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tsup.config.ts.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +19 -26
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/en-US.json.hbs +78 -15
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/zh-CN.json.hbs +78 -15
- package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +4 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/index.ts.hbs +3 -4
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +53 -27
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AdminWorkspace.svelte.hbs +73 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +55 -45
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AuditManagement.svelte.hbs +61 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/ProfileSettings.svelte.hbs +57 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/RoleManagement.svelte.hbs +49 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/UserManagement.svelte.hbs +174 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/management-store.svelte.ts.hbs +399 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/theme-store.svelte.ts.hbs +7 -39
- package/templates/apps/fullstack/packages/{{projectName}}-shared/svelte.config.js.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +151 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/management-store.test.ts.hbs +101 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/vitest.config.ts.hbs +7 -0
- package/templates/apps/fullstack/playwright.config.ts.hbs +6 -0
- package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +24 -0
- package/templates/apps/fullstack/tsconfig.base.json +0 -1
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +4 -0
- package/templates/apps/h5/src/routes/+page.svelte.hbs +3 -2
- package/templates/apps/mobile-app/index.html.hbs +2 -2
- package/templates/apps/mobile-app/src/App.svelte.hbs +6 -2
- package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +13 -0
- package/templates/apps/website/src/routes/+layout.svelte.hbs +8 -2
- package/templates/apps/website/src/routes/+page.svelte.hbs +4 -3
- package/templates/base/.npmrc.hbs +1 -0
- package/templates/base/Dockerfile.hbs +9 -8
- package/templates/base/e2e/app.spec.ts.hbs +1 -0
- package/templates/base/eslint-rules.js +45 -0
- package/templates/base/eslint.config.js.hbs +6 -8
- package/templates/base/package.json.hbs +39 -42
- package/templates/base/pnpm-workspace.yaml.hbs +26 -0
- package/templates/base/src/app.d.ts.hbs +4 -0
- package/templates/base/src/hooks.server.ts.hbs +0 -8
- package/templates/base/src/lib/server/init.ts.hbs +26 -5
- package/templates/base/src/routes/+layout.server.ts.hbs +18 -0
- package/templates/base/tests/scaffold.test.ts.hbs +21 -1
- package/templates/base/vite.config.ts.hbs +21 -0
- package/templates/features/ai/routes/api/chat/+server.ts +6 -7
- package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +1 -1
- package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +1 -1
- package/templates/features/iam/routes-shared/api/auth/login/+server.ts +14 -10
- package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +6 -4
- package/templates/features/storage/routes/api/upload/+server.ts +10 -8
- package/templates/skills/AGENTS.md +9 -8
- package/templates/skills/CLAUDE.md +0 -10
- package/templates/skills/bridges/admin/AGENTS.md +8 -0
- package/templates/skills/bridges/admin/copilot-instructions.md +2 -0
- package/templates/skills/bridges/api/AGENTS.md +8 -1
- package/templates/skills/bridges/api/copilot-instructions.md +2 -0
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +21 -4
- package/templates/skills/bridges/fullstack/copilot-instructions.md.hbs +5 -0
- package/templates/skills/bridges/h5/AGENTS.md +8 -0
- package/templates/skills/bridges/h5/copilot-instructions.md +2 -0
- package/templates/skills/bridges/mobile-app/AGENTS.md +8 -0
- package/templates/skills/bridges/mobile-app/copilot-instructions.md +2 -0
- package/templates/skills/bridges/website/AGENTS.md +8 -0
- package/templates/skills/bridges/website/copilot-instructions.md +2 -0
- package/templates/skills/copilot-instructions.md +2 -0
- package/templates/skills/hai-api-client/SKILL.md +2 -0
- package/templates/skills/hai-app-create/SKILL.md +9 -7
- package/templates/skills/hai-app-create/development.md +72 -0
- package/templates/skills/hai-app-create/reference.md +44 -32
- package/templates/skills/hai-app-review/SKILL.md +50 -144
- package/templates/skills/hai-app-tests/SKILL.md +3 -1
- package/templates/skills/hai-app-tests/reference.md +47 -27
- package/templates/skills/hai-build/SKILL.md +5 -4
- package/templates/skills/hai-build/reference.md +56 -34
- package/templates/skills/hai-ci/SKILL.md +8 -15
- package/templates/skills/hai-core/SKILL.md +2 -0
- package/templates/skills/hai-core/reference.md +6 -4
- package/templates/skills/hai-framework-sync/SKILL.md +8 -2
- package/templates/skills/hai-pr-review/SKILL.md +5 -4
- package/templates/skills/hai-reldb/SKILL.md +1 -0
- package/templates/skills/hai-reldb/reference.md +3 -0
- package/templates/skills/hai-scheduler/SKILL.md +3 -0
- package/templates/skills/hai-serv/SKILL.md +2 -0
- package/templates/skills/hai-serv/reference.md +18 -2
- package/templates/skills/hai-storage/SKILL.md +3 -1
- package/templates/skills/hai-ui/SKILL.md +6 -0
- package/templates/skills/hai-ui/reference.md +7 -4
- package/templates/skills/hai-vecdb/reference.md +53 -49
- package/dist/chunk-O3ANVICQ.js.map +0 -1
- package/dist/chunk-WUYLYCW2.js +0 -237
- package/dist/chunk-WUYLYCW2.js.map +0 -1
- package/dist/cli-config-templates-OWBEGBZQ.js +0 -5
- package/dist/src-TBIET7YU.js.map +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/tests/api.test.ts.hbs +0 -28
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/tests/api.test.ts.hbs +0 -28
- package/templates/apps/fullstack/apps/{{projectName}}-web/tests/api.test.ts.hbs +0 -55
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/LanguageSwitcher.svelte.hbs +0 -27
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/ThemeSwitcher.svelte.hbs +0 -18
- package/templates/skills/bridges/admin/CLAUDE.md +0 -11
- package/templates/skills/bridges/api/CLAUDE.md +0 -11
- package/templates/skills/bridges/fullstack/CLAUDE.md.hbs +0 -5
- package/templates/skills/bridges/h5/CLAUDE.md +0 -11
- package/templates/skills/bridges/mobile-app/CLAUDE.md +0 -11
- package/templates/skills/bridges/website/CLAUDE.md +0 -11
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { ServContext } from '@h-ai/serv'
|
|
2
|
+
import { audit } from '@h-ai/audit'
|
|
3
|
+
import { err, HaiCommonError, ok } from '@h-ai/core'
|
|
4
|
+
import { iam } from '@h-ai/iam'
|
|
5
|
+
import { serv } from '@h-ai/serv'
|
|
6
|
+
import { createIamProcedures } from '@h-ai/serv/features/iam'
|
|
7
|
+
import { adminContract, authContract, profileContract } from '{{fullstack.contractPackageName}}'
|
|
8
|
+
import {
|
|
9
|
+
createAdminUser,
|
|
10
|
+
getAdminRoles,
|
|
11
|
+
isRoleCode,
|
|
12
|
+
recordAudit,
|
|
13
|
+
toAdminUser,
|
|
14
|
+
updateAdminUser,
|
|
15
|
+
updateProfile,
|
|
16
|
+
uploadAvatar,
|
|
17
|
+
withAdminMutation,
|
|
18
|
+
} from '../admin-service.js'
|
|
19
|
+
import { serverM } from '../messages.js'
|
|
20
|
+
|
|
21
|
+
/** 身份认证沿用框架公共契约与 refresh-cookie 传输。 */
|
|
22
|
+
export function createAuthProcedures() {
|
|
23
|
+
const { currentUser, refresh } = createIamProcedures({ iam }).auth
|
|
24
|
+
const { login, logout, changePassword } = authContract
|
|
25
|
+
// 仅覆写需要业务审计的认证操作,其余直接复用框架;未选认证入口不挂载。
|
|
26
|
+
const audited = serv.implement({ login, logout, changePassword }).context<ServContext>().route('login', async ({ input, context }) => {
|
|
27
|
+
const result = await iam.auth.login(input)
|
|
28
|
+
if (!result.success)
|
|
29
|
+
return result
|
|
30
|
+
await recordAudit({ ...context, session: { userId: result.data.user.id, roles: result.data.roles, permissions: result.data.permissions } }, 'login', 'auth')
|
|
31
|
+
return result
|
|
32
|
+
}).route('logout').auth().handle(async ({ context }) => {
|
|
33
|
+
const result = await iam.auth.logout(context.accessToken ?? '')
|
|
34
|
+
if (!result.success)
|
|
35
|
+
return result
|
|
36
|
+
await recordAudit(context, 'logout', 'auth')
|
|
37
|
+
return result
|
|
38
|
+
}).route('changePassword').auth().handle(async ({ input, context }) => {
|
|
39
|
+
const result = await iam.user.changeCurrentUserPassword(context.accessToken ?? '', input.oldPassword, input.newPassword)
|
|
40
|
+
if (!result.success)
|
|
41
|
+
return result
|
|
42
|
+
await recordAudit(context, 'password-change', 'profile', context.session.userId)
|
|
43
|
+
return result
|
|
44
|
+
}).build()
|
|
45
|
+
return { ...audited, currentUser, refresh }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function createAdminProcedures() {
|
|
49
|
+
return serv.implement(adminContract).context<ServContext>().route('users.list').role('admin').handle(async ({ input }) => {
|
|
50
|
+
const users = await iam.user.listUsers(input)
|
|
51
|
+
if (!users.success)
|
|
52
|
+
return users
|
|
53
|
+
const roles = await iam.authz.getUserRolesForMany(users.data.items.map(user => user.id))
|
|
54
|
+
if (!roles.success)
|
|
55
|
+
return roles
|
|
56
|
+
return ok({ ...users.data, items: users.data.items.map(user => toAdminUser(user, roles.data.get(user.id) ?? [])) })
|
|
57
|
+
}).route('users.create').role('admin').handle(({ input, context }) => withAdminMutation(context, () => createAdminUser(input, context))).route('users.update').role('admin').handle(({ input, context }) => withAdminMutation(context, () => updateAdminUser(input, context))).route('users.delete').role('admin').handle(({ input, context }) => withAdminMutation(context, async () => {
|
|
58
|
+
if (context.session.userId === input.id)
|
|
59
|
+
return err(HaiCommonError.FORBIDDEN, serverM('selfProtection', { locale: context.locale }))
|
|
60
|
+
const result = await iam.user.deleteUser(input.id)
|
|
61
|
+
if (!result.success)
|
|
62
|
+
return result
|
|
63
|
+
await recordAudit(context, 'delete', 'users', input.id)
|
|
64
|
+
return ok({ deleted: true as const })
|
|
65
|
+
})).route('users.avatar').role('admin').handle(({ input, context }) => withAdminMutation(context, () => uploadAvatar(input.id, input, context))).route('roles.list').role('admin').handle(() => getAdminRoles()).route('roles.update').role('admin').handle(({ input, context }) => withAdminMutation(context, async () => {
|
|
66
|
+
const role = await iam.authz.getRole(input.id)
|
|
67
|
+
if (!role.success)
|
|
68
|
+
return role
|
|
69
|
+
if (!role.data || !isRoleCode(role.data.code))
|
|
70
|
+
return err(HaiCommonError.NOT_FOUND, serverM('notFound', { locale: context.locale }))
|
|
71
|
+
const updated = await iam.authz.updateRole(input.id, { description: input.description })
|
|
72
|
+
if (!updated.success)
|
|
73
|
+
return updated
|
|
74
|
+
const roles = await getAdminRoles()
|
|
75
|
+
if (!roles.success)
|
|
76
|
+
return roles
|
|
77
|
+
const result = roles.data.items.find(item => item.id === input.id)
|
|
78
|
+
if (!result)
|
|
79
|
+
return err(HaiCommonError.NOT_FOUND, serverM('notFound', { locale: context.locale }))
|
|
80
|
+
await recordAudit(context, 'update', 'roles', input.id, { fields: ['description'] })
|
|
81
|
+
return ok(result)
|
|
82
|
+
})).route('audit.list').role('admin').handle(async ({ input, context }) => {
|
|
83
|
+
if (input.startDate && input.endDate && input.startDate > input.endDate)
|
|
84
|
+
return err(HaiCommonError.VALIDATION_ERROR, serverM('invalidDateRange', { locale: context.locale }))
|
|
85
|
+
const result = await audit.list({ ...input, startDate: input.startDate ? new Date(input.startDate) : undefined, endDate: input.endDate ? new Date(input.endDate) : undefined })
|
|
86
|
+
if (!result.success)
|
|
87
|
+
return result
|
|
88
|
+
return ok({ ...result.data, page: input.page, pageSize: input.pageSize, items: result.data.items.map(item => ({ ...item, createdAt: new Date(item.createdAt).toISOString() })) })
|
|
89
|
+
}).build()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function createProfileProcedures() {
|
|
93
|
+
return serv.implement(profileContract).context<ServContext>().route('update').auth().handle(({ input, context }) => updateProfile(input, context)).route('avatar').auth().handle(({ input, context }) => uploadAvatar(context.session.userId, input, context)).build()
|
|
94
|
+
}
|
|
@@ -7,6 +7,9 @@ import { appContract } from '{{fullstack.contractPackageName}}'
|
|
|
7
7
|
export interface CreateAppProceduresOptions {
|
|
8
8
|
readonly name: string
|
|
9
9
|
readonly version: string
|
|
10
|
+
readonly env: string
|
|
11
|
+
readonly defaultLocale: string
|
|
12
|
+
readonly supportedLocales: readonly string[]
|
|
10
13
|
readonly startedAt: string
|
|
11
14
|
readonly frontends: readonly AppFrontendTarget[]
|
|
12
15
|
}
|
|
@@ -32,6 +35,9 @@ function createAppInfo(options: CreateAppProceduresOptions): AppInfoOutputData {
|
|
|
32
35
|
return {
|
|
33
36
|
name: options.name,
|
|
34
37
|
version: options.version,
|
|
38
|
+
env: options.env,
|
|
39
|
+
defaultLocale: options.defaultLocale,
|
|
40
|
+
supportedLocales: [...options.supportedLocales],
|
|
35
41
|
startedAt: options.startedAt,
|
|
36
42
|
frontends: [...options.frontends],
|
|
37
43
|
}
|
package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CreateAppProceduresOptions } from './app-procedures.js'
|
|
2
|
+
import { createAdminProcedures, createAuthProcedures, createProfileProcedures } from './admin-procedures.js'
|
|
2
3
|
import { createAppProcedures } from './app-procedures.js'
|
|
3
4
|
|
|
4
5
|
export interface CreateServerProceduresOptions {
|
|
@@ -9,5 +10,8 @@ export interface CreateServerProceduresOptions {
|
|
|
9
10
|
export function createServerProcedures(options: CreateServerProceduresOptions) {
|
|
10
11
|
return {
|
|
11
12
|
app: createAppProcedures(options.app),
|
|
13
|
+
iam: { auth: createAuthProcedures() },
|
|
14
|
+
admin: createAdminProcedures(),
|
|
15
|
+
profile: createProfileProcedures(),
|
|
12
16
|
}
|
|
13
|
-
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { CoreConfig, HaiResult } from '@h-ai/core'
|
|
2
|
+
import { mkdir } from 'node:fs/promises'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import process from 'node:process'
|
|
5
|
+
import { audit, AuditInitConfigSchema } from '@h-ai/audit'
|
|
6
|
+
import { cache, CacheConfigSchema } from '@h-ai/cache'
|
|
7
|
+
import { core } from '@h-ai/core'
|
|
8
|
+
import { crypto } from '@h-ai/crypto'
|
|
9
|
+
import { iam, IamConfigSchema } from '@h-ai/iam'
|
|
10
|
+
import { reldb, ReldbConfigSchema } from '@h-ai/reldb'
|
|
11
|
+
import { ServConfigSchema } from '@h-ai/serv'
|
|
12
|
+
import { storage, StorageConfigSchema } from '@h-ai/storage'
|
|
13
|
+
import { serverM } from './messages.js'
|
|
14
|
+
|
|
15
|
+
let initialized = false
|
|
16
|
+
let initializing: Promise<void> | undefined
|
|
17
|
+
|
|
18
|
+
/** 初始化失败停止启动并反序释放资源;不把功能缺失降级为成功。 */
|
|
19
|
+
export async function initializeModules(): Promise<void> {
|
|
20
|
+
if (initialized)
|
|
21
|
+
return
|
|
22
|
+
initializing ??= runInitialization().finally(() => {
|
|
23
|
+
initializing = undefined
|
|
24
|
+
})
|
|
25
|
+
return initializing
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function runInitialization(): Promise<void> {
|
|
29
|
+
try {
|
|
30
|
+
requireInitialized(core.config.validate('serv', ServConfigSchema), 'serv-config')
|
|
31
|
+
const db = ReldbConfigSchema.parse(core.config.getOrThrow('db'))
|
|
32
|
+
const cacheConfig = CacheConfigSchema.parse(core.config.getOrThrow('cache'))
|
|
33
|
+
const storageConfig = StorageConfigSchema.parse(core.config.getOrThrow('storage'))
|
|
34
|
+
const iamConfig = IamConfigSchema.parse(core.config.getOrThrow('iam'))
|
|
35
|
+
const auditConfig = AuditInitConfigSchema.parse(core.config.get('audit') ?? {})
|
|
36
|
+
if (db.type === 'sqlite' && db.database !== ':memory:')
|
|
37
|
+
await mkdir(path.dirname(db.database), { recursive: true })
|
|
38
|
+
requireInitialized(await reldb.init(db), 'reldb')
|
|
39
|
+
requireInitialized(await cache.init(cacheConfig), 'cache')
|
|
40
|
+
requireInitialized(await crypto.init(), 'crypto')
|
|
41
|
+
requireInitialized(await storage.init(storageConfig), 'storage')
|
|
42
|
+
requireInitialized(await iam.init(iamConfig), 'iam')
|
|
43
|
+
requireInitialized(await audit.init(auditConfig), 'audit')
|
|
44
|
+
await ensureRolesAndAdmin()
|
|
45
|
+
initialized = true
|
|
46
|
+
const app = core.config.getOrThrow<CoreConfig>('core')
|
|
47
|
+
core.logger.info('Application initialized', { name: app.name, version: app.version, environment: app.env })
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
await closeModules()
|
|
51
|
+
throw error
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** 此函数仅用于进程启动边界,业务请求继续返回 HaiResult。 */
|
|
56
|
+
function requireInitialized<T>(result: HaiResult<T>, moduleName: string): T {
|
|
57
|
+
if (!result.success)
|
|
58
|
+
throw new Error(`Failed to initialize ${moduleName}: ${result.error.message} (${result.error.code})`)
|
|
59
|
+
return result.data
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function ensureRolesAndAdmin(): Promise<void> {
|
|
63
|
+
for (const code of ['admin', 'user'] as const) {
|
|
64
|
+
const existing = requireInitialized(await iam.authz.getRoleByCode(code), 'iam-role')
|
|
65
|
+
if (!existing)
|
|
66
|
+
requireInitialized(await iam.authz.createRole({ code, name: serverM(code), description: serverM(code === 'admin' ? 'adminDescription' : 'userDescription'), isSystem: true }), 'iam-role')
|
|
67
|
+
}
|
|
68
|
+
const users = requireInitialized(await iam.user.listUsers({ page: 1, pageSize: 1 }), 'iam-users')
|
|
69
|
+
if (users.total > 0)
|
|
70
|
+
return
|
|
71
|
+
const password = process.env.HAI_ADMIN_DEFAULT_PASSWORD?.trim()
|
|
72
|
+
if (!password)
|
|
73
|
+
throw new Error('Set HAI_ADMIN_DEFAULT_PASSWORD before the first start to create the administrator account')
|
|
74
|
+
const registered = requireInitialized(await iam.user.register({ username: 'admin', password, displayName: serverM('admin') }), 'iam-bootstrap')
|
|
75
|
+
const adminRole = requireInitialized(await iam.authz.getRoleByCode('admin'), 'iam-role')
|
|
76
|
+
if (!adminRole)
|
|
77
|
+
throw new Error('Administrator role is missing')
|
|
78
|
+
const assigned = await iam.authz.syncRoles(registered.user.id, [adminRole.id])
|
|
79
|
+
if (!assigned.success) {
|
|
80
|
+
const removed = await iam.user.deleteUser(registered.user.id)
|
|
81
|
+
if (!removed.success)
|
|
82
|
+
core.logger.error('Failed to clean up administrator bootstrap', { error: removed.error.code })
|
|
83
|
+
requireInitialized(assigned, 'iam-bootstrap-role')
|
|
84
|
+
}
|
|
85
|
+
core.logger.info('Initial administrator created', { username: 'admin', passwordSource: 'HAI_ADMIN_DEFAULT_PASSWORD' })
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** 关闭顺序与依赖初始化顺序相反,同时覆盖启动失败路径。 */
|
|
89
|
+
export async function closeModules(): Promise<void> {
|
|
90
|
+
for (const [name, module] of [['audit', audit], ['iam', iam], ['storage', storage], ['crypto', crypto], ['cache', cache], ['reldb', reldb]] as const) {
|
|
91
|
+
if (!module.isInitialized)
|
|
92
|
+
continue
|
|
93
|
+
const result = await module.close()
|
|
94
|
+
if (result && !result.success)
|
|
95
|
+
core.logger.error('Failed to close application module', { module: name, error: result.error.code })
|
|
96
|
+
}
|
|
97
|
+
initialized = false
|
|
98
|
+
}
|