create-fullstack-scaffold 0.4.10 → 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.
- package/package.json +1 -1
- package/template/src/cli/modules/admin/index.ts +200 -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 +144 -0
- package/template/src/cli/modules/dispute/index.ts +150 -0
- package/template/src/cli/modules/file/index.ts +55 -0
- package/template/src/cli/modules/index.ts +30 -5
- package/template/src/cli/modules/order/index.ts +170 -0
- package/template/src/cli/modules/permission/index.ts +195 -0
- package/template/src/cli/modules/tenant/index.ts +142 -0
- package/template/src/cli/modules/ticket/index.ts +167 -0
- package/template/src/cli/rpc/client.ts +2 -2
package/package.json
CHANGED
|
@@ -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
|
}
|
|
@@ -0,0 +1,170 @@
|
|
|
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 { limit: number; offset: number; status?: string; 'customer-name'?: string }
|
|
17
|
+
try {
|
|
18
|
+
const client = getClient()
|
|
19
|
+
const query: Record<string, string> = {
|
|
20
|
+
limit: String(p.limit),
|
|
21
|
+
offset: String(p.offset),
|
|
22
|
+
}
|
|
23
|
+
if (p.status) query.status = p.status
|
|
24
|
+
if (p['customer-name']) query.customerName = p['customer-name']
|
|
25
|
+
const res = await client.api.orders.$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 orders')
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
site.command('get', {
|
|
35
|
+
description: 'Get order by ID',
|
|
36
|
+
parameters: z.object({
|
|
37
|
+
id: z.string().describe('Order 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.orders[':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 order')
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
site.command('create', {
|
|
53
|
+
description: 'Create a new order',
|
|
54
|
+
parameters: z.object({
|
|
55
|
+
'customer-name': z.string().min(1).describe('Customer name'),
|
|
56
|
+
'customer-email': z.string().email().describe('Customer email'),
|
|
57
|
+
'product-name': z.string().min(1).describe('Product name'),
|
|
58
|
+
amount: z.coerce.number().positive().describe('Order amount'),
|
|
59
|
+
}),
|
|
60
|
+
handler: async (params: unknown) => {
|
|
61
|
+
const p = params as { 'customer-name': string; 'customer-email': string; 'product-name': string; amount: number }
|
|
62
|
+
try {
|
|
63
|
+
const client = getClient()
|
|
64
|
+
const res = await client.api.orders.$post({
|
|
65
|
+
json: {
|
|
66
|
+
customerName: p['customer-name'],
|
|
67
|
+
customerEmail: p['customer-email'],
|
|
68
|
+
productName: p['product-name'],
|
|
69
|
+
amount: p.amount,
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
const data = await res.json()
|
|
73
|
+
return ok(data, ['Order created'])
|
|
74
|
+
} catch (err) {
|
|
75
|
+
return fail(err instanceof Error ? err.message : 'Failed to create order')
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
site.command('update', {
|
|
81
|
+
description: 'Update order status',
|
|
82
|
+
parameters: z.object({
|
|
83
|
+
id: z.string().describe('Order ID'),
|
|
84
|
+
status: z.enum(['pending', 'processing', 'completed', 'cancelled', 'disputed']).optional().describe('New status'),
|
|
85
|
+
}),
|
|
86
|
+
handler: async (params: unknown) => {
|
|
87
|
+
const p = params as { id: string; status?: string }
|
|
88
|
+
const { id, ...rest } = p
|
|
89
|
+
const body: Record<string, string | null> = {}
|
|
90
|
+
if (rest.status) body.status = rest.status
|
|
91
|
+
try {
|
|
92
|
+
const client = getClient()
|
|
93
|
+
const res = await client.api.orders[':id'].$put({ param: { id }, json: body })
|
|
94
|
+
const data = await res.json()
|
|
95
|
+
return ok(data, ['Order updated'])
|
|
96
|
+
} catch (err) {
|
|
97
|
+
return fail(err instanceof Error ? err.message : 'Failed to update order')
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
site.command('delete', {
|
|
103
|
+
description: 'Delete an order',
|
|
104
|
+
parameters: z.object({
|
|
105
|
+
id: z.string().describe('Order ID'),
|
|
106
|
+
}),
|
|
107
|
+
handler: async (params: unknown) => {
|
|
108
|
+
const p = params as { id: string }
|
|
109
|
+
try {
|
|
110
|
+
const client = getClient()
|
|
111
|
+
const res = await client.api.orders[':id'].$delete({ param: { id: p.id } })
|
|
112
|
+
const data = await res.json()
|
|
113
|
+
return ok(data, ['Order deleted'])
|
|
114
|
+
} catch (err) {
|
|
115
|
+
return fail(err instanceof Error ? err.message : 'Failed to delete order')
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
site.command('process', {
|
|
121
|
+
description: 'Process an order',
|
|
122
|
+
parameters: z.object({
|
|
123
|
+
id: z.string().describe('Order ID'),
|
|
124
|
+
}),
|
|
125
|
+
handler: async (params: unknown) => {
|
|
126
|
+
const p = params as { id: string }
|
|
127
|
+
try {
|
|
128
|
+
const client = getClient()
|
|
129
|
+
const res = await client.api.orders[':id'].process.$put({ param: { id: p.id } })
|
|
130
|
+
const data = await res.json()
|
|
131
|
+
return ok(data, ['Order processed'])
|
|
132
|
+
} catch (err) {
|
|
133
|
+
return fail(err instanceof Error ? err.message : 'Failed to process order')
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
site.command('cancel', {
|
|
139
|
+
description: 'Cancel an order',
|
|
140
|
+
parameters: z.object({
|
|
141
|
+
id: z.string().describe('Order ID'),
|
|
142
|
+
}),
|
|
143
|
+
handler: async (params: unknown) => {
|
|
144
|
+
const p = params as { id: string }
|
|
145
|
+
try {
|
|
146
|
+
const client = getClient()
|
|
147
|
+
const res = await client.api.orders[':id'].cancel.$put({ param: { id: p.id } })
|
|
148
|
+
const data = await res.json()
|
|
149
|
+
return ok(data, ['Order cancelled'])
|
|
150
|
+
} catch (err) {
|
|
151
|
+
return fail(err instanceof Error ? err.message : 'Failed to cancel order')
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
site.command('cart', {
|
|
157
|
+
description: 'Get current cart',
|
|
158
|
+
parameters: z.object({}),
|
|
159
|
+
handler: async () => {
|
|
160
|
+
try {
|
|
161
|
+
const client = getClient()
|
|
162
|
+
const res = await client.api.cart.$get()
|
|
163
|
+
const data = await res.json()
|
|
164
|
+
return ok(data)
|
|
165
|
+
} catch (err) {
|
|
166
|
+
return fail(err instanceof Error ? err.message : 'Failed to get cart')
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
})
|
|
170
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
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 { limit: number; offset: number; 'user-id'?: string; action?: string; 'resource-type'?: string }
|
|
156
|
+
try {
|
|
157
|
+
const client = getClient()
|
|
158
|
+
const query: Record<string, string> = {
|
|
159
|
+
limit: String(p.limit),
|
|
160
|
+
offset: String(p.offset),
|
|
161
|
+
}
|
|
162
|
+
if (p['user-id']) query.userId = p['user-id']
|
|
163
|
+
if (p.action) query.action = p.action
|
|
164
|
+
if (p['resource-type']) query.resourceType = p['resource-type']
|
|
165
|
+
const res = await client.api['audit-logs'].$get({ query })
|
|
166
|
+
const data = await res.json()
|
|
167
|
+
return ok(data)
|
|
168
|
+
} catch (err) {
|
|
169
|
+
return fail(err instanceof Error ? err.message : 'Failed to list audit logs')
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
site.command('update-role-permissions', {
|
|
175
|
+
description: 'Update permissions for a role',
|
|
176
|
+
parameters: z.object({
|
|
177
|
+
id: z.string().describe('Role ID'),
|
|
178
|
+
'permission-ids': z.string().describe('Comma-separated permission IDs'),
|
|
179
|
+
}),
|
|
180
|
+
handler: async (params: unknown) => {
|
|
181
|
+
const p = params as { id: string; 'permission-ids': string }
|
|
182
|
+
try {
|
|
183
|
+
const client = getClient()
|
|
184
|
+
const res = await client.api.roles[':id'].permissions.$put({
|
|
185
|
+
param: { id: p.id },
|
|
186
|
+
json: { permissionIds: p['permission-ids'].split(',') },
|
|
187
|
+
})
|
|
188
|
+
const data = await res.json()
|
|
189
|
+
return ok(data, ['Role permissions updated'])
|
|
190
|
+
} catch (err) {
|
|
191
|
+
return fail(err instanceof Error ? err.message : 'Failed to update role permissions')
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
})
|
|
195
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
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 registerTicketCommands(site: SiteInstance) {
|
|
7
|
+
site.command('list', {
|
|
8
|
+
description: 'List all tickets',
|
|
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.tickets.$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 tickets')
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
site.command('get', {
|
|
29
|
+
description: 'Get ticket by ID',
|
|
30
|
+
parameters: z.object({
|
|
31
|
+
id: z.string().describe('Ticket 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.tickets[':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 ticket')
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
site.command('create', {
|
|
47
|
+
description: 'Create a new ticket',
|
|
48
|
+
parameters: z.object({
|
|
49
|
+
'customer-name': z.string().min(1).describe('Customer name'),
|
|
50
|
+
'customer-email': z.string().email().describe('Customer email'),
|
|
51
|
+
subject: z.string().min(1).describe('Ticket subject'),
|
|
52
|
+
description: z.string().min(1).describe('Ticket description'),
|
|
53
|
+
category: z.enum(['technical', 'billing', 'feature_request', 'bug_report', 'general']).default('general').describe('Category'),
|
|
54
|
+
priority: z.enum(['low', 'medium', 'high', 'urgent']).default('medium').describe('Priority'),
|
|
55
|
+
}),
|
|
56
|
+
handler: async (params: unknown) => {
|
|
57
|
+
const p = params as {
|
|
58
|
+
'customer-name': string
|
|
59
|
+
'customer-email': string
|
|
60
|
+
subject: string
|
|
61
|
+
description: string
|
|
62
|
+
category: string
|
|
63
|
+
priority: string
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const client = getClient()
|
|
67
|
+
const res = await client.api.tickets.$post({
|
|
68
|
+
json: {
|
|
69
|
+
customerName: p['customer-name'],
|
|
70
|
+
customerEmail: p['customer-email'],
|
|
71
|
+
subject: p.subject,
|
|
72
|
+
description: p.description,
|
|
73
|
+
category: p.category as 'technical' | 'billing' | 'feature_request' | 'bug_report' | 'general',
|
|
74
|
+
priority: p.priority as 'low' | 'medium' | 'high' | 'urgent',
|
|
75
|
+
},
|
|
76
|
+
})
|
|
77
|
+
const data = await res.json()
|
|
78
|
+
return ok(data, ['Ticket created'])
|
|
79
|
+
} catch (err) {
|
|
80
|
+
return fail(err instanceof Error ? err.message : 'Failed to create ticket')
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
site.command('update', {
|
|
86
|
+
description: 'Update ticket status',
|
|
87
|
+
parameters: z.object({
|
|
88
|
+
id: z.string().describe('Ticket ID'),
|
|
89
|
+
status: z.enum(['open', 'closed', 'in_progress', 'waiting_customer', 'resolved']).optional().describe('New status'),
|
|
90
|
+
'assigned-to': z.string().optional().describe('Assign to user'),
|
|
91
|
+
}),
|
|
92
|
+
handler: async (params: unknown) => {
|
|
93
|
+
const p = params as { id: string; status?: string; 'assigned-to'?: string }
|
|
94
|
+
const { id, ...rest } = p
|
|
95
|
+
const body: Record<string, string> = {}
|
|
96
|
+
if (rest.status) body.status = rest.status
|
|
97
|
+
if (rest['assigned-to']) body.assignedTo = rest['assigned-to']
|
|
98
|
+
try {
|
|
99
|
+
const client = getClient()
|
|
100
|
+
const res = await client.api.tickets[':id'].$put({ param: { id }, json: body })
|
|
101
|
+
const data = await res.json()
|
|
102
|
+
return ok(data, ['Ticket updated'])
|
|
103
|
+
} catch (err) {
|
|
104
|
+
return fail(err instanceof Error ? err.message : 'Failed to update ticket')
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
site.command('delete', {
|
|
110
|
+
description: 'Delete a ticket',
|
|
111
|
+
parameters: z.object({
|
|
112
|
+
id: z.string().describe('Ticket 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.tickets[':id'].$delete({ param: { id: p.id } })
|
|
119
|
+
const data = await res.json()
|
|
120
|
+
return ok(data, ['Ticket deleted'])
|
|
121
|
+
} catch (err) {
|
|
122
|
+
return fail(err instanceof Error ? err.message : 'Failed to delete ticket')
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
site.command('reply', {
|
|
128
|
+
description: 'Reply to a ticket',
|
|
129
|
+
parameters: z.object({
|
|
130
|
+
id: z.string().describe('Ticket ID'),
|
|
131
|
+
content: z.string().min(1).describe('Reply content'),
|
|
132
|
+
author: z.string().min(1).describe('Author name'),
|
|
133
|
+
}),
|
|
134
|
+
handler: async (params: unknown) => {
|
|
135
|
+
const p = params as { id: string; content: string; author: string }
|
|
136
|
+
try {
|
|
137
|
+
const client = getClient()
|
|
138
|
+
const res = await client.api.tickets[':id'].reply.$post({
|
|
139
|
+
param: { id: p.id },
|
|
140
|
+
json: { content: p.content, author: p.author },
|
|
141
|
+
})
|
|
142
|
+
const data = await res.json()
|
|
143
|
+
return ok(data, ['Reply sent'])
|
|
144
|
+
} catch (err) {
|
|
145
|
+
return fail(err instanceof Error ? err.message : 'Failed to reply ticket')
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
site.command('close', {
|
|
151
|
+
description: 'Close a ticket',
|
|
152
|
+
parameters: z.object({
|
|
153
|
+
id: z.string().describe('Ticket 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.tickets[':id'].close.$put({ param: { id: p.id } })
|
|
160
|
+
const data = await res.json()
|
|
161
|
+
return ok(data, ['Ticket closed'])
|
|
162
|
+
} catch (err) {
|
|
163
|
+
return fail(err instanceof Error ? err.message : 'Failed to close ticket')
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
})
|
|
167
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { hc } from 'hono/client'
|
|
2
|
-
import type {
|
|
2
|
+
import type { AppType } from '@server/index'
|
|
3
3
|
import { WSClientImpl } from '@shared/core/ws-client'
|
|
4
4
|
import { SSEClientImpl } from '@shared/core/sse-client'
|
|
5
5
|
|
|
@@ -16,7 +16,7 @@ export interface CliFetchExtendOptions {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export function createRPCClient(baseUrl: string) {
|
|
19
|
-
return hc<
|
|
19
|
+
return hc<AppType>(baseUrl, {
|
|
20
20
|
webSocket: url => new WSClientImpl(url) as unknown as WebSocket,
|
|
21
21
|
sse: url => new SSEClientImpl(url),
|
|
22
22
|
})
|