create-bubbles 0.1.25 → 0.1.26

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 (203) hide show
  1. package/dist/index.js +3 -9
  2. package/package.json +1 -1
  3. package/template-nextjs-vinext-eslint/Dockerfile +4 -4
  4. package/template-nextjs-vinext-eslint/README.md +6 -2
  5. package/template-nextjs-vinext-eslint/docker-compose.yaml +4 -4
  6. package/template-nextjs-vinext-eslint/eslint.config.js +2 -0
  7. package/template-nextjs-vinext-eslint/package.json +28 -26
  8. package/template-nextjs-vinext-eslint/pnpm-workspace.yaml +13 -1
  9. package/template-nextjs-vinext-eslint/src/utils/request/core/index.ts +63 -45
  10. package/template-nextjs-vinext-eslint/src/utils/request/core/utils.ts +12 -6
  11. package/template-nextjs-vinext-eslint/vite.config.ts +1 -1
  12. package/template-react-rsbuild-biome/.agents/rules/SVG/344/275/277/347/224/250.md +8 -0
  13. package/template-react-rsbuild-biome/AGENTS.md +11 -0
  14. package/template-react-rsbuild-biome/index.html +2 -2
  15. package/template-react-rsbuild-biome/package.json +25 -24
  16. package/template-react-rsbuild-biome/pnpm-workspace.yaml +5 -0
  17. package/template-react-rsbuild-biome/rsbuild.config.ts +1 -1
  18. package/template-react-rsbuild-biome/src/assets/icon/logo.svg +1 -1
  19. package/template-react-rsbuild-biome/src/assets/svg/draft.svg +4 -0
  20. package/template-react-rsbuild-biome/src/components/Icon/svg-icon/README.md +37 -0
  21. package/template-react-rsbuild-biome/src/components/Icon/svg-icon/SvgIcon.tsx +27 -0
  22. package/template-react-rsbuild-biome/src/env.d.ts +7 -0
  23. package/template-react-rsbuild-biome/src/utils/request/index.ts +3 -4
  24. package/template-react-rsbuild-biome/tsconfig.json +1 -2
  25. package/template-taro-react-oxc/.oxlintrc.json +0 -1
  26. package/template-taro-react-oxc/package.json +53 -52
  27. package/template-taro-react-oxc/pnpm-workspace.yaml +46 -0
  28. package/template-taro-vue-eslint/package.json +56 -56
  29. package/template-vp-monorepo-react-hono/apps/web/.agents/rules/SVG/344/275/277/347/224/250.md +8 -0
  30. package/template-vp-monorepo-react-hono/apps/web/AGENTS.md +11 -0
  31. package/template-vp-monorepo-react-hono/apps/web/package.json +1 -0
  32. package/template-vp-monorepo-react-hono/apps/web/src/assets/svg/draft.svg +4 -0
  33. package/template-vp-monorepo-react-hono/apps/web/src/components/Icon/svg-icon/README.md +37 -0
  34. package/template-vp-monorepo-react-hono/apps/web/src/components/Icon/svg-icon/SvgIcon.tsx +27 -0
  35. package/template-vp-monorepo-react-hono/apps/web/tsconfig.json +1 -1
  36. package/template-vp-monorepo-react-hono/apps/web/vite.config.ts +2 -0
  37. package/template-vp-monorepo-react-hono/pnpm-workspace.yaml +1 -0
  38. package/template-vp-monorepo-react-nestjs/.agents/rules//344/273/243/347/240/201/350/256/276/350/256/241.md +4 -0
  39. package/template-vp-monorepo-react-nestjs/.agents/rules//345/205/261/344/272/253/344/273/243/347/240/201.md +4 -0
  40. package/template-vp-monorepo-react-nestjs/.spaces/01./344/274/201/344/270/232/347/272/247/351/241/271/347/233/256/347/272/247//351/234/200/346/261/202/346/226/207/346/241/243.md +177 -0
  41. package/template-vp-monorepo-react-nestjs/.spaces/server/04.upload.md +4504 -0
  42. package/template-vp-monorepo-react-nestjs/.spaces/server/05./344/270/212/344/274/240/346/224/271/351/200/240/346/226/271/346/241/210/README.md +858 -0
  43. package/template-vp-monorepo-react-nestjs/.spaces/server/06./345/205/254/345/205/261/351/230/237/345/210/227/346/216/245/345/205/245/346/226/271/346/241/210/README.md +1890 -0
  44. package/template-vp-monorepo-react-nestjs/.spaces/server/06./345/205/254/345/205/261/351/230/237/345/210/227/346/216/245/345/205/245/346/226/271/346/241/210//344/270/212/344/274/240/346/270/205/347/220/206.md +1465 -0
  45. package/template-vp-monorepo-react-nestjs/AGENTS.md +8 -12
  46. package/template-vp-monorepo-react-nestjs/apps/server/.agents/rules/error-handling.md +84 -0
  47. package/template-vp-monorepo-react-nestjs/apps/server/.env.development +60 -0
  48. package/template-vp-monorepo-react-nestjs/apps/server/.env.production +16 -0
  49. package/template-vp-monorepo-react-nestjs/apps/server/AGENTS.md +9 -0
  50. package/template-vp-monorepo-react-nestjs/apps/server/drizzle/0000_military_colonel_america.sql +11 -0
  51. package/template-vp-monorepo-react-nestjs/apps/server/drizzle/0001_loud_siren.sql +27 -0
  52. package/template-vp-monorepo-react-nestjs/apps/server/drizzle/meta/0000_snapshot.json +97 -0
  53. package/template-vp-monorepo-react-nestjs/apps/server/drizzle/meta/0001_snapshot.json +354 -0
  54. package/template-vp-monorepo-react-nestjs/apps/server/drizzle/meta/_journal.json +20 -0
  55. package/template-vp-monorepo-react-nestjs/apps/server/package.json +4 -0
  56. package/template-vp-monorepo-react-nestjs/apps/server/src/app.module.ts +14 -2
  57. package/template-vp-monorepo-react-nestjs/apps/server/src/common/adapters/fastify.adapter.ts +6 -0
  58. package/template-vp-monorepo-react-nestjs/apps/server/src/config/index.ts +2 -0
  59. package/template-vp-monorepo-react-nestjs/apps/server/src/config/queue.config.ts +105 -0
  60. package/template-vp-monorepo-react-nestjs/apps/server/src/config/storage.config.ts +33 -0
  61. package/template-vp-monorepo-react-nestjs/apps/server/src/database/schema.ts +57 -1
  62. package/template-vp-monorepo-react-nestjs/apps/server/src/main.ts +1 -0
  63. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/auth/auth.service.ts +1 -3
  64. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/auth/dto/auth-repsponse.dto.ts +2 -2
  65. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/task-queue/queue-worker.bootstrap.ts +24 -0
  66. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/task-queue/task-queue.constants.ts +24 -0
  67. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/task-queue/task-queue.contracts.ts +35 -0
  68. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/task-queue/task-queue.module.ts +56 -0
  69. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/task-queue/task-queue.processor.ts +62 -0
  70. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/task-queue/task-queue.service.ts +58 -0
  71. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/test/error-catelog.spec.ts +34 -0
  72. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/dto/initiate-mutipart-upload.dto.ts +26 -0
  73. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/dto/upload-params.dto.ts +15 -0
  74. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/storage/exact-size.transform.ts +66 -0
  75. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/storage/minio-storage.adapter.ts +239 -0
  76. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/storage/storage.port.ts +61 -0
  77. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/upload.constants.ts +23 -0
  78. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/upload.controller.ts +109 -0
  79. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/upload.errors.ts +80 -0
  80. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/upload.module.ts +20 -0
  81. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/upload.reponsitory.ts +166 -0
  82. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/upload.service.ts +696 -0
  83. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/utils/object-key.ts +10 -0
  84. package/template-vp-monorepo-react-nestjs/apps/server/test/task-queue.integration.spec.ts +120 -0
  85. package/template-vp-monorepo-react-nestjs/apps/server/test/task-queue.processor.spec.ts +63 -0
  86. package/template-vp-monorepo-react-nestjs/apps/server/test/task-queue.service.spec.ts +106 -0
  87. package/template-vp-monorepo-react-nestjs/apps/web/.agents/rules/SVG/344/275/277/347/224/250.md +8 -0
  88. package/template-vp-monorepo-react-nestjs/apps/web/.agents/rules//345/274/271/347/252/227/347/273/204/344/273/266.md +4 -0
  89. package/template-vp-monorepo-react-nestjs/apps/web/.agents/rules//346/226/207/344/273/266/346/213/206/345/210/206.md +4 -0
  90. package/template-vp-monorepo-react-nestjs/apps/web/.agents/rules//347/273/204/344/273/266/345/221/275/345/220/215.md +3 -0
  91. package/template-vp-monorepo-react-nestjs/apps/web/.agents/rules//350/241/250/346/240/274/351/241/265/351/235/242.md +11 -0
  92. package/template-vp-monorepo-react-nestjs/apps/web/.agents/rules//351/241/265/351/235/242/346/216/245/345/217/243.md +3 -0
  93. package/template-vp-monorepo-react-nestjs/apps/web/.env.dev +1 -1
  94. package/template-vp-monorepo-react-nestjs/apps/web/AGENTS.md +16 -0
  95. package/template-vp-monorepo-react-nestjs/apps/web/README.MD +153 -0
  96. package/template-vp-monorepo-react-nestjs/apps/web/index.html +3 -2
  97. package/template-vp-monorepo-react-nestjs/apps/web/package.json +6 -1
  98. package/template-vp-monorepo-react-nestjs/apps/web/public/favicon.svg +9 -1
  99. package/template-vp-monorepo-react-nestjs/apps/web/src/App.module.css +5 -0
  100. package/template-vp-monorepo-react-nestjs/apps/web/src/App.tsx +16 -9
  101. package/template-vp-monorepo-react-nestjs/apps/web/src/api/auth.ts +9 -0
  102. package/template-vp-monorepo-react-nestjs/apps/web/src/assets/svg/draft.svg +4 -0
  103. package/template-vp-monorepo-react-nestjs/apps/web/src/components/DraftProTable/DraftProTable.module.css +78 -0
  104. package/template-vp-monorepo-react-nestjs/apps/web/src/components/DraftProTable/DraftProTable.tsx +99 -0
  105. package/template-vp-monorepo-react-nestjs/apps/web/src/components/DraftProTable/README.md +34 -0
  106. package/template-vp-monorepo-react-nestjs/apps/web/src/components/FullHeightProTable/FullHeightProTable.module.css +54 -0
  107. package/template-vp-monorepo-react-nestjs/apps/web/src/components/FullHeightProTable/FullHeightProTable.tsx +41 -0
  108. package/template-vp-monorepo-react-nestjs/apps/web/src/components/Icon/svg-icon/README.md +37 -0
  109. package/template-vp-monorepo-react-nestjs/apps/web/src/components/Icon/svg-icon/SvgIcon.tsx +27 -0
  110. package/template-vp-monorepo-react-nestjs/apps/web/src/components/Loading/PageLoading.module.css +7 -0
  111. package/template-vp-monorepo-react-nestjs/apps/web/src/components/Loading/PageLoading.tsx +2 -1
  112. package/template-vp-monorepo-react-nestjs/apps/web/src/config/theme.ts +20 -0
  113. package/template-vp-monorepo-react-nestjs/apps/web/src/layouts/BasicLayout.module.css +17 -0
  114. package/template-vp-monorepo-react-nestjs/apps/web/src/layouts/BasicLayout.tsx +106 -0
  115. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/i18n/index.module.css +23 -0
  116. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/i18n/index.tsx +53 -0
  117. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/components/ProjectFormDialog.tsx +132 -0
  118. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/config/columns.tsx +121 -0
  119. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/config/index.ts +77 -0
  120. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/draft/components/DraftProjectFormDialog.tsx +140 -0
  121. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/draft/config/columns.tsx +127 -0
  122. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/draft/config/index.ts +39 -0
  123. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/draft/config/projects.ts +48 -0
  124. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/draft/index.module.css +49 -0
  125. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/draft/index.tsx +163 -0
  126. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/index.module.css +53 -0
  127. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/index.tsx +135 -0
  128. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/home/index.module.css +23 -0
  129. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/home/index.tsx +26 -43
  130. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/login/api.ts +11 -0
  131. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/login/index.tsx +183 -0
  132. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/login/login.css +164 -0
  133. package/template-vp-monorepo-react-nestjs/apps/web/src/router/index.tsx +36 -12
  134. package/template-vp-monorepo-react-nestjs/apps/web/src/styles/index.css +11 -4
  135. package/template-vp-monorepo-react-nestjs/apps/web/src/utils/request/core/index.ts +55 -47
  136. package/template-vp-monorepo-react-nestjs/apps/web/src/utils/request/index.ts +10 -5
  137. package/template-vp-monorepo-react-nestjs/apps/web/src/utils/storage/cookie.ts +50 -0
  138. package/template-vp-monorepo-react-nestjs/apps/web/src/utils/storage/session.ts +33 -0
  139. package/template-vp-monorepo-react-nestjs/apps/web/test/draft-projects.spec.ts +89 -0
  140. package/template-vp-monorepo-react-nestjs/apps/web/test/request.spec.ts +145 -0
  141. package/template-vp-monorepo-react-nestjs/apps/web/test/storage.spec.ts +108 -0
  142. package/template-vp-monorepo-react-nestjs/apps/web/test/tsconfig.json +7 -0
  143. package/template-vp-monorepo-react-nestjs/apps/web/tsconfig.json +1 -1
  144. package/template-vp-monorepo-react-nestjs/apps/web/vite.config.ts +7 -0
  145. package/template-vp-monorepo-react-nestjs/apps/web-vue/tsconfig.tsbuildinfo +1 -0
  146. package/template-vp-monorepo-react-nestjs/docker-compose.yml +19 -0
  147. package/template-vp-monorepo-react-nestjs/mise.toml +1 -1
  148. package/template-vp-monorepo-react-nestjs/package.json +1 -6
  149. package/template-vp-monorepo-react-nestjs/pnpm-workspace.yaml +9 -1
  150. package/template-vp-react/.agents/rules/SVG/344/275/277/347/224/250.md +8 -0
  151. package/template-vp-react/AGENTS.md +11 -0
  152. package/template-vp-react/commitlint.config.js +0 -0
  153. package/template-vp-react/package.json +21 -26
  154. package/template-vp-react/pnpm-workspace.yaml +6 -0
  155. package/template-vp-react/src/assets/svg/draft.svg +4 -0
  156. package/template-vp-react/src/components/Icon/svg-icon/README.md +37 -0
  157. package/template-vp-react/src/components/Icon/svg-icon/SvgIcon.tsx +27 -0
  158. package/template-vp-react/src/utils/request/index.ts +18 -5
  159. package/template-vp-react/tsconfig.json +2 -3
  160. package/template-vp-react/vite.config.ts +2 -0
  161. package/template-vp-react-shadcn/.agents/rules/SVG/344/275/277/347/224/250.md +8 -0
  162. package/template-vp-react-shadcn/.vscode/settings.json +2 -2
  163. package/template-vp-react-shadcn/{agents.md → AGENTS.md} +12 -2
  164. package/template-vp-react-shadcn/README.md +41 -0
  165. package/template-vp-react-shadcn/commitlint.config.js +1 -1
  166. package/template-vp-react-shadcn/components.json +1 -1
  167. package/template-vp-react-shadcn/package.json +25 -32
  168. package/template-vp-react-shadcn/pnpm-workspace.yaml +10 -0
  169. package/template-vp-react-shadcn/src/App.tsx +10 -10
  170. package/template-vp-react-shadcn/src/assets/svg/draft.svg +4 -0
  171. package/template-vp-react-shadcn/src/components/Icon/svg-icon/README.md +37 -0
  172. package/template-vp-react-shadcn/src/components/Icon/svg-icon/SvgIcon.tsx +27 -0
  173. package/template-vp-react-shadcn/src/components/Loading/PageLoading.tsx +4 -4
  174. package/template-vp-react-shadcn/src/components/Player/VideoJS/index.tsx +94 -78
  175. package/template-vp-react-shadcn/src/components/Player/index.ts +1 -1
  176. package/template-vp-react-shadcn/src/components/ui/button.tsx +25 -32
  177. package/template-vp-react-shadcn/src/components/ui/spinner.tsx +8 -7
  178. package/template-vp-react-shadcn/src/components/ui/toast.tsx +229 -0
  179. package/template-vp-react-shadcn/src/lib/utils.ts +3 -3
  180. package/template-vp-react-shadcn/src/main.tsx +5 -4
  181. package/template-vp-react-shadcn/src/pages/home/index.tsx +43 -35
  182. package/template-vp-react-shadcn/src/router/index.tsx +15 -15
  183. package/template-vp-react-shadcn/src/styles/index.css +4 -4
  184. package/template-vp-react-shadcn/src/styles/shadcn.css +2 -2
  185. package/template-vp-react-shadcn/src/utils/env/index.tsx +2 -2
  186. package/template-vp-react-shadcn/src/utils/request/alova-core/index.ts +102 -30
  187. package/template-vp-react-shadcn/src/utils/request/alova-core/utils.ts +5 -2
  188. package/template-vp-react-shadcn/src/utils/request/index.ts +32 -19
  189. package/template-vp-react-shadcn/src/utils/request/readme.md +41 -38
  190. package/template-vp-react-shadcn/tsconfig.json +2 -3
  191. package/template-vp-react-shadcn/vite.config.ts +29 -15
  192. package/template-vp-vue-eslint-vapor/package.json +26 -33
  193. package/template-vp-vue-eslint-vapor/pnpm-workspace.yaml +29 -1
  194. package/template-vue-vp-eslint/README.md +3 -10
  195. package/template-vue-vp-eslint/package.json +27 -34
  196. package/template-vue-vp-eslint/pnpm-workspace.yaml +27 -2
  197. package/template-vue-vp-eslint/src/layout/default/index.vue +4 -3
  198. package/template-vp-monorepo-react-nestjs/.agents/auth.config.ts +0 -0
  199. package/template-vp-monorepo-react-nestjs/.spaces/server/todo.md +0 -991
  200. package/template-vp-monorepo-react-nestjs/apps/server/README.md +0 -3
  201. package/template-vp-react-shadcn/.oxfmtrc.jsonc +0 -8
  202. package/template-vp-react-shadcn/.oxlintrc.jsonc +0 -3
  203. package/template-vp-react-shadcn/src/components/ui/sonner.tsx +0 -43
