create-fullstack-scaffold 0.1.1 → 0.2.0

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 (155) hide show
  1. package/dist/cli/index.js +1673 -696
  2. package/dist/cli/index.js.map +1 -1
  3. package/package.json +16 -10
  4. package/template/.husky/pre-commit +1 -1
  5. package/template/modules.config.ts +29 -2
  6. package/template/package.json +12 -12
  7. package/template/patches/{typescript+5.8.3.patch → typescript+5.9.3.patch} +2 -4
  8. package/template/playwright.config.ts +7 -1
  9. package/template/src/admin/App.tsx +2 -2
  10. package/template/src/admin/components/CaptchaModal.tsx +7 -9
  11. package/template/src/admin/layouts/Header.tsx +56 -22
  12. package/template/src/admin/layouts/Layout.tsx +14 -9
  13. package/template/src/admin/layouts/Sidebar.tsx +122 -105
  14. package/template/src/admin/pages/CategoryManagementPage.tsx +241 -0
  15. package/template/src/admin/pages/ContentPage.tsx +2 -0
  16. package/template/src/admin/pages/DashboardPage.tsx +2 -0
  17. package/template/src/admin/pages/DisputesPage.tsx +2 -0
  18. package/template/src/admin/pages/MediaTestPage.tsx +1 -1
  19. package/template/src/admin/pages/OrdersPage.tsx +2 -0
  20. package/template/src/admin/pages/PluginDashboardPage.tsx +297 -0
  21. package/template/src/admin/pages/PluginManagementPage.tsx +340 -0
  22. package/template/src/admin/pages/PluginReviewPage.tsx +255 -0
  23. package/template/src/admin/pages/SystemLogsPage.tsx +11 -2
  24. package/template/src/admin/pages/TicketsPage.tsx +2 -0
  25. package/template/src/admin/pages/UsersPage.tsx +3 -2
  26. package/template/src/admin/stores/adminStore.ts +5 -0
  27. package/template/src/cli/index.ts +17 -19
  28. package/template/src/cli/modules/auth/index.ts +65 -0
  29. package/template/src/cli/modules/config/index.ts +74 -77
  30. package/template/src/cli/modules/index.ts +28 -6
  31. package/template/src/cli/modules/notification/index.ts +95 -79
  32. package/template/src/cli/modules/plugin/index.ts +111 -0
  33. package/template/src/cli/modules/todo/index.ts +99 -51
  34. package/template/src/cli/utils/auto-command.ts +7 -25
  35. package/template/src/cli/utils/index.ts +3 -1
  36. package/template/src/client/App.tsx +36 -16
  37. package/template/src/client/Layout.tsx +67 -10
  38. package/template/src/client/components/AuthButton.tsx +25 -18
  39. package/template/src/client/components/BottomTabBar.tsx +107 -0
  40. package/template/src/client/components/Navigation.tsx +162 -52
  41. package/template/src/client/components/__tests__/App.test.tsx +48 -32
  42. package/template/src/client/components/__tests__/AuthButton.test.tsx +78 -77
  43. package/template/src/client/components/__tests__/Navigation.test.tsx +31 -20
  44. package/template/src/client/components/index.ts +1 -0
  45. package/template/src/client/contexts/PresetContext.tsx +10 -0
  46. package/template/src/client/main.tsx +63 -8
  47. package/template/src/client/pages/CartPage.tsx +244 -0
  48. package/template/src/client/pages/CategoriesPage.tsx +100 -0
  49. package/template/src/client/pages/ContentDetailPage.tsx +9 -14
  50. package/template/src/client/pages/ContentListPage.tsx +4 -11
  51. package/template/src/client/pages/DashboardPage.tsx +261 -0
  52. package/template/src/client/pages/DeveloperDashboardPage.tsx +211 -0
  53. package/template/src/client/pages/LoginPage.tsx +127 -0
  54. package/template/src/client/pages/OrdersPage.tsx +196 -0
  55. package/template/src/client/pages/PluginDetailPage.tsx +345 -0
  56. package/template/src/client/pages/PluginsPage.tsx +223 -0
  57. package/template/src/client/pages/ProfilePage.tsx +206 -0
  58. package/template/src/client/pages/PublishPage.tsx +336 -0
  59. package/template/src/client/pages/RegisterPage.tsx +136 -0
  60. package/template/src/client/pages/SearchPage.tsx +204 -0
  61. package/template/src/client/pages/SettingsPage.tsx +220 -0
  62. package/template/src/client/pages/TopicsPage.tsx +180 -0
  63. package/template/src/client/pages/__tests__/LoginPage.test.tsx +170 -0
  64. package/template/src/client/pages/__tests__/RegisterPage.test.tsx +168 -0
  65. package/template/src/client/preset-ui-config.ts +492 -0
  66. package/template/src/client/services/apiClient.ts +14 -4
  67. package/template/src/client/stores/__tests__/authStore.test.ts +293 -38
  68. package/template/src/client/stores/__tests__/todoStore.test.ts +7 -17
  69. package/template/src/client/stores/authStore.ts +58 -5
  70. package/template/src/client/stores/chatWSStore.ts +9 -0
  71. package/template/src/client/stores/notificationStore.ts +4 -0
  72. package/template/src/client/stores/pluginStore.ts +279 -0
  73. package/template/src/client/stores/todoStore.ts +2 -6
  74. package/template/src/server/core/__tests__/isr-cache.test.ts +117 -0
  75. package/template/src/server/core/__tests__/isr-invalidation.test.ts +72 -0
  76. package/template/src/server/core/__tests__/ssr-renderer.test.ts +89 -0
  77. package/template/src/server/core/isr-cache.ts +239 -0
  78. package/template/src/server/core/isr-invalidation.ts +45 -0
  79. package/template/src/server/core/module-loader.ts +14 -7
  80. package/template/src/server/core/ssr-renderer.ts +240 -0
  81. package/template/src/server/db/init.ts +257 -10
  82. package/template/src/server/db/schema/developers.ts +20 -0
  83. package/template/src/server/db/schema/index.ts +2 -0
  84. package/template/src/server/db/schema/plugins.ts +114 -0
  85. package/template/src/server/db/test-setup.ts +91 -0
  86. package/template/src/server/entries/cloudflare.ts +79 -7
  87. package/template/src/server/entries/node.ts +48 -5
  88. package/template/src/server/middleware/__tests__/captcha.test.ts +23 -13
  89. package/template/src/server/middleware/auth.ts +8 -1
  90. package/template/src/server/middleware/captcha.ts +14 -4
  91. package/template/src/server/middleware/rate-limit.ts +7 -2
  92. package/template/src/server/module-admin/module.ts +9 -5
  93. package/template/src/server/module-admin/routes/admin-notification-routes.ts +43 -14
  94. package/template/src/server/module-admin/routes/admin-routes.ts +0 -2
  95. package/template/src/server/module-admin/routes/client-auth-routes.ts +90 -0
  96. package/template/src/server/module-admin/routes/dashboard-routes.ts +79 -0
  97. package/template/src/server/module-admin/services/admin-service.ts +68 -11
  98. package/template/src/server/module-auth/__tests__/auth-service.test.ts +239 -0
  99. package/template/src/server/module-auth/index.ts +7 -0
  100. package/template/src/server/module-auth/module.ts +40 -0
  101. package/template/src/server/module-auth/routes/auth-routes.ts +94 -0
  102. package/template/src/server/module-auth/routes/profile-routes.ts +31 -0
  103. package/template/src/server/module-auth/services/auth-service.ts +100 -0
  104. package/template/src/server/module-content/module.ts +10 -4
  105. package/template/src/server/module-content/routes/public-content-routes.ts +2 -2
  106. package/template/src/server/module-content/routes/topics-routes.ts +205 -0
  107. package/template/src/server/module-content/services/content-service.ts +18 -2
  108. package/template/src/server/module-dispute/routes/dispute-routes.ts +2 -1
  109. package/template/src/server/module-dispute/services/dispute-service.ts +1 -1
  110. package/template/src/server/module-notifications/__tests__/sse-rpc.test.ts +14 -14
  111. package/template/src/server/module-notifications/routes/notification-routes.ts +34 -8
  112. package/template/src/server/module-order/__tests__/order-route.test.ts +22 -2
  113. package/template/src/server/module-order/module.ts +15 -0
  114. package/template/src/server/module-order/routes/cart-routes.ts +103 -0
  115. package/template/src/server/module-order/routes/orders-mock-routes.ts +67 -0
  116. package/template/src/server/module-order/services/order-service.ts +1 -1
  117. package/template/src/server/module-plugin/__tests__/plugin-query-service.test.ts +203 -0
  118. package/template/src/server/module-plugin/__tests__/plugin-service.test.ts +234 -0
  119. package/template/src/server/module-plugin/index.ts +2 -0
  120. package/template/src/server/module-plugin/module.ts +52 -0
  121. package/template/src/server/module-plugin/routes/plugin-admin-routes.ts +261 -0
  122. package/template/src/server/module-plugin/routes/plugin-routes.ts +354 -0
  123. package/template/src/server/module-plugin/services/admin-category-service.ts +99 -0
  124. package/template/src/server/module-plugin/services/admin-plugin-service.ts +170 -0
  125. package/template/src/server/module-plugin/services/admin-stats-service.ts +41 -0
  126. package/template/src/server/module-plugin/services/plugin-query-service.ts +360 -0
  127. package/template/src/server/module-plugin/services/plugin-review-service.ts +95 -0
  128. package/template/src/server/module-plugin/services/plugin-service.ts +163 -0
  129. package/template/src/server/module-ticket/services/ticket-service.ts +1 -1
  130. package/template/src/server/module-todos/routes/todos-routes.ts +0 -4
  131. package/template/src/server/route-registry.ts +16 -1
  132. package/template/src/server/test-utils/test-client.ts +1 -2
  133. package/template/src/server/utils/auth.ts +6 -0
  134. package/template/src/server/utils/json.ts +13 -0
  135. package/template/src/shared/core/module-manifest.ts +3 -6
  136. package/template/src/shared/modules/auth/index.ts +12 -0
  137. package/template/src/shared/modules/auth/schemas.ts +50 -0
  138. package/template/src/shared/modules/cart/index.ts +1 -0
  139. package/template/src/shared/modules/cart/schemas.ts +41 -0
  140. package/template/src/shared/modules/community/index.ts +1 -0
  141. package/template/src/shared/modules/community/schemas.ts +58 -0
  142. package/template/src/shared/modules/dashboard/index.ts +1 -0
  143. package/template/src/shared/modules/dashboard/schemas.ts +35 -0
  144. package/template/src/shared/modules/index.ts +41 -0
  145. package/template/src/shared/modules/order/schemas.ts +29 -0
  146. package/template/src/shared/modules/plugins/index.ts +48 -0
  147. package/template/src/shared/modules/plugins/schemas.ts +227 -0
  148. package/template/src/shared/schemas/index.ts +130 -0
  149. package/template/tests/e2e/todo.spec.ts +23 -18
  150. package/template/tests/e2e/visual-screenshots.spec.ts +1824 -0
  151. package/template/uploads/.gitkeep +0 -0
  152. package/template/vite.config.ts +2 -1
  153. package/template/vitest.setup.ts +11 -0
  154. package/template/wrangler.toml +4 -3
  155. package/template/package-lock.json +0 -14554
