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.
- package/dist/index.js +452 -0
- package/package.json +57 -0
- package/template/.dockerignore +59 -0
- package/template/.github/workflows/ci.yml +157 -0
- package/template/.husky/pre-commit +1 -0
- package/template/.husky/pre-push +1 -0
- package/template/.lintstagedrc.cjs +4 -0
- package/template/.nvmrc +1 -0
- package/template/.prettierrc +9 -0
- package/template/.vscode/settings.json +13 -0
- package/template/CLAUDE.md +175 -0
- package/template/CONTRIBUTING.md +32 -0
- package/template/Dockerfile +90 -0
- package/template/GETTING_STARTED.md +35 -0
- package/template/LICENSE +21 -0
- package/template/README.md +116 -0
- package/template/apps/api/.dockerignore +51 -0
- package/template/apps/api/.env.local.example +62 -0
- package/template/apps/api/emails/account-deleted-email.tsx +69 -0
- package/template/apps/api/emails/components/email-layout.tsx +154 -0
- package/template/apps/api/emails/config.ts +22 -0
- package/template/apps/api/emails/password-changed-email.tsx +88 -0
- package/template/apps/api/emails/password-reset-email.tsx +86 -0
- package/template/apps/api/emails/verification-email.tsx +85 -0
- package/template/apps/api/emails/welcome-email.tsx +70 -0
- package/template/apps/api/package.json +84 -0
- package/template/apps/api/prisma/migrations/20251012111439_init/migration.sql +13 -0
- package/template/apps/api/prisma/migrations/20251018162629_add_better_auth_fields/migration.sql +67 -0
- package/template/apps/api/prisma/migrations/20251019142208_add_user_role_enum/migration.sql +5 -0
- package/template/apps/api/prisma/migrations/20251019182151_user_auth/migration.sql +7 -0
- package/template/apps/api/prisma/migrations/20251019211416_faster_session_lookup/migration.sql +2 -0
- package/template/apps/api/prisma/migrations/20251119124337_add_upload_model/migration.sql +26 -0
- package/template/apps/api/prisma/migrations/20251120071241_add_scope_to_account/migration.sql +2 -0
- package/template/apps/api/prisma/migrations/20251120072608_add_oauth_token_expiration_fields/migration.sql +10 -0
- package/template/apps/api/prisma/migrations/20251120144705_add_audit_logs/migration.sql +29 -0
- package/template/apps/api/prisma/migrations/20251127123614_remove_impersonated_by/migration.sql +8 -0
- package/template/apps/api/prisma/migrations/20251127125630_remove_audit_logs/migration.sql +11 -0
- package/template/apps/api/prisma/migrations/migration_lock.toml +3 -0
- package/template/apps/api/prisma/schema.prisma +116 -0
- package/template/apps/api/prisma/seed.ts +159 -0
- package/template/apps/api/prisma.config.ts +14 -0
- package/template/apps/api/src/app.ts +377 -0
- package/template/apps/api/src/common/logger.service.ts +227 -0
- package/template/apps/api/src/config/env.ts +60 -0
- package/template/apps/api/src/config/rate-limit.ts +29 -0
- package/template/apps/api/src/hooks/auth.ts +122 -0
- package/template/apps/api/src/plugins/auth.ts +198 -0
- package/template/apps/api/src/plugins/database.ts +45 -0
- package/template/apps/api/src/plugins/logger.ts +33 -0
- package/template/apps/api/src/plugins/multipart.ts +16 -0
- package/template/apps/api/src/plugins/scalar.ts +20 -0
- package/template/apps/api/src/plugins/schedule.ts +52 -0
- package/template/apps/api/src/plugins/services.ts +66 -0
- package/template/apps/api/src/plugins/swagger.ts +56 -0
- package/template/apps/api/src/routes/accounts.ts +91 -0
- package/template/apps/api/src/routes/admin-sessions.ts +92 -0
- package/template/apps/api/src/routes/metrics.ts +71 -0
- package/template/apps/api/src/routes/password.ts +46 -0
- package/template/apps/api/src/routes/sessions.ts +53 -0
- package/template/apps/api/src/routes/stats.ts +38 -0
- package/template/apps/api/src/routes/uploads-serve.ts +27 -0
- package/template/apps/api/src/routes/uploads.ts +154 -0
- package/template/apps/api/src/routes/users.ts +114 -0
- package/template/apps/api/src/routes/verification.ts +90 -0
- package/template/apps/api/src/server.ts +34 -0
- package/template/apps/api/src/services/accounts.service.ts +125 -0
- package/template/apps/api/src/services/authorization.service.ts +162 -0
- package/template/apps/api/src/services/email.service.ts +170 -0
- package/template/apps/api/src/services/file-storage.service.ts +267 -0
- package/template/apps/api/src/services/metrics.service.ts +175 -0
- package/template/apps/api/src/services/password.service.ts +56 -0
- package/template/apps/api/src/services/sessions.service.spec.ts +134 -0
- package/template/apps/api/src/services/sessions.service.ts +276 -0
- package/template/apps/api/src/services/stats.service.ts +273 -0
- package/template/apps/api/src/services/uploads.service.ts +163 -0
- package/template/apps/api/src/services/users.service.spec.ts +249 -0
- package/template/apps/api/src/services/users.service.ts +198 -0
- package/template/apps/api/src/utils/file-validation.ts +108 -0
- package/template/apps/api/start.sh +33 -0
- package/template/apps/api/test/helpers/fastify-app.ts +24 -0
- package/template/apps/api/test/helpers/mock-authorization.ts +16 -0
- package/template/apps/api/test/helpers/mock-logger.ts +28 -0
- package/template/apps/api/test/helpers/mock-prisma.ts +30 -0
- package/template/apps/api/test/helpers/test-db.ts +125 -0
- package/template/apps/api/test/integration/auth-flow.integration.spec.ts +449 -0
- package/template/apps/api/test/integration/password.integration.spec.ts +427 -0
- package/template/apps/api/test/integration/rate-limit.integration.spec.ts +51 -0
- package/template/apps/api/test/integration/sessions.integration.spec.ts +445 -0
- package/template/apps/api/test/integration/users.integration.spec.ts +211 -0
- package/template/apps/api/test/setup.ts +31 -0
- package/template/apps/api/tsconfig.json +26 -0
- package/template/apps/api/vitest.config.ts +35 -0
- package/template/apps/web/.env.local.example +11 -0
- package/template/apps/web/components.json +24 -0
- package/template/apps/web/next.config.ts +22 -0
- package/template/apps/web/package.json +56 -0
- package/template/apps/web/postcss.config.js +5 -0
- package/template/apps/web/public/apple-icon.png +0 -0
- package/template/apps/web/public/icon.png +0 -0
- package/template/apps/web/public/robots.txt +3 -0
- package/template/apps/web/src/app/(admin)/admin/layout.tsx +222 -0
- package/template/apps/web/src/app/(admin)/admin/page.tsx +157 -0
- package/template/apps/web/src/app/(admin)/admin/sessions/page.tsx +18 -0
- package/template/apps/web/src/app/(admin)/admin/users/page.tsx +20 -0
- package/template/apps/web/src/app/(auth)/forgot-password/page.tsx +177 -0
- package/template/apps/web/src/app/(auth)/login/page.tsx +159 -0
- package/template/apps/web/src/app/(auth)/reset-password/page.tsx +245 -0
- package/template/apps/web/src/app/(auth)/signup/page.tsx +153 -0
- package/template/apps/web/src/app/dashboard/change-password/page.tsx +255 -0
- package/template/apps/web/src/app/dashboard/page.tsx +296 -0
- package/template/apps/web/src/app/error.tsx +32 -0
- package/template/apps/web/src/app/examples/file-upload/page.tsx +200 -0
- package/template/apps/web/src/app/favicon.ico +0 -0
- package/template/apps/web/src/app/global-error.tsx +96 -0
- package/template/apps/web/src/app/globals.css +22 -0
- package/template/apps/web/src/app/icon.png +0 -0
- package/template/apps/web/src/app/layout.tsx +34 -0
- package/template/apps/web/src/app/not-found.tsx +28 -0
- package/template/apps/web/src/app/page.tsx +192 -0
- package/template/apps/web/src/components/admin/activity-feed.tsx +101 -0
- package/template/apps/web/src/components/admin/charts/auth-breakdown-chart.tsx +114 -0
- package/template/apps/web/src/components/admin/charts/chart-tooltip.tsx +124 -0
- package/template/apps/web/src/components/admin/charts/realtime-metrics-chart.tsx +511 -0
- package/template/apps/web/src/components/admin/charts/role-distribution-chart.tsx +102 -0
- package/template/apps/web/src/components/admin/charts/session-activity-chart.tsx +90 -0
- package/template/apps/web/src/components/admin/charts/user-growth-chart.tsx +108 -0
- package/template/apps/web/src/components/admin/health-indicator.tsx +175 -0
- package/template/apps/web/src/components/admin/refresh-control.tsx +90 -0
- package/template/apps/web/src/components/admin/session-revoke-all-dialog.tsx +79 -0
- package/template/apps/web/src/components/admin/session-revoke-dialog.tsx +74 -0
- package/template/apps/web/src/components/admin/sessions-management-table.tsx +372 -0
- package/template/apps/web/src/components/admin/stat-card.tsx +137 -0
- package/template/apps/web/src/components/admin/user-create-dialog.tsx +152 -0
- package/template/apps/web/src/components/admin/user-delete-dialog.tsx +73 -0
- package/template/apps/web/src/components/admin/user-edit-dialog.tsx +170 -0
- package/template/apps/web/src/components/admin/users-management-table.tsx +285 -0
- package/template/apps/web/src/components/auth/email-verification-banner.tsx +85 -0
- package/template/apps/web/src/components/auth/github-button.tsx +40 -0
- package/template/apps/web/src/components/auth/google-button.tsx +54 -0
- package/template/apps/web/src/components/auth/protected-route.tsx +66 -0
- package/template/apps/web/src/components/auth/redirect-if-authenticated.tsx +31 -0
- package/template/apps/web/src/components/auth/with-auth.tsx +30 -0
- package/template/apps/web/src/components/error/error-card.tsx +47 -0
- package/template/apps/web/src/components/error/forbidden.tsx +25 -0
- package/template/apps/web/src/components/landing/command-block.tsx +64 -0
- package/template/apps/web/src/components/landing/feature-card.tsx +60 -0
- package/template/apps/web/src/components/landing/included-feature-card.tsx +63 -0
- package/template/apps/web/src/components/landing/logo.tsx +41 -0
- package/template/apps/web/src/components/landing/tech-badge.tsx +11 -0
- package/template/apps/web/src/components/layout/auth-nav.tsx +58 -0
- package/template/apps/web/src/components/layout/footer.tsx +3 -0
- package/template/apps/web/src/config/landing-data.ts +152 -0
- package/template/apps/web/src/config/site.ts +5 -0
- package/template/apps/web/src/hooks/api/__tests__/use-users.test.tsx +181 -0
- package/template/apps/web/src/hooks/api/use-admin-sessions.ts +75 -0
- package/template/apps/web/src/hooks/api/use-admin-stats.ts +33 -0
- package/template/apps/web/src/hooks/api/use-sessions.ts +52 -0
- package/template/apps/web/src/hooks/api/use-uploads.ts +156 -0
- package/template/apps/web/src/hooks/api/use-users.ts +149 -0
- package/template/apps/web/src/hooks/use-mobile.ts +21 -0
- package/template/apps/web/src/hooks/use-realtime-metrics.ts +120 -0
- package/template/apps/web/src/lib/__tests__/utils.test.ts +29 -0
- package/template/apps/web/src/lib/api.ts +151 -0
- package/template/apps/web/src/lib/auth.ts +13 -0
- package/template/apps/web/src/lib/env.ts +52 -0
- package/template/apps/web/src/lib/form-utils.ts +11 -0
- package/template/apps/web/src/lib/utils.ts +1 -0
- package/template/apps/web/src/providers.tsx +34 -0
- package/template/apps/web/src/store/atoms.ts +15 -0
- package/template/apps/web/src/test/helpers/test-utils.tsx +44 -0
- package/template/apps/web/src/test/setup.ts +8 -0
- package/template/apps/web/tailwind.config.ts +5 -0
- package/template/apps/web/tsconfig.json +26 -0
- package/template/apps/web/vitest.config.ts +32 -0
- package/template/assets/logo-512.png +0 -0
- package/template/assets/logo.svg +4 -0
- package/template/docker-compose.prod.yml +66 -0
- package/template/docker-compose.yml +36 -0
- package/template/eslint.config.ts +119 -0
- package/template/package.json +77 -0
- package/template/packages/tailwind-config/package.json +9 -0
- package/template/packages/tailwind-config/theme.css +179 -0
- package/template/packages/types/package.json +29 -0
- package/template/packages/types/src/__tests__/schemas.test.ts +255 -0
- package/template/packages/types/src/api-response.ts +53 -0
- package/template/packages/types/src/health-check.ts +11 -0
- package/template/packages/types/src/pagination.ts +41 -0
- package/template/packages/types/src/role.ts +5 -0
- package/template/packages/types/src/session.ts +48 -0
- package/template/packages/types/src/stats.ts +113 -0
- package/template/packages/types/src/upload.ts +51 -0
- package/template/packages/types/src/user.ts +36 -0
- package/template/packages/types/tsconfig.json +5 -0
- package/template/packages/types/vitest.config.ts +21 -0
- package/template/packages/ui/components.json +21 -0
- package/template/packages/ui/package.json +108 -0
- package/template/packages/ui/src/__tests__/button.test.tsx +70 -0
- package/template/packages/ui/src/alert-dialog.tsx +141 -0
- package/template/packages/ui/src/alert.tsx +66 -0
- package/template/packages/ui/src/animated-theme-toggler.tsx +167 -0
- package/template/packages/ui/src/avatar.tsx +53 -0
- package/template/packages/ui/src/badge.tsx +36 -0
- package/template/packages/ui/src/button.tsx +84 -0
- package/template/packages/ui/src/card.tsx +92 -0
- package/template/packages/ui/src/checkbox.tsx +32 -0
- package/template/packages/ui/src/data-table/data-table-column-header.tsx +68 -0
- package/template/packages/ui/src/data-table/data-table-pagination.tsx +99 -0
- package/template/packages/ui/src/data-table/data-table-toolbar.tsx +55 -0
- package/template/packages/ui/src/data-table/data-table-view-options.tsx +63 -0
- package/template/packages/ui/src/data-table/data-table.tsx +167 -0
- package/template/packages/ui/src/dialog.tsx +143 -0
- package/template/packages/ui/src/dropdown-menu.tsx +257 -0
- package/template/packages/ui/src/empty-state.tsx +52 -0
- package/template/packages/ui/src/file-upload-input.tsx +202 -0
- package/template/packages/ui/src/form.tsx +168 -0
- package/template/packages/ui/src/hooks/use-mobile.ts +19 -0
- package/template/packages/ui/src/icons/brand-icons.tsx +16 -0
- package/template/packages/ui/src/input.tsx +21 -0
- package/template/packages/ui/src/label.tsx +24 -0
- package/template/packages/ui/src/lib/utils.ts +6 -0
- package/template/packages/ui/src/password-input.tsx +102 -0
- package/template/packages/ui/src/popover.tsx +48 -0
- package/template/packages/ui/src/radio-group.tsx +45 -0
- package/template/packages/ui/src/scroll-area.tsx +58 -0
- package/template/packages/ui/src/select.tsx +187 -0
- package/template/packages/ui/src/separator.tsx +28 -0
- package/template/packages/ui/src/sheet.tsx +139 -0
- package/template/packages/ui/src/sidebar.tsx +726 -0
- package/template/packages/ui/src/skeleton-variants.tsx +87 -0
- package/template/packages/ui/src/skeleton.tsx +13 -0
- package/template/packages/ui/src/slider.tsx +63 -0
- package/template/packages/ui/src/sonner.tsx +25 -0
- package/template/packages/ui/src/spinner.tsx +16 -0
- package/template/packages/ui/src/switch.tsx +31 -0
- package/template/packages/ui/src/table.tsx +116 -0
- package/template/packages/ui/src/tabs.tsx +66 -0
- package/template/packages/ui/src/textarea.tsx +18 -0
- package/template/packages/ui/src/tooltip.tsx +61 -0
- package/template/packages/ui/src/user-avatar.tsx +97 -0
- package/template/packages/ui/test-config.js +3 -0
- package/template/packages/ui/tsconfig.json +12 -0
- package/template/packages/ui/turbo.json +18 -0
- package/template/packages/ui/vitest.config.ts +17 -0
- package/template/packages/ui/vitest.setup.ts +1 -0
- package/template/packages/utils/package.json +23 -0
- package/template/packages/utils/src/__tests__/utils.test.ts +223 -0
- package/template/packages/utils/src/array.ts +18 -0
- package/template/packages/utils/src/async.ts +3 -0
- package/template/packages/utils/src/date.ts +77 -0
- package/template/packages/utils/src/errors.ts +73 -0
- package/template/packages/utils/src/number.ts +11 -0
- package/template/packages/utils/src/string.ts +13 -0
- package/template/packages/utils/tsconfig.json +5 -0
- package/template/packages/utils/vitest.config.ts +21 -0
- package/template/pnpm-workspace.yaml +4 -0
- package/template/tsconfig.base.json +32 -0
- package/template/turbo.json +133 -0
- package/template/vitest.shared.ts +26 -0
- 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");
|
package/template/apps/api/prisma/migrations/20251018162629_add_better_auth_fields/migration.sql
ADDED
|
@@ -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,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,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,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,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
|
+
});
|