@@ -0,0 +1,354 @@
1
+ {
2
+ "id": "6bc757d2-f779-47e3-a923-ec2c16d9c10d",
3
+ "prevId": "4efaf73b-a515-4481-80da-b95ba325bf9f",
4
+ "version": "7",
5
+ "dialect": "postgresql",
6
+ "tables": {
7
+ "public.upload_sessions": {
8
+ "name": "upload_sessions",
9
+ "schema": "",
10
+ "columns": {
11
+ "id": {
12
+ "name": "id",
13
+ "type": "uuid",
14
+ "primaryKey": true,
15
+ "notNull": true
16
+ },
17
+ "owner_id": {
18
+ "name": "owner_id",
19
+ "type": "uuid",
20
+ "primaryKey": false,
21
+ "notNull": true
22
+ },
23
+ "client_upload_id": {
24
+ "name": "client_upload_id",
25
+ "type": "uuid",
26
+ "primaryKey": false,
27
+ "notNull": true
28
+ },
29
+ "bucket": {
30
+ "name": "bucket",
31
+ "type": "varchar(63)",
32
+ "primaryKey": false,
33
+ "notNull": true
34
+ },
35
+ "object_key": {
36
+ "name": "object_key",
37
+ "type": "varchar(1024)",
38
+ "primaryKey": false,
39
+ "notNull": true
40
+ },
41
+ "storage_upload_id": {
42
+ "name": "storage_upload_id",
43
+ "type": "text",
44
+ "primaryKey": false,
45
+ "notNull": true
46
+ },
47
+ "original_name": {
48
+ "name": "original_name",
49
+ "type": "varchar(255)",
50
+ "primaryKey": false,
51
+ "notNull": true
52
+ },
53
+ "content_type": {
54
+ "name": "content_type",
55
+ "type": "varchar(255)",
56
+ "primaryKey": false,
57
+ "notNull": true
58
+ },
59
+ "file_size": {
60
+ "name": "file_size",
61
+ "type": "bigint",
62
+ "primaryKey": false,
63
+ "notNull": true
64
+ },
65
+ "part_size": {
66
+ "name": "part_size",
67
+ "type": "integer",
68
+ "primaryKey": false,
69
+ "notNull": true
70
+ },
71
+ "total_parts": {
72
+ "name": "total_parts",
73
+ "type": "integer",
74
+ "primaryKey": false,
75
+ "notNull": true
76
+ },
77
+ "status": {
78
+ "name": "status",
79
+ "type": "upload_status",
80
+ "typeSchema": "public",
81
+ "primaryKey": false,
82
+ "notNull": true,
83
+ "default": "'uploading'"
84
+ },
85
+ "expires_at": {
86
+ "name": "expires_at",
87
+ "type": "timestamp with time zone",
88
+ "primaryKey": false,
89
+ "notNull": true
90
+ },
91
+ "object_etag": {
92
+ "name": "object_etag",
93
+ "type": "text",
94
+ "primaryKey": false,
95
+ "notNull": false
96
+ },
97
+ "created_at": {
98
+ "name": "created_at",
99
+ "type": "timestamp with time zone",
100
+ "primaryKey": false,
101
+ "notNull": true,
102
+ "default": "now()"
103
+ },
104
+ "updated_at": {
105
+ "name": "updated_at",
106
+ "type": "timestamp with time zone",
107
+ "primaryKey": false,
108
+ "notNull": true,
109
+ "default": "now()"
110
+ },
111
+ "completed_at": {
112
+ "name": "completed_at",
113
+ "type": "timestamp with time zone",
114
+ "primaryKey": false,
115
+ "notNull": false
116
+ }
117
+ },
118
+ "indexes": {
119
+ "upload_sessions_owner_client_uq": {
120
+ "name": "upload_sessions_owner_client_uq",
121
+ "columns": [
122
+ {
123
+ "expression": "owner_id",
124
+ "isExpression": false,
125
+ "asc": true,
126
+ "nulls": "last"
127
+ },
128
+ {
129
+ "expression": "client_upload_id",
130
+ "isExpression": false,
131
+ "asc": true,
132
+ "nulls": "last"
133
+ }
134
+ ],
135
+ "isUnique": true,
136
+ "concurrently": false,
137
+ "method": "btree",
138
+ "with": {}
139
+ },
140
+ "upload_sessions_bucket_key_uq": {
141
+ "name": "upload_sessions_bucket_key_uq",
142
+ "columns": [
143
+ {
144
+ "expression": "bucket",
145
+ "isExpression": false,
146
+ "asc": true,
147
+ "nulls": "last"
148
+ },
149
+ {
150
+ "expression": "object_key",
151
+ "isExpression": false,
152
+ "asc": true,
153
+ "nulls": "last"
154
+ }
155
+ ],
156
+ "isUnique": true,
157
+ "concurrently": false,
158
+ "method": "btree",
159
+ "with": {}
160
+ },
161
+ "upload_sessions_owner_status_expires_idx": {
162
+ "name": "upload_sessions_owner_status_expires_idx",
163
+ "columns": [
164
+ {
165
+ "expression": "owner_id",
166
+ "isExpression": false,
167
+ "asc": true,
168
+ "nulls": "last"
169
+ },
170
+ {
171
+ "expression": "status",
172
+ "isExpression": false,
173
+ "asc": true,
174
+ "nulls": "last"
175
+ },
176
+ {
177
+ "expression": "expires_at",
178
+ "isExpression": false,
179
+ "asc": true,
180
+ "nulls": "last"
181
+ }
182
+ ],
183
+ "isUnique": false,
184
+ "concurrently": false,
185
+ "method": "btree",
186
+ "with": {}
187
+ },
188
+ "upload_sessions_status_expires_idx": {
189
+ "name": "upload_sessions_status_expires_idx",
190
+ "columns": [
191
+ {
192
+ "expression": "status",
193
+ "isExpression": false,
194
+ "asc": true,
195
+ "nulls": "last"
196
+ },
197
+ {
198
+ "expression": "expires_at",
199
+ "isExpression": false,
200
+ "asc": true,
201
+ "nulls": "last"
202
+ }
203
+ ],
204
+ "isUnique": false,
205
+ "concurrently": false,
206
+ "method": "btree",
207
+ "with": {}
208
+ },
209
+ "upload_sessions_status_updated_idx": {
210
+ "name": "upload_sessions_status_updated_idx",
211
+ "columns": [
212
+ {
213
+ "expression": "status",
214
+ "isExpression": false,
215
+ "asc": true,
216
+ "nulls": "last"
217
+ },
218
+ {
219
+ "expression": "updated_at",
220
+ "isExpression": false,
221
+ "asc": true,
222
+ "nulls": "last"
223
+ }
224
+ ],
225
+ "isUnique": false,
226
+ "concurrently": false,
227
+ "method": "btree",
228
+ "with": {}
229
+ }
230
+ },
231
+ "foreignKeys": {
232
+ "upload_sessions_owner_id_users_id_fk": {
233
+ "name": "upload_sessions_owner_id_users_id_fk",
234
+ "tableFrom": "upload_sessions",
235
+ "tableTo": "users",
236
+ "columnsFrom": [
237
+ "owner_id"
238
+ ],
239
+ "columnsTo": [
240
+ "id"
241
+ ],
242
+ "onDelete": "restrict",
243
+ "onUpdate": "no action"
244
+ }
245
+ },
246
+ "compositePrimaryKeys": {},
247
+ "uniqueConstraints": {},
248
+ "policies": {},
249
+ "checkConstraints": {},
250
+ "isRLSEnabled": false
251
+ },
252
+ "public.users": {
253
+ "name": "users",
254
+ "schema": "",
255
+ "columns": {
256
+ "id": {
257
+ "name": "id",
258
+ "type": "uuid",
259
+ "primaryKey": true,
260
+ "notNull": true,
261
+ "default": "gen_random_uuid()"
262
+ },
263
+ "name": {
264
+ "name": "name",
265
+ "type": "varchar(100)",
266
+ "primaryKey": false,
267
+ "notNull": true
268
+ },
269
+ "account": {
270
+ "name": "account",
271
+ "type": "varchar(32)",
272
+ "primaryKey": false,
273
+ "notNull": true
274
+ },
275
+ "password_hash": {
276
+ "name": "password_hash",
277
+ "type": "varchar(255)",
278
+ "primaryKey": false,
279
+ "notNull": true
280
+ },
281
+ "status": {
282
+ "name": "status",
283
+ "type": "user_status",
284
+ "typeSchema": "public",
285
+ "primaryKey": false,
286
+ "notNull": true,
287
+ "default": "'active'"
288
+ },
289
+ "create_at": {
290
+ "name": "create_at",
291
+ "type": "timestamp",
292
+ "primaryKey": false,
293
+ "notNull": true,
294
+ "default": "now()"
295
+ },
296
+ "update_at": {
297
+ "name": "update_at",
298
+ "type": "timestamp with time zone",
299
+ "primaryKey": false,
300
+ "notNull": true,
301
+ "default": "now()"
302
+ }
303
+ },
304
+ "indexes": {},
305
+ "foreignKeys": {},
306
+ "compositePrimaryKeys": {},
307
+ "uniqueConstraints": {
308
+ "users_account_unique": {
309
+ "name": "users_account_unique",
310
+ "nullsNotDistinct": false,
311
+ "columns": [
312
+ "account"
313
+ ]
314
+ }
315
+ },
316
+ "policies": {},
317
+ "checkConstraints": {},
318
+ "isRLSEnabled": false
319
+ }
320
+ },
321
+ "enums": {
322
+ "public.upload_status": {
323
+ "name": "upload_status",
324
+ "schema": "public",
325
+ "values": [
326
+ "uploading",
327
+ "completing",
328
+ "completed",
329
+ "aborting",
330
+ "aborted",
331
+ "expired"
332
+ ]
333
+ },
334
+ "public.user_status": {
335
+ "name": "user_status",
336
+ "schema": "public",
337
+ "values": [
338
+ "active",
339
+ "locked",
340
+ "disabled"
341
+ ]
342
+ }
343
+ },
344
+ "schemas": {},
345
+ "sequences": {},
346
+ "roles": {},
347
+ "policies": {},
348
+ "views": {},
349
+ "_meta": {
350
+ "columns": {},
351
+ "schemas": {},
352
+ "tables": {}
353
+ }
354
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "version": "7",
3
+ "dialect": "postgresql",
4
+ "entries": [
5
+ {
6
+ "idx": 0,
7
+ "version": "7",
8
+ "when": 1786876205265,
9
+ "tag": "0000_military_colonel_america",
10
+ "breakpoints": true
11
+ },
12
+ {
13
+ "idx": 1,
14
+ "version": "7",
15
+ "when": 1787991631984,
16
+ "tag": "0001_loud_siren",
17
+ "breakpoints": true
18
+ }
19
+ ]
20
+ }
@@ -19,13 +19,17 @@
19
19
  "test": "vp test"
