@h-ai/cli 0.1.0-alpha.59 → 0.1.0-alpha.61
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 +19 -1
- package/README.ja.md +19 -1
- package/README.md +19 -1
- package/dist/{chunk-WUSFJFW5.js → chunk-WW2MIO6T.js} +437 -126
- package/dist/chunk-WW2MIO6T.js.map +1 -0
- package/dist/cli-config-templates-QRF6B5T4.js +5 -0
- package/dist/{cli-config-templates-6XA7V6CC.js.map → cli-config-templates-QRF6B5T4.js.map} +1 -1
- package/dist/index.js +28 -7
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/templates/README.md +14 -0
- package/templates/apps/api/.gitignore.hbs +1 -0
- package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +12 -0
- package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +4 -7
- package/templates/apps/api/apps/{{projectName}}-service/src/lib/server/init.ts.hbs +11 -1
- package/templates/apps/api/apps/{{projectName}}-service/tests/service.test.ts.hbs +2 -1
- package/templates/apps/api/e2e/api-service.spec.ts.hbs +72 -6
- package/templates/apps/api/package.json.hbs +4 -0
- package/templates/apps/fullstack/.gitignore.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +10 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/project.inlang/settings.json.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +10 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/project.inlang/settings.json.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +2 -0
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/project.inlang/settings.json.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +10 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/project.inlang/settings.json.hbs +1 -1
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +63 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-contract.ts.hbs +8 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +18 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_ai.yml.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_audit.yml.hbs +1 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_cache.yml.hbs +1 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +7 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_db.yml.hbs +1 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_deploy.yml.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_iam.yml.hbs +1 -21
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_payment.yml.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +52 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_scheduler.yml.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +8 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_storage.yml.hbs +1 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_vecdb.yml.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +2 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +7 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +11 -4
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +12 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/en-US.json.hbs +2 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/zh-CN.json.hbs +2 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/project.inlang/settings.json.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +3 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AdminWorkspace.svelte.hbs +32 -23
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/ProfileSettings.svelte.hbs +24 -24
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/UserManagement.svelte.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/management-store.svelte.ts.hbs +20 -20
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/management-store.test.ts.hbs +7 -8
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +35 -23
- package/templates/apps/mobile-app/src/App.svelte.hbs +43 -0
- package/templates/base/.gitignore.hbs +1 -2
- package/templates/base/e2e/app.spec.ts.hbs +147 -0
- package/templates/base/package.json.hbs +1 -1
- package/templates/base/src/hooks.server.ts.hbs +2 -2
- package/templates/base/src/lib/server/init.ts.hbs +1 -1
- package/templates/base/src/routes/+layout.server.ts.hbs +6 -0
- package/templates/base/tests/scaffold.test.ts.hbs +3 -0
- package/templates/base/vite.config.ts.hbs +1 -0
- package/templates/features/iam/routes-admin/(auth)/+layout.svelte.hbs +2 -16
- package/templates/features/iam/routes-admin/(auth)/auth/forgot-password/+page.svelte.hbs +10 -0
- package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +6 -95
- package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +6 -117
- package/templates/features/iam/routes-admin/(auth)/auth/reset-password/+page.svelte.hbs +10 -0
- package/templates/features/iam/routes-h5/auth/forgot-password/+page.svelte.hbs +10 -0
- package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +6 -97
- package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +6 -112
- package/templates/features/iam/routes-h5/auth/reset-password/+page.svelte.hbs +10 -0
- package/templates/features/iam/routes-shared/api/auth/config/+server.ts +4 -0
- package/templates/features/iam/routes-shared/api/auth/login/+server.ts +1 -33
- package/templates/features/iam/routes-shared/api/auth/login/ldap/+server.ts +4 -0
- package/templates/features/iam/routes-shared/api/auth/login/otp/+server.ts +4 -0
- package/templates/features/iam/routes-shared/api/auth/otp/send/+server.ts +4 -0
- package/templates/features/iam/routes-shared/api/auth/password-reset/confirm/+server.ts +4 -0
- package/templates/features/iam/routes-shared/api/auth/password-reset/request/+server.ts +4 -0
- package/templates/features/iam/routes-shared/api/auth/register/+server.ts +1 -23
- package/templates/features/iam/routes-website/auth/forgot-password/+page.svelte.hbs +10 -0
- package/templates/features/iam/routes-website/auth/login/+page.svelte.hbs +11 -0
- package/templates/features/iam/routes-website/auth/register/+page.svelte.hbs +11 -0
- package/templates/features/iam/routes-website/auth/reset-password/+page.svelte.hbs +10 -0
- package/templates/skills/hai-api-client/SKILL.md +5 -0
- package/templates/skills/hai-app-create/reference.md +10 -0
- package/templates/skills/hai-iam/SKILL.md +9 -2
- package/templates/skills/hai-iam/reference.md +76 -11
- package/templates/skills/hai-reach/SKILL.md +2 -0
- package/templates/skills/hai-ui/reference.md +47 -0
- package/dist/chunk-WUSFJFW5.js.map +0 -1
- package/dist/cli-config-templates-6XA7V6CC.js +0 -5
|
@@ -1,122 +1,11 @@
|
|
|
1
|
-
{{!-- Admin 注册页面 --}}
|
|
2
1
|
<script lang='ts'>
|
|
3
|
-
|
|
4
|
-
* 注册页面
|
|
5
|
-
*/
|
|
2
|
+
import { goto } from '$app/navigation'
|
|
6
3
|
import { resolve } from '$app/paths'
|
|
7
|
-
{
|
|
8
|
-
import
|
|
9
|
-
{
|
|
10
|
-
import { Alert, Button, FormField, Input } from '@h-ai/ui'
|
|
4
|
+
import { page } from '$app/state'
|
|
5
|
+
import { kit } from '@h-ai/kit'
|
|
6
|
+
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
11
7
|
|
|
12
|
-
|
|
13
|
-
let email = $state('')
|
|
14
|
-
let password = $state('')
|
|
15
|
-
let confirmPassword = $state('')
|
|
16
|
-
let error = $state('')
|
|
17
|
-
let loading = $state(false)
|
|
18
|
-
|
|
19
|
-
async function handleRegister(event: SubmitEvent) {
|
|
20
|
-
event.preventDefault()
|
|
21
|
-
error = ''
|
|
22
|
-
if (password !== confirmPassword) {
|
|
23
|
-
{{#if hasI18n}}
|
|
24
|
-
error = m.auth_error_password_mismatch()
|
|
25
|
-
{{else}}
|
|
26
|
-
error = '密码不一致'
|
|
27
|
-
{{/if}}
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
loading = true
|
|
31
|
-
try {
|
|
32
|
-
const res = await fetch('/api/auth/register', {
|
|
33
|
-
method: 'POST',
|
|
34
|
-
headers: { 'Content-Type': 'application/json' },
|
|
35
|
-
body: JSON.stringify({ username, email, password, confirmPassword }),
|
|
36
|
-
})
|
|
37
|
-
const data = await res.json()
|
|
38
|
-
if (data.success) {
|
|
39
|
-
window.location.href = '/auth/login'
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
{{#if hasI18n}}
|
|
43
|
-
error = data.error?.message || m.auth_register_failed()
|
|
44
|
-
{{else}}
|
|
45
|
-
error = data.error?.message || '注册失败'
|
|
46
|
-
{{/if}}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
{{#if hasI18n}}
|
|
51
|
-
error = m.auth_error_network()
|
|
52
|
-
{{else}}
|
|
53
|
-
error = '网络错误'
|
|
54
|
-
{{/if}}
|
|
55
|
-
}
|
|
56
|
-
finally {
|
|
57
|
-
loading = false
|
|
58
|
-
}
|
|
59
|
-
}
|
|
8
|
+
const actions = kit.client.createAuthActions()
|
|
60
9
|
</script>
|
|
61
10
|
|
|
62
|
-
<
|
|
63
|
-
{{#if hasI18n}}
|
|
64
|
-
<title>{m.auth_register_title()}</title>
|
|
65
|
-
{{else}}
|
|
66
|
-
<title>注册</title>
|
|
67
|
-
{{/if}}
|
|
68
|
-
</svelte:head>
|
|
69
|
-
|
|
70
|
-
{{#if hasI18n}}
|
|
71
|
-
<h2 class='text-2xl font-bold text-center mb-6'>{m.auth_register_title()}</h2>
|
|
72
|
-
{{else}}
|
|
73
|
-
<h2 class='text-2xl font-bold text-center mb-6'>注册</h2>
|
|
74
|
-
{{/if}}
|
|
75
|
-
|
|
76
|
-
{#if error}
|
|
77
|
-
<Alert variant='error' class='mb-4'>{error}</Alert>
|
|
78
|
-
{/if}
|
|
79
|
-
|
|
80
|
-
<form onsubmit={handleRegister} class='space-y-4'>
|
|
81
|
-
{{#if hasI18n}}
|
|
82
|
-
<FormField label={m.auth_label_username()} required>
|
|
83
|
-
<Input id='username' type='text' bind:value={username} required />
|
|
84
|
-
</FormField>
|
|
85
|
-
<FormField label={m.auth_label_email()} required>
|
|
86
|
-
<Input id='email' type='email' bind:value={email} required />
|
|
87
|
-
</FormField>
|
|
88
|
-
<FormField label={m.auth_label_password()} required>
|
|
89
|
-
<Input id='password' type='password' bind:value={password} required />
|
|
90
|
-
</FormField>
|
|
91
|
-
<FormField label={m.auth_label_confirm_password()} required>
|
|
92
|
-
<Input id='confirmPassword' type='password' bind:value={confirmPassword} required />
|
|
93
|
-
</FormField>
|
|
94
|
-
<Button type='submit' variant='primary' loading={loading} class='w-full'>
|
|
95
|
-
{loading ? m.auth_register_loading() : m.auth_register_submit()}
|
|
96
|
-
</Button>
|
|
97
|
-
{{else}}
|
|
98
|
-
<FormField label='用户名' required>
|
|
99
|
-
<Input id='username' type='text' bind:value={username} required />
|
|
100
|
-
</FormField>
|
|
101
|
-
<FormField label='邮箱' required>
|
|
102
|
-
<Input id='email' type='email' bind:value={email} required />
|
|
103
|
-
</FormField>
|
|
104
|
-
<FormField label='密码' required>
|
|
105
|
-
<Input id='password' type='password' bind:value={password} required />
|
|
106
|
-
</FormField>
|
|
107
|
-
<FormField label='确认密码' required>
|
|
108
|
-
<Input id='confirmPassword' type='password' bind:value={confirmPassword} required />
|
|
109
|
-
</FormField>
|
|
110
|
-
<Button type='submit' variant='primary' loading={loading} class='w-full'>
|
|
111
|
-
{loading ? '注册中...' : '注册'}
|
|
112
|
-
</Button>
|
|
113
|
-
{{/if}}
|
|
114
|
-
</form>
|
|
115
|
-
|
|
116
|
-
<div class='text-center mt-4'>
|
|
117
|
-
{{#if hasI18n}}
|
|
118
|
-
<a href={resolve('/auth/login', {})} class='link link-primary text-sm'>{m.auth_register_to_login()}</a>
|
|
119
|
-
{{else}}
|
|
120
|
-
<a href={resolve('/auth/login', {})} class='link link-primary text-sm'>已有账号?立即登录</a>
|
|
121
|
-
{{/if}}
|
|
122
|
-
</div>
|
|
11
|
+
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='register' loginUrl={resolve('/auth/login', {})} registerUrl={resolve('/auth/register', {})} onauthenticated={() => goto(resolve('/admin', {}))} />
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script lang='ts'>
|
|
2
|
+
import { resolve } from '$app/paths'
|
|
3
|
+
import { page } from '$app/state'
|
|
4
|
+
import { kit } from '@h-ai/kit'
|
|
5
|
+
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
6
|
+
|
|
7
|
+
const actions = kit.client.createAuthActions()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='reset' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('/auth/login', {})} />
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script lang='ts'>
|
|
2
|
+
import { resolve } from '$app/paths'
|
|
3
|
+
import { page } from '$app/state'
|
|
4
|
+
import { kit } from '@h-ai/kit'
|
|
5
|
+
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
6
|
+
|
|
7
|
+
const actions = kit.client.createAuthActions()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='forgot' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('/auth/login', {})} />
|
|
@@ -1,102 +1,11 @@
|
|
|
1
|
-
{{!-- H5 登录页面 --}}
|
|
2
1
|
<script lang='ts'>
|
|
3
|
-
|
|
4
|
-
* H5 登录页面
|
|
5
|
-
*/
|
|
2
|
+
import { goto } from '$app/navigation'
|
|
6
3
|
import { resolve } from '$app/paths'
|
|
7
|
-
{
|
|
8
|
-
import
|
|
9
|
-
{
|
|
10
|
-
import { Alert, Button, FormField, Input } from '@h-ai/ui'
|
|
4
|
+
import { page } from '$app/state'
|
|
5
|
+
import { kit } from '@h-ai/kit'
|
|
6
|
+
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
11
7
|
|
|
12
|
-
|
|
13
|
-
let password = $state('')
|
|
14
|
-
let error = $state('')
|
|
15
|
-
let loading = $state(false)
|
|
16
|
-
|
|
17
|
-
async function handleLogin(event: SubmitEvent) {
|
|
18
|
-
event.preventDefault()
|
|
19
|
-
error = ''
|
|
20
|
-
loading = true
|
|
21
|
-
try {
|
|
22
|
-
const res = await fetch('/api/auth/login', {
|
|
23
|
-
method: 'POST',
|
|
24
|
-
headers: { 'Content-Type': 'application/json' },
|
|
25
|
-
body: JSON.stringify({ identifier: username, password }),
|
|
26
|
-
})
|
|
27
|
-
const data = await res.json()
|
|
28
|
-
if (data.success) {
|
|
29
|
-
window.location.href = '/'
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
{{#if hasI18n}}
|
|
33
|
-
error = data.error?.message || m.auth_login_failed()
|
|
34
|
-
{{else}}
|
|
35
|
-
error = data.error?.message || '登录失败'
|
|
36
|
-
{{/if}}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
{{#if hasI18n}}
|
|
41
|
-
error = m.auth_error_network()
|
|
42
|
-
{{else}}
|
|
43
|
-
error = '网络错误'
|
|
44
|
-
{{/if}}
|
|
45
|
-
}
|
|
46
|
-
finally {
|
|
47
|
-
loading = false
|
|
48
|
-
}
|
|
49
|
-
}
|
|
8
|
+
const authActions = kit.client.createAuthActions()
|
|
50
9
|
</script>
|
|
51
10
|
|
|
52
|
-
<
|
|
53
|
-
{{#if hasI18n}}
|
|
54
|
-
<title>{m.auth_login_title()}</title>
|
|
55
|
-
{{else}}
|
|
56
|
-
<title>登录</title>
|
|
57
|
-
{{/if}}
|
|
58
|
-
</svelte:head>
|
|
59
|
-
|
|
60
|
-
<div class='p-4 pt-12 space-y-4'>
|
|
61
|
-
{{#if hasI18n}}
|
|
62
|
-
<h1 class='text-2xl font-black tracking-tight text-center'>{m.auth_login_title()}</h1>
|
|
63
|
-
{{else}}
|
|
64
|
-
<h1 class='text-2xl font-black tracking-tight text-center'>登录</h1>
|
|
65
|
-
{{/if}}
|
|
66
|
-
|
|
67
|
-
{#if error}
|
|
68
|
-
<Alert variant='error'>{error}</Alert>
|
|
69
|
-
{/if}
|
|
70
|
-
|
|
71
|
-
<form onsubmit={handleLogin} class='space-y-4'>
|
|
72
|
-
{{#if hasI18n}}
|
|
73
|
-
<FormField label={m.auth_label_username()} required>
|
|
74
|
-
<Input id='username' type='text' bind:value={username} required />
|
|
75
|
-
</FormField>
|
|
76
|
-
<FormField label={m.auth_label_password()} required>
|
|
77
|
-
<Input id='password' type='password' bind:value={password} required />
|
|
78
|
-
</FormField>
|
|
79
|
-
<Button type='submit' variant='primary' loading={loading} class='w-full'>
|
|
80
|
-
{loading ? m.auth_login_loading() : m.auth_login_submit()}
|
|
81
|
-
</Button>
|
|
82
|
-
{{else}}
|
|
83
|
-
<FormField label='用户名' required>
|
|
84
|
-
<Input id='username' type='text' bind:value={username} required />
|
|
85
|
-
</FormField>
|
|
86
|
-
<FormField label='密码' required>
|
|
87
|
-
<Input id='password' type='password' bind:value={password} required />
|
|
88
|
-
</FormField>
|
|
89
|
-
<Button type='submit' variant='primary' loading={loading} class='w-full'>
|
|
90
|
-
{loading ? '登录中...' : '登录'}
|
|
91
|
-
</Button>
|
|
92
|
-
{{/if}}
|
|
93
|
-
</form>
|
|
94
|
-
|
|
95
|
-
<div class='text-center'>
|
|
96
|
-
{{#if hasI18n}}
|
|
97
|
-
<a href={resolve('/auth/register', {})} class='link link-primary text-sm'>{m.auth_login_to_register()}</a>
|
|
98
|
-
{{else}}
|
|
99
|
-
<a href={resolve('/auth/register', {})} class='link link-primary text-sm'>没有账号?立即注册</a>
|
|
100
|
-
{{/if}}
|
|
101
|
-
</div>
|
|
102
|
-
</div>
|
|
11
|
+
<AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='login' loginUrl={resolve('/auth/login', {})} registerUrl={resolve('/auth/register', {})} onauthenticated={() => goto(resolve('/', {}))} />
|
|
@@ -1,117 +1,11 @@
|
|
|
1
|
-
{{!-- H5 注册页面 --}}
|
|
2
1
|
<script lang='ts'>
|
|
3
|
-
|
|
4
|
-
* H5 注册页面
|
|
5
|
-
*/
|
|
2
|
+
import { goto } from '$app/navigation'
|
|
6
3
|
import { resolve } from '$app/paths'
|
|
7
|
-
{
|
|
8
|
-
import
|
|
9
|
-
{
|
|
10
|
-
import { Alert, Button, FormField, Input } from '@h-ai/ui'
|
|
4
|
+
import { page } from '$app/state'
|
|
5
|
+
import { kit } from '@h-ai/kit'
|
|
6
|
+
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
11
7
|
|
|
12
|
-
|
|
13
|
-
let password = $state('')
|
|
14
|
-
let confirmPassword = $state('')
|
|
15
|
-
let error = $state('')
|
|
16
|
-
let loading = $state(false)
|
|
17
|
-
|
|
18
|
-
async function handleRegister(event: SubmitEvent) {
|
|
19
|
-
event.preventDefault()
|
|
20
|
-
error = ''
|
|
21
|
-
if (password !== confirmPassword) {
|
|
22
|
-
{{#if hasI18n}}
|
|
23
|
-
error = m.auth_error_password_mismatch()
|
|
24
|
-
{{else}}
|
|
25
|
-
error = '密码不一致'
|
|
26
|
-
{{/if}}
|
|
27
|
-
return
|
|
28
|
-
}
|
|
29
|
-
loading = true
|
|
30
|
-
try {
|
|
31
|
-
const res = await fetch('/api/auth/register', {
|
|
32
|
-
method: 'POST',
|
|
33
|
-
headers: { 'Content-Type': 'application/json' },
|
|
34
|
-
body: JSON.stringify({ username, password, confirmPassword }),
|
|
35
|
-
})
|
|
36
|
-
const data = await res.json()
|
|
37
|
-
if (data.success) {
|
|
38
|
-
window.location.href = '/auth/login'
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
{{#if hasI18n}}
|
|
42
|
-
error = data.error?.message || m.auth_register_failed()
|
|
43
|
-
{{else}}
|
|
44
|
-
error = data.error?.message || '注册失败'
|
|
45
|
-
{{/if}}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
{{#if hasI18n}}
|
|
50
|
-
error = m.auth_error_network()
|
|
51
|
-
{{else}}
|
|
52
|
-
error = '网络错误'
|
|
53
|
-
{{/if}}
|
|
54
|
-
}
|
|
55
|
-
finally {
|
|
56
|
-
loading = false
|
|
57
|
-
}
|
|
58
|
-
}
|
|
8
|
+
const authActions = kit.client.createAuthActions()
|
|
59
9
|
</script>
|
|
60
10
|
|
|
61
|
-
<
|
|
62
|
-
{{#if hasI18n}}
|
|
63
|
-
<title>{m.auth_register_title()}</title>
|
|
64
|
-
{{else}}
|
|
65
|
-
<title>注册</title>
|
|
66
|
-
{{/if}}
|
|
67
|
-
</svelte:head>
|
|
68
|
-
|
|
69
|
-
<div class='p-4 pt-12 space-y-4'>
|
|
70
|
-
{{#if hasI18n}}
|
|
71
|
-
<h1 class='text-2xl font-black tracking-tight text-center'>{m.auth_register_title()}</h1>
|
|
72
|
-
{{else}}
|
|
73
|
-
<h1 class='text-2xl font-black tracking-tight text-center'>注册</h1>
|
|
74
|
-
{{/if}}
|
|
75
|
-
|
|
76
|
-
{#if error}
|
|
77
|
-
<Alert variant='error'>{error}</Alert>
|
|
78
|
-
{/if}
|
|
79
|
-
|
|
80
|
-
<form onsubmit={handleRegister} class='space-y-4'>
|
|
81
|
-
{{#if hasI18n}}
|
|
82
|
-
<FormField label={m.auth_label_username()} required>
|
|
83
|
-
<Input id='username' type='text' bind:value={username} required />
|
|
84
|
-
</FormField>
|
|
85
|
-
<FormField label={m.auth_label_password()} required>
|
|
86
|
-
<Input id='password' type='password' bind:value={password} required />
|
|
87
|
-
</FormField>
|
|
88
|
-
<FormField label={m.auth_label_confirm_password()} required>
|
|
89
|
-
<Input id='confirmPassword' type='password' bind:value={confirmPassword} required />
|
|
90
|
-
</FormField>
|
|
91
|
-
<Button type='submit' variant='primary' loading={loading} class='w-full'>
|
|
92
|
-
{loading ? m.auth_register_loading() : m.auth_register_submit()}
|
|
93
|
-
</Button>
|
|
94
|
-
{{else}}
|
|
95
|
-
<FormField label='用户名' required>
|
|
96
|
-
<Input id='username' type='text' bind:value={username} required />
|
|
97
|
-
</FormField>
|
|
98
|
-
<FormField label='密码' required>
|
|
99
|
-
<Input id='password' type='password' bind:value={password} required />
|
|
100
|
-
</FormField>
|
|
101
|
-
<FormField label='确认密码' required>
|
|
102
|
-
<Input id='confirmPassword' type='password' bind:value={confirmPassword} required />
|
|
103
|
-
</FormField>
|
|
104
|
-
<Button type='submit' variant='primary' loading={loading} class='w-full'>
|
|
105
|
-
{loading ? '注册中...' : '注册'}
|
|
106
|
-
</Button>
|
|
107
|
-
{{/if}}
|
|
108
|
-
</form>
|
|
109
|
-
|
|
110
|
-
<div class='text-center'>
|
|
111
|
-
{{#if hasI18n}}
|
|
112
|
-
<a href={resolve('/auth/login', {})} class='link link-primary text-sm'>{m.auth_register_to_login()}</a>
|
|
113
|
-
{{else}}
|
|
114
|
-
<a href={resolve('/auth/login', {})} class='link link-primary text-sm'>已有账号?立即登录</a>
|
|
115
|
-
{{/if}}
|
|
116
|
-
</div>
|
|
117
|
-
</div>
|
|
11
|
+
<AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='register' loginUrl={resolve('/auth/login', {})} registerUrl={resolve('/auth/register', {})} onauthenticated={() => goto(resolve('/', {}))} />
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script lang='ts'>
|
|
2
|
+
import { resolve } from '$app/paths'
|
|
3
|
+
import { page } from '$app/state'
|
|
4
|
+
import { kit } from '@h-ai/kit'
|
|
5
|
+
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
6
|
+
|
|
7
|
+
const actions = kit.client.createAuthActions()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='reset' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('/auth/login', {})} />
|
|
@@ -1,36 +1,4 @@
|
|
|
1
|
-
import type { CoreConfig } from '@h-ai/core'
|
|
2
|
-
/**
|
|
3
|
-
* 登录 API
|
|
4
|
-
*/
|
|
5
|
-
import type { RequestHandler } from './$types'
|
|
6
|
-
import { core } from '@h-ai/core'
|
|
7
1
|
import { iam } from '@h-ai/iam'
|
|
8
2
|
import { kit } from '@h-ai/kit'
|
|
9
|
-
import { z } from 'zod'
|
|
10
3
|
|
|
11
|
-
const
|
|
12
|
-
identifier: z.string().trim().min(1).max(254),
|
|
13
|
-
password: z.string().min(1).max(128),
|
|
14
|
-
}).strict()
|
|
15
|
-
|
|
16
|
-
export const POST: RequestHandler = kit.handler(async ({ request, cookies, locals }) => {
|
|
17
|
-
const { identifier, password } = await kit.validate.body(request, LoginInputSchema)
|
|
18
|
-
|
|
19
|
-
const result = await iam.auth.login({ identifier, password })
|
|
20
|
-
if (!result.success) {
|
|
21
|
-
return kit.response.fromError(result.error, locals.requestId)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const { tokens } = result.data
|
|
25
|
-
const config = core.config.getOrThrow<CoreConfig>('core')
|
|
26
|
-
|
|
27
|
-
cookies.set('session_token', tokens.accessToken, {
|
|
28
|
-
path: '/',
|
|
29
|
-
httpOnly: true,
|
|
30
|
-
sameSite: 'lax',
|
|
31
|
-
secure: config.env === 'production' || config.env === 'staging',
|
|
32
|
-
maxAge: tokens.expiresIn,
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
return kit.response.ok({ authenticated: true }, locals.requestId)
|
|
36
|
-
})
|
|
4
|
+
export const POST = kit.auth.createApi(iam).login
|
|
@@ -1,26 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 注册 API
|
|
3
|
-
*/
|
|
4
|
-
import type { RequestHandler } from './$types'
|
|
5
1
|
import { iam } from '@h-ai/iam'
|
|
6
2
|
import { kit } from '@h-ai/kit'
|
|
7
3
|
|
|
8
|
-
export const POST
|
|
9
|
-
const body = await request.json()
|
|
10
|
-
const { username, email, password, confirmPassword } = body
|
|
11
|
-
|
|
12
|
-
if (!username || !password) {
|
|
13
|
-
return kit.response.badRequest('Username and password are required')
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (password !== confirmPassword) {
|
|
17
|
-
return kit.response.badRequest('Passwords do not match')
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const result = await iam.user.register({ username, email, password })
|
|
21
|
-
if (!result.success) {
|
|
22
|
-
return kit.response.badRequest(result.error.message)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return kit.response.ok({ message: 'Registration successful' }, locals.requestId)
|
|
26
|
-
}
|
|
4
|
+
export const POST = kit.auth.createApi(iam).register
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script lang='ts'>
|
|
2
|
+
import { resolve } from '$app/paths'
|
|
3
|
+
import { page } from '$app/state'
|
|
4
|
+
import { kit } from '@h-ai/kit'
|
|
5
|
+
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
6
|
+
|
|
7
|
+
const actions = kit.client.createAuthActions()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='forgot' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('/auth/login', {})} />
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<script lang='ts'>
|
|
2
|
+
import { goto } from '$app/navigation'
|
|
3
|
+
import { resolve } from '$app/paths'
|
|
4
|
+
import { page } from '$app/state'
|
|
5
|
+
import { kit } from '@h-ai/kit'
|
|
6
|
+
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
7
|
+
|
|
8
|
+
const authActions = kit.client.createAuthActions()
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='login' loginUrl={resolve('/auth/login', {})} registerUrl={resolve('/auth/register', {})} onauthenticated={() => goto(resolve('/', {}))} />
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<script lang='ts'>
|
|
2
|
+
import { goto } from '$app/navigation'
|
|
3
|
+
import { resolve } from '$app/paths'
|
|
4
|
+
import { page } from '$app/state'
|
|
5
|
+
import { kit } from '@h-ai/kit'
|
|
6
|
+
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
7
|
+
|
|
8
|
+
const authActions = kit.client.createAuthActions()
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='register' loginUrl={resolve('/auth/login', {})} registerUrl={resolve('/auth/register', {})} onauthenticated={() => goto(resolve('/', {}))} />
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script lang='ts'>
|
|
2
|
+
import { resolve } from '$app/paths'
|
|
3
|
+
import { page } from '$app/state'
|
|
4
|
+
import { kit } from '@h-ai/kit'
|
|
5
|
+
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
6
|
+
|
|
7
|
+
const actions = kit.client.createAuthActions()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='reset' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('/auth/login', {})} />
|
|
@@ -174,3 +174,8 @@ if (login.success) {
|
|
|
174
174
|
- 客户端不导入 `@h-ai/crypto` 的内部 transport 工厂,只通过 `transport: { crypto }` 装配。
|
|
175
175
|
- 前端只依赖 `@h-ai/api-client` 和 contract 类型,不依赖业务模块实现。
|
|
176
176
|
- httpOnly cookie 模式需服务端与客户端同步配置,不支持跨域刷新(`SameSite=Strict`)。
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
## 配置驱动认证页面
|
|
180
|
+
|
|
181
|
+
`apiClient.createAuthActions(client)` 为含标准 iam contract 的客户端提供 publicConfig、login、register、sendOtp、requestPasswordReset、confirmPasswordReset。login 输入使用 identifier/password/method/code,method 为 password、ldap、email、sms。成功认证自动调用既有安全 TokenStorage;应用只需把动作传给 AuthPanel,并在 onauthenticated 中刷新自己的用户状态或导航。
|
|
@@ -312,6 +312,16 @@ interface CreateArticleInput {
|
|
|
312
312
|
|
|
313
313
|
## 初始化顺序
|
|
314
314
|
|
|
315
|
+
CLI 项目统一忽略 `**/data/`,含根目录、workspace 子目录及 `.gitkeep`;数据库/上传文件不入 Git。Fullstack 的服务端 config 始终保留 core、serv、db、cache、iam、storage、audit、reach、ai、vecdb、payment、scheduler、deploy 共 13 份 YAML,未启用模块整份注释,可选字段及替代 Provider 按块注释,`.env.example` 也保留未启用模块的注释凭据。
|
|
316
|
+
|
|
317
|
+
H5 认证路由独立显示;业务页面使用 `@h-ai/ui` 的 `BottomNav` 与移动端壳样式,导航启用 centered/maxWidth/safeArea,与页面同宽。
|
|
318
|
+
|
|
319
|
+
扩展这些配置前先核对当前安装版本及模块公开 Schema。在服务端 workspace 安装需要的依赖,在 `src/server/runtime.ts` 按依赖顺序校验/初始化、失败清理并反序关闭,再接入 contract/procedure。整份注释文件先去掉一层外层注释,可选块只开启需要的字段;切换 Provider 时替换旧连接块并保持名称唯一。配置文件存在不代表模块已启用。crypto.init() 无配置参数,datapipe 无生命周期,不增加虚构的 YAML。
|
|
320
|
+
|
|
321
|
+
环境变量叶子覆盖只遍历 YAML 已有路径,注释键须先取消注释,或用 HAI_DB/HAI_CACHE 等整体对象覆盖。camelCase 不拆词,例如 connectTimeout → HAI_CACHE_CONNECTTIMEOUT;密钥仅写入被忽略的环境文件或部署环境。语言列表在前端/shared 的 project.inlang/settings.json 中同步,core 只配置 defaultLocale。
|
|
322
|
+
|
|
323
|
+
Fullstack 自动生成的 .env 默认只启用管理员初始密码和 PUBLIC_API_BASE,其余环境覆盖保留注释,YAML 修改可直接生效。显式开启环境覆盖后,修改 YAML 时须核对该覆盖是否仍适用。
|
|
324
|
+
|
|
315
325
|
复用当前应用服务端启动/关闭入口;SvelteKit 通常为 `$lib/server/init.ts`,API/fullstack 使用已有 service/serv 初始化文件。下列是启用 IAM 时的常见依赖关系,不要求无关应用安装这些模块:
|
|
316
326
|
|
|
317
327
|
```
|
|
@@ -13,15 +13,21 @@ description: "使用 @h-ai/iam 实现登录、注册、会话、OTP/LDAP/API Key
|
|
|
13
13
|
| 适用场景 | 应用身份认证、会话失效或角色权限管理 |
|
|
14
14
|
| 输入 | IamConfigInput、已初始化依赖、凭据、用户/角色/权限 ID |
|
|
15
15
|
| 输出 | 认证、会话与授权操作的 HaiResult |
|
|
16
|
-
| 限制 | 仅服务端,先初始化 reldb/cache(crypto 未初始化时由 IAM 初始化);公开认证入口是 iam.auth。客户端经 contract/API
|
|
16
|
+
| 限制 | 仅服务端,先初始化 reldb/cache(crypto 未初始化时由 IAM 初始化);公开认证入口是 iam.auth。客户端经 contract/API 调用,凭证只用于认证和安全存储,不进入日志或公开配置。 |
|
|
17
17
|
|
|
18
18
|
## 使用路径
|
|
19
19
|
|
|
20
|
-
- `_iam.yml`
|
|
20
|
+
- `_iam.yml` 只存 IamConfigSettingsInput,运行时回调单独传给 iam.init;不把 reldb/cache 句柄混入配置。
|
|
21
21
|
- 角色资料与 permissionIds 一次保存:未传保持、空数组清空、未知 ID 失败;人数查询失败不能显示为 0。
|
|
22
22
|
- IAM 服务端是 iam.auth;typed HTTP client 的路径是 apiClient.iam.auth。
|
|
23
23
|
- OTP/API Key 等先核对 login 开关;不把未启用能力的 NOT_INITIALIZED 当认证失败。
|
|
24
24
|
|
|
25
|
+
- 认证页面使用 iam.publicConfig + AuthPanel,协议用弹窗,表单通过 header/extraFields/footer 或 AuthPanel 的 login*/register* snippet 扩展;Kit 使用 kit.auth.createApi/publicPaths + kit.client.createAuthActions,HTTP 客户端使用 apiClient.createAuthActions。
|
|
26
|
+
- LDAP/Reach 均可选:基础认证无需安装;默认 LDAP 关闭,选用默认客户端才安装 ldapts 并设置 login.ldap + ldap。OTP 邮件/短信由 otp.email/sms 控制,选择 Reach 标准模板或自定义发送回调;具体签名与失败处理见参考中的“可选 LDAP 与通知”。
|
|
27
|
+
- 多节点共享 DB + Redis,Memory 只用于单进程。重置的策略失败计入 maxAttempts;OTP 消费不解除 resendInterval。管理员 createUser 不受自助注册开关限制。
|
|
28
|
+
|
|
29
|
+
- 协议支持配置中的 userAgreementMarkdown/privacyPolicyMarkdown 正文及 URL;同主题优先 Markdown,由安全渲染器在弹窗显示。
|
|
30
|
+
|
|
25
31
|
## 按需参考
|
|
26
32
|
|
|
27
33
|
详细配置、API 签名、错误码和范本在 [reference.md](reference.md)。按下列标题定位所需小节,不默认通读;用例中的业务变量需结合当前应用补齐。
|
|
@@ -30,6 +36,7 @@ description: "使用 @h-ai/iam 实现登录、注册、会话、OTP/LDAP/API Key
|
|
|
30
36
|
- 依赖
|
|
31
37
|
- 适用场景
|
|
32
38
|
- 使用步骤
|
|
39
|
+
- 可选 LDAP 与通知 — 安装、TLS/AD 配置、标准模板、自定义发送
|
|
33
40
|
- 核心 API
|
|
34
41
|
- HTTP API 契约
|
|
35
42
|
- 错误码 — `HaiIamError`
|