create-blitzpack 0.1.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 (259) hide show
  1. package/dist/index.js +452 -0
  2. package/package.json +57 -0
  3. package/template/.dockerignore +59 -0
  4. package/template/.github/workflows/ci.yml +157 -0
  5. package/template/.husky/pre-commit +1 -0
  6. package/template/.husky/pre-push +1 -0
  7. package/template/.lintstagedrc.cjs +4 -0
  8. package/template/.nvmrc +1 -0
  9. package/template/.prettierrc +9 -0
  10. package/template/.vscode/settings.json +13 -0
  11. package/template/CLAUDE.md +175 -0
  12. package/template/CONTRIBUTING.md +32 -0
  13. package/template/Dockerfile +90 -0
  14. package/template/GETTING_STARTED.md +35 -0
  15. package/template/LICENSE +21 -0
  16. package/template/README.md +116 -0
  17. package/template/apps/api/.dockerignore +51 -0
  18. package/template/apps/api/.env.local.example +62 -0
  19. package/template/apps/api/emails/account-deleted-email.tsx +69 -0
  20. package/template/apps/api/emails/components/email-layout.tsx +154 -0
  21. package/template/apps/api/emails/config.ts +22 -0
  22. package/template/apps/api/emails/password-changed-email.tsx +88 -0
  23. package/template/apps/api/emails/password-reset-email.tsx +86 -0
  24. package/template/apps/api/emails/verification-email.tsx +85 -0
  25. package/template/apps/api/emails/welcome-email.tsx +70 -0
  26. package/template/apps/api/package.json +84 -0
  27. package/template/apps/api/prisma/migrations/20251012111439_init/migration.sql +13 -0
  28. package/template/apps/api/prisma/migrations/20251018162629_add_better_auth_fields/migration.sql +67 -0
  29. package/template/apps/api/prisma/migrations/20251019142208_add_user_role_enum/migration.sql +5 -0
  30. package/template/apps/api/prisma/migrations/20251019182151_user_auth/migration.sql +7 -0
  31. package/template/apps/api/prisma/migrations/20251019211416_faster_session_lookup/migration.sql +2 -0
  32. package/template/apps/api/prisma/migrations/20251119124337_add_upload_model/migration.sql +26 -0
  33. package/template/apps/api/prisma/migrations/20251120071241_add_scope_to_account/migration.sql +2 -0
  34. package/template/apps/api/prisma/migrations/20251120072608_add_oauth_token_expiration_fields/migration.sql +10 -0
  35. package/template/apps/api/prisma/migrations/20251120144705_add_audit_logs/migration.sql +29 -0
  36. package/template/apps/api/prisma/migrations/20251127123614_remove_impersonated_by/migration.sql +8 -0
  37. package/template/apps/api/prisma/migrations/20251127125630_remove_audit_logs/migration.sql +11 -0
  38. package/template/apps/api/prisma/migrations/migration_lock.toml +3 -0
  39. package/template/apps/api/prisma/schema.prisma +116 -0
  40. package/template/apps/api/prisma/seed.ts +159 -0
  41. package/template/apps/api/prisma.config.ts +14 -0
  42. package/template/apps/api/src/app.ts +377 -0
  43. package/template/apps/api/src/common/logger.service.ts +227 -0
  44. package/template/apps/api/src/config/env.ts +60 -0
  45. package/template/apps/api/src/config/rate-limit.ts +29 -0
  46. package/template/apps/api/src/hooks/auth.ts +122 -0
  47. package/template/apps/api/src/plugins/auth.ts +198 -0
  48. package/template/apps/api/src/plugins/database.ts +45 -0
  49. package/template/apps/api/src/plugins/logger.ts +33 -0
  50. package/template/apps/api/src/plugins/multipart.ts +16 -0
  51. package/template/apps/api/src/plugins/scalar.ts +20 -0
  52. package/template/apps/api/src/plugins/schedule.ts +52 -0
  53. package/template/apps/api/src/plugins/services.ts +66 -0
  54. package/template/apps/api/src/plugins/swagger.ts +56 -0
  55. package/template/apps/api/src/routes/accounts.ts +91 -0
  56. package/template/apps/api/src/routes/admin-sessions.ts +92 -0
  57. package/template/apps/api/src/routes/metrics.ts +71 -0
  58. package/template/apps/api/src/routes/password.ts +46 -0
  59. package/template/apps/api/src/routes/sessions.ts +53 -0
  60. package/template/apps/api/src/routes/stats.ts +38 -0
  61. package/template/apps/api/src/routes/uploads-serve.ts +27 -0
  62. package/template/apps/api/src/routes/uploads.ts +154 -0
  63. package/template/apps/api/src/routes/users.ts +114 -0
  64. package/template/apps/api/src/routes/verification.ts +90 -0
  65. package/template/apps/api/src/server.ts +34 -0
  66. package/template/apps/api/src/services/accounts.service.ts +125 -0
  67. package/template/apps/api/src/services/authorization.service.ts +162 -0
  68. package/template/apps/api/src/services/email.service.ts +170 -0
  69. package/template/apps/api/src/services/file-storage.service.ts +267 -0
  70. package/template/apps/api/src/services/metrics.service.ts +175 -0
  71. package/template/apps/api/src/services/password.service.ts +56 -0
  72. package/template/apps/api/src/services/sessions.service.spec.ts +134 -0
  73. package/template/apps/api/src/services/sessions.service.ts +276 -0
  74. package/template/apps/api/src/services/stats.service.ts +273 -0
  75. package/template/apps/api/src/services/uploads.service.ts +163 -0
  76. package/template/apps/api/src/services/users.service.spec.ts +249 -0
  77. package/template/apps/api/src/services/users.service.ts +198 -0
  78. package/template/apps/api/src/utils/file-validation.ts +108 -0
  79. package/template/apps/api/start.sh +33 -0
  80. package/template/apps/api/test/helpers/fastify-app.ts +24 -0
  81. package/template/apps/api/test/helpers/mock-authorization.ts +16 -0
  82. package/template/apps/api/test/helpers/mock-logger.ts +28 -0
  83. package/template/apps/api/test/helpers/mock-prisma.ts +30 -0
  84. package/template/apps/api/test/helpers/test-db.ts +125 -0
  85. package/template/apps/api/test/integration/auth-flow.integration.spec.ts +449 -0
  86. package/template/apps/api/test/integration/password.integration.spec.ts +427 -0
  87. package/template/apps/api/test/integration/rate-limit.integration.spec.ts +51 -0
  88. package/template/apps/api/test/integration/sessions.integration.spec.ts +445 -0
  89. package/template/apps/api/test/integration/users.integration.spec.ts +211 -0
  90. package/template/apps/api/test/setup.ts +31 -0
  91. package/template/apps/api/tsconfig.json +26 -0
  92. package/template/apps/api/vitest.config.ts +35 -0
  93. package/template/apps/web/.env.local.example +11 -0
  94. package/template/apps/web/components.json +24 -0
  95. package/template/apps/web/next.config.ts +22 -0
  96. package/template/apps/web/package.json +56 -0
  97. package/template/apps/web/postcss.config.js +5 -0
  98. package/template/apps/web/public/apple-icon.png +0 -0
  99. package/template/apps/web/public/icon.png +0 -0
  100. package/template/apps/web/public/robots.txt +3 -0
  101. package/template/apps/web/src/app/(admin)/admin/layout.tsx +222 -0
  102. package/template/apps/web/src/app/(admin)/admin/page.tsx +157 -0
  103. package/template/apps/web/src/app/(admin)/admin/sessions/page.tsx +18 -0
  104. package/template/apps/web/src/app/(admin)/admin/users/page.tsx +20 -0
  105. package/template/apps/web/src/app/(auth)/forgot-password/page.tsx +177 -0
  106. package/template/apps/web/src/app/(auth)/login/page.tsx +159 -0
  107. package/template/apps/web/src/app/(auth)/reset-password/page.tsx +245 -0
  108. package/template/apps/web/src/app/(auth)/signup/page.tsx +153 -0
  109. package/template/apps/web/src/app/dashboard/change-password/page.tsx +255 -0
  110. package/template/apps/web/src/app/dashboard/page.tsx +296 -0
  111. package/template/apps/web/src/app/error.tsx +32 -0
  112. package/template/apps/web/src/app/examples/file-upload/page.tsx +200 -0
  113. package/template/apps/web/src/app/favicon.ico +0 -0
  114. package/template/apps/web/src/app/global-error.tsx +96 -0
  115. package/template/apps/web/src/app/globals.css +22 -0
  116. package/template/apps/web/src/app/icon.png +0 -0
  117. package/template/apps/web/src/app/layout.tsx +34 -0
  118. package/template/apps/web/src/app/not-found.tsx +28 -0
  119. package/template/apps/web/src/app/page.tsx +192 -0
  120. package/template/apps/web/src/components/admin/activity-feed.tsx +101 -0
  121. package/template/apps/web/src/components/admin/charts/auth-breakdown-chart.tsx +114 -0
  122. package/template/apps/web/src/components/admin/charts/chart-tooltip.tsx +124 -0
  123. package/template/apps/web/src/components/admin/charts/realtime-metrics-chart.tsx +511 -0
  124. package/template/apps/web/src/components/admin/charts/role-distribution-chart.tsx +102 -0
  125. package/template/apps/web/src/components/admin/charts/session-activity-chart.tsx +90 -0
  126. package/template/apps/web/src/components/admin/charts/user-growth-chart.tsx +108 -0
  127. package/template/apps/web/src/components/admin/health-indicator.tsx +175 -0
  128. package/template/apps/web/src/components/admin/refresh-control.tsx +90 -0
  129. package/template/apps/web/src/components/admin/session-revoke-all-dialog.tsx +79 -0
  130. package/template/apps/web/src/components/admin/session-revoke-dialog.tsx +74 -0
  131. package/template/apps/web/src/components/admin/sessions-management-table.tsx +372 -0
  132. package/template/apps/web/src/components/admin/stat-card.tsx +137 -0
  133. package/template/apps/web/src/components/admin/user-create-dialog.tsx +152 -0
  134. package/template/apps/web/src/components/admin/user-delete-dialog.tsx +73 -0
  135. package/template/apps/web/src/components/admin/user-edit-dialog.tsx +170 -0
  136. package/template/apps/web/src/components/admin/users-management-table.tsx +285 -0
  137. package/template/apps/web/src/components/auth/email-verification-banner.tsx +85 -0
  138. package/template/apps/web/src/components/auth/github-button.tsx +40 -0
  139. package/template/apps/web/src/components/auth/google-button.tsx +54 -0
  140. package/template/apps/web/src/components/auth/protected-route.tsx +66 -0
  141. package/template/apps/web/src/components/auth/redirect-if-authenticated.tsx +31 -0
  142. package/template/apps/web/src/components/auth/with-auth.tsx +30 -0
  143. package/template/apps/web/src/components/error/error-card.tsx +47 -0
  144. package/template/apps/web/src/components/error/forbidden.tsx +25 -0
  145. package/template/apps/web/src/components/landing/command-block.tsx +64 -0
  146. package/template/apps/web/src/components/landing/feature-card.tsx +60 -0
  147. package/template/apps/web/src/components/landing/included-feature-card.tsx +63 -0
  148. package/template/apps/web/src/components/landing/logo.tsx +41 -0
  149. package/template/apps/web/src/components/landing/tech-badge.tsx +11 -0
  150. package/template/apps/web/src/components/layout/auth-nav.tsx +58 -0
  151. package/template/apps/web/src/components/layout/footer.tsx +3 -0
  152. package/template/apps/web/src/config/landing-data.ts +152 -0
  153. package/template/apps/web/src/config/site.ts +5 -0
  154. package/template/apps/web/src/hooks/api/__tests__/use-users.test.tsx +181 -0
  155. package/template/apps/web/src/hooks/api/use-admin-sessions.ts +75 -0
  156. package/template/apps/web/src/hooks/api/use-admin-stats.ts +33 -0
  157. package/template/apps/web/src/hooks/api/use-sessions.ts +52 -0
  158. package/template/apps/web/src/hooks/api/use-uploads.ts +156 -0
  159. package/template/apps/web/src/hooks/api/use-users.ts +149 -0
  160. package/template/apps/web/src/hooks/use-mobile.ts +21 -0
  161. package/template/apps/web/src/hooks/use-realtime-metrics.ts +120 -0
  162. package/template/apps/web/src/lib/__tests__/utils.test.ts +29 -0
  163. package/template/apps/web/src/lib/api.ts +151 -0
  164. package/template/apps/web/src/lib/auth.ts +13 -0
  165. package/template/apps/web/src/lib/env.ts +52 -0
  166. package/template/apps/web/src/lib/form-utils.ts +11 -0
  167. package/template/apps/web/src/lib/utils.ts +1 -0
  168. package/template/apps/web/src/providers.tsx +34 -0
  169. package/template/apps/web/src/store/atoms.ts +15 -0
  170. package/template/apps/web/src/test/helpers/test-utils.tsx +44 -0
  171. package/template/apps/web/src/test/setup.ts +8 -0
  172. package/template/apps/web/tailwind.config.ts +5 -0
  173. package/template/apps/web/tsconfig.json +26 -0
  174. package/template/apps/web/vitest.config.ts +32 -0
  175. package/template/assets/logo-512.png +0 -0
  176. package/template/assets/logo.svg +4 -0
  177. package/template/docker-compose.prod.yml +66 -0
  178. package/template/docker-compose.yml +36 -0
  179. package/template/eslint.config.ts +119 -0
  180. package/template/package.json +77 -0
  181. package/template/packages/tailwind-config/package.json +9 -0
  182. package/template/packages/tailwind-config/theme.css +179 -0
  183. package/template/packages/types/package.json +29 -0
  184. package/template/packages/types/src/__tests__/schemas.test.ts +255 -0
  185. package/template/packages/types/src/api-response.ts +53 -0
  186. package/template/packages/types/src/health-check.ts +11 -0
  187. package/template/packages/types/src/pagination.ts +41 -0
  188. package/template/packages/types/src/role.ts +5 -0
  189. package/template/packages/types/src/session.ts +48 -0
  190. package/template/packages/types/src/stats.ts +113 -0
  191. package/template/packages/types/src/upload.ts +51 -0
  192. package/template/packages/types/src/user.ts +36 -0
  193. package/template/packages/types/tsconfig.json +5 -0
  194. package/template/packages/types/vitest.config.ts +21 -0
  195. package/template/packages/ui/components.json +21 -0
  196. package/template/packages/ui/package.json +108 -0
  197. package/template/packages/ui/src/__tests__/button.test.tsx +70 -0
  198. package/template/packages/ui/src/alert-dialog.tsx +141 -0
  199. package/template/packages/ui/src/alert.tsx +66 -0
  200. package/template/packages/ui/src/animated-theme-toggler.tsx +167 -0
  201. package/template/packages/ui/src/avatar.tsx +53 -0
  202. package/template/packages/ui/src/badge.tsx +36 -0
  203. package/template/packages/ui/src/button.tsx +84 -0
  204. package/template/packages/ui/src/card.tsx +92 -0
  205. package/template/packages/ui/src/checkbox.tsx +32 -0
  206. package/template/packages/ui/src/data-table/data-table-column-header.tsx +68 -0
  207. package/template/packages/ui/src/data-table/data-table-pagination.tsx +99 -0
  208. package/template/packages/ui/src/data-table/data-table-toolbar.tsx +55 -0
  209. package/template/packages/ui/src/data-table/data-table-view-options.tsx +63 -0
  210. package/template/packages/ui/src/data-table/data-table.tsx +167 -0
  211. package/template/packages/ui/src/dialog.tsx +143 -0
  212. package/template/packages/ui/src/dropdown-menu.tsx +257 -0
  213. package/template/packages/ui/src/empty-state.tsx +52 -0
  214. package/template/packages/ui/src/file-upload-input.tsx +202 -0
  215. package/template/packages/ui/src/form.tsx +168 -0
  216. package/template/packages/ui/src/hooks/use-mobile.ts +19 -0
  217. package/template/packages/ui/src/icons/brand-icons.tsx +16 -0
  218. package/template/packages/ui/src/input.tsx +21 -0
  219. package/template/packages/ui/src/label.tsx +24 -0
  220. package/template/packages/ui/src/lib/utils.ts +6 -0
  221. package/template/packages/ui/src/password-input.tsx +102 -0
  222. package/template/packages/ui/src/popover.tsx +48 -0
  223. package/template/packages/ui/src/radio-group.tsx +45 -0
  224. package/template/packages/ui/src/scroll-area.tsx +58 -0
  225. package/template/packages/ui/src/select.tsx +187 -0
  226. package/template/packages/ui/src/separator.tsx +28 -0
  227. package/template/packages/ui/src/sheet.tsx +139 -0
  228. package/template/packages/ui/src/sidebar.tsx +726 -0
  229. package/template/packages/ui/src/skeleton-variants.tsx +87 -0
  230. package/template/packages/ui/src/skeleton.tsx +13 -0
  231. package/template/packages/ui/src/slider.tsx +63 -0
  232. package/template/packages/ui/src/sonner.tsx +25 -0
  233. package/template/packages/ui/src/spinner.tsx +16 -0
  234. package/template/packages/ui/src/switch.tsx +31 -0
  235. package/template/packages/ui/src/table.tsx +116 -0
  236. package/template/packages/ui/src/tabs.tsx +66 -0
  237. package/template/packages/ui/src/textarea.tsx +18 -0
  238. package/template/packages/ui/src/tooltip.tsx +61 -0
  239. package/template/packages/ui/src/user-avatar.tsx +97 -0
  240. package/template/packages/ui/test-config.js +3 -0
  241. package/template/packages/ui/tsconfig.json +12 -0
  242. package/template/packages/ui/turbo.json +18 -0
  243. package/template/packages/ui/vitest.config.ts +17 -0
  244. package/template/packages/ui/vitest.setup.ts +1 -0
  245. package/template/packages/utils/package.json +23 -0
  246. package/template/packages/utils/src/__tests__/utils.test.ts +223 -0
  247. package/template/packages/utils/src/array.ts +18 -0
  248. package/template/packages/utils/src/async.ts +3 -0
  249. package/template/packages/utils/src/date.ts +77 -0
  250. package/template/packages/utils/src/errors.ts +73 -0
  251. package/template/packages/utils/src/number.ts +11 -0
  252. package/template/packages/utils/src/string.ts +13 -0
  253. package/template/packages/utils/tsconfig.json +5 -0
  254. package/template/packages/utils/vitest.config.ts +21 -0
  255. package/template/pnpm-workspace.yaml +4 -0
  256. package/template/tsconfig.base.json +32 -0
  257. package/template/turbo.json +133 -0
  258. package/template/vitest.shared.ts +26 -0
  259. package/template/vitest.workspace.ts +9 -0
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "@repo/api",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "tsx watch --clear-screen=false --ignore '../../packages/**' --ignore '**/node_modules/**' src/server.ts",
8
+ "build": "tsc && tsc-alias -p tsconfig.json --resolve-full-paths",
9
+ "start": "node dist/src/server.js",
10
+ "start:dev": "tsx src/server.ts",
11
+ "typecheck": "tsc --noEmit",
12
+ "lint": "eslint \"src/**/*.ts\"",
13
+ "lint:fix": "eslint \"src/**/*.ts\" --fix",
14
+ "test": "vitest run",
15
+ "test:unit": "vitest run src/",
16
+ "test:integration": "vitest run test/integration/",
17
+ "test:watch": "vitest",
18
+ "test:coverage": "vitest run --coverage",
19
+ "email:dev": "email dev --dir emails --port 3001",
20
+ "db:generate": "prisma generate",
21
+ "db:migrate": "prisma migrate dev",
22
+ "db:push": "prisma db push",
23
+ "db:studio": "prisma studio",
24
+ "db:seed": "tsx prisma/seed.ts",
25
+ "db:reset": "prisma migrate reset --force --skip-seed",
26
+ "db:reset:seed": "prisma migrate reset --force"
27
+ },
28
+ "dependencies": {
29
+ "@aws-sdk/client-s3": "^3.934.0",
30
+ "@fastify/cookie": "^11.0.1",
31
+ "@fastify/cors": "^10.0.1",
32
+ "@fastify/formbody": "^8.0.1",
33
+ "@fastify/helmet": "^12.0.1",
34
+ "@fastify/multipart": "^9.3.0",
35
+ "@fastify/rate-limit": "^10.1.1",
36
+ "@fastify/schedule": "^5.0.2",
37
+ "@fastify/static": "^8.3.0",
38
+ "@fastify/swagger": "^9.3.0",
39
+ "@fastify/swagger-ui": "^5.0.1",
40
+ "@prisma/adapter-pg": "^7.0.1",
41
+ "@prisma/client": "7.0.1",
42
+ "@react-email/components": "^1.0.1",
43
+ "@react-email/preview-server": "^5.0.4",
44
+ "@repo/packages-types": "workspace:*",
45
+ "@scalar/fastify-api-reference": "^1.39.3",
46
+ "bcryptjs": "^3.0.2",
47
+ "better-auth": "^1.3.27",
48
+ "close-with-grace": "^2.1.0",
49
+ "dotenv-flow": "^4.1.0",
50
+ "fastify": "^5.2.0",
51
+ "fastify-plugin": "^5.0.1",
52
+ "fastify-type-provider-zod": "^5.0.1",
53
+ "pg": "^8.16.3",
54
+ "pino": "^10.0.0",
55
+ "pino-pretty": "^13.1.2",
56
+ "react": "^19.0.0",
57
+ "react-dom": "^19.0.0",
58
+ "react-email": "^5.0.4",
59
+ "resend": "^6.5.0",
60
+ "sharp": "^0.34.5",
61
+ "toad-scheduler": "^3.1.0",
62
+ "zod": "^4.1.12"
63
+ },
64
+ "devDependencies": {
65
+ "@repo/packages-utils": "workspace:*",
66
+ "@types/bcryptjs": "^3.0.0",
67
+ "@types/node": "^24.7.1",
68
+ "@types/pg": "^8.15.6",
69
+ "@types/react": "^19.0.6",
70
+ "@types/react-dom": "^19.0.2",
71
+ "@typescript-eslint/eslint-plugin": "^8.20.0",
72
+ "@typescript-eslint/parser": "^8.20.0",
73
+ "dotenv": "^17.2.3",
74
+ "dotenv-cli": "^10.0.0",
75
+ "eslint": "^9.18.0",
76
+ "eslint-config-prettier": "^10.1.8",
77
+ "prisma": "7.0.1",
78
+ "tsc-alias": "^1.8.16",
79
+ "tsx": "^4.20.6",
80
+ "typescript": "^5.7.2",
81
+ "vite-tsconfig-paths": "^5.1.4",
82
+ "vitest": "^3.2.4"
83
+ }
84
+ }
@@ -0,0 +1,13 @@
1
+ -- CreateTable
2
+ CREATE TABLE "users" (
3
+ "id" TEXT NOT NULL,
4
+ "email" TEXT NOT NULL,
5
+ "name" TEXT,
6
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
7
+ "updatedAt" TIMESTAMP(3) NOT NULL,
8
+
9
+ CONSTRAINT "users_pkey" PRIMARY KEY ("id")
10
+ );
11
+
12
+ -- CreateIndex
13
+ CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
@@ -0,0 +1,67 @@
1
+ -- AlterTable
2
+ ALTER TABLE "users" ADD COLUMN "emailVerified" BOOLEAN NOT NULL DEFAULT false,
3
+ ADD COLUMN "image" TEXT;
4
+
5
+ -- CreateTable
6
+ CREATE TABLE "sessions" (
7
+ "id" TEXT NOT NULL,
8
+ "userId" TEXT NOT NULL,
9
+ "expiresAt" TIMESTAMP(3) NOT NULL,
10
+ "token" TEXT NOT NULL,
11
+ "ipAddress" TEXT,
12
+ "userAgent" TEXT,
13
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
14
+ "updatedAt" TIMESTAMP(3) NOT NULL,
15
+
16
+ CONSTRAINT "sessions_pkey" PRIMARY KEY ("id")
17
+ );
18
+
19
+ -- CreateTable
20
+ CREATE TABLE "accounts" (
21
+ "id" TEXT NOT NULL,
22
+ "userId" TEXT NOT NULL,
23
+ "accountId" TEXT NOT NULL,
24
+ "providerId" TEXT NOT NULL,
25
+ "accessToken" TEXT,
26
+ "refreshToken" TEXT,
27
+ "idToken" TEXT,
28
+ "expiresAt" TIMESTAMP(3),
29
+ "password" TEXT,
30
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
31
+ "updatedAt" TIMESTAMP(3) NOT NULL,
32
+
33
+ CONSTRAINT "accounts_pkey" PRIMARY KEY ("id")
34
+ );
35
+
36
+ -- CreateTable
37
+ CREATE TABLE "verifications" (
38
+ "id" TEXT NOT NULL,
39
+ "identifier" TEXT NOT NULL,
40
+ "value" TEXT NOT NULL,
41
+ "expiresAt" TIMESTAMP(3) NOT NULL,
42
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
43
+ "updatedAt" TIMESTAMP(3) NOT NULL,
44
+
45
+ CONSTRAINT "verifications_pkey" PRIMARY KEY ("id")
46
+ );
47
+
48
+ -- CreateIndex
49
+ CREATE UNIQUE INDEX "sessions_token_key" ON "sessions"("token");
50
+
51
+ -- CreateIndex
52
+ CREATE INDEX "sessions_userId_idx" ON "sessions"("userId");
53
+
54
+ -- CreateIndex
55
+ CREATE INDEX "accounts_userId_idx" ON "accounts"("userId");
56
+
57
+ -- CreateIndex
58
+ CREATE UNIQUE INDEX "accounts_providerId_accountId_key" ON "accounts"("providerId", "accountId");
59
+
60
+ -- CreateIndex
61
+ CREATE UNIQUE INDEX "verifications_identifier_value_key" ON "verifications"("identifier", "value");
62
+
63
+ -- AddForeignKey
64
+ ALTER TABLE "sessions" ADD CONSTRAINT "sessions_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
65
+
66
+ -- AddForeignKey
67
+ ALTER TABLE "accounts" ADD CONSTRAINT "accounts_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1,5 @@
1
+ -- CreateEnum
2
+ CREATE TYPE "Role" AS ENUM ('user', 'admin', 'super_admin');
3
+
4
+ -- AlterTable
5
+ ALTER TABLE "users" ADD COLUMN "role" "Role" NOT NULL DEFAULT 'user';
@@ -0,0 +1,7 @@
1
+ -- AlterTable
2
+ ALTER TABLE "sessions" ADD COLUMN "impersonatedBy" TEXT;
3
+
4
+ -- AlterTable
5
+ ALTER TABLE "users" ADD COLUMN "banExpires" TIMESTAMP(3),
6
+ ADD COLUMN "banReason" TEXT,
7
+ ADD COLUMN "banned" BOOLEAN NOT NULL DEFAULT false;
@@ -0,0 +1,2 @@
1
+ -- CreateIndex
2
+ CREATE INDEX "sessions_expiresAt_idx" ON "sessions"("expiresAt");
@@ -0,0 +1,26 @@
1
+ -- CreateTable
2
+ CREATE TABLE "uploads" (
3
+ "id" TEXT NOT NULL,
4
+ "filename" TEXT NOT NULL,
5
+ "originalName" TEXT NOT NULL,
6
+ "mimeType" TEXT NOT NULL,
7
+ "size" INTEGER NOT NULL,
8
+ "url" TEXT NOT NULL,
9
+ "userId" TEXT NOT NULL,
10
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
11
+ "updatedAt" TIMESTAMP(3) NOT NULL,
12
+
13
+ CONSTRAINT "uploads_pkey" PRIMARY KEY ("id")
14
+ );
15
+
16
+ -- CreateIndex
17
+ CREATE UNIQUE INDEX "uploads_filename_key" ON "uploads"("filename");
18
+
19
+ -- CreateIndex
20
+ CREATE INDEX "uploads_userId_idx" ON "uploads"("userId");
21
+
22
+ -- CreateIndex
23
+ CREATE INDEX "uploads_createdAt_idx" ON "uploads"("createdAt");
24
+
25
+ -- AddForeignKey
26
+ ALTER TABLE "uploads" ADD CONSTRAINT "uploads_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1,2 @@
1
+ -- AlterTable
2
+ ALTER TABLE "accounts" ADD COLUMN "scope" TEXT;
@@ -0,0 +1,10 @@
1
+ /*
2
+ Warnings:
3
+
4
+ - You are about to drop the column `expiresAt` on the `accounts` table. All the data in the column will be lost.
5
+
6
+ */
7
+ -- AlterTable
8
+ ALTER TABLE "accounts" DROP COLUMN "expiresAt",
9
+ ADD COLUMN "accessTokenExpiresAt" TIMESTAMP(3),
10
+ ADD COLUMN "refreshTokenExpiresAt" TIMESTAMP(3);
@@ -0,0 +1,29 @@
1
+ -- CreateTable
2
+ CREATE TABLE "audit_logs" (
3
+ "id" TEXT NOT NULL,
4
+ "userId" TEXT NOT NULL,
5
+ "action" TEXT NOT NULL,
6
+ "resourceType" TEXT NOT NULL,
7
+ "resourceId" TEXT,
8
+ "changes" JSONB,
9
+ "ipAddress" TEXT,
10
+ "userAgent" TEXT,
11
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
12
+
13
+ CONSTRAINT "audit_logs_pkey" PRIMARY KEY ("id")
14
+ );
15
+
16
+ -- CreateIndex
17
+ CREATE INDEX "audit_logs_userId_idx" ON "audit_logs"("userId");
18
+
19
+ -- CreateIndex
20
+ CREATE INDEX "audit_logs_action_idx" ON "audit_logs"("action");
21
+
22
+ -- CreateIndex
23
+ CREATE INDEX "audit_logs_createdAt_idx" ON "audit_logs"("createdAt");
24
+
25
+ -- CreateIndex
26
+ CREATE INDEX "audit_logs_resourceType_idx" ON "audit_logs"("resourceType");
27
+
28
+ -- AddForeignKey
29
+ ALTER TABLE "audit_logs" ADD CONSTRAINT "audit_logs_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1,8 @@
1
+ /*
2
+ Warnings:
3
+
4
+ - You are about to drop the column `impersonatedBy` on the `sessions` table. All the data in the column will be lost.
5
+
6
+ */
7
+ -- AlterTable
8
+ ALTER TABLE "sessions" DROP COLUMN "impersonatedBy";
@@ -0,0 +1,11 @@
1
+ /*
2
+ Warnings:
3
+
4
+ - You are about to drop the `audit_logs` table. If the table is not empty, all the data it contains will be lost.
5
+
6
+ */
7
+ -- DropForeignKey
8
+ ALTER TABLE "audit_logs" DROP CONSTRAINT "audit_logs_userId_fkey";
9
+
10
+ -- DropTable
11
+ DROP TABLE "audit_logs";
@@ -0,0 +1,3 @@
1
+ # Please do not edit this file manually
2
+ # It should be added in your version-control system (e.g., Git)
3
+ provider = "postgresql"
@@ -0,0 +1,116 @@
1
+ // This is your Prisma schema file,
2
+ // learn more about it in the docs: https://pris.ly/d/prisma-schema
3
+
4
+ generator client {
5
+ provider = "prisma-client"
6
+ output = "../src/generated/client"
7
+ }
8
+
9
+ datasource db {
10
+ provider = "postgresql"
11
+ }
12
+
13
+ // Role enum for user permissions
14
+ enum Role {
15
+ user
16
+ admin
17
+ super_admin
18
+ }
19
+
20
+ // User model with better-auth support
21
+ model User {
22
+ id String @id @default(cuid())
23
+ email String @unique
24
+ emailVerified Boolean @default(false)
25
+ name String?
26
+ image String?
27
+ role Role @default(user)
28
+ banned Boolean @default(false)
29
+ banReason String?
30
+ banExpires DateTime?
31
+ createdAt DateTime @default(now())
32
+ updatedAt DateTime @updatedAt
33
+
34
+ sessions Session[]
35
+ accounts Account[]
36
+ uploads Upload[]
37
+
38
+ @@map("users")
39
+ }
40
+
41
+ // Session model for better-auth
42
+ model Session {
43
+ id String @id @default(cuid())
44
+ userId String
45
+ expiresAt DateTime
46
+ token String @unique
47
+ ipAddress String?
48
+ userAgent String?
49
+ createdAt DateTime @default(now())
50
+ updatedAt DateTime @updatedAt
51
+
52
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
53
+
54
+ @@index([userId])
55
+ @@index([expiresAt])
56
+ @@map("sessions")
57
+ }
58
+
59
+ // Account model for OAuth providers
60
+ // IMPORTANT: This schema MUST match Better Auth's expected fields exactly
61
+ // Do NOT modify field names without checking Better Auth documentation
62
+ // https://www.better-auth.com/docs/concepts/database#account-model
63
+ model Account {
64
+ id String @id @default(cuid())
65
+ userId String
66
+ accountId String
67
+ providerId String
68
+ accessToken String?
69
+ refreshToken String?
70
+ idToken String?
71
+ accessTokenExpiresAt DateTime? // OAuth access token expiration
72
+ refreshTokenExpiresAt DateTime? // OAuth refresh token expiration
73
+ scope String? // OAuth scopes granted
74
+ password String? // For credential provider (email/password)
75
+ createdAt DateTime @default(now())
76
+ updatedAt DateTime @updatedAt
77
+
78
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
79
+
80
+ @@unique([providerId, accountId])
81
+ @@index([userId])
82
+ @@map("accounts")
83
+ }
84
+
85
+ // Verification model for email verification and password reset
86
+ model Verification {
87
+ id String @id @default(cuid())
88
+ identifier String
89
+ value String
90
+ expiresAt DateTime
91
+ createdAt DateTime @default(now())
92
+ updatedAt DateTime @updatedAt
93
+
94
+ @@unique([identifier, value])
95
+ @@map("verifications")
96
+ }
97
+
98
+ // Upload model for file tracking
99
+ model Upload {
100
+ id String @id @default(cuid())
101
+ filename String @unique // stored filename (unique)
102
+ originalName String // user's original filename
103
+ mimeType String
104
+ size Int // bytes
105
+ url String // public URL
106
+ userId String
107
+ createdAt DateTime @default(now())
108
+ updatedAt DateTime @updatedAt
109
+
110
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
111
+
112
+ @@index([userId])
113
+ @@index([createdAt])
114
+ @@map("uploads")
115
+ }
116
+
@@ -0,0 +1,159 @@
1
+ import 'dotenv-flow/config';
2
+
3
+ import { PrismaPg } from '@prisma/adapter-pg';
4
+ import { betterAuth } from 'better-auth';
5
+ import { prismaAdapter } from 'better-auth/adapters/prisma';
6
+ import { admin } from 'better-auth/plugins';
7
+ import { Pool } from 'pg';
8
+
9
+ import { PrismaClient, Role } from '../src/generated/client/client.js';
10
+
11
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL });
12
+ const adapter = new PrismaPg(pool);
13
+ const prisma = new PrismaClient({ adapter });
14
+
15
+ const auth = betterAuth({
16
+ database: prismaAdapter(prisma, {
17
+ provider: 'postgresql',
18
+ }),
19
+ baseURL: 'http://localhost:8080',
20
+ secret: 'temp-seed-secret',
21
+ emailAndPassword: {
22
+ enabled: true,
23
+ },
24
+ plugins: [
25
+ admin({
26
+ defaultRole: 'user',
27
+ adminRoles: ['admin', 'super_admin'],
28
+ }),
29
+ ],
30
+ });
31
+
32
+ async function main() {
33
+ console.log('🌱 Seeding database...');
34
+
35
+ // SAFETY: Block seeding in production
36
+ if (process.env.NODE_ENV === 'production') {
37
+ console.error('❌ SAFETY ERROR: Seed script is blocked in production!');
38
+ console.error(
39
+ ' Test accounts with weak passwords should NEVER be created in production.'
40
+ );
41
+ console.error(
42
+ ' If you need to seed production data, create a separate seed-prod.ts file.'
43
+ );
44
+ process.exit(1);
45
+ }
46
+
47
+ console.log(`📍 Environment: ${process.env.NODE_ENV || 'development'}`);
48
+
49
+ // Delete all existing test accounts first
50
+ await prisma.account.deleteMany({
51
+ where: {
52
+ providerId: 'credential',
53
+ accountId: {
54
+ in: [
55
+ 'admin@example.com',
56
+ 'alice@example.com',
57
+ 'bob@example.com',
58
+ 'charlie@example.com',
59
+ ],
60
+ },
61
+ },
62
+ });
63
+
64
+ await prisma.user.deleteMany({
65
+ where: {
66
+ email: {
67
+ in: [
68
+ 'admin@example.com',
69
+ 'alice@example.com',
70
+ 'bob@example.com',
71
+ 'charlie@example.com',
72
+ ],
73
+ },
74
+ },
75
+ });
76
+
77
+ console.log('🗑️ Cleaned up existing test accounts');
78
+
79
+ const usersData = [
80
+ {
81
+ email: 'admin@example.com',
82
+ name: 'Super Admin',
83
+ password: 'Admin@123456',
84
+ role: Role.super_admin,
85
+ emailVerified: true,
86
+ },
87
+ {
88
+ email: 'alice@example.com',
89
+ name: 'Alice Johnson',
90
+ password: 'Alice@123456',
91
+ role: Role.user,
92
+ emailVerified: false,
93
+ },
94
+ {
95
+ email: 'bob@example.com',
96
+ name: 'Bob Smith',
97
+ password: 'Bob@123456',
98
+ role: Role.admin,
99
+ emailVerified: false,
100
+ },
101
+ {
102
+ email: 'charlie@example.com',
103
+ name: 'Charlie Davis',
104
+ password: 'Charlie@123456',
105
+ role: Role.user,
106
+ emailVerified: false,
107
+ },
108
+ ];
109
+
110
+ for (const userData of usersData) {
111
+ // Create user with Better Auth (this handles password hashing correctly)
112
+ const response = await auth.api.signUpEmail({
113
+ body: {
114
+ email: userData.email,
115
+ password: userData.password,
116
+ name: userData.name,
117
+ },
118
+ });
119
+
120
+ if (!response || !response.user) {
121
+ console.error(`❌ Failed to create user: ${userData.email}`);
122
+ continue;
123
+ }
124
+
125
+ // Update user role and email verification status
126
+ await prisma.user.update({
127
+ where: { id: response.user.id },
128
+ data: {
129
+ role: userData.role,
130
+ emailVerified: userData.emailVerified,
131
+ },
132
+ });
133
+
134
+ console.log(
135
+ `✅ Created: ${userData.email} (${userData.role}) - emailVerified: ${userData.emailVerified}`
136
+ );
137
+ }
138
+
139
+ console.log('');
140
+ console.log('✅ Database seeded successfully!');
141
+ console.log('');
142
+ console.log('Test accounts:');
143
+ console.log(' admin@example.com / Admin@123456 (super_admin)');
144
+ console.log(' alice@example.com / Alice@123456 (user)');
145
+ console.log(' bob@example.com / Bob@123456 (admin)');
146
+ console.log(' charlie@example.com / Charlie@123456 (user)');
147
+ console.log('');
148
+ console.log('⚠️ IMPORTANT: Change passwords after first login!');
149
+ }
150
+
151
+ main()
152
+ .catch((e) => {
153
+ console.error('❌ Seed failed:', e);
154
+ process.exit(1);
155
+ })
156
+ .finally(async () => {
157
+ await prisma.$disconnect();
158
+ await pool.end();
159
+ });
@@ -0,0 +1,14 @@
1
+ import 'dotenv-flow/config';
2
+
3
+ import { defineConfig, env } from 'prisma/config';
4
+
5
+ export default defineConfig({
6
+ schema: 'prisma/schema.prisma',
7
+ migrations: {
8
+ path: 'prisma/migrations',
9
+ seed: 'tsx prisma/seed.ts',
10
+ },
11
+ datasource: {
12
+ url: env('DATABASE_URL'),
13
+ },
14
+ });