20
20
  },
21
21
  "dependencies": {
22
+ "@aws-sdk/client-s3": "^3.1120.0",
22
23
  "@nestjs-modules/ioredis": "catalog:",
24
+ "@nestjs/bullmq": "catalog:",
23
25
  "@nestjs/common": "catalog:",
24
26
  "@nestjs/config": "catalog:",
25
27
  "@nestjs/core": "catalog:",
26
28
  "@nestjs/mapped-types": "catalog:",
27
29
  "@nestjs/platform-fastify": "catalog:",
28
30
  "argon2": "catalog:",
31
+ "bullmq": "catalog:",
32
+ "dayjs": "catalog:",
29
33
  "drizzle-orm": "catalog:",
30
34
  "drizzle-zod": "catalog:",
31
35
  "fastify": "5.8.5",
@@ -1,4 +1,12 @@
1
- import { appConfig, databaseConfig, llmConfig, redisConfig, sessionConfig } from '@/config'
1
+ import {
2
+ appConfig,
3
+ databaseConfig,
4
+ llmConfig,
5
+ queueConfig,
6
+ redisConfig,
7
+ sessionConfig,
8
+ storageConfig,
9
+ } from '@/config'
2
10
  import { RedisModule } from '@nestjs-modules/ioredis'
3
11
  import { Module } from '@nestjs/common'
4
12
  import { ConfigModule, ConfigService } from '@nestjs/config'
@@ -9,8 +17,10 @@ import { AppService } from './app.service'
9
17
  import { GlobalExceptionFilter } from './common/filters/global-exception.filter'
10
18
  import { DatabaseModule } from './database/db.module'
11
19
  import { AuthModule } from './modules/auth/auth.module'
20
+ import { TaskQueueModule } from './modules/task-queue/task-queue.module'
12
21
  import { TestDbModule } from './modules/test/db/db.module'
13
22
  import { TestRedisModule } from './modules/test/redis/redis.module'
23
+ import { UploadModule } from './modules/upload/upload.module'
14
24
  import { ENV_ARR } from './utils/env-arr'
15
25
 
16
26
  @Module({
@@ -18,7 +28,7 @@ import { ENV_ARR } from './utils/env-arr'
18
28
  ConfigModule.forRoot({
19
29
  isGlobal: true,
20
30
  envFilePath: ENV_ARR,
21
- load: [appConfig, databaseConfig, llmConfig, redisConfig, sessionConfig],
31
+ load: [appConfig, databaseConfig, llmConfig, queueConfig, redisConfig, sessionConfig, storageConfig],
22
32
  }),
23
33
  DatabaseModule,
24
34
  RedisModule.forRootAsync({
@@ -37,9 +47,11 @@ import { ENV_ARR } from './utils/env-arr'
37
47
  },
38
48
  }),
39
49
  }),
