create-fullstack-scaffold 0.4.10 → 0.4.12

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 (31) hide show
  1. package/dist/cli/index.js.map +1 -1
  2. package/package.json +1 -1
  3. package/template/src/admin/App.tsx +4 -1
  4. package/template/src/admin/components/NotificationDrawer.tsx +13 -42
  5. package/template/src/admin/components/ThemeToggle.tsx +2 -2
  6. package/template/src/admin/layouts/Header.tsx +12 -9
  7. package/template/src/admin/pages/ContentPage.tsx +8 -10
  8. package/template/src/admin/pages/DashboardPage.tsx +30 -17
  9. package/template/src/admin/pages/DisputesPage.tsx +20 -14
  10. package/template/src/admin/pages/LoginPage.tsx +14 -3
  11. package/template/src/admin/pages/OrdersPage.tsx +10 -6
  12. package/template/src/admin/pages/PermissionsPage.tsx +1 -3
  13. package/template/src/admin/pages/RegisterPage.tsx +6 -4
  14. package/template/src/admin/pages/RolesPage.tsx +10 -3
  15. package/template/src/admin/pages/SystemLogsPage.tsx +12 -4
  16. package/template/src/admin/pages/TicketsPage.tsx +15 -15
  17. package/template/src/admin/pages/UsersPage.tsx +10 -6
  18. package/template/src/admin/stores/themeStore.ts +1 -1
  19. package/template/src/cli/modules/admin/index.ts +192 -0
  20. package/template/src/cli/modules/captcha/index.ts +40 -0
  21. package/template/src/cli/modules/chat/index.ts +21 -0
  22. package/template/src/cli/modules/content/index.ts +152 -0
  23. package/template/src/cli/modules/dispute/index.ts +156 -0
  24. package/template/src/cli/modules/file/index.ts +60 -0
  25. package/template/src/cli/modules/index.ts +30 -5
  26. package/template/src/cli/modules/order/index.ts +183 -0
  27. package/template/src/cli/modules/permission/index.ts +201 -0
  28. package/template/src/cli/modules/tenant/index.ts +142 -0
  29. package/template/src/cli/modules/ticket/index.ts +178 -0
  30. package/template/src/cli/rpc/client.ts +2 -2
  31. package/template/src/client/index.css +22 -24
@@ -1,4 +1,4 @@
1
- import { useState, useEffect } from 'react'
1
+ import { useState, useEffect, useCallback } from 'react'
2
2
  import {
3
3
  Table,
4
4
  Card,
@@ -37,18 +37,20 @@ export const UsersPage: React.FC = () => {
37
37
  const [form] = Form.useForm<UserFormData>()
38
38
  const { roleLabels } = useRoleLabels()
39
39
 
40
- const fetchUsers = async () => {
40
+ const fetchUsers = useCallback(async () => {
41
41
  try {
42
- const data = await api(apiClient.api.admin.users.$get()).withLoading(t('users.loading')).json()
42
+ const data = await api(apiClient.api.admin.users.$get())
43
+ .withLoading(t('users.loading'))
44
+ .json()
43
45
  setUsers(data)
44
46
  } catch {
45
47
  // handled by api-request
46
48
  }
47
- }
49
+ }, [t])
48
50
 
49
51
  useEffect(() => {
50
52
  fetchUsers()
51
- }, [])
53
+ }, [fetchUsers])
52
54
 
