create-fullstack-scaffold 0.4.9 → 0.4.11

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 (139) hide show
  1. package/dist/cli/index.js +499 -100
  2. package/dist/cli/index.js.map +1 -1
  3. package/package.json +2 -2
  4. package/template/CLAUDE.md +18 -6
  5. package/template/drizzle/0004_add_merchants_products.sql +30 -0
  6. package/template/drizzle/meta/_journal.json +7 -0
  7. package/template/eslint-rules/module-boundary.js +6 -2
  8. package/template/eslint-rules/no-cross-module-service-import.js +105 -0
  9. package/template/eslint-rules/no-disable-type-safe-client.js +5 -0
  10. package/template/eslint.config.js +9 -0
  11. package/template/lint-scripts/config/project.config.ts +21 -0
  12. package/template/lint-scripts/validate-all.ts +48 -12
  13. package/template/lint-scripts/validators/index.ts +29 -0
  14. package/template/lint-scripts/validators/module-public-api.validator.ts +103 -0
  15. package/template/lint-scripts/validators/schema-uniqueness.validator.ts +147 -0
  16. package/template/modules.config.ts +2 -0
  17. package/template/package.json +2 -0
  18. package/template/playwright.config.ts +14 -0
  19. package/template/src/admin/App.tsx +40 -3
  20. package/template/src/admin/components/LanguageSwitcher.tsx +22 -0
  21. package/template/src/admin/components/NotificationDrawer.tsx +108 -38
  22. package/template/src/admin/components/StatsCard.tsx +3 -1
  23. package/template/src/admin/components/ThemeToggle.tsx +28 -0
  24. package/template/src/admin/hooks/useRoles.ts +2 -2
  25. package/template/src/admin/i18n/index.ts +18 -0
  26. package/template/src/admin/i18n/locales/en-US.json +381 -0
  27. package/template/src/admin/i18n/locales/zh-CN.json +381 -0
  28. package/template/src/admin/i18n/useLanguage.ts +21 -0
  29. package/template/src/admin/layouts/Header.tsx +37 -16
  30. package/template/src/admin/layouts/Layout.tsx +7 -3
  31. package/template/src/admin/layouts/Sidebar.tsx +70 -68
  32. package/template/src/admin/main.tsx +1 -0
  33. package/template/src/admin/pages/ContentPage.tsx +68 -56
  34. package/template/src/admin/pages/DashboardPage.tsx +82 -76
  35. package/template/src/admin/pages/DisputesPage.tsx +61 -53
  36. package/template/src/admin/pages/LoginPage.tsx +41 -33
  37. package/template/src/admin/pages/OrdersPage.tsx +69 -64
  38. package/template/src/admin/pages/PermissionsPage.tsx +10 -8
  39. package/template/src/admin/pages/PluginDashboardPage.tsx +3 -1
  40. package/template/src/admin/pages/PluginManagementPage.tsx +3 -1
  41. package/template/src/admin/pages/RegisterPage.tsx +18 -16
  42. package/template/src/admin/pages/RolesPage.tsx +51 -49
  43. package/template/src/admin/pages/SettingsPage.tsx +22 -22
  44. package/template/src/admin/pages/SystemLogsPage.tsx +32 -30
  45. package/template/src/admin/pages/TicketsPage.tsx +67 -59
  46. package/template/src/admin/pages/UsersPage.tsx +63 -53
  47. package/template/src/admin/pages/__tests__/RolesPage.test.tsx +2 -2
  48. package/template/src/admin/stores/themeStore.ts +32 -0
  49. package/template/src/cli/modules/admin/index.ts +200 -0
  50. package/template/src/cli/modules/captcha/index.ts +40 -0
  51. package/template/src/cli/modules/chat/index.ts +21 -0
  52. package/template/src/cli/modules/content/index.ts +144 -0
  53. package/template/src/cli/modules/dispute/index.ts +150 -0
  54. package/template/src/cli/modules/file/index.ts +55 -0
  55. package/template/src/cli/modules/index.ts +30 -5
  56. package/template/src/cli/modules/order/index.ts +170 -0
  57. package/template/src/cli/modules/permission/index.ts +195 -0
  58. package/template/src/cli/modules/tenant/index.ts +142 -0
  59. package/template/src/cli/modules/ticket/index.ts +167 -0
  60. package/template/src/cli/rpc/client.ts +2 -2
  61. package/template/src/client/index.css +73 -0
  62. package/template/src/merchant/App.tsx +2 -0
  63. package/template/src/merchant/components/MerchantGuard.tsx +2 -6
  64. package/template/src/merchant/components/__tests__/MerchantGuard.test.tsx +117 -0
  65. package/template/src/merchant/layouts/Header.tsx +2 -2
  66. package/template/src/merchant/pages/DashboardPage.tsx +2 -2
  67. package/template/src/merchant/pages/DisputesPage.tsx +14 -10
  68. package/template/src/merchant/pages/LoginPage.tsx +49 -0
  69. package/template/src/merchant/pages/OrdersPage.tsx +17 -10
  70. package/template/src/merchant/pages/ProductsPage.tsx +31 -8
  71. package/template/src/merchant/pages/SettingsPage.tsx +3 -7
  72. package/template/src/merchant/stores/__tests__/merchantStore.test.ts +317 -0
  73. package/template/src/merchant/stores/merchantStore.ts +24 -27
  74. package/template/src/server/db/schema/index.ts +2 -0
  75. package/template/src/server/db/schema/merchants.ts +26 -0
  76. package/template/src/server/db/schema/products.ts +25 -0
  77. package/template/src/server/db/test-setup.ts +237 -0
  78. package/template/src/server/middleware/__tests__/audit-log.test.ts +144 -0
  79. package/template/src/server/middleware/__tests__/cors.test.ts +84 -0
  80. package/template/src/server/middleware/__tests__/logger.test.ts +117 -0
  81. package/template/src/server/middleware/__tests__/rate-limit.test.ts +73 -0
  82. package/template/src/server/middleware/__tests__/realtime-env.test.ts +56 -0
  83. package/template/src/server/middleware/__tests__/tenant-isolation.test.ts +150 -0
  84. package/template/src/server/module-admin/__tests__/admin-routes.test.ts +1 -1
  85. package/template/src/server/module-admin/module.ts +4 -0
  86. package/template/src/server/module-admin/routes/admin-notification-routes.ts +3 -3
  87. package/template/src/server/module-admin/routes/user-management-routes.ts +2 -2
  88. package/template/src/server/module-auth/__tests__/auth-routes.test.ts +315 -0
  89. package/template/src/server/module-auth/__tests__/profile-routes.test.ts +83 -0
  90. package/template/src/server/module-auth/module.ts +2 -0
  91. package/template/src/server/module-content/module.ts +1 -0
  92. package/template/src/server/module-content/routes/content-routes.ts +2 -2
  93. package/template/src/server/module-dispute/module.ts +1 -0
  94. package/template/src/server/module-dispute/routes/dispute-routes.ts +2 -2
  95. package/template/src/server/module-merchant/__tests__/merchant-routes.test.ts +218 -0
  96. package/template/src/server/module-merchant/__tests__/merchant-service.test.ts +183 -0
  97. package/template/src/server/module-merchant/index.ts +10 -0
  98. package/template/src/server/module-merchant/module.ts +33 -0
  99. package/template/src/server/module-merchant/routes/merchant-routes.ts +138 -0
  100. package/template/src/server/module-merchant/services/merchant-service.ts +292 -0
  101. package/template/src/server/module-notifications/index.ts +18 -0
  102. package/template/src/server/module-notifications/module.ts +2 -0
  103. package/template/src/server/module-order/module.ts +1 -0
  104. package/template/src/server/module-order/routes/order-routes.ts +2 -2
  105. package/template/src/server/module-permission/routes/role-routes.ts +3 -3
  106. package/template/src/server/module-plugin/__tests__/admin-category-service.test.ts +181 -0
  107. package/template/src/server/module-plugin/__tests__/admin-plugin-service.test.ts +277 -0
  108. package/template/src/server/module-plugin/__tests__/admin-stats-service.test.ts +170 -0
  109. package/template/src/server/module-plugin/__tests__/plugin-review-service.test.ts +260 -0
  110. package/template/src/server/module-plugin/__tests__/plugin-seed-service.test.ts +170 -0
  111. package/template/src/server/module-plugin/module.ts +3 -0
  112. package/template/src/server/module-tenant/__tests__/tenant-routes.test.ts +142 -0
  113. package/template/src/server/module-tenant/__tests__/tenant-service.test.ts +152 -0
  114. package/template/src/server/module-tenant/module.ts +1 -0
  115. package/template/src/server/module-tenant/services/tenant-service.ts +1 -37
  116. package/template/src/server/module-ticket/module.ts +1 -0
  117. package/template/src/server/module-ticket/routes/ticket-routes.ts +2 -2
  118. package/template/src/server/module-todos/module.ts +3 -0
  119. package/template/src/server/route-registry.ts +4 -0
  120. package/template/src/server/utils/__tests__/date.test.ts +130 -0
  121. package/template/src/server/utils/__tests__/env.test.ts +25 -0
  122. package/template/src/server/utils/__tests__/generate.test.ts +82 -0
  123. package/template/src/server/utils/__tests__/id-helpers.test.ts +29 -0
  124. package/template/src/server/utils/__tests__/json.test.ts +49 -0
  125. package/template/src/server/utils/__tests__/permission-utils.test.ts +26 -0
  126. package/template/src/server/utils/__tests__/uuid.test.ts +25 -0
  127. package/template/src/shared/core/module-manifest.ts +15 -0
  128. package/template/src/shared/modules/admin/schemas.ts +1 -1
  129. package/template/src/shared/modules/content/schemas.ts +2 -2
  130. package/template/src/shared/modules/dispute/schemas.ts +2 -2
  131. package/template/src/shared/modules/index.ts +181 -1
  132. package/template/src/shared/modules/merchant/index.ts +12 -0
  133. package/template/src/shared/modules/merchant/schemas.ts +63 -0
  134. package/template/src/shared/modules/order/schemas.ts +2 -2
  135. package/template/src/shared/modules/permission/index.ts +1 -1
  136. package/template/src/shared/modules/permission/schemas.ts +1 -1
  137. package/template/src/shared/modules/role/schemas.ts +2 -2
  138. package/template/src/shared/modules/ticket/schemas.ts +2 -2
  139. package/template/src/shared/schemas/index.ts +74 -2
