@h-ai/cli 0.1.0-alpha.59 → 0.1.0-alpha.60
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 +9 -1
- package/README.ja.md +9 -1
- package/README.md +9 -1
- package/dist/{chunk-WUSFJFW5.js → chunk-P6X63D3I.js} +64 -6
- package/dist/chunk-P6X63D3I.js.map +1 -0
- package/dist/cli-config-templates-EFABFSCY.js +5 -0
- package/dist/{cli-config-templates-6XA7V6CC.js.map → cli-config-templates-EFABFSCY.js.map} +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/templates/README.md +8 -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/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 +10 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_iam.yml.hbs +46 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +21 -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/mobile-app/src/App.svelte.hbs +43 -0
- package/templates/base/e2e/app.spec.ts.hbs +105 -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-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 +45 -0
- package/dist/chunk-WUSFJFW5.js.map +0 -1
- package/dist/cli-config-templates-6XA7V6CC.js +0 -5
|
@@ -89,10 +89,10 @@ const haiHandle = kit.createHandle({
|
|
|
89
89
|
data: session.data,
|
|
90
90
|
}
|
|
91
91
|
},
|
|
92
|
-
operations: iam.auth,
|
|
93
92
|
loginUrl: '/auth/login',
|
|
94
93
|
protectedPaths: ['/admin/*', '/api/*'],
|
|
95
|
-
publicPaths: ['/api/auth
|
|
94
|
+
publicPaths: [...kit.auth.publicPaths, '/api/auth/logout', '/api/public/*', '/api/health'],
|
|
95
|
+
operations: () => iam.auth,
|
|
96
96
|
},
|
|
97
97
|
{{/if}}
|
|
98
98
|
rateLimit: { windowMs: 60000, maxRequests: 200 },
|
|
@@ -190,7 +190,7 @@ export async function initApp(): Promise<void> {
|
|
|
190
190
|
|
|
191
191
|
{{#if features.iam}}
|
|
192
192
|
// ─── 初始化 IAM ───
|
|
193
|
-
const iamResult = await iam.init(iamConfig)
|
|
193
|
+
const iamResult = await iam.init({ ...iamConfig{{#if features.reach}}, reach{{/if}} })
|
|
194
194
|
if (!iamResult.success) {
|
|
195
195
|
throw new Error(`IAM initialization failed: ${iamResult.error.message}`)
|
|
196
196
|
}
|
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
{{#unless isSvelteOnlyApp}}
|
|
3
3
|
import type { CoreConfig } from '@h-ai/core'
|
|
4
4
|
import { core } from '@h-ai/core'
|
|
5
|
+
{{#if features.iam}}
|
|
6
|
+
import { iam } from '@h-ai/iam'
|
|
7
|
+
{{/if}}
|
|
5
8
|
|
|
6
9
|
/** 只向浏览器提供公开配置,避免暴露日志上下文、凭证或其它模块配置。 */
|
|
7
10
|
export function load() {
|
|
8
11
|
const config = core.config.getOrThrow<CoreConfig>('core')
|
|
9
12
|
return {
|
|
13
|
+
{{#if features.iam}}
|
|
14
|
+
iamPublicConfig: iam.publicConfig,
|
|
15
|
+
{{/if}}
|
|
10
16
|
appConfig: {
|
|
11
17
|
name: config.name,
|
|
12
18
|
version: config.version,
|
|
@@ -55,6 +55,9 @@ describe('generated scaffold', () => {
|
|
|
55
55
|
const config = core.config.getOrThrow<CoreConfig>('core')
|
|
56
56
|
expect(core.logger.getLevel()).toBe(config.logging?.level ?? 'info')
|
|
57
57
|
expect(load()).toEqual({
|
|
58
|
+
{{#if features.iam}}
|
|
59
|
+
iamPublicConfig: null,
|
|
60
|
+
{{/if}}
|
|
58
61
|
appConfig: { name: config.name, version: config.version, env: config.env, defaultLocale: config.defaultLocale },
|
|
59
62
|
})
|
|
60
63
|
})
|
|
@@ -1,21 +1,7 @@
|
|
|
1
|
-
{{!-- 认证布局(登录/注册共用) --}}
|
|
2
1
|
<script lang='ts'>
|
|
3
|
-
/**
|
|
4
|
-
* 认证布局(登录/注册共用)
|
|
5
|
-
*/
|
|
6
2
|
import type { Snippet } from 'svelte'
|
|
7
3
|
|
|
8
|
-
|
|
9
|
-
children: Snippet
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const { children }: Props = $props()
|
|
4
|
+
const { children }: { children: Snippet } = $props()
|
|
13
5
|
</script>
|
|
14
6
|
|
|
15
|
-
|
|
16
|
-
<div class='card w-full max-w-md bg-base-100 shadow-xl'>
|
|
17
|
-
<div class='card-body'>
|
|
18
|
-
{@render children()}
|
|
19
|
-
</div>
|
|
20
|
-
</div>
|
|
21
|
-
</div>
|
|
7
|
+
{@render children()}
|
|
@@ -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,100 +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 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 = '/admin'
|
|
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 actions = 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
|
-
{{#if hasI18n}}
|
|
61
|
-
<h2 class='text-2xl font-bold text-center mb-6'>{m.auth_login_title()}</h2>
|
|
62
|
-
{{else}}
|
|
63
|
-
<h2 class='text-2xl font-bold text-center mb-6'>登录</h2>
|
|
64
|
-
{{/if}}
|
|
65
|
-
|
|
66
|
-
{#if error}
|
|
67
|
-
<Alert variant='error' class='mb-4'>{error}</Alert>
|
|
68
|
-
{/if}
|
|
69
|
-
|
|
70
|
-
<form onsubmit={handleLogin} class='space-y-4'>
|
|
71
|
-
{{#if hasI18n}}
|
|
72
|
-
<FormField label={m.auth_label_username()} required>
|
|
73
|
-
<Input id='username' type='text' bind:value={username} required />
|
|
74
|
-
</FormField>
|
|
75
|
-
<FormField label={m.auth_label_password()} required>
|
|
76
|
-
<Input id='password' type='password' bind:value={password} required />
|
|
77
|
-
</FormField>
|
|
78
|
-
<Button type='submit' variant='primary' loading={loading} class='w-full'>
|
|
79
|
-
{loading ? m.auth_login_loading() : m.auth_login_submit()}
|
|
80
|
-
</Button>
|
|
81
|
-
{{else}}
|
|
82
|
-
<FormField label='用户名' required>
|
|
83
|
-
<Input id='username' type='text' bind:value={username} required />
|
|
84
|
-
</FormField>
|
|
85
|
-
<FormField label='密码' required>
|
|
86
|
-
<Input id='password' type='password' bind:value={password} required />
|
|
87
|
-
</FormField>
|
|
88
|
-
<Button type='submit' variant='primary' loading={loading} class='w-full'>
|
|
89
|
-
{loading ? '登录中...' : '登录'}
|
|
90
|
-
</Button>
|
|
91
|
-
{{/if}}
|
|
92
|
-
</form>
|
|
93
|
-
|
|
94
|
-
<div class='text-center mt-4'>
|
|
95
|
-
{{#if hasI18n}}
|
|
96
|
-
<a href={resolve('/auth/register', {})} class='link link-primary text-sm'>{m.auth_login_to_register()}</a>
|
|
97
|
-
{{else}}
|
|
98
|
-
<a href={resolve('/auth/register', {})} class='link link-primary text-sm'>没有账号?立即注册</a>
|
|
99
|
-
{{/if}}
|
|
100
|
-
</div>
|
|
11
|
+
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='login' loginUrl={resolve('/auth/login', {})} registerUrl={resolve('/auth/register', {})} onauthenticated={() => goto(resolve('/admin', {}))} />
|
|
@@ -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
|