53
55
  const handleCreate = () => {
54
56
  setEditingUser(null)
@@ -271,7 +273,9 @@ export const UsersPage: React.FC = () => {
271
273
  >
272
274
  <Select placeholder={t('users.rolePlaceholder')}>
273
275
  <Select.Option value={Role.SUPER_ADMIN}>{t('users.superAdmin')}</Select.Option>
274
- <Select.Option value={Role.CUSTOMER_SERVICE}>{t('users.customerService')}</Select.Option>
276
+ <Select.Option value={Role.CUSTOMER_SERVICE}>
277
+ {t('users.customerService')}
278
+ </Select.Option>
275
279
  <Select.Option value={Role.USER}>{t('users.normalUser')}</Select.Option>
276
280
  </Select>
277
281
  </Form.Item>
@@ -21,7 +21,7 @@ export const useThemeStore = create<ThemeState>()(
21
21
  {
22
22
  name: 'admin-theme',
23
23
  onRehydrateStorage: () => {
24
- return (state) => {
24
+ return state => {
25
25
  if (state) {
26
26
  document.documentElement.setAttribute('data-theme', state.mode)
27
27
  }
@@ -0,0 +1,192 @@
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
+ import type { Role } from '@shared/modules/permission'
6
+
7
+ export function registerAdminCommands(site: SiteInstance) {
8
+ site.command('dashboard', {
9
+ description: 'Get dashboard statistics',
10
+ parameters: z.object({}),
11
+ handler: async () => {
12
+ try {
13
+ const client = getClient()
14
+ const res = await client.api.admin.dashboard.stats.$get()
15
+ const data = await res.json()
16
+ return ok(data)
17
+ } catch (err) {
18
+ return fail(err instanceof Error ? err.message : 'Failed to get dashboard stats')
19
+ }
20
+ },
21
+ })
22
+
23
+ site.command('stats', {
24
+ description: 'Get system statistics',
25
+ parameters: z.object({}),
26
+ handler: async () => {
27
+ try {
28
+ const client = getClient()
29
+ const res = await client.api.admin.stats.$get()
30
+ const data = await res.json()
31
+ return ok(data)
32
+ } catch (err) {
33
+ return fail(err instanceof Error ? err.message : 'Failed to get system stats')
34
+ }
35
+ },
36
+ })
37
+
38
+ site.command('health', {
39
+ description: 'Check system health',
40
+ parameters: z.object({}),
41
+ handler: async () => {
42
+ try {
43
+ const client = getClient()
44
+ const res = await client.api.admin.health.$get()
45
+ const data = await res.json()
46
+ return ok(data)
47
+ } catch (err) {
48
+ return fail(err instanceof Error ? err.message : 'Failed to check health')
49
+ }
50
+ },
51
+ })
52
+
53
+ site.command('list-users', {
54
+ description: 'List all users',
55
+ parameters: z.object({}),
56
+ handler: async () => {
57
+ try {
58
+ const client = getClient()
59
+ const res = await client.api.admin.users.$get()
60
+ const data = await res.json()
61
+ return ok(data)
62
+ } catch (err) {
63
+ return fail(err instanceof Error ? err.message : 'Failed to list users')
64
+ }
65
+ },
66
+ })
67
+
68
+ site.command('get-user', {
69
+ description: 'Get a user by ID',
70
+ parameters: z.object({
71
+ id: z.string().describe('User ID'),
72
+ }),
73
+ handler: async (params: unknown) => {
74
+ const p = params as { id: string }
75
+ try {
76
+ const client = getClient()
77
+ const res = await client.api.admin.users[':id'].$get({ param: { id: p.id } })
78
+ const data = await res.json()
79
+ return ok(data)
80
+ } catch (err) {
81
+ return fail(err instanceof Error ? err.message : 'Failed to get user')
82
+ }
83
+ },
84
+ })
85
+
86
+ site.command('create-user', {
87
+ description: 'Create a new user',
88
+ parameters: z.object({
89
+ username: z.string().min(1).describe('Username'),
90
+ email: z.string().email().describe('Email'),
91
+ password: z.string().min(6).describe('Password'),
92
+ role: z.enum(['super_admin', 'customer_service', 'user']).default('user').describe('Role'),
93
+ }),
94
+ handler: async (params: unknown) => {
95
+ type CreateParams = { username: string; email: string; password: string; role: 'super_admin' | 'customer_service' | 'user' }
96
+ const p = params as CreateParams
97
+ try {
98
+ const client = getClient()
99
+ const res = await client.api.admin.users.$post({
100
+ json: {
101
+ username: p.username,
102
+ email: p.email,
103
+ password: p.password,
104
+ role: p.role as Role,
105
+ },
106
+ })
107
+ const data = await res.json()
108
+ return ok(data, ['User created'])
109
+ } catch (err) {
110
+ return fail(err instanceof Error ? err.message : 'Failed to create user')
111
+ }
112
+ },
113
+ })
114
+
115
+ site.command('update-user', {
116
+ description: 'Update a user',
117
+ parameters: z.object({
118
+ id: z.string().describe('User ID'),
119
+ username: z.string().optional().describe('New username'),
120
+ email: z.string().email().optional().describe('New email'),
121
+ role: z.enum(['super_admin', 'customer_service', 'user']).optional().describe('New role'),
122
+ }),
123
+ handler: async (params: unknown) => {
124
+ type UpdateParams = { id: string; username?: string; email?: string; role?: 'super_admin' | 'customer_service' | 'user' }
125
+ const p = params as UpdateParams
126
+ const { id, ...rest } = p
127
+ const body: Record<string, string | undefined> = {}
128
+ if (rest.username) body.username = rest.username
129
+ if (rest.email) body.email = rest.email
130
+ if (rest.role) body.role = rest.role
131
+ try {
132
+ const client = getClient()
133
+ const res = await client.api.admin.users[':id'].$put({ param: { id }, json: body })
134
+ const data = await res.json()
135
+ return ok(data, ['User updated'])
136
+ } catch (err) {
137
+ return fail(err instanceof Error ? err.message : 'Failed to update user')
138
+ }
139
+ },
140
+ })
141
+
142
+ site.command('delete-user', {
143
+ description: 'Delete a user',
144
+ parameters: z.object({
145
+ id: z.string().describe('User ID'),
146
+ }),
147
+ handler: async (params: unknown) => {
148
+ const p = params as { id: string }
149
+ try {
150
+ const client = getClient()
151
+ const res = await client.api.admin.users[':id'].$delete({ param: { id: p.id } })
152
+ const data = await res.json()
153
+ return ok(data, ['User deleted'])
154
+ } catch (err) {
155
+ return fail(err instanceof Error ? err.message : 'Failed to delete user')
156
+ }
157
+ },
158
+ })
159
+
160
+ site.command('activity', {
161
+ description: 'Get recent activity',
162
+ parameters: z.object({
163
+ limit: z.coerce.number().default(10).describe('Limit results'),
164
+ }),
165
+ handler: async (params: unknown) => {
166
+ const p = params as { limit: number }
167
+ try {
168
+ const client = getClient()
169
+ const res = await client.api.admin.activity.$get({ query: { limit: String(p.limit) } })
170
+ const data = await res.json()
171
+ return ok(data)
172
+ } catch (err) {
173
+ return fail(err instanceof Error ? err.message : 'Failed to get activity')
174
+ }
175
+ },
176
+ })
177
+
178
+ site.command('clear-todos', {
179
+ description: 'Clear all todos (dangerous)',
180
+ parameters: z.object({}),
181
+ handler: async () => {
182
+ try {
183
+ const client = getClient()
184
+ const res = await client.api.admin.todos.all.$delete()
185
+ const data = await res.json()
186
+ return ok(data, ['All todos cleared'])
187
+ } catch (err) {
188
+ return fail(err instanceof Error ? err.message : 'Failed to clear todos')
189
+ }
190
+ },
191
+ })
192
+ }
@@ -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,152 @@
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
52
+ .enum(['article', 'announcement', 'tutorial', 'news', 'policy'])
53
+ .default('article')
54
+ .describe('Category'),
55
+ tags: z.string().optional().describe('Comma-separated tags'),
56
+ }),
57
+ handler: async (params: unknown) => {
58
+ const p = params as { title: string; content: string; category: string; tags?: string }
59
+ try {
60
+ const client = getClient()
61
+ const tags = p.tags ? p.tags.split(',') : undefined
62
+ const res = await client.api.contents.$post({
63
+ json: {
64
+ title: p.title,
65
+ content: p.content,
66
+ category: p.category as 'article' | 'announcement' | 'tutorial' | 'news' | 'policy',
67
+ tags,
68
+ },
69
+ })
70
+ const data = await res.json()
71
+ return ok(data, ['Content created'])
72
+ } catch (err) {
73
+ return fail(err instanceof Error ? err.message : 'Failed to create content')
74
+ }
75
+ },
76
+ })
77
+
78
+ site.command('update', {
79
+ description: 'Update content',
80
+ parameters: z.object({
81
+ id: z.string().describe('Content ID'),
82
+ title: z.string().optional().describe('New title'),
83
+ content: z.string().optional().describe('New body'),
84
+ }),
85
+ handler: async (params: unknown) => {
86
+ const p = params as { id: string; title?: string; content?: string }
87
+ const { id, ...body } = p
88
+ try {
89
+ const client = getClient()
90
+ const res = await client.api.contents[':id'].$put({ param: { id }, json: body })
91
+ const data = await res.json()
92
+ return ok(data, ['Content updated'])
93
+ } catch (err) {
94
+ return fail(err instanceof Error ? err.message : 'Failed to update content')
95
+ }
96
+ },
97
+ })
98
+
99
+ site.command('delete', {
100
+ description: 'Delete content',
101
+ parameters: z.object({
102
+ id: z.string().describe('Content ID'),
103
+ }),
104
+ handler: async (params: unknown) => {
105
+ const p = params as { id: string }
106
+ try {
107
+ const client = getClient()
108
+ const res = await client.api.contents[':id'].$delete({ param: { id: p.id } })
109
+ const data = await res.json()
110
+ return ok(data, ['Content deleted'])
111
+ } catch (err) {
112
+ return fail(err instanceof Error ? err.message : 'Failed to delete content')
113
+ }
114
+ },
115
+ })
116
+
117
+ site.command('publish', {
118
+ description: 'Publish content',
119
+ parameters: z.object({
120
+ id: z.string().describe('Content ID'),
121
+ }),
122
+ handler: async (params: unknown) => {
123
+ const p = params as { id: string }
124
+ try {
125
+ const client = getClient()
126
+ const res = await client.api.contents[':id'].publish.$put({ param: { id: p.id } })
127
+ const data = await res.json()
128
+ return ok(data, ['Content published'])
129
+ } catch (err) {
130
+ return fail(err instanceof Error ? err.message : 'Failed to publish content')
131
+ }
132
+ },
133
+ })
134
+
135
+ site.command('archive', {
136
+ description: 'Archive content',
137
+ parameters: z.object({
138
+ id: z.string().describe('Content ID'),
139
+ }),
140
+ handler: async (params: unknown) => {
141
+ const p = params as { id: string }
142
+ try {
143
+ const client = getClient()
144
+ const res = await client.api.contents[':id'].archive.$put({ param: { id: p.id } })
145
+ const data = await res.json()
146
+ return ok(data, ['Content archived'])
147
+ } catch (err) {
148
+ return fail(err instanceof Error ? err.message : 'Failed to archive content')
149
+ }
150
+ },
151
+ })
152
+ }
@@ -0,0 +1,156 @@
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
54
+ .enum(['refund', 'product_quality', 'service_quality', 'delivery', 'other'])
55
+ .default('other')
56
+ .describe('Type'),
57
+ description: z.string().min(1).describe('Description'),
58
+ amount: z.coerce.number().positive().describe('Amount'),
59
+ }),
60
+ handler: async (params: unknown) => {
61
+ const p = params as {
62
+ 'order-id': string
63
+ 'order-no': string
64
+ 'customer-name': string
65
+ 'customer-email': string
66
+ type: string
67
+ description: string
68
+ amount: number
69
+ }
70
+ try {
71
+ const client = getClient()
72
+ const res = await client.api.disputes.$post({
73
+ json: {
74
+ orderId: p['order-id'],
75
+ orderNo: p['order-no'],
76
+ customerName: p['customer-name'],
77
+ customerEmail: p['customer-email'],
78
+ type: p.type as 'refund' | 'product_quality' | 'service_quality' | 'delivery' | 'other',
79
+ description: p.description,
80
+ amount: p.amount,
81
+ },
82
+ })
83
+ const data = await res.json()
84
+ return ok(data, ['Dispute created'])
85
+ } catch (err) {
86
+ return fail(err instanceof Error ? err.message : 'Failed to create dispute')
87
+ }
88
+ },
89
+ })
90
+
91
+ site.command('update', {
92
+ description: 'Update a dispute status',
93
+ parameters: z.object({
94
+ id: z.string().describe('Dispute ID'),
95
+ status: z
96
+ .enum(['pending', 'resolved', 'investigating', 'rejected'])
97
+ .optional()
98
+ .describe('New status'),
99
+ }),
100
+ handler: async (params: unknown) => {
101
+ const p = params as { id: string; status?: string }
102
+ const { id, ...rest } = p
103
+ const body: Record<string, string | null> = {}
104
+ if (rest.status) body.status = rest.status
105
+ try {
106
+ const client = getClient()
107
+ const res = await client.api.disputes[':id'].$put({ param: { id }, json: body })
108
+ const data = await res.json()
109
+ return ok(data, ['Dispute updated'])
110
+ } catch (err) {
111
+ return fail(err instanceof Error ? err.message : 'Failed to update dispute')
112
+ }
113
+ },
114
+ })
115
+
116
+ site.command('delete', {
117
+ description: 'Delete a dispute',
118
+ parameters: z.object({
119
+ id: z.string().describe('Dispute ID'),
120
+ }),
121
+ handler: async (params: unknown) => {
122
+ const p = params as { id: string }
123
+ try {
124
+ const client = getClient()
125
+ const res = await client.api.disputes[':id'].$delete({ param: { id: p.id } })
126
+ const data = await res.json()
127
+ return ok(data, ['Dispute deleted'])
128
+ } catch (err) {
129
+ return fail(err instanceof Error ? err.message : 'Failed to delete dispute')
130
+ }
131
+ },
132
+ })
133
+
134
+ site.command('resolve', {
135
+ description: 'Resolve a dispute',
136
+ parameters: z.object({
137
+ id: z.string().describe('Dispute ID'),
138
+ resolution: z.string().min(1).describe('Resolution notes'),
139
+ 'resolved-by': z.string().describe('Resolver name'),
140
+ }),
141
+ handler: async (params: unknown) => {
142
+ const p = params as { id: string; resolution: string; 'resolved-by': string }
143
+ try {
144
+ const client = getClient()
145
+ const res = await client.api.disputes[':id'].resolve.$put({
146
+ param: { id: p.id },
147
+ json: { resolution: p.resolution, resolvedBy: p['resolved-by'] },
148
+ })
149
+ const data = await res.json()
150
+ return ok(data, ['Dispute resolved'])
151
+ } catch (err) {
152
+ return fail(err instanceof Error ? err.message : 'Failed to resolve dispute')
153
+ }
154
+ },
155
+ })
156
+ }
@@ -0,0 +1,60 @@
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 {
17
+ namespace: string
18
+ filename: string
19
+ private: boolean
20
+ 'expiry-seconds': number
21
+ }
22
+ try {
23
+ const client = getClient()
24
+ const res = await client.api['generate-url'].$post({
25
+ json: {
26
+ namespace: p.namespace,
27
+ filename: p.filename,
28
+ isPrivate: p.private,
29
+ expirySeconds: p['expiry-seconds'],
30
+ },
31
+ })
32
+ const data = await res.json()
33
+ return ok(data)
34
+ } catch (err) {
35
+ return fail(err instanceof Error ? err.message : 'Failed to generate URL')
36
+ }
37
+ },
38
+ })
39
+
40
+ site.command('info', {
41
+ description: 'Check if a public file exists',
42
+ parameters: z.object({
43
+ namespace: z.string().describe('File namespace'),
44
+ filename: z.string().describe('File name'),
45
+ }),
46
+ handler: async (params: unknown) => {
47
+ const p = params as { namespace: string; filename: string }
48
+ try {
49
+ const client = getClient()
50
+ const res = await client.api.public[':namespace'][':filename'].$get({
51
+ param: { namespace: p.namespace, filename: p.filename },
52
+ })
53
+ const data = await res.json()
54
+ return ok(data)
55
+ } catch (err) {
56
+ return fail(err instanceof Error ? err.message : 'Failed to get file info')
57
+ }
58
+ },
59
+ })
60
+ }