@@ -0,0 +1,200 @@
1
+ import type { SiteInstance } from '@dyyz1993/xcli-core'
2
+ import { ok, fail } from '@dyyz1993/xcli-core'
3
+ import { z } from 'zod'
4
+ import { getClient } from '@cli/utils/api'
5
+
6
+ export function registerAdminCommands(site: SiteInstance) {
7
+ site.command('dashboard', {
8
+ description: 'Get dashboard statistics',
9
+ parameters: z.object({}),
10
+ handler: async () => {
11
+ try {
12
+ const client = getClient()
13
+ const res = await client.api.admin.dashboard.stats.$get()
14
+ const data = await res.json()
15
+ return ok(data)
16
+ } catch (err) {
17
+ return fail(err instanceof Error ? err.message : 'Failed to get dashboard stats')
18
+ }
19
+ },
20
+ })
21
+
22
+ site.command('stats', {
23
+ description: 'Get system statistics',
24
+ parameters: z.object({}),
25
+ handler: async () => {
26
+ try {
27
+ const client = getClient()
28
+ const res = await client.api.admin.stats.$get()
29
+ const data = await res.json()
30
+ return ok(data)
31
+ } catch (err) {
32
+ return fail(err instanceof Error ? err.message : 'Failed to get system stats')
33
+ }
34
+ },
35
+ })
36
+
37
+ site.command('health', {
38
+ description: 'Check system health',
39
+ parameters: z.object({}),
40
+ handler: async () => {
41
+ try {
42
+ const client = getClient()
43
+ const res = await client.api.admin.health.$get()
44
+ const data = await res.json()
45
+ return ok(data)
46
+ } catch (err) {
47
+ return fail(err instanceof Error ? err.message : 'Failed to check health')
48
+ }
49
+ },
50
+ })
51
+
52
+ site.command('list-users', {
53
+ description: 'List all users',
54
+ parameters: z.object({}),
55
+ handler: async () => {
56
+ try {
57
+ const client = getClient()
58
+ const res = await client.api.admin.users.$get()
59
+ const data = await res.json()
60
+ return ok(data)
61
+ } catch (err) {
62
+ return fail(err instanceof Error ? err.message : 'Failed to list users')
63
+ }
64
+ },
65
+ })
66
+
67
+ site.command('get-user', {
68
+ description: 'Get a user by ID',
69
+ parameters: z.object({
70
+ id: z.string().describe('User ID'),
71
+ }),
72
+ handler: async (params: unknown) => {
73
+ const p = params as { id: string }
74
+ try {
75
+ const client = getClient()
76
+ const res = await client.api.admin.users[':id'].$get({ param: { id: p.id } })
77
+ const data = await res.json()
78
+ return ok(data)
79
+ } catch (err) {
80
+ return fail(err instanceof Error ? err.message : 'Failed to get user')
81
+ }
82
+ },
83
+ })
84
+
85
+ site.command('create-user', {
86
+ description: 'Create a new user',
87
+ parameters: z.object({
88
+ username: z.string().min(1).describe('Username'),
89
+ email: z.string().email().describe('Email'),
90
+ password: z.string().min(6).describe('Password'),
91
+ role: z.enum(['super_admin', 'customer_service', 'user']).default('user').describe('Role'),
92
+ }),
93
+ handler: async (params: unknown) => {
94
+ const p = params as {
95
+ username: string
96
+ email: string
97
+ password: string
98
+ role: 'super_admin' | 'customer_service' | 'user'
99
+ }
100
+ try {
101
+ const client = getClient()
102
+ const res = await client.api.admin.users.$post({
103
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
104
+ json: {
105
+ username: p.username,
106
+ email: p.email,
107
+ password: p.password,
108
+ role: p.role,
109
+ } as any,
110
+ })
111
+ const data = await res.json()
112
+ return ok(data, ['User created'])
113
+ } catch (err) {
114
+ return fail(err instanceof Error ? err.message : 'Failed to create user')
115
+ }
116
+ },
117
+ })
118
+
119
+ site.command('update-user', {
120
+ description: 'Update a user',
121
+ parameters: z.object({
122
+ id: z.string().describe('User ID'),
123
+ username: z.string().optional().describe('New username'),
124
+ email: z.string().email().optional().describe('New email'),
125
+ role: z.enum(['super_admin', 'customer_service', 'user']).optional().describe('New role'),
126
+ }),
127
+ handler: async (params: unknown) => {
128
+ const p = params as {
129
+ id: string
130
+ username?: string
131
+ email?: string
132
+ role?: 'super_admin' | 'customer_service' | 'user'
133
+ }
134
+ const { id, ...rest } = p
135
+ const body: Record<string, string | undefined> = {}
136
+ if (rest.username) body.username = rest.username
137
+ if (rest.email) body.email = rest.email
138
+ if (rest.role) body.role = rest.role
139
+ try {
140
+ const client = getClient()
141
+ const res = await client.api.admin.users[':id'].$put({ param: { id }, json: body as Record<string, string | null | undefined> })
142
+ const data = await res.json()
143
+ return ok(data, ['User updated'])
144
+ } catch (err) {
145
+ return fail(err instanceof Error ? err.message : 'Failed to update user')
146
+ }
147
+ },
148
+ })
149
+
150
+ site.command('delete-user', {
151
+ description: 'Delete a user',
152
+ parameters: z.object({
153
+ id: z.string().describe('User ID'),
154
+ }),
155
+ handler: async (params: unknown) => {
156
+ const p = params as { id: string }
157
+ try {
158
+ const client = getClient()
159
+ const res = await client.api.admin.users[':id'].$delete({ param: { id: p.id } })
160
+ const data = await res.json()
161
+ return ok(data, ['User deleted'])
162
+ } catch (err) {
163
+ return fail(err instanceof Error ? err.message : 'Failed to delete user')
164
+ }
165
+ },
166
+ })
167
+
168
+ site.command('activity', {
169
+ description: 'Get recent activity',
170
+ parameters: z.object({
171
+ limit: z.coerce.number().default(10).describe('Limit results'),
172
+ }),
173
+ handler: async (params: unknown) => {
174
+ const p = params as { limit: number }
175
+ try {
176
+ const client = getClient()
177
+ const res = await client.api.admin.activity.$get({ query: { limit: String(p.limit) } })
178
+ const data = await res.json()
179
+ return ok(data)
180
+ } catch (err) {
181
+ return fail(err instanceof Error ? err.message : 'Failed to get activity')
182
+ }
183
+ },
184
+ })
185
+
186
+ site.command('clear-todos', {
187
+ description: 'Clear all todos (dangerous)',
188
+ parameters: z.object({}),
189
+ handler: async () => {
190
+ try {
191
+ const client = getClient()
192
+ const res = await client.api.admin.todos.all.$delete()
193
+ const data = await res.json()
194
+ return ok(data, ['All todos cleared'])
195
+ } catch (err) {
196
+ return fail(err instanceof Error ? err.message : 'Failed to clear todos')
197
+ }
198
+ },
199
+ })
200
+ }
@@ -0,0 +1,40 @@
1
+ import type { SiteInstance } from '@dyyz1993/xcli-core'
2
+ import { ok, fail } from '@dyyz1993/xcli-core'
3
+ import { z } from 'zod'
4
+ import { getClient } from '@cli/utils/api'
5
+
6
+ export function registerCaptchaCommands(site: SiteInstance) {
7
+ site.command('get', {
8
+ description: 'Get a new captcha',
9
+ parameters: z.object({}),
10
+ handler: async () => {
11
+ try {
12
+ const client = getClient()
13
+ const res = await client.api.captcha.$get()
14
+ const data = await res.json()
15
+ return ok(data)
16
+ } catch (err) {
17
+ return fail(err instanceof Error ? err.message : 'Failed to get captcha')
18
+ }
19
+ },
20
+ })
21
+
22
+ site.command('verify', {
23
+ description: 'Verify a captcha code',
24
+ parameters: z.object({
25
+ id: z.string().describe('Captcha ID'),
26
+ code: z.string().min(1).describe('Captcha code'),
27
+ }),
28
+ handler: async (params: unknown) => {
29
+ const p = params as { id: string; code: string }
30
+ try {
31
+ const client = getClient()
32
+ const res = await client.api['verify-captcha'].$post({ json: p })
33
+ const data = await res.json()
34
+ return ok(data)
35
+ } catch (err) {
36
+ return fail(err instanceof Error ? err.message : 'Failed to verify captcha')
37
+ }
38
+ },
39
+ })
40
+ }
@@ -0,0 +1,21 @@
1
+ import type { SiteInstance } from '@dyyz1993/xcli-core'
2
+ import { ok, fail } from '@dyyz1993/xcli-core'
3
+ import { z } from 'zod'
4
+ import { getClient } from '@cli/utils/api'
5
+
6
+ export function registerChatCommands(site: SiteInstance) {
7
+ site.command('status', {
8
+ description: 'Get WebSocket connection status',
9
+ parameters: z.object({}),
10
+ handler: async () => {
11
+ try {
12
+ const client = getClient()
13
+ const res = await client.api.chat.ws.status.$get()
14
+ const data = await res.json()
15
+ return ok(data)
16
+ } catch (err) {
17
+ return fail(err instanceof Error ? err.message : 'Failed to get chat status')
18
+ }
19
+ },
20
+ })
21
+ }
@@ -0,0 +1,144 @@
1
+ import type { SiteInstance } from '@dyyz1993/xcli-core'
2
+ import { ok, fail } from '@dyyz1993/xcli-core'
3
+ import { z } from 'zod'
4
+ import { getClient } from '@cli/utils/api'
5
+
6
+ export function registerContentCommands(site: SiteInstance) {
7
+ site.command('list', {
8
+ description: 'List all contents',
9
+ parameters: z.object({
10
+ limit: z.coerce.number().default(20).describe('Limit results'),
11
+ offset: z.coerce.number().default(0).describe('Offset'),
12
+ }),
13
+ handler: async (params: unknown) => {
14
+ const p = params as { limit: number; offset: number }
15
+ try {
16
+ const client = getClient()
17
+ const res = await client.api.contents.$get({
18
+ query: { limit: String(p.limit), offset: String(p.offset) },
19
+ })
20
+ const data = await res.json()
21
+ return ok(data)
22
+ } catch (err) {
23
+ return fail(err instanceof Error ? err.message : 'Failed to list contents')
24
+ }
25
+ },
26
+ })
27
+
28
+ site.command('get', {
29
+ description: 'Get content by ID',
30
+ parameters: z.object({
31
+ id: z.string().describe('Content ID'),
32
+ }),
33
+ handler: async (params: unknown) => {
34
+ const p = params as { id: string }
35
+ try {
36
+ const client = getClient()
37
+ const res = await client.api.contents[':id'].$get({ param: { id: p.id } })
38
+ const data = await res.json()
39
+ return ok(data)
40
+ } catch (err) {
41
+ return fail(err instanceof Error ? err.message : 'Failed to get content')
42
+ }
43
+ },
44
+ })
45
+
46
+ site.command('create', {
47
+ description: 'Create new content',
48
+ parameters: z.object({
49
+ title: z.string().min(1).describe('Content title'),
50
+ content: z.string().min(1).describe('Content body'),
51
+ category: z.enum(['article', 'announcement', 'tutorial', 'news', 'policy']).default('article').describe('Category'),
52
+ tags: z.string().optional().describe('Comma-separated tags'),
53
+ }),
54
+ handler: async (params: unknown) => {
55
+ const p = params as { title: string; content: string; category: string; tags?: string }
56
+ try {
57
+ const client = getClient()
58
+ const tags = p.tags ? p.tags.split(',') : undefined
59
+ const res = await client.api.contents.$post({
60
+ json: { title: p.title, content: p.content, category: p.category as 'article' | 'announcement' | 'tutorial' | 'news' | 'policy', tags },
61
+ })
62
+ const data = await res.json()
63
+ return ok(data, ['Content created'])
64
+ } catch (err) {
65
+ return fail(err instanceof Error ? err.message : 'Failed to create content')
66
+ }
67
+ },
68
+ })
69
+
70
+ site.command('update', {
71
+ description: 'Update content',
72
+ parameters: z.object({
73
+ id: z.string().describe('Content ID'),
74
+ title: z.string().optional().describe('New title'),
75
+ content: z.string().optional().describe('New body'),
76
+ }),
77
+ handler: async (params: unknown) => {
78
+ const p = params as { id: string; title?: string; content?: string }
79
+ const { id, ...body } = p
80
+ try {
81
+ const client = getClient()
82
+ const res = await client.api.contents[':id'].$put({ param: { id }, json: body })
83
+ const data = await res.json()
84
+ return ok(data, ['Content updated'])
85
+ } catch (err) {
86
+ return fail(err instanceof Error ? err.message : 'Failed to update content')
87
+ }
88
+ },
89
+ })
90
+
91
+ site.command('delete', {
92
+ description: 'Delete content',
93
+ parameters: z.object({
94
+ id: z.string().describe('Content ID'),
95
+ }),
96
+ handler: async (params: unknown) => {
97
+ const p = params as { id: string }
98
+ try {
99
+ const client = getClient()
100
+ const res = await client.api.contents[':id'].$delete({ param: { id: p.id } })
101
+ const data = await res.json()
102
+ return ok(data, ['Content deleted'])
103
+ } catch (err) {
104
+ return fail(err instanceof Error ? err.message : 'Failed to delete content')
105
+ }
106
+ },
107
+ })
108
+
109
+ site.command('publish', {
110
+ description: 'Publish content',
111
+ parameters: z.object({
112
+ id: z.string().describe('Content ID'),
113
+ }),
114
+ handler: async (params: unknown) => {
115
+ const p = params as { id: string }
116
+ try {
117
+ const client = getClient()
118
+ const res = await client.api.contents[':id'].publish.$put({ param: { id: p.id } })
119
+ const data = await res.json()
120
+ return ok(data, ['Content published'])
121
+ } catch (err) {
122
+ return fail(err instanceof Error ? err.message : 'Failed to publish content')
123
+ }
124
+ },
125
+ })
126
+
127
+ site.command('archive', {
128
+ description: 'Archive content',
129
+ parameters: z.object({
130
+ id: z.string().describe('Content ID'),
131
+ }),
132
+ handler: async (params: unknown) => {
133
+ const p = params as { id: string }
134
+ try {
135
+ const client = getClient()
136
+ const res = await client.api.contents[':id'].archive.$put({ param: { id: p.id } })
137
+ const data = await res.json()
138
+ return ok(data, ['Content archived'])
139
+ } catch (err) {
140
+ return fail(err instanceof Error ? err.message : 'Failed to archive content')
141
+ }
142
+ },
143
+ })
144
+ }
@@ -0,0 +1,150 @@
1
+ import type { SiteInstance } from '@dyyz1993/xcli-core'
2
+ import { ok, fail } from '@dyyz1993/xcli-core'
3
+ import { z } from 'zod'
4
+ import { getClient } from '@cli/utils/api'
5
+
6
+ export function registerDisputeCommands(site: SiteInstance) {
7
+ site.command('list', {
8
+ description: 'List all disputes',
9
+ parameters: z.object({
10
+ limit: z.coerce.number().default(20).describe('Limit results'),
11
+ offset: z.coerce.number().default(0).describe('Offset'),
12
+ }),
13
+ handler: async (params: unknown) => {
14
+ const p = params as { limit: number; offset: number }
15
+ try {
16
+ const client = getClient()
17
+ const res = await client.api.disputes.$get({
18
+ query: { limit: String(p.limit), offset: String(p.offset) },
19
+ })
20
+ const data = await res.json()
21
+ return ok(data)
22
+ } catch (err) {
23
+ return fail(err instanceof Error ? err.message : 'Failed to list disputes')
24
+ }
25
+ },
26
+ })
27
+
28
+ site.command('get', {
29
+ description: 'Get dispute by ID',
30
+ parameters: z.object({
31
+ id: z.string().describe('Dispute ID'),
32
+ }),
33
+ handler: async (params: unknown) => {
34
+ const p = params as { id: string }
35
+ try {
36
+ const client = getClient()
37
+ const res = await client.api.disputes[':id'].$get({ param: { id: p.id } })
38
+ const data = await res.json()
39
+ return ok(data)
40
+ } catch (err) {
41
+ return fail(err instanceof Error ? err.message : 'Failed to get dispute')
42
+ }
43
+ },
44
+ })
45
+
46
+ site.command('create', {
47
+ description: 'Create a new dispute',
48
+ parameters: z.object({
49
+ 'order-id': z.string().describe('Order ID'),
50
+ 'order-no': z.string().describe('Order number'),
51
+ 'customer-name': z.string().describe('Customer name'),
52
+ 'customer-email': z.string().email().describe('Customer email'),
53
+ type: z.enum(['refund', 'product_quality', 'service_quality', 'delivery', 'other']).default('other').describe('Type'),
54
+ description: z.string().min(1).describe('Description'),
55
+ amount: z.coerce.number().positive().describe('Amount'),
56
+ }),
57
+ handler: async (params: unknown) => {
58
+ const p = params as {
59
+ 'order-id': string
60
+ 'order-no': string
61
+ 'customer-name': string
62
+ 'customer-email': string
63
+ type: string
64
+ description: string
65
+ amount: number
66
+ }
67
+ try {
68
+ const client = getClient()
69
+ const res = await client.api.disputes.$post({
70
+ json: {
71
+ orderId: p['order-id'],
72
+ orderNo: p['order-no'],
73
+ customerName: p['customer-name'],
74
+ customerEmail: p['customer-email'],
75
+ type: p.type as 'refund' | 'product_quality' | 'service_quality' | 'delivery' | 'other',
76
+ description: p.description,
77
+ amount: p.amount,
78
+ },
79
+ })
80
+ const data = await res.json()
81
+ return ok(data, ['Dispute created'])
82
+ } catch (err) {
83
+ return fail(err instanceof Error ? err.message : 'Failed to create dispute')
84
+ }
85
+ },
86
+ })
87
+
88
+ site.command('update', {
89
+ description: 'Update a dispute status',
90
+ parameters: z.object({
91
+ id: z.string().describe('Dispute ID'),
92
+ status: z.enum(['pending', 'resolved', 'investigating', 'rejected']).optional().describe('New status'),
93
+ }),
94
+ handler: async (params: unknown) => {
95
+ const p = params as { id: string; status?: string }
96
+ const { id, ...rest } = p
97
+ const body: Record<string, string | null> = {}
98
+ if (rest.status) body.status = rest.status
99
+ try {
100
+ const client = getClient()
101
+ const res = await client.api.disputes[':id'].$put({ param: { id }, json: body })
102
+ const data = await res.json()
103
+ return ok(data, ['Dispute updated'])
104
+ } catch (err) {
105
+ return fail(err instanceof Error ? err.message : 'Failed to update dispute')
106
+ }
107
+ },
108
+ })
109
+
110
+ site.command('delete', {
111
+ description: 'Delete a dispute',
112
+ parameters: z.object({
113
+ id: z.string().describe('Dispute ID'),
114
+ }),
115
+ handler: async (params: unknown) => {
116
+ const p = params as { id: string }
117
+ try {
118
+ const client = getClient()
119
+ const res = await client.api.disputes[':id'].$delete({ param: { id: p.id } })
120
+ const data = await res.json()
121
+ return ok(data, ['Dispute deleted'])
122
+ } catch (err) {
123
+ return fail(err instanceof Error ? err.message : 'Failed to delete dispute')
124
+ }
125
+ },
126
+ })
127
+
128
+ site.command('resolve', {
129
+ description: 'Resolve a dispute',
130
+ parameters: z.object({
131
+ id: z.string().describe('Dispute ID'),
132
+ resolution: z.string().min(1).describe('Resolution notes'),
133
+ 'resolved-by': z.string().describe('Resolver name'),
134
+ }),
135
+ handler: async (params: unknown) => {
136
+ const p = params as { id: string; resolution: string; 'resolved-by': string }
137
+ try {
138
+ const client = getClient()
139
+ const res = await client.api.disputes[':id'].resolve.$put({
140
+ param: { id: p.id },
141
+ json: { resolution: p.resolution, resolvedBy: p['resolved-by'] },
142
+ })
143
+ const data = await res.json()
144
+ return ok(data, ['Dispute resolved'])
145
+ } catch (err) {
146
+ return fail(err instanceof Error ? err.message : 'Failed to resolve dispute')
147
+ }
148
+ },
149
+ })
150
+ }
@@ -0,0 +1,55 @@
1
+ import type { SiteInstance } from '@dyyz1993/xcli-core'
2
+ import { ok, fail } from '@dyyz1993/xcli-core'
3
+ import { z } from 'zod'
4
+ import { getClient } from '@cli/utils/api'
5
+
6
+ export function registerFileCommands(site: SiteInstance) {
7
+ site.command('generate-url', {
8
+ description: 'Generate a file URL',
9
+ parameters: z.object({
10
+ namespace: z.string().describe('File namespace'),
11
+ filename: z.string().describe('File name'),
12
+ private: z.boolean().default(false).describe('Generate private URL'),
13
+ 'expiry-seconds': z.coerce.number().default(3600).describe('URL expiry in seconds'),
14
+ }),
15
+ handler: async (params: unknown) => {
16
+ const p = params as { namespace: string; filename: string; private: boolean; 'expiry-seconds': number }
17
+ try {
18
+ const client = getClient()
19
+ const res = await client.api['generate-url'].$post({
20
+ json: {
21
+ namespace: p.namespace,
22
+ filename: p.filename,
23
+ isPrivate: p.private,
24
+ expirySeconds: p['expiry-seconds'],
25
+ },
26
+ })
27
+ const data = await res.json()
28
+ return ok(data)
29
+ } catch (err) {
30
+ return fail(err instanceof Error ? err.message : 'Failed to generate URL')
31
+ }
32
+ },
33
+ })
34
+
35
+ site.command('info', {
36
+ description: 'Check if a public file exists',
37
+ parameters: z.object({
38
+ namespace: z.string().describe('File namespace'),
39
+ filename: z.string().describe('File name'),
40
+ }),
41
+ handler: async (params: unknown) => {
42
+ const p = params as { namespace: string; filename: string }
43
+ try {
44
+ const client = getClient()
45
+ const res = await client.api.public[':namespace'][':filename'].$get({
46
+ param: { namespace: p.namespace, filename: p.filename },
47
+ })
48
+ const data = await res.json()
49
+ return ok(data)
50
+ } catch (err) {
51
+ return fail(err instanceof Error ? err.message : 'Failed to get file info')
52
+ }
53
+ },
54
+ })
55
+ }
@@ -4,15 +4,20 @@ import { registerNotificationCommands } from './notification'
4
4
  import { registerConfigCommands } from './config'
