@h-ai/cli 0.1.0-alpha.66 → 0.1.0-alpha.68
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 +86 -241
- package/README.ja.md +86 -241
- package/README.md +86 -241
- package/dist/{src-RDQ2EIBT.js → chunk-FI56GONY.js} +32 -2669
- package/dist/chunk-FI56GONY.js.map +1 -0
- package/dist/chunk-MBPB37FR.js +1348 -0
- package/dist/chunk-MBPB37FR.js.map +1 -0
- package/dist/{chunk-QYX6V7H2.js → chunk-ZFNBUF77.js} +347 -276
- package/dist/chunk-ZFNBUF77.js.map +1 -0
- package/dist/cli-config-templates-W2LLPMB6.js +5 -0
- package/dist/{cli-config-templates-3XSFIXBP.js.map → cli-config-templates-W2LLPMB6.js.map} +1 -1
- package/dist/{cli-skill-templates-TSYEQTCY.js → cli-skill-templates-US5CCAJ4.js} +3 -3
- package/dist/{cli-skill-templates-TSYEQTCY.js.map → cli-skill-templates-US5CCAJ4.js.map} +1 -1
- package/dist/index.js +1304 -532
- package/dist/index.js.map +1 -1
- package/dist/metafile-esm.json +1 -0
- package/dist/src-DSJJXM2F.js +3849 -0
- package/dist/src-DSJJXM2F.js.map +1 -0
- package/package.json +9 -8
- package/templates/README.md +48 -145
- package/templates/apps/admin/messages/ja-JP.json +38 -0
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +16 -6
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +3 -3
- package/templates/apps/api/.dockerignore.hbs +14 -7
- package/templates/apps/api/.gitignore.hbs +7 -0
- package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/api/apps/{{projectName}}-contract/src/index.ts.hbs +2 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +4 -1
- package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +33 -8
- package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +0 -10
- package/templates/apps/api/apps/{{projectName}}-service/config/_serv.yml.hbs +2 -1
- package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -2
- package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +24 -11
- package/templates/apps/api/apps/{{projectName}}-service/src/index.ts.hbs +8 -1
- package/templates/apps/api/apps/{{projectName}}-service/src/lib/server/init.ts.hbs +68 -11
- package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/index.ts.hbs +10 -2
- package/templates/apps/api/e2e/api-service.spec.ts.hbs +2 -2
- package/templates/apps/api/hai.build.json.hbs +1 -1
- package/templates/apps/api/images/app.Dockerfile.hbs +35 -0
- package/templates/apps/api/package.json.hbs +6 -1
- package/templates/apps/api/playwright.config.ts.hbs +15 -7
- package/templates/apps/fullstack/.dockerignore.hbs +14 -9
- package/templates/apps/fullstack/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +24 -6
- package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/public/runtime-config.js.hbs +4 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +7 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +2 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +23 -5
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +16 -6
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/messages/ja-JP.json.hbs +12 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +24 -6
- package/templates/apps/fullstack/apps/{{projectName}}-web/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +2 -2
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +30 -9
- package/templates/apps/fullstack/hai.build.json.hbs +2 -2
- package/templates/apps/fullstack/images/app.Dockerfile.hbs +36 -0
- package/templates/apps/fullstack/images/web.Dockerfile.hbs +34 -0
- package/templates/apps/fullstack/package.json.hbs +3 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/index.ts.hbs +2 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +3 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +32 -8
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +0 -6
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +8 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +2 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +6 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +9 -5
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +3 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/messages.ts.hbs +16 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +7 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +3 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +58 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +13 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +40 -14
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/ja-JP.json.hbs +105 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AdminWorkspace.svelte.hbs +3 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +6 -6
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +7 -7
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +4 -4
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +8 -8
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +13 -14
- package/templates/apps/fullstack/playwright.config.ts.hbs +15 -6
- package/templates/apps/h5/messages/ja-JP.json +40 -0
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +16 -5
- package/templates/apps/mobile-app/messages/ja-JP.json +36 -0
- package/templates/apps/mobile-app/src/App.svelte.hbs +15 -5
- package/templates/apps/mobile-app/src/lib/api.ts.hbs +173 -14
- package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +2 -2
- package/templates/apps/website/messages/ja-JP.json +32 -0
- package/templates/apps/website/src/routes/+layout.svelte.hbs +13 -5
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +1 -1
- package/templates/base/.dockerignore.hbs +14 -8
- package/templates/base/.gitignore.hbs +7 -0
- package/templates/base/e2e/app.spec.ts.hbs +23 -8
- package/templates/base/hai.build.json.hbs +1 -1
- package/templates/base/{Dockerfile.hbs → images/app.Dockerfile.hbs} +19 -17
- package/templates/base/package.json.hbs +11 -3
- package/templates/base/playwright.config.ts +15 -4
- package/templates/base/src/hooks.server.ts.hbs +15 -6
- package/templates/base/src/lib/server/init.ts.hbs +139 -30
- package/templates/base/vite.config.ts.hbs +5 -3
- package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +11 -6
- package/templates/features/payment/routes/api/v1/payment/create/+server.ts +18 -25
- package/templates/features/payment/routes/api/v1/payment/notify/[provider]/+server.ts +4 -0
- package/templates/i18n/project.inlang/settings.json +1 -1
- package/templates/shared/.gitattributes +6 -0
- package/templates/shared/e2e-env.ts +98 -0
- package/templates/shared/run-e2e.mjs +51 -12
- package/templates/skills/AGENTS.md +7 -3
- package/templates/skills/bridges/admin/AGENTS.md +6 -2
- package/templates/skills/bridges/api/AGENTS.md +3 -1
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +5 -1
- package/templates/skills/bridges/h5/AGENTS.md +6 -2
- package/templates/skills/bridges/mobile-app/AGENTS.md +6 -2
- package/templates/skills/bridges/website/AGENTS.md +6 -2
- package/templates/skills/hai-ai/SKILL.md +8 -2
- package/templates/skills/hai-ai/guide.md +134 -60
- package/templates/skills/hai-ai/reference.md +51 -11
- package/templates/skills/hai-api-client/SKILL.md +15 -160
- package/templates/skills/hai-api-client/reference.md +178 -0
- package/templates/skills/hai-api-contract/SKILL.md +16 -273
- package/templates/skills/hai-api-contract/reference.md +280 -0
- package/templates/skills/hai-app-create/SKILL.md +6 -2
- package/templates/skills/hai-app-create/development.md +24 -8
- package/templates/skills/hai-app-create/reference.md +7 -5
- package/templates/skills/hai-app-review/SKILL.md +7 -3
- package/templates/skills/hai-app-review/differential.md +34 -0
- package/templates/skills/hai-app-tests/SKILL.md +6 -3
- package/templates/skills/hai-app-tests/reference.md +5 -5
- package/templates/skills/hai-app-tests/validation.md +38 -0
- package/templates/skills/hai-audit/SKILL.md +24 -147
- package/templates/skills/hai-audit/reference.md +148 -0
- package/templates/skills/hai-build/SKILL.md +14 -8
- package/templates/skills/hai-build/reference.md +16 -13
- package/templates/skills/hai-cache/SKILL.md +20 -181
- package/templates/skills/hai-cache/reference.md +229 -0
- package/templates/skills/hai-capacitor/SKILL.md +7 -4
- package/templates/skills/hai-capacitor/reference.md +15 -9
- package/templates/skills/hai-ci/SKILL.md +2 -0
- package/templates/skills/hai-core/SKILL.md +7 -3
- package/templates/skills/hai-core/reference.md +25 -5
- package/templates/skills/hai-crypto/SKILL.md +22 -250
- package/templates/skills/hai-crypto/reference.md +310 -0
- package/templates/skills/hai-datapipe/SKILL.md +16 -195
- package/templates/skills/hai-datapipe/reference.md +204 -0
- package/templates/skills/hai-deploy/SKILL.md +24 -54
- package/templates/skills/hai-deploy/middleware.md +51 -43
- package/templates/skills/hai-framework-sync/SKILL.md +2 -2
- package/templates/skills/hai-framework-sync/sync.md +1 -1
- package/templates/skills/hai-iam/SKILL.md +25 -11
- package/templates/skills/hai-iam/reference.md +181 -154
- package/templates/skills/hai-kit/SKILL.md +35 -174
- package/templates/skills/hai-kit/reference.md +190 -0
- package/templates/skills/hai-payment/SKILL.md +33 -263
- package/templates/skills/hai-payment/reference.md +295 -0
- package/templates/skills/hai-pr-review/SKILL.md +21 -88
- package/templates/skills/hai-reach/SKILL.md +6 -3
- package/templates/skills/hai-reach/reference.md +83 -22
- package/templates/skills/hai-reldb/SKILL.md +6 -3
- package/templates/skills/hai-reldb/reference.md +68 -25
- package/templates/skills/hai-scheduler/SKILL.md +18 -195
- package/templates/skills/hai-scheduler/reference.md +216 -0
- package/templates/skills/hai-serv/SKILL.md +25 -10
- package/templates/skills/hai-serv/reference.md +123 -87
- package/templates/skills/hai-spec-implement/SKILL.md +5 -1
- package/templates/skills/hai-spec-implement/evaluation.md +18 -0
- package/templates/skills/hai-spec-implement/handoff.md +47 -0
- package/templates/skills/hai-spec-implement/templates.md +5 -0
- package/templates/skills/hai-spec-implement/workflow.md +5 -3
- package/templates/skills/hai-spec-review/SKILL.md +4 -0
- package/templates/skills/hai-storage/SKILL.md +23 -238
- package/templates/skills/hai-storage/reference.md +282 -0
- package/templates/skills/hai-ui/SKILL.md +17 -3
- package/templates/skills/hai-ui/guide.md +3 -3
- package/templates/skills/hai-ui/reference.md +26 -11
- package/templates/skills/hai-ui-design/SKILL.md +31 -0
- package/templates/skills/hai-ui-design/patterns.md +26 -0
- package/templates/skills/hai-ui-review/SKILL.md +30 -0
- package/templates/skills/hai-ui-review/checklist.md +22 -0
- package/templates/skills/hai-vecdb/SKILL.md +14 -10
- package/templates/skills/hai-vecdb/reference.md +33 -3
- package/dist/chunk-FWN5B54L.js +0 -468
- package/dist/chunk-FWN5B54L.js.map +0 -1
- package/dist/chunk-QYX6V7H2.js.map +0 -1
- package/dist/cli-config-templates-3XSFIXBP.js +0 -5
- package/dist/src-RDQ2EIBT.js.map +0 -1
- package/templates/apps/api/Dockerfile.hbs +0 -35
- package/templates/apps/fullstack/Dockerfile.hbs +0 -57
- /package/templates/apps/fullstack/{docker → images/web}/19-runtime-config.envsh.hbs +0 -0
- /package/templates/apps/fullstack/{docker → images/web}/nginx.conf.hbs +0 -0
- /package/templates/apps/fullstack/{docker → images/web}/runtime-config.js.template.hbs +0 -0
- /package/templates/base/{docker → images/web}/nginx.conf.hbs +0 -0
|
@@ -21,7 +21,14 @@ async function main(): Promise<void> {
|
|
|
21
21
|
})
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
main().catch((error: unknown) => {
|
|
24
|
+
main().catch(async (error: unknown) => {
|
|
25
25
|
core.logger.error('API service failed to start', { error })
|
|
26
26
|
process.exitCode = 1
|
|
27
|
+
try {
|
|
28
|
+
// 包括初始化后的 app/transport 创建失败,均须释放已建立的连接。
|
|
29
|
+
await closeApp()
|
|
30
|
+
}
|
|
31
|
+
catch (cleanupError) {
|
|
32
|
+
core.logger.error('API service startup cleanup failed', { error: cleanupError })
|
|
33
|
+
}
|
|
27
34
|
})
|
|
@@ -21,23 +21,47 @@
|
|
|
21
21
|
|
|
22
22
|
let initialized = false
|
|
23
23
|
let initPromise: Promise<void> | null = null
|
|
24
|
+
/** 记录启动/关闭后未确认释放的资源,使失败清理可以重试。 */
|
|
25
|
+
let cleanupPending = false
|
|
26
|
+
let closePromise: Promise<void> | null = null
|
|
24
27
|
{{#if features.crypto}}let cryptoInitialized = false
|
|
25
28
|
{{/if}}
|
|
26
29
|
|
|
30
|
+
/**
|
|
31
|
+
* 合并并发启动;任一步失败先逆序清理,未释放的资源会在下次启动前重试。
|
|
32
|
+
* @throws Error | AggregateError - 启动失败,或启动与资源清理同时失败。
|
|
33
|
+
* @example
|
|
34
|
+
* await initApp()
|
|
35
|
+
*/
|
|
27
36
|
export async function initApp(): Promise<void> {
|
|
37
|
+
if (closePromise)
|
|
38
|
+
await closePromise
|
|
28
39
|
if (initialized)
|
|
29
40
|
return
|
|
30
41
|
if (initPromise)
|
|
31
42
|
return initPromise
|
|
43
|
+
if (cleanupPending) {
|
|
44
|
+
await closeApp()
|
|
45
|
+
return await initApp()
|
|
46
|
+
}
|
|
32
47
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
await initPromise
|
|
48
|
+
cleanupPending = true
|
|
49
|
+
initPromise = doInit().then(() => {
|
|
36
50
|
initialized = true
|
|
37
|
-
|
|
38
|
-
|
|
51
|
+
cleanupPending = false
|
|
52
|
+
}).catch(async (error: unknown) => {
|
|
53
|
+
// 私有清理不等待 initPromise,避免启动失败时等待自身。
|
|
54
|
+
try {
|
|
55
|
+
await cleanupModules()
|
|
56
|
+
}
|
|
57
|
+
catch (cleanupError) {
|
|
58
|
+
throw new AggregateError([error, cleanupError], 'Application startup and cleanup failed')
|
|
59
|
+
}
|
|
60
|
+
throw error
|
|
61
|
+
}).finally(() => {
|
|
39
62
|
initPromise = null
|
|
40
|
-
}
|
|
63
|
+
})
|
|
64
|
+
return await initPromise
|
|
41
65
|
}
|
|
42
66
|
|
|
43
67
|
async function doInit(): Promise<void> {
|
|
@@ -160,16 +184,38 @@ async function doInit(): Promise<void> {
|
|
|
160
184
|
core.logger.info('API service initialized')
|
|
161
185
|
}
|
|
162
186
|
|
|
187
|
+
/**
|
|
188
|
+
* 等待启动并关闭所有模块;失败时保留重试入口。
|
|
189
|
+
* @throws AggregateError - 任一资源未确认释放。
|
|
190
|
+
* @example
|
|
191
|
+
* await closeApp()
|
|
192
|
+
*/
|
|
163
193
|
export async function closeApp(): Promise<void> {
|
|
164
|
-
|
|
194
|
+
closePromise ??= doCloseApp().finally(() => {
|
|
195
|
+
closePromise = null
|
|
196
|
+
})
|
|
197
|
+
return await closePromise
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** 等待启动结束后逐一释放;HaiResult 失败与异常都会聚合报告并保留重试入口。 */
|
|
201
|
+
async function doCloseApp(): Promise<void> {
|
|
202
|
+
if (!initialized && !initPromise && !cleanupPending)
|
|
165
203
|
return
|
|
166
204
|
|
|
205
|
+
if (initPromise)
|
|
206
|
+
await initPromise.catch(() => undefined)
|
|
207
|
+
await cleanupModules()
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** 不等待启动屏障,供启动失败与显式关闭共用的逆序清理。 */
|
|
211
|
+
async function cleanupModules(): Promise<void> {
|
|
212
|
+
const failures: unknown[] = []
|
|
167
213
|
const closers: Array<readonly [string, () => unknown]> = [
|
|
168
214
|
{{#if features.ai}} ['ai', () => ai.close()],
|
|
169
215
|
{{/if}}{{#if features.storage}} ['storage', () => storage.close()],
|
|
170
216
|
{{/if}}{{#if features.iam}} ['iam', () => iam.close()],
|
|
171
|
-
{{/if}}{{#if features.reach}} ['reach', () => reach.close()],
|
|
172
217
|
{{/if}}{{#if features.vecdb}} ['vecdb', () => vecdb.close()],
|
|
218
|
+
{{/if}}{{#if features.reach}} ['reach', () => reach.close()],
|
|
173
219
|
{{/if}}{{#if features.cache}} ['cache', () => cache.close()],
|
|
174
220
|
{{/if}}{{#if features.db}} ['reldb', () => reldb.close()],
|
|
175
221
|
{{/if}} ]
|
|
@@ -180,14 +226,25 @@ export async function closeApp(): Promise<void> {
|
|
|
180
226
|
|
|
181
227
|
for (const [name, close] of closers) {
|
|
182
228
|
try {
|
|
183
|
-
await close()
|
|
229
|
+
const result = await close()
|
|
230
|
+
if (result && typeof result === 'object' && 'success' in result && result.success === false) {
|
|
231
|
+
failures.push('error' in result ? result.error : new Error('Module close failed'))
|
|
232
|
+
core.logger.warn(`Module close failed: ${name}`)
|
|
233
|
+
continue
|
|
234
|
+
}
|
|
235
|
+
{{#if features.crypto}} if (name === 'crypto')
|
|
236
|
+
cryptoInitialized = false
|
|
237
|
+
{{/if}}
|
|
184
238
|
}
|
|
185
239
|
catch (error) {
|
|
240
|
+
failures.push(error)
|
|
186
241
|
core.logger.warn(`Module close failed: ${name}`, { error })
|
|
187
242
|
}
|
|
188
243
|
}
|
|
189
244
|
|
|
190
245
|
initialized = false
|
|
191
|
-
|
|
192
|
-
|
|
246
|
+
cleanupPending = failures.length > 0
|
|
247
|
+
if (cleanupPending)
|
|
248
|
+
throw new AggregateError(failures, 'Application resource cleanup failed; retry closeApp()')
|
|
249
|
+
core.logger.info('API service closed')
|
|
193
250
|
}
|
|
@@ -7,12 +7,20 @@ import type { AppProcedureDeps } from './app-procedures.js'
|
|
|
7
7
|
import { storage } from '@h-ai/storage'
|
|
8
8
|
{{/if}}import { createAppProcedures } from './app-procedures.js'
|
|
9
9
|
|
|
10
|
+
// @hai-generator:procedures:imports
|
|
11
|
+
|
|
10
12
|
/** 创建应用级 procedures 聚合。 */
|
|
11
13
|
export function {{api.serviceProceduresFactoryName}}(deps: { app: AppProcedureDeps }) {
|
|
12
14
|
return {
|
|
15
|
+
// @hai-generator:procedures:entries
|
|
13
16
|
{{#if features.iam}} iam: createIamProcedures({ iam }),
|
|
14
|
-
{{/if}}{{#if features.storage}} storage: createStorageProcedures({
|
|
15
|
-
|
|
17
|
+
{{/if}}{{#if features.storage}} storage: createStorageProcedures({
|
|
18
|
+
storage,
|
|
19
|
+
authorize: ({ context, keys }) => keys.every(key =>
|
|
20
|
+
key.startsWith(`users/${encodeURIComponent(context.session.userId)}/`)),
|
|
21
|
+
}),
|
|
22
|
+
{{/if}}{{#if features.ai}} // AI 默认绑定可信 session.userId,跨主体访问需另行提供 authorizeObject 策略。
|
|
23
|
+
ai: createAiProcedures({ ai }),
|
|
16
24
|
{{/if}} app: createAppProcedures(deps.app),
|
|
17
25
|
}
|
|
18
26
|
}
|
|
@@ -19,7 +19,7 @@ test.beforeAll(async () => {
|
|
|
19
19
|
auth: { storage: apiClient.tokenStorage.memory() },
|
|
20
20
|
{{/if}}
|
|
21
21
|
{{#if features.crypto}}
|
|
22
|
-
transport:
|
|
22
|
+
transport: { crypto },
|
|
23
23
|
{{/if}}
|
|
24
24
|
})
|
|
25
25
|
expect(result.success).toBe(true)
|
|
@@ -46,7 +46,7 @@ test('service app.info endpoint returns HaiResult', async () => {
|
|
|
46
46
|
return
|
|
47
47
|
expect(body.data.name).toBe(process.env.HAI_CORE_NAME ?? '{{projectName}} API Service')
|
|
48
48
|
expect(body.data.version).toBe(process.env.HAI_CORE_VERSION ?? '0.1.0')
|
|
49
|
-
expect(body.data.env).toBe(
|
|
49
|
+
expect(body.data.env).toBe('test')
|
|
50
50
|
expect(typeof body.data.transportEnabled).toBe('boolean')
|
|
51
51
|
})
|
|
52
52
|
{{#if features.iam}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{ "images": [{ "name": "app", "
|
|
1
|
+
{ "images": [{ "name": "app", "dockerfile": "images/app.Dockerfile", "containerPort": 3000 }] }
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
FROM node:24.22.1-bookworm-slim AS build
|
|
2
|
+
ARG SQLITE_PREBUILD_MIRROR=
|
|
3
|
+
ENV npm_config_better_sqlite3_binary_host_mirror=$SQLITE_PREBUILD_MIRROR
|
|
4
|
+
|
|
5
|
+
ENV PNPM_HOME=/pnpm
|
|
6
|
+
ENV PATH=$PNPM_HOME:$PATH
|
|
7
|
+
ENV CI=true
|
|
8
|
+
ENV ONNXRUNTIME_NODE_INSTALL_CUDA=skip
|
|
9
|
+
WORKDIR /workspace
|
|
10
|
+
|
|
11
|
+
RUN corepack enable && corepack prepare pnpm@12.11.2 --activate && pnpm config set store-dir /pnpm/store
|
|
12
|
+
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
|
|
13
|
+
COPY images/dependencies/ ./
|
|
14
|
+
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm fetch --ignore-scripts
|
|
15
|
+
COPY . .
|
|
16
|
+
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm install --offline --frozen-lockfile --ignore-scripts
|
|
17
|
+
RUN pnpm compile
|
|
18
|
+
RUN --mount=type=cache,id={{projectName}}-sqlite,target=/root/.npm/_prebuilds if [ -d node_modules/better-sqlite3 ]; then cd node_modules/better-sqlite3; for attempt in 1 2 3; do node -e "require('node:child_process').execFileSync(process.execPath,[require.resolve('prebuild-install/bin.js'),'--verbose'],{stdio:'inherit'})" && break; [ "$attempt" = 3 ] && exit 1; sleep 3; done; fi
|
|
19
|
+
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store --mount=type=cache,id={{projectName}}-sqlite,target=/root/.npm/_prebuilds pnpm pm deploy --filter {{api.servicePackageName}} --prod /prod/app
|
|
20
|
+
|
|
21
|
+
FROM node:24.22.1-bookworm-slim AS runtime-base
|
|
22
|
+
|
|
23
|
+
ENV NODE_ENV=production
|
|
24
|
+
ENV HOST=0.0.0.0
|
|
25
|
+
ENV PORT=3000
|
|
26
|
+
WORKDIR /app
|
|
27
|
+
|
|
28
|
+
COPY --from=build --chown=node:node /prod/app ./
|
|
29
|
+
RUN mkdir -p /app/data && chown node:node /app/data
|
|
30
|
+
|
|
31
|
+
USER node
|
|
32
|
+
EXPOSE 3000
|
|
33
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
|
|
34
|
+
CMD node -e "fetch('http://127.0.0.1:'+process.env.PORT+'/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
|
|
35
|
+
CMD ["node", "dist/index.js"]
|
|
@@ -13,9 +13,11 @@
|
|
|
13
13
|
"start:prod": "pnpm --filter {{api.servicePackageName}} start:prod",
|
|
14
14
|
"dev:test": "pnpm --filter {{api.servicePackageName}} dev:test",
|
|
15
15
|
"dev:prod": "pnpm --filter {{api.servicePackageName}} dev:prod",
|
|
16
|
-
"compile": "pnpm
|
|
16
|
+
"compile": "pnpm -r --if-present build",
|
|
17
17
|
"build": "hai build",
|
|
18
|
+
{{#if features.deploy}}
|
|
18
19
|
"deploy": "hai deploy apps/{{projectName}}-service",
|
|
20
|
+
{{/if}}
|
|
19
21
|
"check": "pnpm typecheck",
|
|
20
22
|
"typecheck": "pnpm --filter {{api.contractPackageName}} build && pnpm -r --if-present typecheck",
|
|
21
23
|
"lint": "eslint *.js *.ts e2e && pnpm -r --if-present lint",
|
|
@@ -30,6 +32,9 @@
|
|
|
30
32
|
"{{api.contractPackageName}}": "workspace:*"
|
|
31
33
|
},
|
|
32
34
|
"devDependencies": {
|
|
35
|
+
{{#if features.deploy}}
|
|
36
|
+
"@h-ai/deploy": "{{haiPackageSpecifier}}",
|
|
37
|
+
{{/if}}
|
|
33
38
|
"@h-ai/cli": "{{haiPackageSpecifier}}",
|
|
34
39
|
"@h-ai/api-client": "{{haiPackageSpecifier}}",
|
|
35
40
|
{{#if features.crypto}}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import process from 'node:process'
|
|
2
2
|
import { defineConfig } from '@playwright/test'
|
|
3
|
+
import { createE2eNotificationEnv, createE2eServerEnv, createE2eTransportRing } from './scripts/e2e-env.js'
|
|
3
4
|
|
|
4
5
|
const defaultBaseURL = 'http://127.0.0.1:3000'
|
|
5
6
|
const baseURL = process.env.BASE_URL || defaultBaseURL
|
|
@@ -33,25 +34,32 @@ function resolvePortFromBaseUrl(urlString: string, fallbackPort: string): string
|
|
|
33
34
|
export default defineConfig({
|
|
34
35
|
testDir: './e2e',
|
|
35
36
|
globalSetup: './e2e/capture-evidence.ts',
|
|
36
|
-
fullyParallel:
|
|
37
|
+
fullyParallel: true,
|
|
37
38
|
forbidOnly: !!process.env.CI,
|
|
38
|
-
retries:
|
|
39
|
-
workers:
|
|
39
|
+
retries: 0,
|
|
40
|
+
workers: 2,
|
|
40
41
|
reporter: 'list',
|
|
41
42
|
timeout: 30_000,
|
|
42
43
|
use: {
|
|
43
44
|
baseURL,
|
|
44
45
|
channel: 'chrome',
|
|
45
|
-
trace: 'on-
|
|
46
|
+
trace: 'retain-on-failure',
|
|
46
47
|
screenshot: 'only-on-failure',
|
|
47
48
|
},
|
|
48
49
|
webServer: [{
|
|
49
50
|
name: '{{api.servicePackageName}}',
|
|
50
|
-
command: 'pnpm --filter {{api.servicePackageName}}
|
|
51
|
-
env: {
|
|
51
|
+
command: 'pnpm --filter {{api.servicePackageName}} exec node dist/index.js',
|
|
52
|
+
env: createE2eServerEnv({
|
|
53
|
+
...createE2eNotificationEnv(),
|
|
54
|
+
HAI_CORE_ENV: 'test',
|
|
55
|
+
HAI_DB: JSON.stringify({ type: 'sqlite', database: `${process.env.HAI_E2E_DATA_ROOT}/api.db` }),
|
|
56
|
+
HAI_CACHE: JSON.stringify({ type: 'memory' }),
|
|
57
|
+
HAI_STORAGE: JSON.stringify({ type: 'local', root: `${process.env.HAI_E2E_DATA_ROOT}/uploads` }),
|
|
58
|
+
HAI_VECDB: JSON.stringify({ type: 'lancedb', path: `${process.env.HAI_E2E_DATA_ROOT}/vecdb` }),
|
|
59
|
+
HAI_CRYPTO_TRANSPORT_SERVERKEYRING: createE2eTransportRing(),
|
|
52
60
|
HOST: serviceHost,
|
|
53
61
|
PORT: servicePort,
|
|
54
|
-
},
|
|
62
|
+
}),
|
|
55
63
|
url: `${baseURL}/health`,
|
|
56
64
|
reuseExistingServer: false,
|
|
57
65
|
timeout: 120_000,
|
|
@@ -1,18 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
**/.git
|
|
2
|
+
**/node_modules
|
|
3
|
+
**/.pnpm-store
|
|
4
|
+
**/.cache
|
|
5
|
+
**/.svelte-kit
|
|
6
|
+
**/.turbo
|
|
7
|
+
**/build
|
|
8
|
+
**/dist
|
|
9
|
+
**/coverage
|
|
10
|
+
**/playwright-report
|
|
11
|
+
**/test-results
|
|
12
|
+
**/src-tauri/target
|
|
13
|
+
**/hash-core/target
|
|
14
|
+
tmp
|
|
2
15
|
**/*.tar
|
|
3
|
-
Dockerfile
|
|
4
16
|
.github
|
|
5
17
|
.agents
|
|
6
18
|
.claude
|
|
7
19
|
.codex
|
|
8
20
|
.opencode
|
|
9
|
-
.turbo
|
|
10
|
-
**/build
|
|
11
|
-
**/dist
|
|
12
|
-
**/node_modules
|
|
13
|
-
node_modules
|
|
14
|
-
playwright-report
|
|
15
|
-
test-results
|
|
16
21
|
**/data
|
|
17
22
|
*.log
|
|
18
23
|
# 仅保留公共基础 .env;分环境配置、私有覆盖及已有交付包不进入构建上下文。
|
|
@@ -2,9 +2,19 @@
|
|
|
2
2
|
{{#if fullstack.frontends.app}}
|
|
3
3
|
# {{projectName}}-app
|
|
4
4
|
|
|
5
|
+
## 简介
|
|
6
|
+
|
|
5
7
|
App/H5 前端工程,复用 shared 的响应式 mini 后台,使用 Svelte 5 + Vite + Capacitor,可输出 Web 预览并同步 Android / iOS 原生工程。
|
|
6
8
|
|
|
7
|
-
##
|
|
9
|
+
## 核心功能清单
|
|
10
|
+
|
|
11
|
+
- 响应式 H5 页面与 Capacitor Android/iOS 原生壳。
|
|
12
|
+
- 共享管理界面、个人设置和接口客户端。
|
|
13
|
+
- 原生使用安全 TokenStorage,浏览器预览使用 Cookie 刷新。
|
|
14
|
+
|
|
15
|
+
## 使用入门
|
|
16
|
+
|
|
17
|
+
### i18n 与 shared 协同
|
|
8
18
|
|
|
9
19
|
- 本端页面文案:`messages/{locale}.json` + `project.inlang/settings.json`
|
|
10
20
|
- 共享 Shell/主题/语言/API:`packages/{{fullstack.sharedPackageName}}`
|
|
@@ -13,7 +23,7 @@ App/H5 前端工程,复用 shared 的响应式 mini 后台,使用 Svelte 5 +
|
|
|
13
23
|
|
|
14
24
|
自定义建议:移动端独有引导、运营位、按钮文案放在本目录 `messages/`;跨端通用组件放到 shared。
|
|
15
25
|
|
|
16
|
-
|
|
26
|
+
### 编译、打包与部署
|
|
17
27
|
|
|
18
28
|
```bash
|
|
19
29
|
# 开发预览,默认端口 {{fullstack.ports.app}}
|
|
@@ -31,15 +41,23 @@ pnpm --filter {{projectName}}-app cap:build:android:release
|
|
|
31
41
|
pnpm --filter {{projectName}}-app cap:build:ios
|
|
32
42
|
```
|
|
33
43
|
|
|
34
|
-
|
|
44
|
+
本地开发通过根 `.env` 的 `PUBLIC_API_BASE` 配置代理。原生打包前,在本端 `public/runtime-config.js` 中设置 `globalThis.__HAI_PUBLIC_CONFIG__ = { apiBase: 'https://api.example.com/api/v1' }`,再编译并同步原生工程;这里只能放公开地址,不能放密钥。Android 模拟器访问宿主机通常使用 `http://10.0.2.2:3000/api/v1`。
|
|
35
45
|
|
|
36
|
-
|
|
46
|
+
### IAM 认证配置
|
|
37
47
|
|
|
38
|
-
服务端 `config/_iam.yml` 统一控制登录、注册、重置、协议与 `ui` 品牌。邮件/短信可用 `_reach.yml` 中的 Provider/标准模板或自定义发送回调。`AuthPanel` 使用安全公开配置及框架认证动作;无需在页面适配 Token
|
|
48
|
+
服务端 `config/_iam.yml` 统一控制登录、注册、重置、协议与 `ui` 品牌。邮件/短信可用 `_reach.yml` 中的 Provider/标准模板或自定义发送回调。`AuthPanel` 使用安全公开配置及框架认证动作;无需在页面适配 Token、验证码倒计时和错误提示。单实例可使用 Memory;多实例需共享数据库与 Redis,并按工程根 README 配置写入路由和传输会话。
|
|
39
49
|
|
|
40
|
-
LDAP/Reach 均可选:基础认证无需安装。默认 LDAP 客户端需在 IAM 服务端 workspace 安装 ldapts,再配置 login.ldap 与连接信息。自定义 onOtpSendEmail/onOtpSendSms/onPasswordResetRequest 回调可独立于 Reach 使用,投递失败须拒绝 Promise
|
|
50
|
+
LDAP/Reach 均可选:基础认证无需安装。默认 LDAP 客户端需在 IAM 服务端 workspace 安装 ldapts,再配置 login.ldap 与连接信息。自定义 onOtpSendEmail/onOtpSendSms/onPasswordResetRequest 回调可独立于 Reach 使用,投递失败须拒绝 Promise;回调第三参数 context 提供 expiresAt/idempotencyKey,发送服务按稳定键持久化去重,异常/超时为结果未知且不自动重发。协议在 Modal 显示;LoginForm/RegisterForm 提供 header/extraFields/footer,AuthPanel 透传 loginHeader/loginFields/loginFooter 和 registerHeader/registerFields/registerFooter。配置及例子见生成项目的 hai-iam/hai-ui skills。
|
|
41
51
|
|
|
42
52
|
协议支持配置中的 userAgreementMarkdown/privacyPolicyMarkdown 正文及 URL;同主题优先 Markdown,由安全渲染器在弹窗显示。
|
|
43
53
|
|
|
44
54
|
完整契约见生成应用的 `.agents/skills/hai-iam/reference.md`。
|
|
55
|
+
|
|
56
|
+
## 开发测试
|
|
57
|
+
|
|
58
|
+
从工程根执行 `pnpm typecheck`、`pnpm lint`、`pnpm build`、`pnpm test` 和 `pnpm test:e2e`。浏览器预览通过后,原生壳仍需在对应系统或设备单独验收。
|
|
59
|
+
|
|
60
|
+
## 许可证
|
|
61
|
+
|
|
62
|
+
Apache-2.0
|
|
45
63
|
{{/if}}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
<meta charset="UTF-8" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
8
8
|
<meta name="theme-color" content="#2563eb" />
|
|
9
|
+
<script src="/runtime-config.js"></script>
|
|
9
10
|
<title>{{html fullstack.appName}}</title>
|
|
10
11
|
<link
|
|
11
12
|
rel="icon"
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
<script lang="ts">
|
|
4
4
|
import type { ManagementApiOptions } from '{{fullstack.sharedPackageName}}'
|
|
5
5
|
import { capacitor, createCapacitorTokenStorage } from '@h-ai/capacitor'
|
|
6
|
+
import { core } from '@h-ai/core'
|
|
6
7
|
import { Alert } from '@h-ai/ui/components/compounds/index'
|
|
7
8
|
import { Button, Spinner } from '@h-ai/ui/components/primitives/index'
|
|
8
9
|
import { onMount } from 'svelte'
|
|
@@ -29,7 +30,12 @@
|
|
|
29
30
|
}
|
|
30
31
|
onMount(() => {
|
|
31
32
|
void initialize()
|
|
32
|
-
return () =>
|
|
33
|
+
return () => {
|
|
34
|
+
void capacitor.close().then((closed) => {
|
|
35
|
+
if (!closed.success)
|
|
36
|
+
core.logger.warn('Native listener cleanup failed; retry on next initialization')
|
|
37
|
+
})
|
|
38
|
+
}
|
|
33
39
|
})
|
|
34
40
|
</script>
|
|
35
41
|
|
|
@@ -12,7 +12,7 @@ import { defineConfig } from 'vitest/config'
|
|
|
12
12
|
const envDir = fileURLToPath(new URL('../../', import.meta.url))
|
|
13
13
|
|
|
14
14
|
export default defineConfig(({ mode, command }) => {
|
|
15
|
-
const envResult = command === 'build' ? { success: true as const, data: {} as Record<string, string> } : core.config.loadEnvFiles({ mode, envDir })
|
|
15
|
+
const envResult = command === 'build' || process.env.HAI_E2E === '1' ? { success: true as const, data: {} as Record<string, string> } : core.config.loadEnvFiles({ mode, envDir })
|
|
16
16
|
if (!envResult.success)
|
|
17
17
|
throw new Error(envResult.error.message)
|
|
18
18
|
const env = envResult.data
|
|
@@ -51,7 +51,7 @@ export default defineConfig(({ mode, command }) => {
|
|
|
51
51
|
},
|
|
52
52
|
envPrefix: ['VITE_', 'PUBLIC_'],
|
|
53
53
|
ssr: {
|
|
54
|
-
noExternal: [
|
|
54
|
+
noExternal: ['@h-ai/ui', /{{fullstack.sharedPackageName}}/],
|
|
55
55
|
},
|
|
56
56
|
test: {
|
|
57
57
|
environment: 'node',
|
|
@@ -2,9 +2,19 @@
|
|
|
2
2
|
{{#if fullstack.frontends.desktop}}
|
|
3
3
|
# {{projectName}}-desktop
|
|
4
4
|
|
|
5
|
+
## 简介
|
|
6
|
+
|
|
5
7
|
桌面端工程,参考 `apps/desktop-app` 使用 Tauri v2 + Svelte 5 + Vite,前端通过共享 contract 调用 `{{fullstack.servPackageName}}`。
|
|
6
8
|
|
|
7
|
-
##
|
|
9
|
+
## 核心功能清单
|
|
10
|
+
|
|
11
|
+
- Tauri v2 桌面壳与共享管理界面。
|
|
12
|
+
- 运行时配置 API 地址,无需按环境重复打包。
|
|
13
|
+
- 原生令牌仅存内存,重启后重新登录。
|
|
14
|
+
|
|
15
|
+
## 使用入门
|
|
16
|
+
|
|
17
|
+
### i18n 与 shared 协同
|
|
8
18
|
|
|
9
19
|
- 本端页面文案:`messages/{locale}.json` + `project.inlang/settings.json`
|
|
10
20
|
- 共享 Shell/主题/语言/API:`packages/{{fullstack.sharedPackageName}}`
|
|
@@ -14,7 +24,7 @@
|
|
|
14
24
|
|
|
15
25
|
自定义建议:桌面端菜单、状态、快捷操作文案放在本目录 `messages/`;跨端通用能力放到 shared。
|
|
16
26
|
|
|
17
|
-
|
|
27
|
+
### 编译、打包与部署
|
|
18
28
|
|
|
19
29
|
```bash
|
|
20
30
|
# 仅前端开发,默认端口 {{fullstack.ports.desktop}}
|
|
@@ -32,13 +42,21 @@ pnpm build --target desktop --platform windows --arch x64
|
|
|
32
42
|
|
|
33
43
|
打包前需要安装 Rust 工具链和 Tauri 平台依赖。构建不读取 .env.prod/.env.local;启动已安装程序前设置进程 PUBLIC_API_BASE(HTTP(S),不含凭据、查询和片段)。Rust 会在页面加载前注入公开地址,仅将该 Origin 加入 CSP;不需要为 test/prod 重打安装包。缺省连接 http://localhost:3000/api/v1。
|
|
34
44
|
|
|
35
|
-
|
|
45
|
+
### IAM 认证配置
|
|
36
46
|
|
|
37
|
-
服务端 `config/_iam.yml` 统一控制登录、注册、重置、协议与 `ui` 品牌。邮件/短信可用 `_reach.yml` 中的 Provider/标准模板或自定义发送回调。`AuthPanel` 使用安全公开配置及框架认证动作;无需在页面适配 Token
|
|
47
|
+
服务端 `config/_iam.yml` 统一控制登录、注册、重置、协议与 `ui` 品牌。邮件/短信可用 `_reach.yml` 中的 Provider/标准模板或自定义发送回调。`AuthPanel` 使用安全公开配置及框架认证动作;无需在页面适配 Token、验证码倒计时和错误提示。单实例可使用 Memory;多实例需共享数据库与 Redis,并按工程根 README 配置写入路由和传输会话。
|
|
38
48
|
|
|
39
|
-
LDAP/Reach 均可选:基础认证无需安装。默认 LDAP 客户端需在 IAM 服务端 workspace 安装 ldapts,再配置 login.ldap 与连接信息。自定义 onOtpSendEmail/onOtpSendSms/onPasswordResetRequest 回调可独立于 Reach 使用,投递失败须拒绝 Promise
|
|
49
|
+
LDAP/Reach 均可选:基础认证无需安装。默认 LDAP 客户端需在 IAM 服务端 workspace 安装 ldapts,再配置 login.ldap 与连接信息。自定义 onOtpSendEmail/onOtpSendSms/onPasswordResetRequest 回调可独立于 Reach 使用,投递失败须拒绝 Promise;回调第三参数 context 提供 expiresAt/idempotencyKey,发送服务按稳定键持久化去重,异常/超时为结果未知且不自动重发。协议在 Modal 显示;LoginForm/RegisterForm 提供 header/extraFields/footer,AuthPanel 透传 loginHeader/loginFields/loginFooter 和 registerHeader/registerFields/registerFooter。配置及例子见生成项目的 hai-iam/hai-ui skills。
|
|
40
50
|
|
|
41
51
|
协议支持配置中的 userAgreementMarkdown/privacyPolicyMarkdown 正文及 URL;同主题优先 Markdown,由安全渲染器在弹窗显示。
|
|
42
52
|
|
|
43
53
|
完整契约见生成应用的 `.agents/skills/hai-iam/reference.md`。
|
|
54
|
+
|
|
55
|
+
## 开发测试
|
|
56
|
+
|
|
57
|
+
从工程根执行 `pnpm typecheck`、`pnpm lint`、`pnpm build`、`pnpm test` 和 `pnpm test:e2e`。浏览器预览通过后,原生壳仍需在对应系统或设备单独验收。
|
|
58
|
+
|
|
59
|
+
## 许可证
|
|
60
|
+
|
|
61
|
+
Apache-2.0
|
|
44
62
|
{{/if}}
|
|
@@ -13,7 +13,7 @@ const host = process.env.TAURI_DEV_HOST
|
|
|
13
13
|
const envDir = fileURLToPath(new URL('../../', import.meta.url))
|
|
14
14
|
|
|
15
15
|
export default defineConfig(({ mode, command }) => {
|
|
16
|
-
const envResult = command === 'build' ? { success: true as const, data: {} as Record<string, string> } : core.config.loadEnvFiles({ mode, envDir })
|
|
16
|
+
const envResult = command === 'build' || process.env.HAI_E2E === '1' ? { success: true as const, data: {} as Record<string, string> } : core.config.loadEnvFiles({ mode, envDir })
|
|
17
17
|
if (!envResult.success)
|
|
18
18
|
throw new Error(envResult.error.message)
|
|
19
19
|
const env = envResult.data
|
|
@@ -2,14 +2,24 @@
|
|
|
2
2
|
{{#if fullstack.hasMiniapp}}
|
|
3
3
|
# {{projectName}}-miniapp
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## 简介
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- `messages/zh-CN.json`、`messages/en-US.json`:小程序端自有文案
|
|
7
|
+
小程序预留目录,目前没有可运行的小程序应用。
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
## 核心功能清单
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
- 已提供 `project.inlang/settings.json` 与中英文 `messages/`。
|
|
12
|
+
- 可复用 `packages/{{fullstack.contractPackageName}}` 的接口类型。
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
## 使用入门
|
|
15
|
+
|
|
16
|
+
选定小程序框架后再实现页面、请求适配和认证。小程序文案放本目录 `messages/`,跨端通用内容放 `packages/{{fullstack.sharedPackageName}}`;消息格式插件使用工程根已安装的依赖。
|
|
17
|
+
|
|
18
|
+
## 开发测试
|
|
19
|
+
|
|
20
|
+
本目录不参与 workspace 构建、单元测试或 E2E。接入实际框架后增加对应命令,并在目标平台验证登录、请求、导航与发布包。
|
|
21
|
+
|
|
22
|
+
## 许可证
|
|
23
|
+
|
|
24
|
+
Apache-2.0
|
|
15
25
|
{{/if}}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{{! @skipIfEmpty }}
|
|
2
|
+
{{#if fullstack.hasMiniapp}}
|
|
3
|
+
{
|
|
4
|
+
"$schema": "https://inlang.com/schema/inlang-message-format",
|
|
5
|
+
"mini_title": "{{projectName}} ミニアプリ",
|
|
6
|
+
"mini_status": "予約済みのプレースホルダー",
|
|
7
|
+
"mini_description": "ミニアプリの対象では、まだビルド可能なパッケージは生成しません。標準のinlang project.inlangとmessagesを用意しているため、後でミニアプリ用フレームワークに組み込めます。",
|
|
8
|
+
"mini_shared_contract": "packages/{{fullstack.contractPackageName}}のスキーマとルート定数を再利用してください。",
|
|
9
|
+
"mini_own_messages": "ミニアプリ固有の文案はapps/{{projectName}}-miniapp/messagesに配置します。",
|
|
10
|
+
"mini_customize": "端末間の言語切り替えを揃えるため、言語一覧をsharedと一致させてください。"
|
|
11
|
+
}
|
|
12
|
+
{{/if}}
|
|
@@ -2,9 +2,19 @@
|
|
|
2
2
|
{{#if fullstack.frontends.web}}
|
|
3
3
|
# {{projectName}}-web
|
|
4
4
|
|
|
5
|
+
## 简介
|
|
6
|
+
|
|
5
7
|
响应式 mini 后台 Web 前端,通过 `packages/{{fullstack.sharedPackageName}}` 调用后端 API,并通过 `@h-ai/ui` 组件搭建页面。
|
|
6
8
|
|
|
7
|
-
##
|
|
9
|
+
## 核心功能清单
|
|
10
|
+
|
|
11
|
+
- 浏览器访问共享管理界面与个人设置。
|
|
12
|
+
- 与 API 共用接口类型、登录状态、主题和语言。
|
|
13
|
+
- 可单独构建 Web 镜像并增量更新。
|
|
14
|
+
|
|
15
|
+
## 使用入门
|
|
16
|
+
|
|
17
|
+
### i18n 与 shared 协同
|
|
8
18
|
|
|
9
19
|
- 本端页面文案:`messages/{locale}.json` + `project.inlang/settings.json`
|
|
10
20
|
- 共享 Shell/主题/语言/API:`packages/{{fullstack.sharedPackageName}}`
|
|
@@ -12,7 +22,7 @@
|
|
|
12
22
|
|
|
13
23
|
自定义建议:Web 独有文案放在本目录 `messages/`;跨端通用文案或组件放到 shared。
|
|
14
24
|
|
|
15
|
-
|
|
25
|
+
### 编译、打包与部署
|
|
16
26
|
|
|
17
27
|
```bash
|
|
18
28
|
# 开发预览,默认端口 {{fullstack.ports.web}}
|
|
@@ -22,15 +32,23 @@ pnpm --filter {{projectName}}-web dev
|
|
|
22
32
|
pnpm --filter {{projectName}}-web build
|
|
23
33
|
```
|
|
24
34
|
|
|
25
|
-
从工程根运行 `pnpm build --target docker`
|
|
35
|
+
从工程根运行 `pnpm build --target docker` 导出镜像。默认带部署工具,可运行 `pnpm run deploy --env test --build` 一次完成构建发布,或 `pnpm run deploy --env prod --bundle-only` 交付已有镜像及跨平台脚本。完整包包含自建中间件,人工部署与按服务更新见工程根 README。
|
|
26
36
|
|
|
27
|
-
|
|
37
|
+
### IAM 认证配置
|
|
28
38
|
|
|
29
|
-
服务端 `config/_iam.yml` 统一控制登录、注册、重置、协议与 `ui` 品牌。邮件/短信可用 `_reach.yml` 中的 Provider/标准模板或自定义发送回调。`AuthPanel` 使用安全公开配置及框架认证动作;无需在页面适配 Token
|
|
39
|
+
服务端 `config/_iam.yml` 统一控制登录、注册、重置、协议与 `ui` 品牌。邮件/短信可用 `_reach.yml` 中的 Provider/标准模板或自定义发送回调。`AuthPanel` 使用安全公开配置及框架认证动作;无需在页面适配 Token、验证码倒计时和错误提示。单实例可使用 Memory;多实例需共享数据库与 Redis,并按工程根 README 配置写入路由和传输会话。
|
|
30
40
|
|
|
31
|
-
LDAP/Reach 均可选:基础认证无需安装。默认 LDAP 客户端需在 IAM 服务端 workspace 安装 ldapts,再配置 login.ldap 与连接信息。自定义 onOtpSendEmail/onOtpSendSms/onPasswordResetRequest 回调可独立于 Reach 使用,投递失败须拒绝 Promise
|
|
41
|
+
LDAP/Reach 均可选:基础认证无需安装。默认 LDAP 客户端需在 IAM 服务端 workspace 安装 ldapts,再配置 login.ldap 与连接信息。自定义 onOtpSendEmail/onOtpSendSms/onPasswordResetRequest 回调可独立于 Reach 使用,投递失败须拒绝 Promise;回调第三参数 context 提供 expiresAt/idempotencyKey,发送服务按稳定键持久化去重,异常/超时为结果未知且不自动重发。协议在 Modal 显示;LoginForm/RegisterForm 提供 header/extraFields/footer,AuthPanel 透传 loginHeader/loginFields/loginFooter 和 registerHeader/registerFields/registerFooter。配置及例子见生成项目的 hai-iam/hai-ui skills。
|
|
32
42
|
|
|
33
43
|
协议支持配置中的 userAgreementMarkdown/privacyPolicyMarkdown 正文及 URL;同主题优先 Markdown,由安全渲染器在弹窗显示。
|
|
34
44
|
|
|
35
45
|
完整契约见生成应用的 `.agents/skills/hai-iam/reference.md`。
|
|
46
|
+
|
|
47
|
+
## 开发测试
|
|
48
|
+
|
|
49
|
+
从工程根执行 `pnpm typecheck`、`pnpm lint`、`pnpm build`、`pnpm test` 和 `pnpm test:e2e`。浏览器预览通过后,原生壳仍需在对应系统或设备单独验收。
|
|
50
|
+
|
|
51
|
+
## 许可证
|
|
52
|
+
|
|
53
|
+
Apache-2.0
|
|
36
54
|
{{/if}}
|