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,51 @@
1
+ # Dependencies
2
+ node_modules
3
+ .pnpm-store
4
+
5
+ # Build outputs
6
+ dist
7
+ build
8
+ .next
9
+ *.tsbuildinfo
10
+
11
+ # Development files
12
+ .env
13
+ .env.local
14
+ .env.*.local
15
+ *.log
16
+ npm-debug.log*
17
+ yarn-debug.log*
18
+ yarn-error.log*
19
+ pnpm-debug.log*
20
+
21
+ # Testing
22
+ coverage
23
+ test-results
24
+ .vitest
25
+
26
+ # IDE
27
+ .vscode
28
+ .idea
29
+ *.swp
30
+ *.swo
31
+ .DS_Store
32
+
33
+ # Git
34
+ .git
35
+ .gitignore
36
+ .gitattributes
37
+
38
+ # Documentation
39
+ *.md
40
+ !README.md
41
+
42
+ # CI/CD
43
+ .github
44
+ .gitlab-ci.yml
45
+ .travis.yml
46
+
47
+ # Misc
48
+ .cache
49
+ .temp
50
+ tmp
51
+ temp
@@ -0,0 +1,62 @@
1
+ NODE_ENV=development
2
+ API_URL=http://localhost:8080
3
+ FRONTEND_URL=http://localhost:3000
4
+ PORT=8080
5
+
6
+ # Database connection string for local Docker PostgreSQL
7
+ # Match credentials from docker-compose.yml
8
+ # Note: Test database will automatically use app_dev_test (suffix added by test runner)
9
+ DATABASE_URL=postgresql://postgres:postgres_dev_password@localhost:5432/app_dev
10
+
11
+
12
+ # Cookie secret for signing cookies
13
+ COOKIE_SECRET=dev-secret-change-in-production-use-random-string
14
+
15
+ # Logging verbosity level
16
+ # - minimal: Only errors and critical warnings (recommended for production)
17
+ # - normal: Standard HTTP requests, business events, errors (default)
18
+ # - detailed: Include debug info, SQL queries, cache operations
19
+ # - verbose: Everything including request/response bodies, timing breakdowns
20
+ LOG_LEVEL=normal
21
+
22
+ # Better Auth configuration
23
+ BETTER_AUTH_SECRET=dev-secret-change-in-production-must-be-at-least-32-characters
24
+ # Base URL of your API for Better Auth callbacks
25
+ BETTER_AUTH_URL=http://localhost:8080
26
+
27
+
28
+
29
+ # Email Service Configuration
30
+ # You MUST verify your domain in Resend dashboard before sending emails
31
+ RESEND_API_KEY=re_xxxxxxxxxxxx
32
+ EMAIL_FROM=noreply@yourdomain.com
33
+
34
+ # OAuth Provider Credentials (Optional but recommended)
35
+ # Google and GitHub OAuth are enabled by default.
36
+ # Google OAuth Setup:
37
+ # 1. Go to: https://console.cloud.google.com/apis/credentials
38
+ # 2. Create OAuth 2.0 Client ID (Web application)
39
+ # 3. Add authorized redirect URI: http://localhost:8080/api/auth/callback/google
40
+ # 4. For production, add: https://<domain>.com/api/auth/callback/google
41
+ GOOGLE_CLIENT_ID=
42
+ GOOGLE_CLIENT_SECRET=
43
+
44
+
45
+ # GitHub OAuth Setup:
46
+ # 1. Go to: https://github.com/settings/developers
47
+ # 2. Create new OAuth App
48
+ # 3. Set Authorization callback URL: http://localhost:8080/api/auth/callback/github
49
+ # 4. For production, add: https://<domain>.com/api/auth/callback/github
50
+ GITHUB_CLIENT_ID=
51
+ GITHUB_CLIENT_SECRET=
52
+
53
+
54
+ # File Storage Configuration (OPTIONAL)
55
+ # Defaults to local file storage if not configured
56
+ # For S3-compatible storage (AWS S3, Cloudflare R2, MinIO, etc.)
57
+ # STORAGE_TYPE=local
58
+ # S3_BUCKET=
59
+ # S3_REGION=auto
60
+ # S3_ACCESS_KEY_ID=
61
+ # S3_SECRET_ACCESS_KEY=
62
+ # S3_ENDPOINT=https://your-account-id.r2.cloudflarestorage.com
@@ -0,0 +1,69 @@
1
+ import { Link, Text } from '@react-email/components';
2
+ import * as React from 'react';
3
+
4
+ import { EmailLayout } from './components/email-layout';
5
+ import { emailConfig } from './config';
6
+
7
+ interface AccountDeletedEmailProps {
8
+ userEmail: string;
9
+ deletedAt: string;
10
+ }
11
+
12
+ export default function AccountDeletedEmail({
13
+ userEmail = 'user@example.com',
14
+ deletedAt = new Date().toLocaleString('en-US', {
15
+ dateStyle: 'medium',
16
+ timeStyle: 'short',
17
+ }),
18
+ }: AccountDeletedEmailProps) {
19
+ const { appName, supportEmail } = emailConfig;
20
+
21
+ return (
22
+ <EmailLayout
23
+ preview={`Your ${appName} account has been deleted`}
24
+ heading="Account deleted"
25
+ >
26
+ <Text style={paragraph}>
27
+ Your account (<strong>{userEmail}</strong>) has been permanently deleted
28
+ on {deletedAt}.
29
+ </Text>
30
+
31
+ <Text style={paragraph}>
32
+ All your data has been removed from our systems. This action cannot be
33
+ undone.
34
+ </Text>
35
+
36
+ <Text style={paragraph}>
37
+ If you didn't request this deletion or believe this was done in error,
38
+ please contact us immediately at{' '}
39
+ <Link href={`mailto:${supportEmail}`} style={link}>
40
+ {supportEmail}
41
+ </Link>
42
+ .
43
+ </Text>
44
+
45
+ <Text style={paragraphSmall}>
46
+ Thank you for being part of {appName}. We're sorry to see you go.
47
+ </Text>
48
+ </EmailLayout>
49
+ );
50
+ }
51
+
52
+ const paragraph: React.CSSProperties = {
53
+ color: '#334155',
54
+ fontSize: '15px',
55
+ lineHeight: '24px',
56
+ margin: '0 0 24px',
57
+ };
58
+
59
+ const paragraphSmall: React.CSSProperties = {
60
+ color: '#94a3b8',
61
+ fontSize: '13px',
62
+ lineHeight: '20px',
63
+ margin: '24px 0 0',
64
+ };
65
+
66
+ const link: React.CSSProperties = {
67
+ color: '#2563eb',
68
+ textDecoration: 'underline',
69
+ };
@@ -0,0 +1,154 @@
1
+ import {
2
+ Body,
3
+ Container,
4
+ Head,
5
+ Heading,
6
+ Hr,
7
+ Html,
8
+ Img,
9
+ Link,
10
+ Preview,
11
+ Section,
12
+ Text,
13
+ } from '@react-email/components';
14
+ import * as React from 'react';
15
+
16
+ import { emailConfig } from '../config';
17
+
18
+ interface EmailLayoutProps {
19
+ preview: string;
20
+ heading: string;
21
+ children: React.ReactNode;
22
+ showFooterLinks?: boolean;
23
+ }
24
+
25
+ export function EmailLayout({
26
+ preview,
27
+ heading,
28
+ children,
29
+ showFooterLinks = true,
30
+ }: EmailLayoutProps) {
31
+ const { appName, logoUrl, primaryColor, supportEmail } = emailConfig;
32
+
33
+ return (
34
+ <Html>
35
+ <Head />
36
+ <Preview>{preview}</Preview>
37
+ <Body style={main}>
38
+ <Container style={container}>
39
+ <Section style={logoSection}>
40
+ {logoUrl ? (
41
+ <Img src={logoUrl} width="48" height="48" alt={appName} />
42
+ ) : (
43
+ <Text style={{ ...logoText, color: primaryColor }}>
44
+ {appName}
45
+ </Text>
46
+ )}
47
+ </Section>
48
+
49
+ <Section style={contentSection}>
50
+ <Heading style={h1}>{heading}</Heading>
51
+ {children}
52
+ </Section>
53
+
54
+ <Hr style={hr} />
55
+
56
+ <Section style={footer}>
57
+ {showFooterLinks && (
58
+ <Text style={footerLinks}>
59
+ <Link href={`mailto:${supportEmail}`} style={footerLink}>
60
+ Contact Support
61
+ </Link>
62
+ </Text>
63
+ )}
64
+ <Text style={footerText}>
65
+ © {new Date().getFullYear()} {appName}. All rights reserved.
66
+ </Text>
67
+ <Text style={footerMuted}>
68
+ If you didn't expect this email, you can safely ignore it.
69
+ </Text>
70
+ </Section>
71
+ </Container>
72
+ </Body>
73
+ </Html>
74
+ );
75
+ }
76
+
77
+ const main: React.CSSProperties = {
78
+ backgroundColor: '#f8fafc',
79
+ fontFamily:
80
+ '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Ubuntu, sans-serif',
81
+ padding: '40px 0',
82
+ };
83
+
84
+ const container: React.CSSProperties = {
85
+ backgroundColor: '#ffffff',
86
+ margin: '0 auto',
87
+ maxWidth: '560px',
88
+ borderRadius: '12px',
89
+ border: '1px solid #e2e8f0',
90
+ overflow: 'hidden',
91
+ };
92
+
93
+ const logoSection: React.CSSProperties = {
94
+ padding: '32px 40px 24px',
95
+ textAlign: 'center',
96
+ };
97
+
98
+ const logoText: React.CSSProperties = {
99
+ fontSize: '24px',
100
+ fontWeight: '700',
101
+ margin: '0',
102
+ letterSpacing: '-0.5px',
103
+ };
104
+
105
+ const contentSection: React.CSSProperties = {
106
+ padding: '0 40px 32px',
107
+ };
108
+
109
+ const h1: React.CSSProperties = {
110
+ color: '#0f172a',
111
+ fontSize: '24px',
112
+ fontWeight: '600',
113
+ lineHeight: '32px',
114
+ margin: '0 0 24px',
115
+ letterSpacing: '-0.25px',
116
+ };
117
+
118
+ const hr: React.CSSProperties = {
119
+ borderColor: '#e2e8f0',
120
+ borderTop: 'none',
121
+ margin: '0',
122
+ };
123
+
124
+ const footer: React.CSSProperties = {
125
+ padding: '24px 40px',
126
+ backgroundColor: '#f8fafc',
127
+ };
128
+
129
+ const footerLinks: React.CSSProperties = {
130
+ textAlign: 'center',
131
+ margin: '0 0 16px',
132
+ };
133
+
134
+ const footerLink: React.CSSProperties = {
135
+ color: '#64748b',
136
+ fontSize: '13px',
137
+ textDecoration: 'underline',
138
+ };
139
+
140
+ const footerText: React.CSSProperties = {
141
+ color: '#64748b',
142
+ fontSize: '13px',
143
+ lineHeight: '20px',
144
+ textAlign: 'center',
145
+ margin: '0 0 8px',
146
+ };
147
+
148
+ const footerMuted: React.CSSProperties = {
149
+ color: '#94a3b8',
150
+ fontSize: '12px',
151
+ lineHeight: '18px',
152
+ textAlign: 'center',
153
+ margin: '0',
154
+ };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Email template configuration.
3
+ *
4
+ * IMPORTANT: Update these values before deploying to production.
5
+ * The logo must be hosted at a publicly accessible URL.
6
+ */
7
+
8
+ export interface EmailConfig {
9
+ appName: string;
10
+ websiteUrl: string;
11
+ supportEmail: string;
12
+ primaryColor: string;
13
+ logoUrl: string | undefined;
14
+ }
15
+
16
+ export const emailConfig: EmailConfig = {
17
+ appName: 'Your App',
18
+ websiteUrl: 'https://yourapp.com',
19
+ supportEmail: 'support@yourapp.com',
20
+ primaryColor: '#2563eb',
21
+ logoUrl: undefined,
22
+ };
@@ -0,0 +1,88 @@
1
+ import { Button, Text } from '@react-email/components';
2
+ import * as React from 'react';
3
+
4
+ import { EmailLayout } from './components/email-layout';
5
+ import { emailConfig } from './config';
6
+
7
+ interface PasswordChangedEmailProps {
8
+ userEmail: string;
9
+ changedAt: string;
10
+ resetPasswordUrl: string;
11
+ }
12
+
13
+ export default function PasswordChangedEmail({
14
+ userEmail = 'user@example.com',
15
+ changedAt = new Date().toLocaleString('en-US', {
16
+ dateStyle: 'medium',
17
+ timeStyle: 'short',
18
+ }),
19
+ resetPasswordUrl = 'https://yourapp.com/forgot-password',
20
+ }: PasswordChangedEmailProps) {
21
+ const { appName } = emailConfig;
22
+
23
+ return (
24
+ <EmailLayout
25
+ preview={`Your ${appName} password was changed`}
26
+ heading="Your password was changed"
27
+ >
28
+ <Text style={paragraph}>
29
+ The password for your account (<strong>{userEmail}</strong>) was
30
+ successfully changed on {changedAt}.
31
+ </Text>
32
+
33
+ <Text style={paragraph}>
34
+ If you made this change, no further action is required.
35
+ </Text>
36
+
37
+ <Text style={warningText}>
38
+ If you didn't change your password, your account may be compromised.
39
+ Reset your password immediately:
40
+ </Text>
41
+
42
+ <Button style={dangerButton} href={resetPasswordUrl}>
43
+ Reset Password
44
+ </Button>
45
+
46
+ <Text style={paragraphSmall}>
47
+ For security, this email was sent to notify you of account changes.
48
+ </Text>
49
+ </EmailLayout>
50
+ );
51
+ }
52
+
53
+ const paragraph: React.CSSProperties = {
54
+ color: '#334155',
55
+ fontSize: '15px',
56
+ lineHeight: '24px',
57
+ margin: '0 0 24px',
58
+ };
59
+
60
+ const warningText: React.CSSProperties = {
61
+ color: '#b45309',
62
+ fontSize: '15px',
63
+ lineHeight: '24px',
64
+ margin: '0 0 24px',
65
+ padding: '16px',
66
+ backgroundColor: '#fef3c7',
67
+ borderRadius: '8px',
68
+ border: '1px solid #fcd34d',
69
+ };
70
+
71
+ const paragraphSmall: React.CSSProperties = {
72
+ color: '#94a3b8',
73
+ fontSize: '13px',
74
+ lineHeight: '20px',
75
+ margin: '24px 0 0',
76
+ };
77
+
78
+ const dangerButton: React.CSSProperties = {
79
+ borderRadius: '8px',
80
+ backgroundColor: '#dc2626',
81
+ color: '#ffffff',
82
+ fontSize: '15px',
83
+ fontWeight: '600',
84
+ textDecoration: 'none',
85
+ textAlign: 'center',
86
+ display: 'inline-block',
87
+ padding: '12px 32px',
88
+ };
@@ -0,0 +1,86 @@
1
+ import { Button, Link, Text } from '@react-email/components';
2
+ import * as React from 'react';
3
+
4
+ import { EmailLayout } from './components/email-layout';
5
+ import { emailConfig } from './config';
6
+
7
+ interface PasswordResetEmailProps {
8
+ resetUrl: string;
9
+ userEmail: string;
10
+ }
11
+
12
+ export default function PasswordResetEmail({
13
+ resetUrl = 'https://example.com/reset-password?token=abc123',
14
+ userEmail = 'user@example.com',
15
+ }: PasswordResetEmailProps) {
16
+ const { primaryColor } = emailConfig;
17
+
18
+ return (
19
+ <EmailLayout preview="Reset your password" heading="Reset your password">
20
+ <Text style={paragraph}>
21
+ We received a request to reset the password for{' '}
22
+ <strong>{userEmail}</strong>.
23
+ </Text>
24
+
25
+ <Text style={paragraph}>
26
+ Click the button below to choose a new password:
27
+ </Text>
28
+
29
+ <Button
30
+ style={{ ...button, backgroundColor: primaryColor }}
31
+ href={resetUrl}
32
+ >
33
+ Reset Password
34
+ </Button>
35
+
36
+ <Text style={paragraphMuted}>Or copy this link into your browser:</Text>
37
+ <Link href={resetUrl} style={link}>
38
+ {resetUrl}
39
+ </Link>
40
+
41
+ <Text style={paragraphSmall}>
42
+ This link expires in 1 hour. If you didn't request this, you can safely
43
+ ignore this email — your password won't be changed.
44
+ </Text>
45
+ </EmailLayout>
46
+ );
47
+ }
48
+
49
+ const paragraph: React.CSSProperties = {
50
+ color: '#334155',
51
+ fontSize: '15px',
52
+ lineHeight: '24px',
53
+ margin: '0 0 24px',
54
+ };
55
+
56
+ const paragraphMuted: React.CSSProperties = {
57
+ color: '#64748b',
58
+ fontSize: '13px',
59
+ lineHeight: '20px',
60
+ margin: '24px 0 8px',
61
+ };
62
+
63
+ const paragraphSmall: React.CSSProperties = {
64
+ color: '#94a3b8',
65
+ fontSize: '13px',
66
+ lineHeight: '20px',
67
+ margin: '24px 0 0',
68
+ };
69
+
70
+ const button: React.CSSProperties = {
71
+ borderRadius: '8px',
72
+ color: '#ffffff',
73
+ fontSize: '15px',
74
+ fontWeight: '600',
75
+ textDecoration: 'none',
76
+ textAlign: 'center',
77
+ display: 'inline-block',
78
+ padding: '12px 32px',
79
+ };
80
+
81
+ const link: React.CSSProperties = {
82
+ color: '#2563eb',
83
+ fontSize: '13px',
84
+ textDecoration: 'underline',
85
+ wordBreak: 'break-all',
86
+ };
@@ -0,0 +1,85 @@
1
+ import { Button, Link, Text } from '@react-email/components';
2
+ import * as React from 'react';
3
+
4
+ import { EmailLayout } from './components/email-layout';
5
+ import { emailConfig } from './config';
6
+
7
+ interface VerificationEmailProps {
8
+ verificationUrl: string;
9
+ userEmail: string;
10
+ }
11
+
12
+ export default function VerificationEmail({
13
+ verificationUrl = 'https://example.com/verify?token=abc123',
14
+ userEmail = 'user@example.com',
15
+ }: VerificationEmailProps) {
16
+ const { appName, primaryColor } = emailConfig;
17
+
18
+ return (
19
+ <EmailLayout
20
+ preview={`Verify your email to get started with ${appName}`}
21
+ heading="Verify your email"
22
+ >
23
+ <Text style={paragraph}>
24
+ Thanks for signing up! Please verify your email address (
25
+ <strong>{userEmail}</strong>) to complete your registration.
26
+ </Text>
27
+
28
+ <Button
29
+ style={{ ...button, backgroundColor: primaryColor }}
30
+ href={verificationUrl}
31
+ >
32
+ Verify Email Address
33
+ </Button>
34
+
35
+ <Text style={paragraphMuted}>Or copy this link into your browser:</Text>
36
+ <Link href={verificationUrl} style={link}>
37
+ {verificationUrl}
38
+ </Link>
39
+
40
+ <Text style={paragraphSmall}>
41
+ This link expires in 24 hours. If you didn't create an account, ignore
42
+ this email.
43
+ </Text>
44
+ </EmailLayout>
45
+ );
46
+ }
47
+
48
+ const paragraph: React.CSSProperties = {
49
+ color: '#334155',
50
+ fontSize: '15px',
51
+ lineHeight: '24px',
52
+ margin: '0 0 24px',
53
+ };
54
+
55
+ const paragraphMuted: React.CSSProperties = {
56
+ color: '#64748b',
57
+ fontSize: '13px',
58
+ lineHeight: '20px',
59
+ margin: '24px 0 8px',
60
+ };
61
+
62
+ const paragraphSmall: React.CSSProperties = {
63
+ color: '#94a3b8',
64
+ fontSize: '13px',
65
+ lineHeight: '20px',
66
+ margin: '24px 0 0',
67
+ };
68
+
69
+ const button: React.CSSProperties = {
70
+ borderRadius: '8px',
71
+ color: '#ffffff',
72
+ fontSize: '15px',
73
+ fontWeight: '600',
74
+ textDecoration: 'none',
75
+ textAlign: 'center',
76
+ display: 'inline-block',
77
+ padding: '12px 32px',
78
+ };
79
+
80
+ const link: React.CSSProperties = {
81
+ color: '#2563eb',
82
+ fontSize: '13px',
83
+ textDecoration: 'underline',
84
+ wordBreak: 'break-all',
85
+ };
@@ -0,0 +1,70 @@
1
+ import { Button, Text } from '@react-email/components';
2
+ import * as React from 'react';
3
+
4
+ import { EmailLayout } from './components/email-layout';
5
+ import { emailConfig } from './config';
6
+
7
+ interface WelcomeEmailProps {
8
+ userName: string;
9
+ dashboardUrl: string;
10
+ }
11
+
12
+ export default function WelcomeEmail({
13
+ userName = 'there',
14
+ dashboardUrl = 'https://yourapp.com/dashboard',
15
+ }: WelcomeEmailProps) {
16
+ const { appName, primaryColor } = emailConfig;
17
+
18
+ return (
19
+ <EmailLayout
20
+ preview={`Welcome to ${appName}! Your account is ready.`}
21
+ heading={`Welcome to ${appName}!`}
22
+ >
23
+ <Text style={paragraph}>Hi {userName},</Text>
24
+
25
+ <Text style={paragraph}>
26
+ Your email has been verified and your account is all set up. We're
27
+ excited to have you on board.
28
+ </Text>
29
+
30
+ <Text style={paragraph}>Get started by exploring your dashboard:</Text>
31
+
32
+ <Button
33
+ style={{ ...button, backgroundColor: primaryColor }}
34
+ href={dashboardUrl}
35
+ >
36
+ Go to Dashboard
37
+ </Button>
38
+
39
+ <Text style={paragraphSmall}>
40
+ If you have any questions, just reply to this email — we're here to
41
+ help.
42
+ </Text>
43
+ </EmailLayout>
44
+ );
45
+ }
46
+
47
+ const paragraph: React.CSSProperties = {
48
+ color: '#334155',
49
+ fontSize: '15px',
50
+ lineHeight: '24px',
51
+ margin: '0 0 24px',
52
+ };
53
+
54
+ const paragraphSmall: React.CSSProperties = {
55
+ color: '#94a3b8',
56
+ fontSize: '13px',
57
+ lineHeight: '20px',
58
+ margin: '24px 0 0',
59
+ };
60
+
61
+ const button: React.CSSProperties = {
62
+ borderRadius: '8px',
63
+ color: '#ffffff',
64
+ fontSize: '15px',
65
+ fontWeight: '600',
66
+ textDecoration: 'none',
67
+ textAlign: 'center',
68
+ display: 'inline-block',
69
+ padding: '12px 32px',
70
+ };