create-tigra 1.1.0 → 2.0.1

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 (243) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +80 -87
  3. package/bin/create-tigra.js +259 -308
  4. package/package.json +49 -41
  5. package/template/_claude/QUICK_REFERENCE.md +193 -0
  6. package/template/_claude/README.md +53 -0
  7. package/template/_claude/commands/create-client.md +881 -0
  8. package/template/_claude/commands/create-server.md +383 -0
  9. package/template/_claude/rules/client/01-project-structure.md +133 -0
  10. package/template/_claude/rules/client/02-components-and-types.md +146 -0
  11. package/template/_claude/rules/client/03-data-and-state.md +156 -0
  12. package/template/_claude/rules/client/04-design-system.md +185 -0
  13. package/template/_claude/rules/client/05-security.md +55 -0
  14. package/template/_claude/rules/client/06-ux-checklist.md +81 -0
  15. package/template/_claude/rules/client/core.md +42 -0
  16. package/template/_claude/rules/global/core.md +77 -0
  17. package/template/_claude/rules/server/core.md +50 -0
  18. package/template/_claude/rules/server/database.md +124 -0
  19. package/template/_claude/rules/server/project-conventions.md +150 -0
  20. package/template/_claude/rules/server/response-handling.md +144 -0
  21. package/template/client/.env.example +5 -0
  22. package/template/client/README.md +36 -0
  23. package/template/client/components.json +23 -0
  24. package/template/client/eslint.config.mjs +18 -0
  25. package/template/client/next.config.ts +34 -0
  26. package/template/client/package.json +44 -0
  27. package/template/client/postcss.config.mjs +7 -0
  28. package/template/client/src/app/(auth)/layout.tsx +18 -0
  29. package/template/client/src/app/(auth)/login/page.tsx +13 -0
  30. package/template/client/src/app/(auth)/register/page.tsx +13 -0
  31. package/template/client/src/app/(main)/dashboard/page.tsx +22 -0
  32. package/template/client/src/app/(main)/layout.tsx +11 -0
  33. package/template/client/src/app/error.tsx +27 -0
  34. package/template/client/src/app/favicon.ico +0 -0
  35. package/template/client/src/app/globals.css +145 -0
  36. package/template/client/src/app/layout.tsx +36 -0
  37. package/template/client/src/app/loading.tsx +11 -0
  38. package/template/client/src/app/not-found.tsx +23 -0
  39. package/template/client/src/app/page.tsx +45 -0
  40. package/template/client/src/app/providers.tsx +43 -0
  41. package/template/client/src/components/common/ConfirmDialog.tsx +56 -0
  42. package/template/client/src/components/common/EmptyState.tsx +31 -0
  43. package/template/client/src/components/common/LoadingSpinner.tsx +30 -0
  44. package/template/client/src/components/common/Pagination.tsx +55 -0
  45. package/template/client/src/components/layout/Footer.tsx +17 -0
  46. package/template/client/src/components/layout/Header.tsx +173 -0
  47. package/template/client/src/components/layout/MainLayout.tsx +18 -0
  48. package/template/client/src/components/ui/alert-dialog.tsx +196 -0
  49. package/template/client/src/components/ui/badge.tsx +48 -0
  50. package/template/client/src/components/ui/button.tsx +64 -0
  51. package/template/client/src/components/ui/card.tsx +92 -0
  52. package/template/client/src/components/ui/input.tsx +21 -0
  53. package/template/client/src/components/ui/label.tsx +24 -0
  54. package/template/client/src/components/ui/select.tsx +190 -0
  55. package/template/client/src/components/ui/skeleton.tsx +13 -0
  56. package/template/client/src/components/ui/table.tsx +116 -0
  57. package/template/client/src/features/auth/components/AuthInitializer.tsx +55 -0
  58. package/template/client/src/features/auth/components/LoginForm.tsx +107 -0
  59. package/template/client/src/features/auth/components/RegisterForm.tsx +178 -0
  60. package/template/client/src/features/auth/hooks/useAuth.ts +84 -0
  61. package/template/client/src/features/auth/services/auth.service.ts +52 -0
  62. package/template/client/src/features/auth/store/authSlice.ts +38 -0
  63. package/template/client/src/features/auth/types/auth.types.ts +32 -0
  64. package/template/client/src/hooks/useDebounce.ts +14 -0
  65. package/template/client/src/hooks/useLocalStorage.ts +55 -0
  66. package/template/client/src/hooks/useMediaQuery.ts +27 -0
  67. package/template/client/src/lib/api/api.types.ts +34 -0
  68. package/template/client/src/lib/api/axios.config.ts +98 -0
  69. package/template/client/src/lib/constants/api-endpoints.ts +18 -0
  70. package/template/client/src/lib/constants/app.constants.ts +12 -0
  71. package/template/client/src/lib/constants/routes.ts +9 -0
  72. package/template/client/src/lib/utils/error.ts +32 -0
  73. package/template/client/src/lib/utils/format.ts +37 -0
  74. package/template/client/src/lib/utils/security.ts +34 -0
  75. package/template/client/src/lib/utils.ts +6 -0
  76. package/template/client/src/middleware.ts +57 -0
  77. package/template/client/src/store/hooks.ts +7 -0
  78. package/template/client/src/store/index.ts +12 -0
  79. package/template/client/src/types/index.ts +3 -0
  80. package/template/client/tsconfig.json +34 -0
  81. package/template/gitignore +34 -0
  82. package/template/server/.dockerignore +66 -0
  83. package/template/server/.env.example +96 -69
  84. package/template/server/.env.production.example +90 -0
  85. package/template/server/Dockerfile +94 -0
  86. package/template/server/docker-compose.yml +82 -111
  87. package/template/server/docs/logging.md +62 -0
  88. package/template/server/eslint.config.mjs +17 -0
  89. package/template/server/package.json +68 -81
  90. package/template/server/phpmyadmin-config.php +26 -0
  91. package/template/server/postman_collection.json +666 -0
  92. package/template/server/prisma/schema.prisma +77 -93
  93. package/template/server/prisma/seed.ts +46 -142
  94. package/template/server/scripts/flush-redis.ts +41 -0
  95. package/template/server/src/app.ts +243 -71
  96. package/template/server/src/config/env.ts +67 -94
  97. package/template/server/src/libs/auth.ts +88 -0
  98. package/template/server/src/libs/cleanup.ts +35 -0
  99. package/template/server/src/libs/cookies.ts +46 -0
  100. package/template/server/src/libs/logger.ts +33 -60
  101. package/template/server/src/libs/monitoring.ts +205 -0
  102. package/template/server/src/libs/password.ts +38 -0
  103. package/template/server/src/libs/prisma.ts +68 -0
  104. package/template/server/src/libs/redis.ts +60 -79
  105. package/template/server/src/libs/requestLogger.ts +66 -0
  106. package/template/server/src/libs/storage/file-storage.service.ts +211 -0
  107. package/template/server/src/libs/storage/file-validator.ts +97 -0
  108. package/template/server/src/libs/storage/filename-sanitizer.ts +71 -0
  109. package/template/server/src/libs/storage/image-optimizer.service.ts +144 -0
  110. package/template/server/src/modules/auth/__tests__/auth.service.test.ts +365 -0
  111. package/template/server/src/modules/auth/auth.controller.ts +90 -141
  112. package/template/server/src/modules/auth/auth.repo.ts +120 -218
  113. package/template/server/src/modules/auth/auth.routes.ts +96 -83
  114. package/template/server/src/modules/auth/auth.schemas.ts +35 -137
  115. package/template/server/src/modules/auth/auth.service.ts +286 -329
  116. package/template/server/src/modules/auth/session.repo.ts +110 -0
  117. package/template/server/src/modules/users/users.controller.ts +120 -0
  118. package/template/server/src/modules/users/users.repo.ts +77 -0
  119. package/template/server/src/modules/users/users.routes.ts +89 -0
  120. package/template/server/src/modules/users/users.schemas.ts +21 -0
  121. package/template/server/src/modules/users/users.service.ts +169 -0
  122. package/template/server/src/server.ts +58 -139
  123. package/template/server/src/shared/errors/AppError.ts +21 -0
  124. package/template/server/src/shared/errors/errors.ts +43 -0
  125. package/template/server/src/shared/responses/paginatedResponse.ts +38 -0
  126. package/template/server/src/shared/responses/successResponse.ts +17 -0
  127. package/template/server/src/shared/schemas/pagination.schema.ts +12 -0
  128. package/template/server/src/shared/types/index.ts +26 -0
  129. package/template/server/src/test/setup.ts +74 -38
  130. package/template/server/tsconfig.json +27 -89
  131. package/template/server/uploads/avatars/.gitkeep +1 -0
  132. package/template/server/vitest.config.ts +43 -98
  133. package/template/.agent/rules/client/01-project-structure.md +0 -326
  134. package/template/.agent/rules/client/02-component-patterns.md +0 -249
  135. package/template/.agent/rules/client/03-typescript-rules.md +0 -226
  136. package/template/.agent/rules/client/04-state-management.md +0 -474
  137. package/template/.agent/rules/client/05-api-integration.md +0 -129
  138. package/template/.agent/rules/client/06-forms-validation.md +0 -129
  139. package/template/.agent/rules/client/07-common-patterns.md +0 -150
  140. package/template/.agent/rules/client/08-color-system.md +0 -93
  141. package/template/.agent/rules/client/09-security-rules.md +0 -97
  142. package/template/.agent/rules/client/10-testing-strategy.md +0 -370
  143. package/template/.agent/rules/global/ai-edit-safety.md +0 -38
  144. package/template/.agent/rules/server/01-db-and-migrations.md +0 -242
  145. package/template/.agent/rules/server/02-general-rules.md +0 -111
  146. package/template/.agent/rules/server/03-migrations.md +0 -20
  147. package/template/.agent/rules/server/04-pagination.md +0 -130
  148. package/template/.agent/rules/server/05-project-conventions.md +0 -71
  149. package/template/.agent/rules/server/06-response-handling.md +0 -173
  150. package/template/.agent/rules/server/07-testing-strategy.md +0 -506
  151. package/template/.agent/rules/server/08-observability.md +0 -180
  152. package/template/.agent/rules/server/10-background-jobs-v2.md +0 -185
  153. package/template/.agent/rules/server/11-rate-limiting-v2.md +0 -210
  154. package/template/.agent/rules/server/12-performance-optimization.md +0 -567
  155. package/template/.claude/rules/client-01-project-structure.md +0 -327
  156. package/template/.claude/rules/client-02-component-patterns.md +0 -250
  157. package/template/.claude/rules/client-03-typescript-rules.md +0 -227
  158. package/template/.claude/rules/client-04-state-management.md +0 -475
  159. package/template/.claude/rules/client-05-api-integration.md +0 -130
  160. package/template/.claude/rules/client-06-forms-validation.md +0 -130
  161. package/template/.claude/rules/client-07-common-patterns.md +0 -151
  162. package/template/.claude/rules/client-08-color-system.md +0 -94
  163. package/template/.claude/rules/client-09-security-rules.md +0 -98
  164. package/template/.claude/rules/client-10-testing-strategy.md +0 -371
  165. package/template/.claude/rules/global-ai-edit-safety.md +0 -39
  166. package/template/.claude/rules/server-01-db-and-migrations.md +0 -243
  167. package/template/.claude/rules/server-02-general-rules.md +0 -112
  168. package/template/.claude/rules/server-03-migrations.md +0 -21
  169. package/template/.claude/rules/server-04-pagination.md +0 -131
  170. package/template/.claude/rules/server-05-project-conventions.md +0 -72
  171. package/template/.claude/rules/server-06-response-handling.md +0 -174
  172. package/template/.claude/rules/server-07-testing-strategy.md +0 -507
  173. package/template/.claude/rules/server-08-observability.md +0 -181
  174. package/template/.claude/rules/server-10-background-jobs-v2.md +0 -186
  175. package/template/.claude/rules/server-11-rate-limiting-v2.md +0 -211
  176. package/template/.claude/rules/server-12-performance-optimization.md +0 -568
  177. package/template/.cursor/rules/client-01-project-structure.mdc +0 -327
  178. package/template/.cursor/rules/client-02-component-patterns.mdc +0 -250
  179. package/template/.cursor/rules/client-03-typescript-rules.mdc +0 -227
  180. package/template/.cursor/rules/client-04-state-management.mdc +0 -475
  181. package/template/.cursor/rules/client-05-api-integration.mdc +0 -130
  182. package/template/.cursor/rules/client-06-forms-validation.mdc +0 -130
  183. package/template/.cursor/rules/client-07-common-patterns.mdc +0 -151
  184. package/template/.cursor/rules/client-08-color-system.mdc +0 -94
  185. package/template/.cursor/rules/client-09-security-rules.mdc +0 -98
  186. package/template/.cursor/rules/client-10-testing-strategy.mdc +0 -371
  187. package/template/.cursor/rules/global-ai-edit-safety.mdc +0 -39
  188. package/template/.cursor/rules/server-01-db-and-migrations.mdc +0 -243
  189. package/template/.cursor/rules/server-02-general-rules.mdc +0 -112
  190. package/template/.cursor/rules/server-03-migrations.mdc +0 -21
  191. package/template/.cursor/rules/server-04-pagination.mdc +0 -131
  192. package/template/.cursor/rules/server-05-project-conventions.mdc +0 -72
  193. package/template/.cursor/rules/server-06-response-handling.mdc +0 -174
  194. package/template/.cursor/rules/server-07-testing-strategy.mdc +0 -507
  195. package/template/.cursor/rules/server-08-observability.mdc +0 -181
  196. package/template/.cursor/rules/server-09-api-documentation-v2.mdc +0 -169
  197. package/template/.cursor/rules/server-10-background-jobs-v2.mdc +0 -186
  198. package/template/.cursor/rules/server-11-rate-limiting-v2.mdc +0 -211
  199. package/template/.cursor/rules/server-12-performance-optimization.mdc +0 -568
  200. package/template/CLAUDE.md +0 -207
  201. package/template/server/.tsc-aliasrc.json +0 -13
  202. package/template/server/IMPORT_FIX_CHECKLIST.md +0 -98
  203. package/template/server/IMPORT_FIX_COMPLETE.md +0 -89
  204. package/template/server/README.md +0 -183
  205. package/template/server/REMAINING_IMPORT_FIXES.md +0 -150
  206. package/template/server/SECURITY.md +0 -190
  207. package/template/server/Tigra-API.postman_collection.json +0 -733
  208. package/template/server/biome.json +0 -42
  209. package/template/server/scripts/fix-all-imports.ps1 +0 -52
  210. package/template/server/scripts/fix-imports-reference.ps1 +0 -16
  211. package/template/server/scripts/fix-imports.mjs +0 -55
  212. package/template/server/scripts/setup-env.js +0 -50
  213. package/template/server/scripts/wait-for-db.js +0 -60
  214. package/template/server/src/hooks/request-timing.hook.ts +0 -26
  215. package/template/server/src/libs/auth/authenticate.middleware.ts +0 -22
  216. package/template/server/src/libs/auth/rbac.middleware.test.ts +0 -134
  217. package/template/server/src/libs/auth/rbac.middleware.ts +0 -147
  218. package/template/server/src/libs/db.ts +0 -76
  219. package/template/server/src/libs/error-handler.ts +0 -89
  220. package/template/server/src/libs/queue.ts +0 -79
  221. package/template/server/src/modules/admin/admin.controller.ts +0 -122
  222. package/template/server/src/modules/admin/admin.routes.ts +0 -62
  223. package/template/server/src/modules/admin/admin.schemas.ts +0 -35
  224. package/template/server/src/modules/admin/admin.service.ts +0 -167
  225. package/template/server/src/modules/auth/auth.integration.test.ts +0 -150
  226. package/template/server/src/modules/auth/auth.service.test.ts +0 -119
  227. package/template/server/src/modules/auth/auth.types.ts +0 -97
  228. package/template/server/src/modules/resources/resources.controller.ts +0 -218
  229. package/template/server/src/modules/resources/resources.repo.ts +0 -253
  230. package/template/server/src/modules/resources/resources.routes.ts +0 -116
  231. package/template/server/src/modules/resources/resources.schemas.ts +0 -146
  232. package/template/server/src/modules/resources/resources.service.ts +0 -218
  233. package/template/server/src/modules/resources/resources.types.ts +0 -73
  234. package/template/server/src/plugins/rate-limit.plugin.ts +0 -21
  235. package/template/server/src/plugins/security.plugin.ts +0 -21
  236. package/template/server/src/routes/health.routes.ts +0 -31
  237. package/template/server/src/types/fastify.d.ts +0 -36
  238. package/template/server/src/utils/errors.ts +0 -108
  239. package/template/server/src/utils/pagination.ts +0 -120
  240. package/template/server/src/utils/response.ts +0 -110
  241. package/template/server/src/workers/file.worker.ts +0 -106
  242. package/template/server/tsconfig.build.json +0 -30
  243. package/template/server/tsconfig.test.json +0 -22