5
5
  import { registerPluginCommands } from './plugin'
6
6
  import { registerAuthCommands } from './auth'
7
+ import { registerAdminCommands } from './admin'
8
+ import { registerCaptchaCommands } from './captcha'
9
+ import { registerChatCommands } from './chat'
10
+ import { registerContentCommands } from './content'
11
+ import { registerDisputeCommands } from './dispute'
12
+ import { registerFileCommands } from './file'
13
+ import { registerOrderCommands } from './order'
14
+ import { registerPermissionCommands } from './permission'
15
+ import { registerTenantCommands } from './tenant'
16
+ import { registerTicketCommands } from './ticket'
7
17
 
8
- /**
9
- * Register all builtin CLI commands to xcli-core.
10
- * Each register function receives a SiteInstance for command registration.
11
- */
12
18
  export function registerBuiltinCommands(app: Core) {
13
19
  const api = app.loader.getAPI()
14
20
 
15
- // Create a builtin site representing the local server
16
21
  const site = api.createSite({
17
22
  name: 'local-server',
18
23
  url: 'http://localhost:3010',
@@ -23,6 +28,16 @@ export function registerBuiltinCommands(app: Core) {
23
28
  registerConfigCommands(site)
24
29
  registerPluginCommands(site)
25
30
  registerAuthCommands(site)
31
+ registerAdminCommands(site)
32
+ registerCaptchaCommands(site)
33
+ registerChatCommands(site)
34
+ registerContentCommands(site)
35
+ registerDisputeCommands(site)
36
+ registerFileCommands(site)
37
+ registerOrderCommands(site)
38
+ registerPermissionCommands(site)
39
+ registerTenantCommands(site)
40
+ registerTicketCommands(site)
26
41
  }
27
42
 
28
43
  export {
@@ -31,4 +46,14 @@ export {
31
46
  registerConfigCommands,
32
47
  registerPluginCommands,
33
48
  registerAuthCommands,
49
+ registerAdminCommands,
50
+ registerCaptchaCommands,
51
+ registerChatCommands,
52
+ registerContentCommands,
53
+ registerDisputeCommands,
54
+ registerFileCommands,
55
+ registerOrderCommands,
56
+ registerPermissionCommands,
57
+ registerTenantCommands,
58
+ registerTicketCommands,
34
59
  }