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.
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
- package/template/src/admin/App.tsx +4 -1
- package/template/src/admin/components/NotificationDrawer.tsx +13 -42
- package/template/src/admin/components/ThemeToggle.tsx +2 -2
- package/template/src/admin/layouts/Header.tsx +12 -9
- package/template/src/admin/pages/ContentPage.tsx +8 -10
- package/template/src/admin/pages/DashboardPage.tsx +30 -17
- package/template/src/admin/pages/DisputesPage.tsx +20 -14
- package/template/src/admin/pages/LoginPage.tsx +14 -3
- package/template/src/admin/pages/OrdersPage.tsx +10 -6
- package/template/src/admin/pages/PermissionsPage.tsx +1 -3
- package/template/src/admin/pages/RegisterPage.tsx +6 -4
- package/template/src/admin/pages/RolesPage.tsx +10 -3
- package/template/src/admin/pages/SystemLogsPage.tsx +12 -4
- package/template/src/admin/pages/TicketsPage.tsx +15 -15
- package/template/src/admin/pages/UsersPage.tsx +10 -6
- package/template/src/admin/stores/themeStore.ts +1 -1
- package/template/src/cli/modules/admin/index.ts +192 -0
- package/template/src/cli/modules/captcha/index.ts +40 -0
- package/template/src/cli/modules/chat/index.ts +21 -0
- package/template/src/cli/modules/content/index.ts +152 -0
- package/template/src/cli/modules/dispute/index.ts +156 -0
- package/template/src/cli/modules/file/index.ts +60 -0
- package/template/src/cli/modules/index.ts +30 -5
- package/template/src/cli/modules/order/index.ts +183 -0
- package/template/src/cli/modules/permission/index.ts +201 -0
- package/template/src/cli/modules/tenant/index.ts +142 -0
- package/template/src/cli/modules/ticket/index.ts +178 -0
- package/template/src/cli/rpc/client.ts +2 -2
- package/template/src/client/index.css +22 -24
|
@@ -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
|
}
|
|
@@ -0,0 +1,183 @@
|
|
|
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 registerOrderCommands(site: SiteInstance) {
|
|
7
|
+
site.command('list', {
|
|
8
|
+
description: 'List all orders',
|
|
9
|
+
parameters: z.object({
|
|
10
|
+
limit: z.coerce.number().default(20).describe('Limit results'),
|
|
11
|
+
offset: z.coerce.number().default(0).describe('Offset'),
|
|
12
|
+
status: z.string().optional().describe('Filter by status'),
|
|
13
|
+
'customer-name': z.string().optional().describe('Filter by customer name'),
|
|
14
|
+
}),
|
|
15
|
+
handler: async (params: unknown) => {
|
|
16
|
+
const p = params as {
|
|
17
|
+
limit: number
|
|
18
|
+
offset: number
|
|
19
|
+
status?: string
|
|
20
|
+
'customer-name'?: string
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const client = getClient()
|
|
24
|
+
const query: Record<string, string> = {
|
|
25
|
+
limit: String(p.limit),
|
|
26
|
+
offset: String(p.offset),
|
|
27
|
+
}
|
|
28
|
+
if (p.status) query.status = p.status
|
|
29
|
+
if (p['customer-name']) query.customerName = p['customer-name']
|
|
30
|
+
const res = await client.api.orders.$get({ query })
|
|
31
|
+
const data = await res.json()
|
|
32
|
+
return ok(data)
|
|
33
|
+
} catch (err) {
|
|
34
|
+
return fail(err instanceof Error ? err.message : 'Failed to list orders')
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
site.command('get', {
|
|
40
|
+
description: 'Get order by ID',
|
|
41
|
+
parameters: z.object({
|
|
42
|
+
id: z.string().describe('Order ID'),
|
|
43
|
+
}),
|
|
44
|
+
handler: async (params: unknown) => {
|
|
45
|
+
const p = params as { id: string }
|
|
46
|
+
try {
|
|
47
|
+
const client = getClient()
|
|
48
|
+
const res = await client.api.orders[':id'].$get({ param: { id: p.id } })
|
|
49
|
+
const data = await res.json()
|
|
50
|
+
return ok(data)
|
|
51
|
+
} catch (err) {
|
|
52
|
+
return fail(err instanceof Error ? err.message : 'Failed to get order')
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
site.command('create', {
|
|
58
|
+
description: 'Create a new order',
|
|
59
|
+
parameters: z.object({
|
|
60
|
+
'customer-name': z.string().min(1).describe('Customer name'),
|
|
61
|
+
'customer-email': z.string().email().describe('Customer email'),
|
|
62
|
+
'product-name': z.string().min(1).describe('Product name'),
|
|
63
|
+
amount: z.coerce.number().positive().describe('Order amount'),
|
|
64
|
+
}),
|
|
65
|
+
handler: async (params: unknown) => {
|
|
66
|
+
const p = params as {
|
|
67
|
+
'customer-name': string
|
|
68
|
+
'customer-email': string
|
|
69
|
+
'product-name': string
|
|
70
|
+
amount: number
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const client = getClient()
|
|
74
|
+
const res = await client.api.orders.$post({
|
|
75
|
+
json: {
|
|
76
|
+
customerName: p['customer-name'],
|
|
77
|
+
customerEmail: p['customer-email'],
|
|
78
|
+
productName: p['product-name'],
|
|
79
|
+
amount: p.amount,
|
|
80
|
+
},
|
|
81
|
+
})
|
|
82
|
+
const data = await res.json()
|
|
83
|
+
return ok(data, ['Order created'])
|
|
84
|
+
} catch (err) {
|
|
85
|
+
return fail(err instanceof Error ? err.message : 'Failed to create order')
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
site.command('update', {
|
|
91
|
+
description: 'Update order status',
|
|
92
|
+
parameters: z.object({
|
|
93
|
+
id: z.string().describe('Order ID'),
|
|
94
|
+
status: z
|
|
95
|
+
.enum(['pending', 'processing', 'completed', 'cancelled', 'disputed'])
|
|
96
|
+
.optional()
|
|
97
|
+
.describe('New status'),
|
|
98
|
+
}),
|
|
99
|
+
handler: async (params: unknown) => {
|
|
100
|
+
const p = params as { id: string; status?: string }
|
|
101
|
+
const { id, ...rest } = p
|
|
102
|
+
const body: Record<string, string | null> = {}
|
|
103
|
+
if (rest.status) body.status = rest.status
|
|
104
|
+
try {
|
|
105
|
+
const client = getClient()
|
|
106
|
+
const res = await client.api.orders[':id'].$put({ param: { id }, json: body })
|
|
107
|
+
const data = await res.json()
|
|
108
|
+
return ok(data, ['Order updated'])
|
|
109
|
+
} catch (err) {
|
|
110
|
+
return fail(err instanceof Error ? err.message : 'Failed to update order')
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
site.command('delete', {
|
|
116
|
+
description: 'Delete an order',
|
|
117
|
+
parameters: z.object({
|
|
118
|
+
id: z.string().describe('Order ID'),
|
|
119
|
+
}),
|
|
120
|
+
handler: async (params: unknown) => {
|
|
121
|
+
const p = params as { id: string }
|
|
122
|
+
try {
|
|
123
|
+
const client = getClient()
|
|
124
|
+
const res = await client.api.orders[':id'].$delete({ param: { id: p.id } })
|
|
125
|
+
const data = await res.json()
|
|
126
|
+
return ok(data, ['Order deleted'])
|
|
127
|
+
} catch (err) {
|
|
128
|
+
return fail(err instanceof Error ? err.message : 'Failed to delete order')
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
site.command('process', {
|
|
134
|
+
description: 'Process an order',
|
|
135
|
+
parameters: z.object({
|
|
136
|
+
id: z.string().describe('Order ID'),
|
|
137
|
+
}),
|
|
138
|
+
handler: async (params: unknown) => {
|
|
139
|
+
const p = params as { id: string }
|
|
140
|
+
try {
|
|
141
|
+
const client = getClient()
|
|
142
|
+
const res = await client.api.orders[':id'].process.$put({ param: { id: p.id } })
|
|
143
|
+
const data = await res.json()
|
|
144
|
+
return ok(data, ['Order processed'])
|
|
145
|
+
} catch (err) {
|
|
146
|
+
return fail(err instanceof Error ? err.message : 'Failed to process order')
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
site.command('cancel', {
|
|
152
|
+
description: 'Cancel an order',
|
|
153
|
+
parameters: z.object({
|
|
154
|
+
id: z.string().describe('Order ID'),
|
|
155
|
+
}),
|
|
156
|
+
handler: async (params: unknown) => {
|
|
157
|
+
const p = params as { id: string }
|
|
158
|
+
try {
|
|
159
|
+
const client = getClient()
|
|
160
|
+
const res = await client.api.orders[':id'].cancel.$put({ param: { id: p.id } })
|
|
161
|
+
const data = await res.json()
|
|
162
|
+
return ok(data, ['Order cancelled'])
|
|
163
|
+
} catch (err) {
|
|
164
|
+
return fail(err instanceof Error ? err.message : 'Failed to cancel order')
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
site.command('cart', {
|
|
170
|
+
description: 'Get current cart',
|
|
171
|
+
parameters: z.object({}),
|
|
172
|
+
handler: async () => {
|
|
173
|
+
try {
|
|
174
|
+
const client = getClient()
|
|
175
|
+
const res = await client.api.cart.$get()
|
|
176
|
+
const data = await res.json()
|
|
177
|
+
return ok(data)
|
|
178
|
+
} catch (err) {
|
|
179
|
+
return fail(err instanceof Error ? err.message : 'Failed to get cart')
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
})
|
|
183
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
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 registerPermissionCommands(site: SiteInstance) {
|
|
7
|
+
site.command('roles', {
|
|
8
|
+
description: 'List all roles',
|
|
9
|
+
parameters: z.object({}),
|
|
10
|
+
handler: async () => {
|
|
11
|
+
try {
|
|
12
|
+
const client = getClient()
|
|
13
|
+
const res = await client.api.permissions.roles.$get()
|
|
14
|
+
const data = await res.json()
|
|
15
|
+
return ok(data)
|
|
16
|
+
} catch (err) {
|
|
17
|
+
return fail(err instanceof Error ? err.message : 'Failed to list roles')
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
site.command('get-role', {
|
|
23
|
+
description: 'Get role details by ID',
|
|
24
|
+
parameters: z.object({
|
|
25
|
+
id: z.string().describe('Role ID'),
|
|
26
|
+
}),
|
|
27
|
+
handler: async (params: unknown) => {
|
|
28
|
+
const p = params as { id: string }
|
|
29
|
+
try {
|
|
30
|
+
const client = getClient()
|
|
31
|
+
const res = await client.api.roles[':id'].$get({ param: { id: p.id } })
|
|
32
|
+
const data = await res.json()
|
|
33
|
+
return ok(data)
|
|
34
|
+
} catch (err) {
|
|
35
|
+
return fail(err instanceof Error ? err.message : 'Failed to get role')
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
site.command('create-role', {
|
|
41
|
+
description: 'Create a new role',
|
|
42
|
+
parameters: z.object({
|
|
43
|
+
name: z.string().min(1).describe('Role name'),
|
|
44
|
+
code: z.string().min(1).describe('Role code'),
|
|
45
|
+
label: z.string().min(1).describe('Role display label'),
|
|
46
|
+
description: z.string().optional().describe('Role description'),
|
|
47
|
+
}),
|
|
48
|
+
handler: async (params: unknown) => {
|
|
49
|
+
const p = params as { name: string; code: string; label: string; description?: string }
|
|
50
|
+
try {
|
|
51
|
+
const client = getClient()
|
|
52
|
+
const res = await client.api.roles.$post({ json: p })
|
|
53
|
+
const data = await res.json()
|
|
54
|
+
return ok(data, ['Role created'])
|
|
55
|
+
} catch (err) {
|
|
56
|
+
return fail(err instanceof Error ? err.message : 'Failed to create role')
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
site.command('update-role', {
|
|
62
|
+
description: 'Update a role',
|
|
63
|
+
parameters: z.object({
|
|
64
|
+
id: z.string().describe('Role ID'),
|
|
65
|
+
name: z.string().optional().describe('New name'),
|
|
66
|
+
description: z.string().optional().describe('New description'),
|
|
67
|
+
}),
|
|
68
|
+
handler: async (params: unknown) => {
|
|
69
|
+
const p = params as { id: string; name?: string; description?: string }
|
|
70
|
+
const { id, ...body } = p
|
|
71
|
+
try {
|
|
72
|
+
const client = getClient()
|
|
73
|
+
const res = await client.api.roles[':id'].$put({ param: { id }, json: body })
|
|
74
|
+
const data = await res.json()
|
|
75
|
+
return ok(data, ['Role updated'])
|
|
76
|
+
} catch (err) {
|
|
77
|
+
return fail(err instanceof Error ? err.message : 'Failed to update role')
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
site.command('delete-role', {
|
|
83
|
+
description: 'Delete a role',
|
|
84
|
+
parameters: z.object({
|
|
85
|
+
id: z.string().describe('Role ID'),
|
|
86
|
+
}),
|
|
87
|
+
handler: async (params: unknown) => {
|
|
88
|
+
const p = params as { id: string }
|
|
89
|
+
try {
|
|
90
|
+
const client = getClient()
|
|
91
|
+
const res = await client.api.roles[':id'].$delete({ param: { id: p.id } })
|
|
92
|
+
const data = await res.json()
|
|
93
|
+
return ok(data, ['Role deleted'])
|
|
94
|
+
} catch (err) {
|
|
95
|
+
return fail(err instanceof Error ? err.message : 'Failed to delete role')
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
site.command('permissions', {
|
|
101
|
+
description: 'List all permissions',
|
|
102
|
+
parameters: z.object({}),
|
|
103
|
+
handler: async () => {
|
|
104
|
+
try {
|
|
105
|
+
const client = getClient()
|
|
106
|
+
const res = await client.api.permissions.$get()
|
|
107
|
+
const data = await res.json()
|
|
108
|
+
return ok(data)
|
|
109
|
+
} catch (err) {
|
|
110
|
+
return fail(err instanceof Error ? err.message : 'Failed to list permissions')
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
site.command('categories', {
|
|
116
|
+
description: 'List permission categories',
|
|
117
|
+
parameters: z.object({}),
|
|
118
|
+
handler: async () => {
|
|
119
|
+
try {
|
|
120
|
+
const client = getClient()
|
|
121
|
+
const res = await client.api.permissions.categories.$get()
|
|
122
|
+
const data = await res.json()
|
|
123
|
+
return ok(data)
|
|
124
|
+
} catch (err) {
|
|
125
|
+
return fail(err instanceof Error ? err.message : 'Failed to list categories')
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
site.command('menu-config', {
|
|
131
|
+
description: 'Get menu configuration',
|
|
132
|
+
parameters: z.object({}),
|
|
133
|
+
handler: async () => {
|
|
134
|
+
try {
|
|
135
|
+
const client = getClient()
|
|
136
|
+
const res = await client.api.permissions['menu-config'].$get()
|
|
137
|
+
const data = await res.json()
|
|
138
|
+
return ok(data)
|
|
139
|
+
} catch (err) {
|
|
140
|
+
return fail(err instanceof Error ? err.message : 'Failed to get menu config')
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
site.command('audit-logs', {
|
|
146
|
+
description: 'List audit logs',
|
|
147
|
+
parameters: z.object({
|
|
148
|
+
limit: z.coerce.number().default(50).describe('Limit results'),
|
|
149
|
+
offset: z.coerce.number().default(0).describe('Offset'),
|
|
150
|
+
'user-id': z.string().optional().describe('Filter by user ID'),
|
|
151
|
+
action: z.string().optional().describe('Filter by action'),
|
|
152
|
+
'resource-type': z.string().optional().describe('Filter by resource type'),
|
|
153
|
+
}),
|
|
154
|
+
handler: async (params: unknown) => {
|
|
155
|
+
const p = params as {
|
|
156
|
+
limit: number
|
|
157
|
+
offset: number
|
|
158
|
+
'user-id'?: string
|
|
159
|
+
action?: string
|
|
160
|
+
'resource-type'?: string
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
const client = getClient()
|
|
164
|
+
const query: Record<string, string> = {
|
|
165
|
+
limit: String(p.limit),
|
|
166
|
+
offset: String(p.offset),
|
|
167
|
+
}
|
|
168
|
+
if (p['user-id']) query.userId = p['user-id']
|
|
169
|
+
if (p.action) query.action = p.action
|
|
170
|
+
if (p['resource-type']) query.resourceType = p['resource-type']
|
|
171
|
+
const res = await client.api['audit-logs'].$get({ query })
|
|
172
|
+
const data = await res.json()
|
|
173
|
+
return ok(data)
|
|
174
|
+
} catch (err) {
|
|
175
|
+
return fail(err instanceof Error ? err.message : 'Failed to list audit logs')
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
site.command('update-role-permissions', {
|
|
181
|
+
description: 'Update permissions for a role',
|
|
182
|
+
parameters: z.object({
|
|
183
|
+
id: z.string().describe('Role ID'),
|
|
184
|
+
'permission-ids': z.string().describe('Comma-separated permission IDs'),
|
|
185
|
+
}),
|
|
186
|
+
handler: async (params: unknown) => {
|
|
187
|
+
const p = params as { id: string; 'permission-ids': string }
|
|
188
|
+
try {
|
|
189
|
+
const client = getClient()
|
|
190
|
+
const res = await client.api.roles[':id'].permissions.$put({
|
|
191
|
+
param: { id: p.id },
|
|
192
|
+
json: { permissionIds: p['permission-ids'].split(',') },
|
|
193
|
+
})
|
|
194
|
+
const data = await res.json()
|
|
195
|
+
return ok(data, ['Role permissions updated'])
|
|
196
|
+
} catch (err) {
|
|
197
|
+
return fail(err instanceof Error ? err.message : 'Failed to update role permissions')
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
})
|
|
201
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
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 registerTenantCommands(site: SiteInstance) {
|
|
7
|
+
site.command('list', {
|
|
8
|
+
description: 'List all tenants',
|
|
9
|
+
parameters: z.object({
|
|
10
|
+
page: z.coerce.number().default(1).describe('Page number'),
|
|
11
|
+
'page-size': z.coerce.number().default(20).describe('Page size'),
|
|
12
|
+
status: z.string().optional().describe('Filter by status'),
|
|
13
|
+
plan: z.string().optional().describe('Filter by plan'),
|
|
14
|
+
}),
|
|
15
|
+
handler: async (params: unknown) => {
|
|
16
|
+
const p = params as { page: number; 'page-size': number; status?: string; plan?: string }
|
|
17
|
+
try {
|
|
18
|
+
const client = getClient()
|
|
19
|
+
const query: Record<string, string> = {
|
|
20
|
+
page: String(p.page),
|
|
21
|
+
pageSize: String(p['page-size']),
|
|
22
|
+
}
|
|
23
|
+
if (p.status) query.status = p.status
|
|
24
|
+
if (p.plan) query.plan = p.plan
|
|
25
|
+
const res = await client.api.tenants.$get({ query })
|
|
26
|
+
const data = await res.json()
|
|
27
|
+
return ok(data)
|
|
28
|
+
} catch (err) {
|
|
29
|
+
return fail(err instanceof Error ? err.message : 'Failed to list tenants')
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
site.command('get', {
|
|
35
|
+
description: 'Get tenant by ID',
|
|
36
|
+
parameters: z.object({
|
|
37
|
+
id: z.string().describe('Tenant ID'),
|
|
38
|
+
}),
|
|
39
|
+
handler: async (params: unknown) => {
|
|
40
|
+
const p = params as { id: string }
|
|
41
|
+
try {
|
|
42
|
+
const client = getClient()
|
|
43
|
+
const res = await client.api.tenants[':id'].$get({ param: { id: p.id } })
|
|
44
|
+
const data = await res.json()
|
|
45
|
+
return ok(data)
|
|
46
|
+
} catch (err) {
|
|
47
|
+
return fail(err instanceof Error ? err.message : 'Failed to get tenant')
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
site.command('get-by-slug', {
|
|
53
|
+
description: 'Get tenant by slug',
|
|
54
|
+
parameters: z.object({
|
|
55
|
+
slug: z.string().describe('Tenant slug'),
|
|
56
|
+
}),
|
|
57
|
+
handler: async (params: unknown) => {
|
|
58
|
+
const p = params as { slug: string }
|
|
59
|
+
try {
|
|
60
|
+
const client = getClient()
|
|
61
|
+
const res = await client.api.tenants.slug[':slug'].$get({ param: { slug: p.slug } })
|
|
62
|
+
const data = await res.json()
|
|
63
|
+
return ok(data)
|
|
64
|
+
} catch (err) {
|
|
65
|
+
return fail(err instanceof Error ? err.message : 'Failed to get tenant by slug')
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
site.command('create', {
|
|
71
|
+
description: 'Create a new tenant',
|
|
72
|
+
parameters: z.object({
|
|
73
|
+
name: z.string().min(1).describe('Tenant name'),
|
|
74
|
+
slug: z.string().min(1).describe('Tenant slug (lowercase, hyphens)'),
|
|
75
|
+
plan: z.enum(['free', 'starter', 'pro', 'enterprise']).default('free').describe('Plan'),
|
|
76
|
+
'max-users': z.coerce.number().int().min(1).max(1000).default(5).describe('Max users'),
|
|
77
|
+
}),
|
|
78
|
+
handler: async (params: unknown) => {
|
|
79
|
+
const p = params as { name: string; slug: string; plan: string; 'max-users': number }
|
|
80
|
+
try {
|
|
81
|
+
const client = getClient()
|
|
82
|
+
const res = await client.api.tenants.$post({
|
|
83
|
+
json: {
|
|
84
|
+
name: p.name,
|
|
85
|
+
slug: p.slug,
|
|
86
|
+
plan: p.plan as 'free' | 'starter' | 'pro' | 'enterprise',
|
|
87
|
+
maxUsers: p['max-users'],
|
|
88
|
+
settings: null,
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
const data = await res.json()
|
|
92
|
+
return ok(data, ['Tenant created'])
|
|
93
|
+
} catch (err) {
|
|
94
|
+
return fail(err instanceof Error ? err.message : 'Failed to create tenant')
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
site.command('update', {
|
|
100
|
+
description: 'Update a tenant',
|
|
101
|
+
parameters: z.object({
|
|
102
|
+
id: z.string().describe('Tenant ID'),
|
|
103
|
+
name: z.string().optional().describe('New name'),
|
|
104
|
+
plan: z.enum(['free', 'starter', 'pro', 'enterprise']).optional().describe('New plan'),
|
|
105
|
+
status: z.enum(['active', 'suspended', 'trial']).optional().describe('New status'),
|
|
106
|
+
}),
|
|
107
|
+
handler: async (params: unknown) => {
|
|
108
|
+
const p = params as { id: string; name?: string; plan?: string; status?: string }
|
|
109
|
+
const { id, ...rest } = p
|
|
110
|
+
const body: Record<string, string | null> = {}
|
|
111
|
+
if (rest.name) body.name = rest.name
|
|
112
|
+
if (rest.plan) body.plan = rest.plan
|
|
113
|
+
if (rest.status) body.status = rest.status
|
|
114
|
+
try {
|
|
115
|
+
const client = getClient()
|
|
116
|
+
const res = await client.api.tenants[':id'].$put({ param: { id }, json: body })
|
|
117
|
+
const data = await res.json()
|
|
118
|
+
return ok(data, ['Tenant updated'])
|
|
119
|
+
} catch (err) {
|
|
120
|
+
return fail(err instanceof Error ? err.message : 'Failed to update tenant')
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
site.command('delete', {
|
|
126
|
+
description: 'Delete a tenant',
|
|
127
|
+
parameters: z.object({
|
|
128
|
+
id: z.string().describe('Tenant ID'),
|
|
129
|
+
}),
|
|
130
|
+
handler: async (params: unknown) => {
|
|
131
|
+
const p = params as { id: string }
|
|
132
|
+
try {
|
|
133
|
+
const client = getClient()
|
|
134
|
+
const res = await client.api.tenants[':id'].$delete({ param: { id: p.id } })
|
|
135
|
+
const data = await res.json()
|
|
136
|
+
return ok(data, ['Tenant deleted'])
|
|
137
|
+
} catch (err) {
|
|
138
|
+
return fail(err instanceof Error ? err.message : 'Failed to delete tenant')
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
})
|
|
142
|
+
}
|