@@ -1,218 +1,120 @@
1
- /**
2
- * Authentication Repository
3
- *
4
- * Database operations for authentication module.
5
- * Handles all Prisma queries related to users and sessions.
6
- *
7
- * @see /mnt/project/02-general-rules.md
8
- */
9
-
10
- import { prisma } from '../../libs/db.js';
11
- import type { User as PrismaUser, Session as PrismaSession } from '@prisma/client';
12
- import type { User } from './auth.types.js';
13
-
14
- /**
15
- * User with password (internal use only)
16
- */
17
- type UserWithPassword = PrismaUser;
18
-
19
- /**
20
- * Session with user relation
21
- */
22
- type SessionWithUser = PrismaSession & {
23
- user: PrismaUser;
24
- };
25
-
26
- /**
27
- * Create a new user
28
- *
29
- * @param data - User creation data
30
- * @returns User object WITHOUT password field
31
- */
32
- export async function createUser(data: {
33
- email: string;
34
- password: string;
35
- name: string;
36
- }): Promise<User> {
37
- const user = await prisma.user.create({
38
- data: {
39
- email: data.email,
40
- password: data.password,
41
- name: data.name,
42
- role: 'USER',
43
- emailVerified: false,
44
- },
45
- select: {
46
- id: true,
47
- email: true,
48
- name: true,
49
- role: true,
50
- emailVerified: true,
51
- createdAt: true,
52
- updatedAt: true,
53
- },
54
- });
55
-
56
- return user;
57
- }
58
-
59
- /**
60
- * Find user by email (without password)
61
- *
62
- * @param email - User email
63
- * @returns User object WITHOUT password field, or null if not found
64
- */
65
- export async function findUserByEmail(email: string): Promise<User | null> {
66
- const user = await prisma.user.findUnique({
67
- where: { email },
68
- select: {
69
- id: true,
70
- email: true,
71
- name: true,
72
- role: true,
73
- emailVerified: true,
74
- createdAt: true,
75
- updatedAt: true,
76
- },
77
- });
78
-
79
- return user;
80
- }
81
-
82
- /**
83
- * Find user by email WITH password
84
- *
85
- * Used for login verification only.
86
- * NEVER return this to the client.
87
- *
88
- * @param email - User email
89
- * @returns User object WITH password field, or null if not found
90
- */
91
- export async function findUserByEmailWithPassword(
92
- email: string
93
- ): Promise<UserWithPassword | null> {
94
- const user = await prisma.user.findUnique({
95
- where: { email },
96
- });
97
-
98
- return user;
99
- }
100
-
101
- /**
102
- * Find user by ID (without password)
103
- *
104
- * @param id - User ID
105
- * @returns User object WITHOUT password field, or null if not found
106
- */
107
- export async function findUserById(id: string): Promise<User | null> {
108
- const user = await prisma.user.findUnique({
109
- where: { id },
110
- select: {
111
- id: true,
112
- email: true,
113
- name: true,
114
- role: true,
115
- emailVerified: true,
116
- createdAt: true,
117
- updatedAt: true,
118
- },
119
- });
120
-
121
- return user;
122
- }
123
-
124
- /**
125
- * Create a new session
126
- *
127
- * @param data - Session creation data
128
- * @returns Created session
129
- */
130
- export async function createSession(data: {
131
- userId: string;
132
- refreshToken: string;
133
- expiresAt: Date;
134
- }): Promise<PrismaSession> {
135
- const session = await prisma.session.create({
136
- data: {
137
- userId: data.userId,
138
- refreshToken: data.refreshToken,
139
- expiresAt: data.expiresAt,
140
- },
141
- });
142
-
143
- return session;
144
- }
145
-
146
- /**
147
- * Find session by refresh token
148
- *
149
- * @param refreshToken - Refresh token
150
- * @returns Session with user relation, or null if not found
151
- */
152
- export async function findSessionByToken(
153
- refreshToken: string
154
- ): Promise<SessionWithUser | null> {
155
- const session = await prisma.session.findFirst({
156
- where: { refreshToken },
157
- include: {
158
- user: true,
159
- },
160
- });
161
-
162
- return session;
163
- }
164
-
165
- /**
166
- * Update session with new refresh token
167
- *
168
- * @param oldToken - Current refresh token
169
- * @param newToken - New refresh token
170
- * @param expiresAt - New expiration date
171
- * @returns Updated session
172
- */
173
- export async function updateSession(
174
- oldToken: string,
175
- newToken: string,
176
- expiresAt: Date
177
- ): Promise<PrismaSession> {
178
- // Since refreshToken is no longer unique at DB level, we use updateMany
179
- // In practice, it should only update one record
180
- await prisma.session.updateMany({
181
- where: { refreshToken: oldToken },
182
- data: {
183
- refreshToken: newToken,
184
- expiresAt,
185
- },
186
- });
187
-
188
- // Return the updated session (requires a fetch since updateMany returns a count)
189
- const session = await prisma.session.findFirstOrThrow({
190
- where: { refreshToken: newToken },
191
- });
192
-
193
- return session;
194
- }
195
-
196
- /**
197
- * Delete session by refresh token
198
- *
199
- * @param refreshToken - Refresh token
200
- */
201
- export async function deleteSession(refreshToken: string): Promise<void> {
202
- await prisma.session.deleteMany({
203
- where: { refreshToken },
204
- });
205
- }
206
-
207
- /**
208
- * Delete all sessions for a user
209
- *
210
- * Used for logout from all devices.
211
- *
212
- * @param userId - User ID
213
- */
214
- export async function deleteAllUserSessions(userId: string): Promise<void> {
215
- await prisma.session.deleteMany({
216
- where: { userId },
217
- });
218
- }
1
+ import { prisma } from '@libs/prisma.js';
2
+
3
+ export async function findUserByEmail(email: string) {
4
+ return prisma.user.findUnique({
5
+ where: { email, deletedAt: null },
6
+ });
7
+ }
8
+
9
+ export async function findUserById(id: string) {
10
+ return prisma.user.findUnique({
11
+ where: { id, deletedAt: null },
12
+ });
13
+ }
14
+
15
+ export async function createUser(data: {
16
+ email: string;
17
+ password: string;
18
+ firstName: string;
19
+ lastName: string;
20
+ }) {
21
+ return prisma.user.create({
22
+ data,
23
+ });
24
+ }
25
+
26
+ export async function createRefreshToken(data: {
27
+ token: string;
28
+ userId: string;
29
+ expiresAt: Date;
30
+ }) {
31
+ return prisma.refreshToken.create({
32
+ data,
33
+ });
34
+ }
35
+
36
+ export async function findRefreshToken(token: string) {
37
+ return prisma.refreshToken.findUnique({
38
+ where: { token },
39
+ });
40
+ }
41
+
42
+ export async function deleteRefreshToken(token: string): Promise<void> {
43
+ try {
44
+ await prisma.refreshToken.delete({
45
+ where: { token },
46
+ });
47
+ } catch (error) {
48
+ // P2025: Record not found — token was already deleted (e.g. concurrent request)
49
+ if (
50
+ error instanceof Error &&
51
+ 'code' in error &&
52
+ (error as { code: string }).code === 'P2025'
53
+ ) {
54
+ return;
55
+ }
56
+ throw error;
57
+ }
58
+ }
59
+
60
+ export async function rotateRefreshToken(
61
+ oldToken: string,
62
+ newData: { token: string; userId: string; expiresAt: Date },
63
+ ): Promise<boolean> {
64
+ try {
65
+ await prisma.$transaction([
66
+ prisma.refreshToken.delete({ where: { token: oldToken } }),
67
+ prisma.refreshToken.create({ data: newData }),
68
+ ]);
69
+ return true;
70
+ } catch (error) {
71
+ // P2025: Old token not found — already consumed by a concurrent request
72
+ if (
73
+ error instanceof Error &&
74
+ 'code' in error &&
75
+ (error as { code: string }).code === 'P2025'
76
+ ) {
77
+ return false;
78
+ }
79
+ throw error;
80
+ }
81
+ }
82
+
83
+ export async function deleteRefreshTokensByUserId(userId: string) {
84
+ return prisma.refreshToken.deleteMany({
85
+ where: { userId },
86
+ });
87
+ }
88
+
89
+ export async function incrementFailedAttempts(userId: string): Promise<void> {
90
+ await prisma.user.update({
91
+ where: { id: userId },
92
+ data: {
93
+ failedLoginAttempts: { increment: 1 },
94
+ },
95
+ });
96
+ }
97
+
98
+ export async function setAccountLock(userId: string, lockedUntil: Date): Promise<void> {
99
+ await prisma.user.update({
100
+ where: { id: userId },
101
+ data: { lockedUntil },
102
+ });
103
+ }
104
+
105
+ export async function resetFailedAttempts(userId: string): Promise<void> {
106
+ await prisma.user.update({
107
+ where: { id: userId },
108
+ data: {
109
+ failedLoginAttempts: 0,
110
+ lockedUntil: null,
111
+ },
112
+ });
113
+ }
114
+
115
+ export async function updateUserPassword(userId: string, hashedPassword: string): Promise<void> {
116
+ await prisma.user.update({
117
+ where: { id: userId },
118
+ data: { password: hashedPassword },
119
+ });
120
+ }
@@ -1,83 +1,96 @@
1
- /**
2
- * Authentication Routes
3
- *
4
- * Fastify route definitions for authentication endpoints.
5
- * Includes rate limiting for security.
6
- *
7
- * @see /mnt/project/11-rate-limiting-v2.md
8
- */
9
-
10
- import type { FastifyInstance } from 'fastify';
11
- import * as authController from './auth.controller.js';
12
-
13
- /**
14
- * Register authentication routes
15
- *
16
- * @param fastify - Fastify instance
17
- */
18
- export async function authRoutes(fastify: FastifyInstance): Promise<void> {
19
- /**
20
- * POST /auth/register
21
- *
22
- * Register a new user account
23
- */
24
- fastify.post('/register', {
25
- config: {
26
- rateLimit: {
27
- max: 3,
28
- timeWindow: '1 hour',
29
- },
30
- },
31
- handler: authController.register,
32
- });
33
-
34
- /**
35
- * POST /auth/login
36
- *
37
- * Login with email and password
38
- */
39
- fastify.post('/login', {
40
- config: {
41
- rateLimit: {
42
- max: 5,
43
- timeWindow: '15 minutes',
44
- },
45
- },
46
- handler: authController.login,
47
- });
48
-
49
- /**
50
- * POST /auth/refresh
51
- *
52
- * Refresh access token using refresh token
53
- */
54
- fastify.post('/refresh', {
55
- config: {
56
- rateLimit: {
57
- max: 10,
58
- timeWindow: '15 minutes',
59
- },
60
- },
61
- handler: authController.refreshTokens,
62
- });
63
-
64
- /**
65
- * POST /auth/logout
66
- *
67
- * Logout user by invalidating refresh token
68
- */
69
- fastify.post('/logout', {
70
- handler: authController.logout,
71
- });
72
-
73
- /**
74
- * GET /auth/me
75
- *
76
- * Get current authenticated user information
77
- * Requires authentication
78
- */
79
- fastify.get('/me', {
80
- preHandler: [fastify.authenticate],
81
- handler: authController.getMe,
82
- });
83
- }
1
+ import type { FastifyInstance } from 'fastify';
2
+ import { authenticate } from '@libs/auth.js';
3
+ import * as authController from './auth.controller.js';
4
+ import {
5
+ registerSchema,
6
+ loginSchema,
7
+ } from './auth.schemas.js';
8
+
9
+ export async function authRoutes(fastify: FastifyInstance): Promise<void> {
10
+ // Register - Strict rate limiting to prevent abuse (5 requests per hour per IP)
11
+ fastify.post('/auth/register', {
12
+ schema: {
13
+ body: registerSchema,
14
+ },
15
+ config: {
16
+ rateLimit: {
17
+ max: 5,
18
+ timeWindow: '1 hour',
19
+ },
20
+ },
21
+ handler: authController.register,
22
+ });
23
+
24
+ // Login - Strict rate limiting to prevent brute force (10 requests per 15 minutes per IP)
25
+ fastify.post('/auth/login', {
26
+ schema: {
27
+ body: loginSchema,
28
+ },
29
+ config: {
30
+ rateLimit: {
31
+ max: 10,
32
+ timeWindow: '15 minutes',
33
+ },
34
+ },
35
+ handler: authController.login,
36
+ });
37
+
38
+ // Logout - reads refresh token from cookie, no body schema needed
39
+ fastify.post('/auth/logout', {
40
+ config: {
41
+ rateLimit: {
42
+ max: 50,
43
+ timeWindow: '15 minutes',
44
+ },
45
+ },
46
+ handler: authController.logout,
47
+ });
48
+
49
+ // Refresh token - reads refresh token from cookie, no body schema needed
50
+ fastify.post('/auth/refresh', {
51
+ config: {
52
+ rateLimit: {
53
+ max: 20,
54
+ timeWindow: '15 minutes',
55
+ },
56
+ },
57
+ handler: authController.refresh,
58
+ });
59
+
60
+ // Get current user
61
+ fastify.get('/auth/me', {
62
+ preValidation: [authenticate],
63
+ config: {
64
+ rateLimit: {
65
+ max: 60,
66
+ timeWindow: '1 minute',
67
+ },
68
+ },
69
+ handler: authController.me,
70
+ });
71
+
72
+ // Get user sessions
73
+ fastify.get('/auth/sessions', {
74
+ preValidation: [authenticate],
75
+ config: {
76
+ rateLimit: {
77
+ max: 30,
78
+ timeWindow: '1 minute',
79
+ },
80
+ },
81
+ handler: authController.getSessions,
82
+ });
83
+
84
+ // Logout from all sessions
85
+ fastify.post('/auth/logout-all', {
86
+ preValidation: [authenticate],
87
+ config: {
88
+ rateLimit: {
89
+ max: 10,
90
+ timeWindow: '15 minutes',
91
+ },
92
+ },
93
+ handler: authController.logoutAllSessions,
94
+ });
95
+
96
+ }