50
+ TaskQueueModule,
40
51
  TestRedisModule,
41
52
  TestDbModule,
42
53
  AuthModule,
54
+ UploadModule,
43
55
  ],
44
56
  controllers: [AppController],
45
57
  providers: [
@@ -8,6 +8,12 @@ export function createFastifyAdapter() {
8
8
  logger: false,
9
9
  })
10
10
 
11
+ adapter
12
+ .getInstance()
13
+ .addContentTypeParser('application/octet-stream', (_request, payload, done) => {
14
+ done(null, payload)
15
+ })
16
+
11
17
  adapter.getInstance().addHook('onRequest', (request, reply, done) => {
12
18
  reply.header('x-request-id', request.id)
13
19
  done()
@@ -1,5 +1,7 @@
1
1
  export { default as appConfig } from './app.config'
2
2
  export { default as databaseConfig } from './database.config'
3
3
  export { default as llmConfig } from './llm.config'
4
+ export { default as queueConfig } from './queue.config'
4
5
  export { default as redisConfig } from './redis.config'
5
6
  export { default as sessionConfig } from './session.config'
7
+ export { default as storageConfig } from './storage.config'
@@ -0,0 +1,105 @@
1
+ import { registerAs } from '@nestjs/config'
2
+
3
+ export interface QueueConfig {
4
+ /** 队列专用 Redis 地址。来自 QUEUE_REDIS_HOST,缺省 localhost。独立于 Session Redis,不与其共用连接配置。 */
5
+ host: string
6
+ /** 队列专用 Redis 端口。来自 QUEUE_REDIS_PORT,缺省 6379,取值范围 1~65535。 */
7
+ port: number
8
+ /** Redis ACL 用户名(Redis 6+),来自 QUEUE_REDIS_USERNAME。可选,无认证开发环境可留空。 */
9
+ username?: string
10
+ /** Redis 密码,来自 QUEUE_REDIS_PASSWORD。可选;生产值应放部署 Secret,不提交进 Git。 */
11
+ password?: string
12
+ /**
13
+ * Redis 逻辑库编号。来自 QUEUE_REDIS_DB。
14
+ * 约定:开发环境 Session 用 DB 0、队列用 DB 1,便于隔离查看;生产建议独立 Redis 且用 DB 0;Redis Cluster 只支持 DB 0。
15
+ */
16
+ db: number
17
+ /**
18
+ * 所有队列 key 的统一前缀。来自 QUEUE_PREFIX,缺省 `bubbles:{NODE_ENV}:queue`。
19
+ * 用于区分项目和环境、避免不同环境撞键。注意:这是 BullMQ 自身的 prefix,不要配置 ioredis 的 keyPrefix。
20
+ */
21
+ prefix: string
22
+ /**
23
+ * 当前 NestJS 进程是否启动 BullMQ Worker(消费者)。来自 QUEUE_WORKER_ENABLED,严格解析 true/false。
24
+ * 设为 false 时仍可投递任务,但本进程不消费,为以后拆分独立 Worker 进程留出入口。
25
+ */
26
+ workerEnabled: boolean
27
+ /** 单个任务的总执行次数上限。来自 QUEUE_DEFAULT_ATTEMPTS,缺省 3。注意是“总共最多 3 次”,不是首次外加 3 次重试。 */
28
+ defaultAttempts: number
29
+ /** 重试退避的基础延迟(毫秒),配合指数退避使用。来自 QUEUE_BACKOFF_DELAY_MS,缺省 1000,即 1s、2s、4s…。 */
30
+ backoffDelayMs: number
31
+ /** 已完成任务(removeOnComplete.age)的保留时长(秒),超时自动清理。来自 QUEUE_COMPLETED_AGE_SECONDS,缺省 86400(24 小时)。 */
32
+ completedAgeSeconds: number
33
+ /** 已完成任务(removeOnComplete.count)的最大保留条数。来自 QUEUE_COMPLETED_COUNT,缺省 1000。与保留时长同时生效,防止 Redis 无限增长。 */
34
+ completedCount: number
35
+ /** 失败任务(removeOnFail.age)的保留时长(秒)。来自 QUEUE_FAILED_AGE_SECONDS,缺省 604800(7 天),比成功任务留得更久,便于排查。 */
36
+ failedAgeSeconds: number
37
+ /** 失败任务(removeOnFail.count)的最大保留条数。来自 QUEUE_FAILED_COUNT,缺省 5000。 */
38
+ failedCount: number
39
+ }
40
+
41
+ function readInteger(
42
+ name: string,
43
+ fallback: number,
44
+ minimum: number,
45
+ maximum = Number.MAX_SAFE_INTEGER,
46
+ ) {
47
+ const rawValue = (process.env[name] ?? String(fallback)).trim()
48
+ const value = Number(rawValue)
49
+
50
+ if (rawValue === '' || !Number.isSafeInteger(value) || value < minimum || value > maximum) {
51
+ throw new Error(name + ' must be an integer between ' + minimum + ' and ' + maximum)
52
+ }
53
+
54
+ return value
55
+ }
56
+
57
+ function readBoolean(name: string, fallback: boolean) {
58
+ const rawValue = process.env[name]
59
+
60
+ if (rawValue === undefined) {
61
+ return fallback
62
+ }
63
+
64
+ const value = rawValue.trim().toLowerCase()
65
+
66
+ if (value === 'true') {
67
+ return true
68
+ }
69
+
70
+ if (value === 'false') {
71
+ return false
72
+ }
73
+
74
+ throw new Error(name + ' must be true or false')
75
+ }
76
+
77
+ function readOptionalText(name: string) {
78
+ const value = process.env[name]?.trim()
79
+ return value ? value : undefined
80
+ }
81
+
82
+ export default registerAs('queue', (): QueueConfig => {
83
+ const environment = process.env.NODE_ENV ?? 'development'
84
+ const prefix = process.env.QUEUE_PREFIX?.trim() || ['bubbles', environment, 'queue'].join(':')
85
+
86
+ if (!prefix) {
87
+ throw new Error('QUEUE_PREFIX must not be empty')
88
+ }
89
+
90
+ return {
91
+ host: process.env.QUEUE_REDIS_HOST?.trim() || 'localhost',
92
+ port: readInteger('QUEUE_REDIS_PORT', 6379, 1, 65_535),
93
+ username: readOptionalText('QUEUE_REDIS_USERNAME'),
94
+ password: readOptionalText('QUEUE_REDIS_PASSWORD'),
95
+ db: readInteger('QUEUE_REDIS_DB', 1, 0),
96
+ prefix,
97
+ workerEnabled: readBoolean('QUEUE_WORKER_ENABLED', true),
98
+ defaultAttempts: readInteger('QUEUE_DEFAULT_ATTEMPTS', 3, 1),
99
+ backoffDelayMs: readInteger('QUEUE_BACKOFF_DELAY_MS', 1_000, 1),
100
+ completedAgeSeconds: readInteger('QUEUE_COMPLETED_AGE_SECONDS', 86_400, 1),
101
+ completedCount: readInteger('QUEUE_COMPLETED_COUNT', 1_000, 1),
102
+ failedAgeSeconds: readInteger('QUEUE_FAILED_AGE_SECONDS', 604_800, 1),
103
+ failedCount: readInteger('QUEUE_FAILED_COUNT', 5_000, 1),
104
+ }
105
+ })
@@ -0,0 +1,33 @@
1
+ import { registerAs } from '@nestjs/config'
2
+
3
+ const SESSION_TTL_MS = 24 * 60 * 60 * 1000
4
+ function readRequired(name: string): string {
5
+ const value = process.env[name]?.trim()
6
+
7
+ if (!value) {
8
+ throw new Error(`${name} is required`)
9
+ }
10
+
11
+ return value
12
+ }
13
+
14
+ function readEndpoint(): string {
15
+ const value = readRequired('STORAGE_ENDPOINT')
16
+ const url = new URL(value)
17
+
18
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
19
+ throw new Error('STORAGE_ENDPOINT must use http or https')
20
+ }
21
+
22
+ return url.toString().replace(/\/$/, '')
23
+ }
24
+
25
+ export default registerAs('storage', () => ({
26
+ endpoint: readEndpoint(),
27
+ region: process.env.STORAGE_REGION?.trim() || 'us-east-1',
28
+ accessKeyId: readRequired('STORAGE_ACCESS_KEY_ID'),
29
+ secretAccessKey: readRequired('STORAGE_SECRET_ACCESS_KEY'),
30
+ bucket: readRequired('STORAGE_BUCKET'),
31
+ forcePathStyle: true,
32
+ sessionTtlMs: SESSION_TTL_MS,
33
+ }))
@@ -1,4 +1,15 @@
1
- import { pgEnum, pgTable, timestamp, uuid, varchar } from 'drizzle-orm/pg-core'
1
+ import {
2
+ bigint,
3
+ index,
4
+ integer,
5
+ pgEnum,
6
+ pgTable,
7
+ text,
8
+ timestamp,
9
+ uniqueIndex,
10
+ uuid,
11
+ varchar,
12
+ } from 'drizzle-orm/pg-core'
2
13
 
3
14
  export const userStatusEnum = pgEnum('user_status', ['active', 'locked', 'disabled'])
4
15
 
@@ -11,3 +22,48 @@ export const users = pgTable('users', {
11
22
  createdAt: timestamp('create_at').defaultNow().notNull(),
12
23
  updatedAt: timestamp('update_at', { withTimezone: true }).defaultNow().notNull(),
13
24
  })
25
+
26
+ export const uploadStatusEnum = pgEnum('upload_status', [
27
+ 'uploading',
28
+ 'completing',
29
+ 'completed',
30
+ 'aborting',
31
+ 'aborted',
32
+ 'expired',
33
+ ])
34
+
35
+ export const uploadSessions = pgTable(
36
+ 'upload_sessions',
37
+ {
38
+ id: uuid('id').primaryKey(),
39
+ ownerId: uuid('owner_id')
40
+ .notNull()
41
+ .references(() => users.id, { onDelete: 'restrict' }),
42
+ clientUploadId: uuid('client_upload_id').notNull(),
43
+ bucket: varchar('bucket', { length: 63 }).notNull(),
44
+ objectKey: varchar('object_key', { length: 1024 }).notNull(),
45
+ storageUploadId: text('storage_upload_id').notNull(),
46
+ originalName: varchar('original_name', { length: 255 }).notNull(),
47
+ contentType: varchar('content_type', { length: 255 }).notNull(),
48
+ fileSize: bigint('file_size', { mode: 'number' }).notNull(),
49
+ partSize: integer('part_size').notNull(),
50
+ totalParts: integer('total_parts').notNull(),
51
+ status: uploadStatusEnum('status').default('uploading').notNull(),
52
+ expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
53
+ objectEtag: text('object_etag'),
54
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
55
+ updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
56
+ completedAt: timestamp('completed_at', { withTimezone: true }),
57
+ },
58
+ (table) => [
59
+ uniqueIndex('upload_sessions_owner_client_uq').on(table.ownerId, table.clientUploadId),
60
+ uniqueIndex('upload_sessions_bucket_key_uq').on(table.bucket, table.objectKey),
61
+ index('upload_sessions_owner_status_expires_idx').on(
62
+ table.ownerId,
63
+ table.status,
64
+ table.expiresAt,
65
+ ),
66
+ index('upload_sessions_status_expires_idx').on(table.status, table.expiresAt),
67
+ index('upload_sessions_status_updated_idx').on(table.status, table.updatedAt),
68
+ ],
69
+ )
@@ -14,6 +14,7 @@ async function bootstrap() {
14
14
  colors: process.env.NODE_ENV === 'development',
15
15
  }),
16
16
  })
17
+ app.enableShutdownHooks()
17
18
  app.enableCors({
18
19
  origin: ['*'],
19
20
  allowedHeaders: ['Content-Type', 'Authorization', 'X-Company-Id'],
@@ -100,9 +100,7 @@ export class AuthService {
100
100
 
101
101
  async getCurrentUser(userId: string): Promise<AuthUser> {
102
102
  const user = await this.useAuthInfrastrutrue(() =>
103
- this.authRepository.findPublicById(userId).catch((cause: unknown) => {
104
- throw new AppException(AUTH_ERRORS.SERVICE_UNAVAILABLE, { cause })
105
- }),
103
+ this.authRepository.findPublicById(userId),
106
104
  )
107
105
 
108
106
  if (!user || user.status !== 'active') {