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
@@ -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
+ }