@@ -0,0 +1,163 @@
1
+ import { eq } from 'drizzle-orm'
2
+ import type { Plugin, CreatePluginInput } from '@shared/schemas'
3
+ import { getDb } from '@server/db'
4
+ import { plugins, type PluginTable } from '@server/db/schema'
5
+ import { generateUUID } from '@server/utils/uuid'
6
+ import { NotFoundError, AuthorizationError, ConflictError } from '@server/utils/app-error'
7
+ import { parseJsonField, serializeJsonField } from '@server/utils/json'
8
+
9
+ function mapRow(row: PluginTable): Plugin {
10
+ return {
11
+ id: row.id,
12
+ name: row.name,
13
+ slug: row.slug,
14
+ description: row.description ?? '',
15
+ readme: row.readme ?? undefined,
16
+ authorId: row.authorId,
17
+ authorName: row.authorName,
18
+ repositoryUrl: row.repositoryUrl ?? undefined,
19
+ homepageUrl: row.homepageUrl ?? undefined,
20
+ npmPackage: row.npmPackage ?? undefined,
21
+ license: row.license ?? undefined,
22
+ version: row.version,
23
+ status: row.status as Plugin['status'],
24
+ downloadCount: row.downloadCount,
25
+ viewCount: row.viewCount,
26
+ featured: row.featured,
27
+ screenshotUrl: row.screenshotUrl ?? undefined,
28
+ siteUrls: parseJsonField<string[]>(row.siteUrls),
29
+ tags: parseJsonField<string[]>(row.tags),
30
+ commands: parseJsonField<Array<{ name: string; description?: string }>>(row.commands),
31
+ rejectReason: row.rejectReason ?? undefined,
32
+ createdAt: row.createdAt.getTime(),
33
+ updatedAt: row.updatedAt.getTime(),
34
+ }
35
+ }
36
+
37
+ export interface CreatePluginData extends CreatePluginInput {
38
+ authorId: string
39
+ authorName: string
40
+ screenshotUrl?: string
41
+ readme?: string
42
+ }
43
+
44
+ export async function createPlugin(data: CreatePluginData): Promise<Plugin> {
45
+ const db = await getDb()
46
+
47
+ const existing = await db.select().from(plugins).where(eq(plugins.slug, data.slug))
48
+ if (existing.length > 0) {
49
+ throw new ConflictError(`Plugin with slug '${data.slug}' already exists`)
50
+ }
51
+
52
+ const id = generateUUID()
53
+ const now = new Date()
54
+ const result = await db
55
+ .insert(plugins)
56
+ .values({
57
+ id,
58
+ name: data.name,
59
+ slug: data.slug,
60
+ description: data.description,
61
+ readme: data.readme ?? null,
62
+ authorId: data.authorId,
63
+ authorName: data.authorName,
64
+ repositoryUrl: data.repositoryUrl ?? null,
65
+ homepageUrl: data.homepageUrl ?? null,
66
+ npmPackage: data.npmPackage ?? null,
67
+ license: data.license ?? null,
68
+ version: '0.0.1',
69
+ status: 'pending',
70
+ screenshotUrl: data.screenshotUrl ?? null,
71
+ siteUrls: serializeJsonField(data.siteUrls),
72
+ tags: serializeJsonField(data.tags),
73
+ commands: serializeJsonField(data.commands),
74
+ createdAt: now,
75
+ updatedAt: now,
76
+ })
77
+ .returning()
78
+
79
+ return mapRow(result[0])
80
+ }
81
+
82
+ export interface UpdatePluginData {
83
+ name?: string
84
+ description?: string
85
+ readme?: string | null
86
+ repositoryUrl?: string | null
87
+ homepageUrl?: string | null
88
+ npmPackage?: string | null
89
+ license?: string | null
90
+ screenshotUrl?: string | null
91
+ siteUrls?: string[] | null
92
+ tags?: string[] | null
93
+ commands?: Array<{ name: string; description?: string | null }> | null
94
+ }
95
+
96
+ export async function updatePlugin(
97
+ slug: string,
98
+ data: UpdatePluginData,
99
+ userId: string
100
+ ): Promise<Plugin> {
101
+ const db = await getDb()
102
+
103
+ const rows = await db.select().from(plugins).where(eq(plugins.slug, slug))
104
+ if (rows.length === 0) {
105
+ throw new NotFoundError('Plugin', slug)
106
+ }
107
+
108
+ const existing = rows[0]
109
+ if (existing.authorId !== userId) {
110
+ throw new AuthorizationError('Only the plugin author can update this plugin')
111
+ }
112
+
113
+ const updateData: Partial<PluginTable> = {
114
+ updatedAt: new Date(),
115
+ }
116
+
117
+ if (data.name !== undefined) updateData.name = data.name
118
+ if (data.description !== undefined) updateData.description = data.description
119
+ if (data.readme !== undefined) updateData.readme = data.readme
120
+ if (data.repositoryUrl !== undefined) updateData.repositoryUrl = data.repositoryUrl
121
+ if (data.homepageUrl !== undefined) updateData.homepageUrl = data.homepageUrl
122
+ if (data.npmPackage !== undefined) updateData.npmPackage = data.npmPackage
123
+ if (data.license !== undefined) updateData.license = data.license
124
+ if (data.screenshotUrl !== undefined) updateData.screenshotUrl = data.screenshotUrl
125
+ if (data.siteUrls !== undefined) updateData.siteUrls = serializeJsonField(data.siteUrls)
126
+ if (data.tags !== undefined) updateData.tags = serializeJsonField(data.tags)
127
+ if (data.commands !== undefined) updateData.commands = serializeJsonField(data.commands)
128
+
129
+ const result = await db.update(plugins).set(updateData).where(eq(plugins.slug, slug)).returning()
130
+ return mapRow(result[0])
131
+ }
132
+
133
+ export async function deletePlugin(slug: string, userId: string): Promise<void> {
134
+ const db = await getDb()
135
+
136
+ const rows = await db.select().from(plugins).where(eq(plugins.slug, slug))
137
+ if (rows.length === 0) {
138
+ throw new NotFoundError('Plugin', slug)
139
+ }
140
+
141
+ const existing = rows[0]
142
+ if (existing.authorId !== userId) {
143
+ throw new AuthorizationError('Only the plugin author can delete this plugin')
144
+ }
145
+
146
+ await db.delete(plugins).where(eq(plugins.slug, slug))
147
+ }
148
+
149
+ export async function trackInstall(slug: string): Promise<void> {
150
+ const db = await getDb()
151
+
152
+ const rows = await db.select().from(plugins).where(eq(plugins.slug, slug))
153
+ if (rows.length === 0) {
154
+ throw new NotFoundError('Plugin', slug)
155
+ }
156
+
157
+ await db
158
+ .update(plugins)
159
+ .set({ downloadCount: rows[0].downloadCount + 1, updatedAt: new Date() })
160
+ .where(eq(plugins.slug, slug))
161
+ }
162
+
163
+ export { mapRow, parseJsonField, serializeJsonField }
@@ -68,7 +68,7 @@ function ticketWithoutReplies(row: TicketTable): Ticket {
68
68
 
69
69
  export async function seedTicketsIfEmpty(): Promise<void> {
70
70
  const db = await getDb()
71
- const existing = await db.select().from(tickets).all()
71
+ const existing = await db.select().from(tickets)
72
72
  if (existing.length === 0) {
73
73
  const PRIORITIES: TicketPriority[] = ['low', 'medium', 'high', 'urgent']
74
74
  const STATUSES: TicketStatus[] = [
@@ -23,8 +23,6 @@ const listRoute = createRoute({
23
23
  method: 'get',
24
24
  path: '/todos',
25
25
  tags: ['todos'],
26
- security: [{ Bearer: [] }],
27
- middleware: [authMiddleware()],
28
26
  responses: {
29
27
  200: successResponse(TodoListSchema, 'List all todos'),
30
28
  500: errorResponse('Internal server error'),
@@ -35,8 +33,6 @@ const getRoute = createRoute({
35
33
  method: 'get',
36
34
  path: '/todos/{id}',
37
35
  tags: ['todos'],
38
- security: [{ Bearer: [] }],
39
- middleware: [authMiddleware()],
40
36
  request: {
41
37
  params: z.object({ id: z.coerce.number().int().positive() }),
42
38
  },
@@ -7,6 +7,7 @@ import { auditLogRoutes } from './module-permission/routes/audit-log-routes'
7
7
  import { notificationRoutes } from './module-notifications/routes/notification-routes'
8
8
  import { chatRoutes } from './module-chat/routes/chat-routes'
9
9
  import { adminRoutes } from './module-admin/routes/admin-routes'
10
+ import { clientAuthRoutes } from './module-admin/routes/client-auth-routes'
10
11
  import { captchaRoutes } from './module-captcha/routes/captcha-routes'
11
12
  import { orderRoutes } from './module-order/routes/order-routes'
12
13
  import { ticketRoutes } from './module-ticket/routes/ticket-routes'
@@ -14,6 +15,13 @@ import { disputeRoutes } from './module-dispute/routes/dispute-routes'
14
15
  import { contentRoutes } from './module-content/routes/content-routes'
15
16
  import { publicContentRoutes } from './module-content/routes/public-content-routes'
16
17
  import { fileRoutes } from './module-file/routes/file-routes'
18
+ import { authRoutes } from './module-auth/routes/auth-routes'
19
+ import { pluginRoutes } from './module-plugin/routes/plugin-routes'
20
+ import { pluginAdminRoutes } from './module-plugin/routes/plugin-admin-routes'
21
+ import { dashboardRoutes } from './module-admin/routes/dashboard-routes'
22
+ import { cartRoutes } from './module-order/routes/cart-routes'
23
+ import { ordersMockRoutes } from './module-order/routes/orders-mock-routes'
24
+ import { topicsRoutes } from './module-content/routes/topics-routes'
17
25
 
18
26
  const apiRateLimit = rateLimitMiddleware({
19
27
  windowMs: 60_000,
@@ -21,13 +29,18 @@ const apiRateLimit = rateLimitMiddleware({
21
29
  message: 'Too many requests',
22
30
  })
23
31
 
24
- // 客户端路由 - 普通用户使用的 API
25
32
  export const clientApiRoutes = new OpenAPIHono()
26
33
  .use('*', apiRateLimit)
34
+ .route('/api', clientAuthRoutes)
35
+ .route('/api', authRoutes)
27
36
  .route('/api', chatRoutes)
28
37
  .route('/api', notificationRoutes)
29
38
  .route('/api', apiRoutes)
39
+ .route('/api', pluginRoutes)
30
40
  .route('/api', publicContentRoutes)
41
+ .route('/api', cartRoutes)
42
+ .route('/api', ordersMockRoutes)
43
+ .route('/api', topicsRoutes)
31
44
 
32
45
  // 管理后台路由 - 普通用户使用的 API + 管理功能
33
46
  export const adminApiRoutes = new OpenAPIHono()
@@ -41,6 +54,8 @@ export const adminApiRoutes = new OpenAPIHono()
41
54
  .route('/api', roleRoutes)
42
55
  .route('/api', auditLogRoutes)
43
56
  .route('/api', adminRoutes)
57
+ .route('/api', pluginAdminRoutes)
58
+ .route('/api', dashboardRoutes)
44
59
 
45
60
  // 导出类型
46
61
  export type ClientApiRoutes = typeof clientApiRoutes
@@ -51,8 +51,7 @@ export function createTestClient(baseUrl?: string, options?: TestClientOptions)
51
51
  })
52
52
  }
53
53
  return hc<AppType>('http://localhost', {
54
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
- fetch: (input: any, init?: any) => {
54
+ fetch: (input: RequestInfo | URL, init?: RequestInit) => {
56
55
  const request = new Request(input, init)
57
56
  Object.entries(defaultHeaders).forEach(([key, value]) => {
58
57
  if (!request.headers.has(key)) {
@@ -42,6 +42,8 @@ const mockTokens: Map<string, string> = new Map([
42
42
  ['user-token', '3'],
43
43
  ])
44
44
 
45
+ const userPasswordHashes: Map<string, string> = new Map()
46
+
45
47
  export function getAuthUser(c: Context): AuthUser {
46
48
  return c.get('authUser')
47
49
  }
@@ -63,3 +65,7 @@ export function getMockUsers(): User[] {
63
65
  export function getMockTokens(): Map<string, string> {
64
66
  return mockTokens
65
67
  }
68
+
69
+ export function getUserPasswordHashes(): Map<string, string> {
70
+ return userPasswordHashes
71
+ }
@@ -0,0 +1,13 @@
1
+ export function parseJsonField<T>(field: string | null | undefined): T | undefined {
2
+ if (!field) return undefined
3
+ try {
4
+ return JSON.parse(field) as T
5
+ } catch {
6
+ return undefined
7
+ }
8
+ }
9
+
10
+ export function serializeJsonField<T>(value: T | undefined | null): string | null {
11
+ if (value === undefined || value === null) return null
12
+ return JSON.stringify(value)
13
+ }
@@ -28,12 +28,9 @@ export interface ModuleManifest {
28
28
  /** Route registration info */
29
29
  routes: {
30
30
  /** Routes mounted under client API (/api) */
31
- client?: {
32
- /** Import path to the route variable (relative to module dir) */
33
- importPath: string
34
- /** Exported variable name from the route file */
35
- exportName: string
36
- }
31
+ client?:
32
+ | { importPath: string; exportName: string }
33
+ | { importPath: string; exportName: string }[]
37
34
  /** Routes mounted under admin API (/api/admin) */
38
35
  admin?: {
39
36
  importPath: string
@@ -0,0 +1,12 @@
1
+ export {
2
+ DeveloperProfileSchema,
3
+ LoginSchema,
4
+ RegisterSchema,
5
+ TokenResponseSchema,
6
+ ProfileSchema,
7
+ type DeveloperProfile,
8
+ type LoginInput,
9
+ type RegisterInput,
10
+ type TokenResponse,
11
+ type Profile,
12
+ } from './schemas'
@@ -0,0 +1,50 @@
1
+ import { z } from '@hono/zod-openapi'
2
+
3
+ export const DeveloperProfileSchema = z.object({
4
+ id: z.string(),
5
+ username: z.string(),
6
+ email: z.string().email(),
7
+ role: z.string(),
8
+ createdAt: z.string().datetime(),
9
+ })
10
+
11
+ export const LoginSchema = z
12
+ .object({
13
+ account: z.string().nullish(),
14
+ email: z.string().email().nullish(),
15
+ password: z.string().min(6),
16
+ })
17
+ .refine(data => data.account || data.email, {
18
+ message: 'Either account or email is required',
19
+ path: ['account'],
20
+ })
21
+
22
+ export const RegisterSchema = z.object({
23
+ username: z.string().min(3).max(50),
24
+ email: z.string().email(),
25
+ password: z.string().min(6).max(100),
26
+ })
27
+
28
+ export const TokenResponseSchema = z.object({
29
+ token: z.string(),
30
+ profile: DeveloperProfileSchema,
31
+ })
32
+
33
+ export const ProfileSchema = z.object({
34
+ id: z.string(),
35
+ username: z.string(),
36
+ email: z.string().email(),
37
+ bio: z.string().optional().nullable(),
38
+ joinDate: z.string(),
39
+ stats: z.object({
40
+ posts: z.number(),
41
+ followers: z.number(),
42
+ following: z.number(),
43
+ }),
44
+ })
45
+
46
+ export type DeveloperProfile = z.infer<typeof DeveloperProfileSchema>
47
+ export type LoginInput = z.infer<typeof LoginSchema>
48
+ export type RegisterInput = z.infer<typeof RegisterSchema>
49
+ export type TokenResponse = z.infer<typeof TokenResponseSchema>
50
+ export type Profile = z.infer<typeof ProfileSchema>
@@ -0,0 +1 @@
1
+ export * from './schemas'
@@ -0,0 +1,41 @@
1
+ import { z } from '@hono/zod-openapi'
2
+
3
+ export const CartItemSchema = z.object({
4
+ id: z.number(),
5
+ name: z.string(),
6
+ variant: z.string(),
7
+ price: z.number(),
8
+ quantity: z.number(),
9
+ color: z.string(),
10
+ })
11
+
12
+ export const CartSummarySchema = z.object({
13
+ subtotal: z.number(),
14
+ shipping: z.number(),
15
+ tax: z.number(),
16
+ total: z.number(),
17
+ totalItems: z.number(),
18
+ })
19
+
20
+ export const CartResponseSchema = z.object({
21
+ items: z.array(CartItemSchema),
22
+ summary: CartSummarySchema,
23
+ })
24
+
25
+ export const AddCartItemSchema = z.object({
26
+ id: z.number(),
27
+ name: z.string(),
28
+ variant: z.string(),
29
+ price: z.number(),
30
+ quantity: z.number().min(1).default(1),
31
+ color: z.string(),
32
+ })
33
+
34
+ export const CartItemIdSchema = z.object({
35
+ id: z.string(),
36
+ })
37
+
38
+ export type CartItem = z.infer<typeof CartItemSchema>
39
+ export type CartSummary = z.infer<typeof CartSummarySchema>
40
+ export type CartResponse = z.infer<typeof CartResponseSchema>
41
+ export type AddCartItemInput = z.infer<typeof AddCartItemSchema>
@@ -0,0 +1 @@
1
+ export * from './schemas'
@@ -0,0 +1,58 @@
1
+ import { z } from '@hono/zod-openapi'
2
+
3
+ export const TopicStatusSchema = z.enum(['hot', 'unanswered', 'solved'])
4
+
5
+ export const TopicTagSchema = z.object({
6
+ label: z.string(),
7
+ color: z.string(),
8
+ })
9
+
10
+ export const TopicAuthorSchema = z.object({
11
+ name: z.string(),
12
+ initials: z.string(),
13
+ })
14
+
15
+ export const TopicSchema = z.object({
16
+ id: z.string(),
17
+ title: z.string(),
18
+ excerpt: z.string(),
19
+ votes: z.number(),
20
+ replyCount: z.number(),
21
+ viewCount: z.number(),
22
+ status: TopicStatusSchema,
23
+ tags: z.array(TopicTagSchema),
24
+ author: TopicAuthorSchema,
25
+ createdAt: z.string(),
26
+ })
27
+
28
+ export const TopicsResponseSchema = z.array(TopicSchema)
29
+
30
+ export const ProfileStatsSchema = z.object({
31
+ topics: z.number(),
32
+ replies: z.number(),
33
+ likes: z.number(),
34
+ })
35
+
36
+ export const ActivityTypeSchema = z.enum(['topic', 'reply', 'like'])
37
+
38
+ export const ProfileActivitySchema = z.object({
39
+ id: z.string(),
40
+ type: ActivityTypeSchema,
41
+ text: z.string(),
42
+ target: z.string(),
43
+ time: z.string(),
44
+ })
45
+
46
+ export const ProfileResponseSchema = z.object({
47
+ stats: ProfileStatsSchema,
48
+ activity: z.array(ProfileActivitySchema),
49
+ })
50
+
51
+ export type TopicStatus = z.infer<typeof TopicStatusSchema>
52
+ export type TopicTag = z.infer<typeof TopicTagSchema>
53
+ export type TopicAuthor = z.infer<typeof TopicAuthorSchema>
54
+ export type Topic = z.infer<typeof TopicSchema>
55
+ export type ProfileStats = z.infer<typeof ProfileStatsSchema>
56
+ export type ActivityType = z.infer<typeof ActivityTypeSchema>
57
+ export type ProfileActivity = z.infer<typeof ProfileActivitySchema>
58
+ export type ProfileResponse = z.infer<typeof ProfileResponseSchema>
@@ -0,0 +1 @@
1
+ export * from './schemas'
@@ -0,0 +1,35 @@
1
+ import { z } from '@hono/zod-openapi'
2
+
3
+ export const DashboardStatSchema = z.object({
4
+ label: z.string(),
5
+ value: z.string(),
6
+ trend: z.number(),
7
+ })
8
+
9
+ export const RevenueDataSchema = z.object({
10
+ month: z.string(),
11
+ value: z.number(),
12
+ })
13
+
14
+ export const ActivityStatusSchema = z.enum(['Active', 'Pending', 'Inactive'])
15
+
16
+ export const ActivitySchema = z.object({
17
+ id: z.number(),
18
+ user: z.string(),
19
+ action: z.string(),
20
+ date: z.string(),
21
+ status: ActivityStatusSchema,
22
+ })
23
+
24
+ export const DashboardResponseSchema = z.object({
25
+ stats: z.array(DashboardStatSchema),
26
+ revenue: z.array(RevenueDataSchema),
27
+ userGrowth: z.array(RevenueDataSchema),
28
+ activity: z.array(ActivitySchema),
29
+ })
30
+
31
+ export type DashboardStat = z.infer<typeof DashboardStatSchema>
32
+ export type RevenueData = z.infer<typeof RevenueDataSchema>
33
+ export type ActivityStatus = z.infer<typeof ActivityStatusSchema>
34
+ export type Activity = z.infer<typeof ActivitySchema>
35
+ export type DashboardResponse = z.infer<typeof DashboardResponseSchema>
@@ -68,3 +68,44 @@ export {
68
68
  type PermissionInfo,
69
69
  type UserPermissions,
70
70
  } from './permission'
71
+ export {
72
+ DeveloperProfileSchema,
73
+ LoginSchema,
74
+ RegisterSchema,
75
+ TokenResponseSchema,
76
+ type DeveloperProfile,
77
+ type LoginInput,
78
+ type RegisterInput,
79
+ type TokenResponse,
80
+ } from './auth'
81
+ export {
82
+ PluginSchema,
83
+ PluginStatusSchema,
84
+ CreatePluginSchema,
85
+ UpdatePluginSchema,
86
+ PluginVersionStatusSchema,
87
+ VersionSchema,
88
+ CategorySchema,
89
+ ReviewSchema,
90
+ CreateReviewSchema,
91
+ MarketplaceStatsSchema,
92
+ PluginListResponseSchema,
93
+ AdminPluginSchema,
94
+ AdminDashboardStatsSchema,
95
+ PluginListQuerySchema,
96
+ PluginSlugSchema,
97
+ type Plugin,
98
+ type PluginStatus,
99
+ type CreatePluginInput,
100
+ type UpdatePluginInput,
101
+ type PluginVersionStatus,
102
+ type Version,
103
+ type Category,
104
+ type Review,
105
+ type CreateReviewInput,
106
+ type MarketplaceStats,
107
+ type PluginListResponse,
108
+ type AdminPlugin,
109
+ type AdminDashboardStats,
110
+ type PluginListQuery,
111
+ } from './plugins'
@@ -61,3 +61,32 @@ export type DeleteResult = z.infer<typeof DeleteResultSchema>
61
61
  export type ProcessOrderInput = z.infer<typeof ProcessOrderSchema>
62
62
  export type CancelOrderInput = z.infer<typeof CancelOrderSchema>
63
63
  export type OrderQueryInput = z.infer<typeof OrderQuerySchema>
64
+
65
+ export const RemoveCartItemResponseSchema = z.object({ removedId: z.string() })
66
+
67
+ export const ECommerceProductSchema = z.object({
68
+ name: z.string(),
69
+ color: z.string(),
70
+ })
71
+
72
+ export const ECommerceOrderStatusSchema = z.enum([
73
+ 'processing',
74
+ 'shipped',
75
+ 'delivered',
76
+ 'cancelled',
77
+ ])
78
+
79
+ export const ECommerceOrderSchema = z.object({
80
+ id: z.string(),
81
+ date: z.string(),
82
+ status: ECommerceOrderStatusSchema,
83
+ products: z.array(ECommerceProductSchema),
84
+ total: z.number(),
85
+ })
86
+
87
+ export const ECommerceOrderListSchema = z.array(ECommerceOrderSchema)
88
+
89
+ export type RemoveCartItemResponse = z.infer<typeof RemoveCartItemResponseSchema>
90
+ export type ECommerceProduct = z.infer<typeof ECommerceProductSchema>
91
+ export type ECommerceOrderStatus = z.infer<typeof ECommerceOrderStatusSchema>
92
+ export type ECommerceOrder = z.infer<typeof ECommerceOrderSchema>
@@ -0,0 +1,48 @@
1
+ export {
2
+ PluginSchema,
3
+ PluginStatusSchema,
4
+ CreatePluginSchema,
5
+ UpdatePluginSchema,
6
+ PluginVersionStatusSchema,
7
+ VersionSchema,
8
+ CategorySchema,
9
+ ReviewSchema,
10
+ CreateReviewSchema,
11
+ MarketplaceStatsSchema,
12
+ PluginListResponseSchema,
13
+ AdminPluginSchema,
14
+ AdminDashboardStatsSchema,
15
+ PluginListQuerySchema,
16
+ PluginSlugSchema,
17
+ PluginSearchQuerySchema,
18
+ PluginDeleteResponseSchema,
19
+ ReviewIdParamsSchema,
20
+ ReviewDeleteResponseSchema,
21
+ CategorySlugParamsSchema,
22
+ CategoryPluginsQuerySchema,
23
+ PluginListAdminSchema,
24
+ AdminListQuerySchema,
25
+ AdminListAllQuerySchema,
26
+ RejectPluginBodySchema,
27
+ BulkApproveBodySchema,
28
+ BulkRejectBodySchema,
29
+ BulkResponseSchema,
30
+ CreateCategoryBodySchema,
31
+ UpdateCategoryBodySchema,
32
+ CategoryIdParamsSchema,
33
+ CategoryIdResponseSchema,
34
+ type Plugin,
35
+ type PluginStatus,
36
+ type CreatePluginInput,
37
+ type UpdatePluginInput,
38
+ type PluginVersionStatus,
39
+ type Version,
40
+ type Category,
41
+ type Review,
42
+ type CreateReviewInput,
43
+ type MarketplaceStats,
44
+ type PluginListResponse,
45
+ type AdminPlugin,
46
+ type AdminDashboardStats,
47
+ type PluginListQuery,
48
+ } from './schemas'