@h-ai/cli 0.1.0-alpha.10

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.
Files changed (109) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +167 -0
  3. package/dist/chunk-2H7UOFLK.js +10 -0
  4. package/dist/chunk-2H7UOFLK.js.map +1 -0
  5. package/dist/chunk-LANIDJC7.js +659 -0
  6. package/dist/chunk-LANIDJC7.js.map +1 -0
  7. package/dist/chunk-YV7WPUI6.js +206 -0
  8. package/dist/chunk-YV7WPUI6.js.map +1 -0
  9. package/dist/config-templates-S5RI2MC6.js +5 -0
  10. package/dist/config-templates-S5RI2MC6.js.map +1 -0
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.js +1650 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/skill-templates-N5DU5FNX.js +5 -0
  15. package/dist/skill-templates-N5DU5FNX.js.map +1 -0
  16. package/dist/src-XJ6YXNAF.js +15030 -0
  17. package/dist/src-XJ6YXNAF.js.map +1 -0
  18. package/package.json +59 -0
  19. package/templates/README.md +126 -0
  20. package/templates/apps/admin/messages/en-US.json +33 -0
  21. package/templates/apps/admin/messages/zh-CN.json +33 -0
  22. package/templates/apps/admin/src/routes/+layout.svelte.hbs +30 -0
  23. package/templates/apps/admin/src/routes/+page.svelte.hbs +35 -0
  24. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +44 -0
  25. package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +45 -0
  26. package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +37 -0
  27. package/templates/apps/admin/src/routes/api/health/+server.ts +12 -0
  28. package/templates/apps/android-app/.env.hbs +3 -0
  29. package/templates/apps/android-app/capacitor.config.ts.hbs +12 -0
  30. package/templates/apps/android-app/messages/en-US.json +24 -0
  31. package/templates/apps/android-app/messages/zh-CN.json +24 -0
  32. package/templates/apps/android-app/src/lib/api.ts.hbs +30 -0
  33. package/templates/apps/android-app/src/lib/capacitor.ts +21 -0
  34. package/templates/apps/android-app/src/routes/+layout.svelte.hbs +42 -0
  35. package/templates/apps/android-app/src/routes/+layout.ts +7 -0
  36. package/templates/apps/android-app/src/routes/+page.svelte.hbs +27 -0
  37. package/templates/apps/api/src/routes/+layout.svelte +14 -0
  38. package/templates/apps/api/src/routes/+page.svelte +11 -0
  39. package/templates/apps/api/src/routes/api/v1/health/+server.ts +14 -0
  40. package/templates/apps/api/src/routes/api/v1/items/+server.ts +37 -0
  41. package/templates/apps/api/src/routes/api/v1/items/[id]/+server.ts +30 -0
  42. package/templates/apps/h5/messages/en-US.json +37 -0
  43. package/templates/apps/h5/messages/zh-CN.json +37 -0
  44. package/templates/apps/h5/src/routes/+layout.svelte.hbs +57 -0
  45. package/templates/apps/h5/src/routes/+page.svelte.hbs +27 -0
  46. package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +27 -0
  47. package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +27 -0
  48. package/templates/apps/website/messages/en-US.json +32 -0
  49. package/templates/apps/website/messages/zh-CN.json +32 -0
  50. package/templates/apps/website/src/routes/+layout.svelte.hbs +66 -0
  51. package/templates/apps/website/src/routes/+page.svelte.hbs +33 -0
  52. package/templates/apps/website/src/routes/about/+page.svelte.hbs +27 -0
  53. package/templates/apps/website/src/routes/api/health/+server.ts +12 -0
  54. package/templates/apps/website/src/routes/contact/+page.svelte.hbs +59 -0
  55. package/templates/apps/website/src/routes/services/+page.svelte.hbs +63 -0
  56. package/templates/base/e2e/helpers.ts +49 -0
  57. package/templates/base/package.json.hbs +67 -0
  58. package/templates/base/playwright.config.ts +32 -0
  59. package/templates/base/src/app.css +2 -0
  60. package/templates/base/src/app.d.ts.hbs +18 -0
  61. package/templates/base/src/app.html.hbs +16 -0
  62. package/templates/base/src/hooks.server.ts.hbs +123 -0
  63. package/templates/base/src/lib/server/init.ts.hbs +190 -0
  64. package/templates/base/svelte.config.js.hbs +30 -0
  65. package/templates/base/tsconfig.json +8 -0
  66. package/templates/base/vite.config.ts.hbs +27 -0
  67. package/templates/features/ai/routes/api/chat/+server.ts +26 -0
  68. package/templates/features/datapipe/routes/api/datapipe/pipeline/+server.ts +41 -0
  69. package/templates/features/iam/routes-admin/(auth)/+layout.svelte.hbs +21 -0
  70. package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +98 -0
  71. package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +124 -0
  72. package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +98 -0
  73. package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +114 -0
  74. package/templates/features/iam/routes-shared/api/auth/login/+server.ts +32 -0
  75. package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +15 -0
  76. package/templates/features/iam/routes-shared/api/auth/me/+server.ts +12 -0
  77. package/templates/features/iam/routes-shared/api/auth/register/+server.ts +26 -0
  78. package/templates/features/payment/routes/api/v1/payment/create/+server.ts +30 -0
  79. package/templates/features/payment/routes/api/v1/payment/notify/[provider]/+server.ts +42 -0
  80. package/templates/features/payment/routes/api/v1/payment/query/[orderNo]/+server.ts +25 -0
  81. package/templates/features/storage/routes/api/upload/+server.ts +35 -0
  82. package/templates/features/vecdb/routes/api/vecdb/search/+server.ts +36 -0
  83. package/templates/i18n/project.inlang/settings.json +11 -0
  84. package/templates/skills/AGENTS.md +26 -0
  85. package/templates/skills/CLAUDE.md +12 -0
  86. package/templates/skills/copilot-instructions.md +61 -0
  87. package/templates/skills/hai-ai/SKILL.md +721 -0
  88. package/templates/skills/hai-api-client/SKILL.md +355 -0
  89. package/templates/skills/hai-app-create/SKILL.md +371 -0
  90. package/templates/skills/hai-app-review/SKILL.md +156 -0
  91. package/templates/skills/hai-app-tests/SKILL.md +468 -0
  92. package/templates/skills/hai-audit/SKILL.md +145 -0
  93. package/templates/skills/hai-build/SKILL.md +319 -0
  94. package/templates/skills/hai-cache/SKILL.md +190 -0
  95. package/templates/skills/hai-capacitor/SKILL.md +377 -0
  96. package/templates/skills/hai-core/SKILL.md +484 -0
  97. package/templates/skills/hai-crypto/SKILL.md +198 -0
  98. package/templates/skills/hai-datapipe/SKILL.md +220 -0
  99. package/templates/skills/hai-deploy/SKILL.md +216 -0
  100. package/templates/skills/hai-iam/SKILL.md +361 -0
  101. package/templates/skills/hai-kit/SKILL.md +484 -0
  102. package/templates/skills/hai-payment/SKILL.md +275 -0
  103. package/templates/skills/hai-reach/SKILL.md +315 -0
  104. package/templates/skills/hai-reldb/SKILL.md +465 -0
  105. package/templates/skills/hai-scheduler/SKILL.md +188 -0
  106. package/templates/skills/hai-storage/SKILL.md +276 -0
  107. package/templates/skills/hai-ui/SKILL.md +438 -0
  108. package/templates/skills/hai-vecdb/SKILL.md +279 -0
  109. package/templates/skills/opencode.json +11 -0
@@ -0,0 +1,98 @@
1
+ {{!-- Admin 登录页面 --}}
2
+ <script lang="ts">
3
+ /**
4
+ * 登录页面
5
+ */
6
+ {{#if hasI18n}}
7
+ import * as m from '$lib/paraglide/messages'
8
+ {{/if}}
9
+
10
+ let username = $state('')
11
+ let password = $state('')
12
+ let error = $state('')
13
+ let loading = $state(false)
14
+
15
+ async function handleLogin() {
16
+ error = ''
17
+ loading = true
18
+ try {
19
+ const res = await fetch('/api/auth/login', {
20
+ method: 'POST',
21
+ headers: { 'Content-Type': 'application/json' },
22
+ body: JSON.stringify({ username, password }),
23
+ })
24
+ const data = await res.json()
25
+ if (data.success) {
26
+ window.location.href = '/admin'
27
+ } else {
28
+ {{#if hasI18n}}
29
+ error = data.error?.message || m.auth_login_failed()
30
+ {{else}}
31
+ error = data.error?.message || '登录失败'
32
+ {{/if}}
33
+ }
34
+ } catch {
35
+ {{#if hasI18n}}
36
+ error = m.auth_error_network()
37
+ {{else}}
38
+ error = '网络错误'
39
+ {{/if}}
40
+ } finally {
41
+ loading = false
42
+ }
43
+ }
44
+ </script>
45
+
46
+ <svelte:head>
47
+ {{#if hasI18n}}
48
+ <title>{m.auth_login_title()}</title>
49
+ {{else}}
50
+ <title>登录</title>
51
+ {{/if}}
52
+ </svelte:head>
53
+
54
+ {{#if hasI18n}}
55
+ <h2 class="card-title justify-center mb-4">{m.auth_login_title()}</h2>
56
+ {{else}}
57
+ <h2 class="card-title justify-center mb-4">登录</h2>
58
+ {{/if}}
59
+
60
+ {#if error}
61
+ <div class="alert alert-error mb-4">
62
+ <span>{error}</span>
63
+ </div>
64
+ {/if}
65
+
66
+ <form onsubmit={(e) => { e.preventDefault(); handleLogin() }} class="space-y-4">
67
+ <div class="form-control">
68
+ {{#if hasI18n}}
69
+ <label class="label" for="username"><span class="label-text">{m.auth_label_username()}</span></label>
70
+ {{else}}
71
+ <label class="label" for="username"><span class="label-text">用户名</span></label>
72
+ {{/if}}
73
+ <input id="username" type="text" class="input input-bordered w-full" bind:value={username} required />
74
+ </div>
75
+ <div class="form-control">
76
+ {{#if hasI18n}}
77
+ <label class="label" for="password"><span class="label-text">{m.auth_label_password()}</span></label>
78
+ {{else}}
79
+ <label class="label" for="password"><span class="label-text">密码</span></label>
80
+ {{/if}}
81
+ <input id="password" type="password" class="input input-bordered w-full" bind:value={password} required />
82
+ </div>
83
+ <button type="submit" class="btn btn-primary w-full" disabled={loading}>
84
+ {{#if hasI18n}}
85
+ {loading ? m.auth_login_loading() : m.auth_login_submit()}
86
+ {{else}}
87
+ {loading ? '登录中...' : '登录'}
88
+ {{/if}}
89
+ </button>
90
+ </form>
91
+
92
+ <div class="text-center mt-4">
93
+ {{#if hasI18n}}
94
+ <a href="/auth/register" class="link link-primary text-sm">{m.auth_login_to_register()}</a>
95
+ {{else}}
96
+ <a href="/auth/register" class="link link-primary text-sm">没有账号?立即注册</a>
97
+ {{/if}}
98
+ </div>
@@ -0,0 +1,124 @@
1
+ {{!-- Admin 注册页面 --}}
2
+ <script lang="ts">
3
+ /**
4
+ * 注册页面
5
+ */
6
+ {{#if hasI18n}}
7
+ import * as m from '$lib/paraglide/messages'
8
+ {{/if}}
9
+
10
+ let username = $state('')
11
+ let email = $state('')
12
+ let password = $state('')
13
+ let confirmPassword = $state('')
14
+ let error = $state('')
15
+ let loading = $state(false)
16
+
17
+ async function handleRegister() {
18
+ error = ''
19
+ if (password !== confirmPassword) {
20
+ {{#if hasI18n}}
21
+ error = m.auth_error_password_mismatch()
22
+ {{else}}
23
+ error = '密码不一致'
24
+ {{/if}}
25
+ return
26
+ }
27
+ loading = true
28
+ try {
29
+ const res = await fetch('/api/auth/register', {
30
+ method: 'POST',
31
+ headers: { 'Content-Type': 'application/json' },
32
+ body: JSON.stringify({ username, email, password, confirmPassword }),
33
+ })
34
+ const data = await res.json()
35
+ if (data.success) {
36
+ window.location.href = '/auth/login'
37
+ } else {
38
+ {{#if hasI18n}}
39
+ error = data.error?.message || m.auth_register_failed()
40
+ {{else}}
41
+ error = data.error?.message || '注册失败'
42
+ {{/if}}
43
+ }
44
+ } catch {
45
+ {{#if hasI18n}}
46
+ error = m.auth_error_network()
47
+ {{else}}
48
+ error = '网络错误'
49
+ {{/if}}
50
+ } finally {
51
+ loading = false
52
+ }
53
+ }
54
+ </script>
55
+
56
+ <svelte:head>
57
+ {{#if hasI18n}}
58
+ <title>{m.auth_register_title()}</title>
59
+ {{else}}
60
+ <title>注册</title>
61
+ {{/if}}
62
+ </svelte:head>
63
+
64
+ {{#if hasI18n}}
65
+ <h2 class="card-title justify-center mb-4">{m.auth_register_title()}</h2>
66
+ {{else}}
67
+ <h2 class="card-title justify-center mb-4">注册</h2>
68
+ {{/if}}
69
+
70
+ {#if error}
71
+ <div class="alert alert-error mb-4">
72
+ <span>{error}</span>
73
+ </div>
74
+ {/if}
75
+
76
+ <form onsubmit={(e) => { e.preventDefault(); handleRegister() }} class="space-y-4">
77
+ <div class="form-control">
78
+ {{#if hasI18n}}
79
+ <label class="label" for="username"><span class="label-text">{m.auth_label_username()}</span></label>
80
+ {{else}}
81
+ <label class="label" for="username"><span class="label-text">用户名</span></label>
82
+ {{/if}}
83
+ <input id="username" type="text" class="input input-bordered w-full" bind:value={username} required />
84
+ </div>
85
+ <div class="form-control">
86
+ {{#if hasI18n}}
87
+ <label class="label" for="email"><span class="label-text">{m.auth_label_email()}</span></label>
88
+ {{else}}
89
+ <label class="label" for="email"><span class="label-text">邮箱</span></label>
90
+ {{/if}}
91
+ <input id="email" type="email" class="input input-bordered w-full" bind:value={email} required />
92
+ </div>
93
+ <div class="form-control">
94
+ {{#if hasI18n}}
95
+ <label class="label" for="password"><span class="label-text">{m.auth_label_password()}</span></label>
96
+ {{else}}
97
+ <label class="label" for="password"><span class="label-text">密码</span></label>
98
+ {{/if}}
99
+ <input id="password" type="password" class="input input-bordered w-full" bind:value={password} required />
100
+ </div>
101
+ <div class="form-control">
102
+ {{#if hasI18n}}
103
+ <label class="label" for="confirmPassword"><span class="label-text">{m.auth_label_confirm_password()}</span></label>
104
+ {{else}}
105
+ <label class="label" for="confirmPassword"><span class="label-text">确认密码</span></label>
106
+ {{/if}}
107
+ <input id="confirmPassword" type="password" class="input input-bordered w-full" bind:value={confirmPassword} required />
108
+ </div>
109
+ <button type="submit" class="btn btn-primary w-full" disabled={loading}>
110
+ {{#if hasI18n}}
111
+ {loading ? m.auth_register_loading() : m.auth_register_submit()}
112
+ {{else}}
113
+ {loading ? '注册中...' : '注册'}
114
+ {{/if}}
115
+ </button>
116
+ </form>
117
+
118
+ <div class="text-center mt-4">
119
+ {{#if hasI18n}}
120
+ <a href="/auth/login" class="link link-primary text-sm">{m.auth_register_to_login()}</a>
121
+ {{else}}
122
+ <a href="/auth/login" class="link link-primary text-sm">已有账号?立即登录</a>
123
+ {{/if}}
124
+ </div>
@@ -0,0 +1,98 @@
1
+ {{!-- H5 登录页面 --}}
2
+ <script lang="ts">
3
+ /**
4
+ * H5 登录页面
5
+ */
6
+ {{#if hasI18n}}
7
+ import * as m from '$lib/paraglide/messages'
8
+ {{/if}}
9
+
10
+ let username = $state('')
11
+ let password = $state('')
12
+ let error = $state('')
13
+ let loading = $state(false)
14
+
15
+ async function handleLogin() {
16
+ error = ''
17
+ loading = true
18
+ try {
19
+ const res = await fetch('/api/auth/login', {
20
+ method: 'POST',
21
+ headers: { 'Content-Type': 'application/json' },
22
+ body: JSON.stringify({ username, password }),
23
+ })
24
+ const data = await res.json()
25
+ if (data.success) {
26
+ window.location.href = '/'
27
+ } else {
28
+ {{#if hasI18n}}
29
+ error = data.error?.message || m.auth_login_failed()
30
+ {{else}}
31
+ error = data.error?.message || '登录失败'
32
+ {{/if}}
33
+ }
34
+ } catch {
35
+ {{#if hasI18n}}
36
+ error = m.auth_error_network()
37
+ {{else}}
38
+ error = '网络错误'
39
+ {{/if}}
40
+ } finally {
41
+ loading = false
42
+ }
43
+ }
44
+ </script>
45
+
46
+ <svelte:head>
47
+ {{#if hasI18n}}
48
+ <title>{m.auth_login_title()}</title>
49
+ {{else}}
50
+ <title>登录</title>
51
+ {{/if}}
52
+ </svelte:head>
53
+
54
+ <div class="p-4 pt-12">
55
+ {{#if hasI18n}}
56
+ <h1 class="text-2xl font-bold text-center mb-8">{m.auth_login_title()}</h1>
57
+ {{else}}
58
+ <h1 class="text-2xl font-bold text-center mb-8">登录</h1>
59
+ {{/if}}
60
+
61
+ {#if error}
62
+ <div class="alert alert-error mb-4">
63
+ <span>{error}</span>
64
+ </div>
65
+ {/if}
66
+
67
+ <form onsubmit={(e) => { e.preventDefault(); handleLogin() }} class="space-y-4">
68
+ <div class="form-control">
69
+ {{#if hasI18n}}
70
+ <input type="text" placeholder={m.auth_label_username()} class="input input-bordered w-full" bind:value={username} required />
71
+ {{else}}
72
+ <input type="text" placeholder="用户名" class="input input-bordered w-full" bind:value={username} required />
73
+ {{/if}}
74
+ </div>
75
+ <div class="form-control">
76
+ {{#if hasI18n}}
77
+ <input type="password" placeholder={m.auth_label_password()} class="input input-bordered w-full" bind:value={password} required />
78
+ {{else}}
79
+ <input type="password" placeholder="密码" class="input input-bordered w-full" bind:value={password} required />
80
+ {{/if}}
81
+ </div>
82
+ <button type="submit" class="btn btn-primary w-full" disabled={loading}>
83
+ {{#if hasI18n}}
84
+ {loading ? m.auth_login_loading() : m.auth_login_submit()}
85
+ {{else}}
86
+ {loading ? '登录中...' : '登录'}
87
+ {{/if}}
88
+ </button>
89
+ </form>
90
+
91
+ <div class="text-center mt-6">
92
+ {{#if hasI18n}}
93
+ <a href="/auth/register" class="link link-primary text-sm">{m.auth_login_to_register()}</a>
94
+ {{else}}
95
+ <a href="/auth/register" class="link link-primary text-sm">没有账号?立即注册</a>
96
+ {{/if}}
97
+ </div>
98
+ </div>
@@ -0,0 +1,114 @@
1
+ {{!-- H5 注册页面 --}}
2
+ <script lang="ts">
3
+ /**
4
+ * H5 注册页面
5
+ */
6
+ {{#if hasI18n}}
7
+ import * as m from '$lib/paraglide/messages'
8
+ {{/if}}
9
+
10
+ let username = $state('')
11
+ let password = $state('')
12
+ let confirmPassword = $state('')
13
+ let error = $state('')
14
+ let loading = $state(false)
15
+
16
+ async function handleRegister() {
17
+ error = ''
18
+ if (password !== confirmPassword) {
19
+ {{#if hasI18n}}
20
+ error = m.auth_error_password_mismatch()
21
+ {{else}}
22
+ error = '密码不一致'
23
+ {{/if}}
24
+ return
25
+ }
26
+ loading = true
27
+ try {
28
+ const res = await fetch('/api/auth/register', {
29
+ method: 'POST',
30
+ headers: { 'Content-Type': 'application/json' },
31
+ body: JSON.stringify({ username, password, confirmPassword }),
32
+ })
33
+ const data = await res.json()
34
+ if (data.success) {
35
+ window.location.href = '/auth/login'
36
+ } else {
37
+ {{#if hasI18n}}
38
+ error = data.error?.message || m.auth_register_failed()
39
+ {{else}}
40
+ error = data.error?.message || '注册失败'
41
+ {{/if}}
42
+ }
43
+ } catch {
44
+ {{#if hasI18n}}
45
+ error = m.auth_error_network()
46
+ {{else}}
47
+ error = '网络错误'
48
+ {{/if}}
49
+ } finally {
50
+ loading = false
51
+ }
52
+ }
53
+ </script>
54
+
55
+ <svelte:head>
56
+ {{#if hasI18n}}
57
+ <title>{m.auth_register_title()}</title>
58
+ {{else}}
59
+ <title>注册</title>
60
+ {{/if}}
61
+ </svelte:head>
62
+
63
+ <div class="p-4 pt-12">
64
+ {{#if hasI18n}}
65
+ <h1 class="text-2xl font-bold text-center mb-8">{m.auth_register_title()}</h1>
66
+ {{else}}
67
+ <h1 class="text-2xl font-bold text-center mb-8">注册</h1>
68
+ {{/if}}
69
+
70
+ {#if error}
71
+ <div class="alert alert-error mb-4">
72
+ <span>{error}</span>
73
+ </div>
74
+ {/if}
75
+
76
+ <form onsubmit={(e) => { e.preventDefault(); handleRegister() }} class="space-y-4">
77
+ <div class="form-control">
78
+ {{#if hasI18n}}
79
+ <input type="text" placeholder={m.auth_label_username()} class="input input-bordered w-full" bind:value={username} required />
80
+ {{else}}
81
+ <input type="text" placeholder="用户名" class="input input-bordered w-full" bind:value={username} required />
82
+ {{/if}}
83
+ </div>
84
+ <div class="form-control">
85
+ {{#if hasI18n}}
86
+ <input type="password" placeholder={m.auth_label_password()} class="input input-bordered w-full" bind:value={password} required />
87
+ {{else}}
88
+ <input type="password" placeholder="密码" class="input input-bordered w-full" bind:value={password} required />
89
+ {{/if}}
90
+ </div>
91
+ <div class="form-control">
92
+ {{#if hasI18n}}
93
+ <input type="password" placeholder={m.auth_label_confirm_password()} class="input input-bordered w-full" bind:value={confirmPassword} required />
94
+ {{else}}
95
+ <input type="password" placeholder="确认密码" class="input input-bordered w-full" bind:value={confirmPassword} required />
96
+ {{/if}}
97
+ </div>
98
+ <button type="submit" class="btn btn-primary w-full" disabled={loading}>
99
+ {{#if hasI18n}}
100
+ {loading ? m.auth_register_loading() : m.auth_register_submit()}
101
+ {{else}}
102
+ {loading ? '注册中...' : '注册'}
103
+ {{/if}}
104
+ </button>
105
+ </form>
106
+
107
+ <div class="text-center mt-6">
108
+ {{#if hasI18n}}
109
+ <a href="/auth/login" class="link link-primary text-sm">{m.auth_register_to_login()}</a>
110
+ {{else}}
111
+ <a href="/auth/login" class="link link-primary text-sm">已有账号?立即登录</a>
112
+ {{/if}}
113
+ </div>
114
+ </div>
@@ -0,0 +1,32 @@
1
+ /**
2
+ * 登录 API
3
+ */
4
+ import type { RequestHandler } from './$types'
5
+ import { iam } from '@h-ai/iam'
6
+ import { kit } from '@h-ai/kit'
7
+
8
+ export const POST: RequestHandler = async ({ request, cookies, locals }) => {
9
+ const body = await request.json()
10
+ const { identifier, password } = body
11
+
12
+ if (!identifier || !password) {
13
+ return kit.response.badRequest('identifier and password are required')
14
+ }
15
+
16
+ const result = await iam.auth.login({ identifier, password })
17
+ if (!result.success) {
18
+ return kit.response.unauthorized(result.error.message)
19
+ }
20
+
21
+ const { tokens } = result.data
22
+
23
+ cookies.set('session_token', tokens.accessToken, {
24
+ path: '/',
25
+ httpOnly: true,
26
+ sameSite: 'lax',
27
+ secure: false,
28
+ maxAge: tokens.expiresIn,
29
+ })
30
+
31
+ return kit.response.ok({ message: 'Login successful' }, locals.requestId)
32
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * 登出 API
3
+ */
4
+ import type { RequestHandler } from './$types'
5
+ import { iam } from '@h-ai/iam'
6
+ import { kit } from '@h-ai/kit'
7
+
8
+ export const POST: RequestHandler = async ({ cookies, locals }) => {
9
+ const token = cookies.get('session_token')
10
+ if (token) {
11
+ await iam.auth.logout(token)
12
+ }
13
+ cookies.delete('session_token', { path: '/' })
14
+ return kit.response.ok({ message: 'Logout successful' }, locals.requestId)
15
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 当前用户信息
3
+ */
4
+ import type { RequestHandler } from './$types'
5
+ import { kit } from '@h-ai/kit'
6
+
7
+ export const GET: RequestHandler = async ({ locals }) => {
8
+ if (!locals.session) {
9
+ return kit.response.unauthorized('Not authenticated')
10
+ }
11
+ return kit.response.ok({ user: locals.session }, locals.requestId)
12
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * 注册 API
3
+ */
4
+ import type { RequestHandler } from './$types'
5
+ import { iam } from '@h-ai/iam'
6
+ import { kit } from '@h-ai/kit'
7
+
8
+ export const POST: RequestHandler = async ({ request, locals }) => {
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
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * 创建支付订单 API
3
+ */
4
+ import type { RequestHandler } from './$types'
5
+ import { kit } from '@h-ai/kit'
6
+ import { payment } from '@h-ai/payment'
7
+
8
+ export const POST: RequestHandler = async ({ request, locals }) => {
9
+ const body = await request.json()
10
+ const { provider, tradeType, outTradeNo, totalAmount, subject, notifyUrl } = body
11
+
12
+ if (!provider || !outTradeNo || !totalAmount || !subject) {
13
+ return kit.response.badRequest('Missing required fields: provider, outTradeNo, totalAmount, subject')
14
+ }
15
+
16
+ const result = await payment.createOrder({
17
+ provider,
18
+ tradeType: tradeType ?? 'JSAPI',
19
+ outTradeNo,
20
+ totalAmount,
21
+ subject,
22
+ notifyUrl: notifyUrl ?? `${new URL(request.url).origin}/api/v1/payment/notify/${provider}`,
23
+ })
24
+
25
+ if (!result.success) {
26
+ return kit.response.internalError(result.error.message)
27
+ }
28
+
29
+ return kit.response.ok(result.data, locals.requestId)
30
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * 支付回调通知 API
3
+ *
4
+ * 处理各支付渠道的异步通知。
5
+ * 路由:POST /api/v1/payment/notify/:provider
6
+ */
7
+ import type { RequestHandler } from './$types'
8
+ import { kit } from '@h-ai/kit'
9
+ import { payment } from '@h-ai/payment'
10
+
11
+ export const POST: RequestHandler = async ({ request, params }) => {
12
+ const provider = params.provider
13
+
14
+ if (!provider) {
15
+ return kit.response.badRequest('Missing provider parameter')
16
+ }
17
+
18
+ const body = await request.text()
19
+ const headers: Record<string, string> = {}
20
+ request.headers.forEach((value, key) => {
21
+ headers[key] = value
22
+ })
23
+
24
+ const result = await payment.handleNotify({
25
+ provider,
26
+ headers,
27
+ body,
28
+ })
29
+
30
+ if (!result.success) {
31
+ return new Response('FAIL', { status: 500 })
32
+ }
33
+
34
+ // 微信支付要求返回 JSON,支付宝要求返回 "success"
35
+ if (provider === 'wechat') {
36
+ return new Response(JSON.stringify({ code: 'SUCCESS', message: '' }), {
37
+ headers: { 'Content-Type': 'application/json' },
38
+ })
39
+ }
40
+
41
+ return new Response('success', { status: 200 })
42
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 查询支付订单 API
3
+ *
4
+ * 路由:GET /api/v1/payment/query/:orderNo
5
+ */
6
+ import type { RequestHandler } from './$types'
7
+ import { kit } from '@h-ai/kit'
8
+ import { payment } from '@h-ai/payment'
9
+
10
+ export const GET: RequestHandler = async ({ params, url, locals }) => {
11
+ const orderNo = params.orderNo
12
+ const provider = url.searchParams.get('provider')
13
+
14
+ if (!orderNo || !provider) {
15
+ return kit.response.badRequest('Missing orderNo or provider parameter')
16
+ }
17
+
18
+ const result = await payment.queryOrder(provider, orderNo)
19
+
20
+ if (!result.success) {
21
+ return kit.response.internalError(result.error.message)
22
+ }
23
+
24
+ return kit.response.ok(result.data, locals.requestId)
25
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 文件上传 API — storage 功能示例
3
+ */
4
+ import type { RequestHandler } from './$types'
5
+ import { kit } from '@h-ai/kit'
6
+ import { storage } from '@h-ai/storage'
7
+
8
+ export const POST: RequestHandler = async ({ request, locals }) => {
9
+ const formData = await request.formData()
10
+ const file = formData.get('file') as File | null
11
+
12
+ if (!file) {
13
+ return kit.response.badRequest('No file provided')
14
+ }
15
+
16
+ const { Buffer } = await import('node:buffer')
17
+ const buffer = Buffer.from(await file.arrayBuffer())
18
+ const key = `uploads/${Date.now()}-${file.name}`
19
+
20
+ const result = await storage.put(key, buffer, {
21
+ contentType: file.type,
22
+ metadata: { originalName: file.name },
23
+ })
24
+
25
+ if (!result.success) {
26
+ return kit.response.internalError(result.error.message)
27
+ }
28
+
29
+ return kit.response.ok({
30
+ key,
31
+ name: file.name,
32
+ size: file.size,
33
+ type: file.type,
34
+ }, locals.requestId)
35
+ }