create-tigra 1.0.7 → 2.0.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 (237) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +80 -87
  3. package/bin/create-tigra.js +242 -309
  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 +80 -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 -12
  202. package/template/server/README.md +0 -183
  203. package/template/server/SECURITY.md +0 -190
  204. package/template/server/Tigra-API.postman_collection.json +0 -733
  205. package/template/server/biome.json +0 -42
  206. package/template/server/scripts/setup-env.js +0 -50
  207. package/template/server/scripts/wait-for-db.js +0 -60
  208. package/template/server/src/hooks/request-timing.hook.ts +0 -26
  209. package/template/server/src/libs/auth/authenticate.middleware.ts +0 -22
  210. package/template/server/src/libs/auth/rbac.middleware.test.ts +0 -134
  211. package/template/server/src/libs/auth/rbac.middleware.ts +0 -147
  212. package/template/server/src/libs/db.ts +0 -76
  213. package/template/server/src/libs/error-handler.ts +0 -89
  214. package/template/server/src/libs/queue.ts +0 -79
  215. package/template/server/src/modules/admin/admin.controller.ts +0 -122
  216. package/template/server/src/modules/admin/admin.routes.ts +0 -62
  217. package/template/server/src/modules/admin/admin.schemas.ts +0 -35
  218. package/template/server/src/modules/admin/admin.service.ts +0 -167
  219. package/template/server/src/modules/auth/auth.integration.test.ts +0 -150
  220. package/template/server/src/modules/auth/auth.service.test.ts +0 -119
  221. package/template/server/src/modules/auth/auth.types.ts +0 -97
  222. package/template/server/src/modules/resources/resources.controller.ts +0 -218
  223. package/template/server/src/modules/resources/resources.repo.ts +0 -253
  224. package/template/server/src/modules/resources/resources.routes.ts +0 -116
  225. package/template/server/src/modules/resources/resources.schemas.ts +0 -146
  226. package/template/server/src/modules/resources/resources.service.ts +0 -218
  227. package/template/server/src/modules/resources/resources.types.ts +0 -73
  228. package/template/server/src/plugins/rate-limit.plugin.ts +0 -21
  229. package/template/server/src/plugins/security.plugin.ts +0 -21
  230. package/template/server/src/routes/health.routes.ts +0 -31
  231. package/template/server/src/types/fastify.d.ts +0 -36
  232. package/template/server/src/utils/errors.ts +0 -108
  233. package/template/server/src/utils/pagination.ts +0 -120
  234. package/template/server/src/utils/response.ts +0 -110
  235. package/template/server/src/workers/file.worker.ts +0 -106
  236. package/template/server/tsconfig.build.json +0 -30
  237. package/template/server/tsconfig.test.json +0 -22
@@ -1,116 +0,0 @@
1
- /**
2
- * Resources Routes
3
- *
4
- * Fastify route definitions for resources 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 resourceController from './resources.controller';
12
-
13
- /**
14
- * Register resources routes
15
- *
16
- * @param fastify - Fastify instance
17
- */
18
- export async function resourceRoutes(fastify: FastifyInstance): Promise<void> {
19
- /**
20
- * GET /resources
21
- *
22
- * Get paginated list of resources with optional filters
23
- */
24
- fastify.get('/', {
25
- config: {
26
- rateLimit: {
27
- max: 100,
28
- timeWindow: '15 minutes',
29
- },
30
- },
31
- handler: resourceController.listResources,
32
- });
33
-
34
- /**
35
- * GET /resources/my
36
- *
37
- * Get current user's resources
38
- * Requires authentication
39
- */
40
- fastify.get('/my', {
41
- config: {
42
- rateLimit: {
43
- max: 1000,
44
- timeWindow: '15 minutes',
45
- },
46
- },
47
- preHandler: [fastify.authenticate, fastify.requireAny()],
48
- handler: resourceController.getMyResources,
49
- });
50
-
51
- /**
52
- * GET /resources/:id
53
- *
54
- * Get single resource by ID with owner information
55
- */
56
- fastify.get('/:id', {
57
- config: {
58
- rateLimit: {
59
- max: 100,
60
- timeWindow: '15 minutes',
61
- },
62
- },
63
- handler: resourceController.getResource,
64
- });
65
-
66
- /**
67
- * POST /resources
68
- *
69
- * Create a new resource
70
- * Requires authentication
71
- */
72
- fastify.post('/', {
73
- config: {
74
- rateLimit: {
75
- max: 1000,
76
- timeWindow: '15 minutes',
77
- },
78
- },
79
- preHandler: [fastify.authenticate, fastify.requireAny()],
80
- handler: resourceController.createResource,
81
- });
82
-
83
- /**
84
- * PATCH /resources/:id
85
- *
86
- * Update a resource (owner only)
87
- * Requires authentication
88
- */
89
- fastify.patch('/:id', {
90
- config: {
91
- rateLimit: {
92
- max: 1000,
93
- timeWindow: '15 minutes',
94
- },
95
- },
96
- preHandler: [fastify.authenticate, fastify.requireAny()],
97
- handler: resourceController.updateResource,
98
- });
99
-
100
- /**
101
- * DELETE /resources/:id
102
- *
103
- * Delete a resource (owner only, soft delete)
104
- * Requires authentication
105
- */
106
- fastify.delete('/:id', {
107
- config: {
108
- rateLimit: {
109
- max: 1000,
110
- timeWindow: '15 minutes',
111
- },
112
- },
113
- preHandler: [fastify.authenticate, fastify.requireAny()],
114
- handler: resourceController.deleteResource,
115
- });
116
- }
@@ -1,146 +0,0 @@
1
- /**
2
- * Resources Schemas
3
- *
4
- * Zod schemas for request validation and type validation.
5
- * Ensures type safety and API documentation consistency.
6
- */
7
-
8
- import { z } from 'zod';
9
-
10
- /**
11
- * Create Resource Schema
12
- *
13
- * Validates resource creation requests.
14
- */
15
- export const CreateResourceSchema = z.object({
16
- title: z
17
- .string()
18
- .min(1, 'Title is required')
19
- .max(200, 'Title must not exceed 200 characters')
20
- .trim(),
21
- summary: z
22
- .string()
23
- .max(1000, 'Summary must not exceed 1000 characters')
24
- .trim()
25
- .optional(),
26
- price: z
27
- .number()
28
- .positive('Price must be positive')
29
- .multipleOf(0.01, 'Price can have at most 2 decimal places')
30
- .max(999999.99, 'Price is too large'),
31
- });
32
-
33
- /**
34
- * Update Resource Schema
35
- *
36
- * Validates resource update requests.
37
- * All fields are optional (partial update).
38
- */
39
- export const UpdateResourceSchema = z.object({
40
- title: z
41
- .string()
42
- .min(1, 'Title cannot be empty')
43
- .max(200, 'Title must not exceed 200 characters')
44
- .trim()
45
- .optional(),
46
- summary: z
47
- .string()
48
- .max(1000, 'Summary must not exceed 1000 characters')
49
- .trim()
50
- .optional(),
51
- price: z
52
- .number()
53
- .positive('Price must be positive')
54
- .multipleOf(0.01, 'Price can have at most 2 decimal places')
55
- .max(999999.99, 'Price is too large')
56
- .optional(),
57
- });
58
-
59
- /**
60
- * Resource Filters Schema
61
- *
62
- * Validates query parameters for filtering resources.
63
- */
64
- export const ResourceFiltersSchema = z.object({
65
- status: z
66
- .enum(['active', 'inactive', 'deleted'])
67
- .optional(),
68
- minPrice: z
69
- .coerce
70
- .number()
71
- .positive('Minimum price must be positive')
72
- .optional(),
73
- maxPrice: z
74
- .coerce
75
- .number()
76
- .positive('Maximum price must be positive')
77
- .optional(),
78
- ownerId: z
79
- .string()
80
- .uuid('Invalid owner ID format')
81
- .optional(),
82
- search: z
83
- .string()
84
- .trim()
85
- .optional(),
86
- });
87
-
88
- /**
89
- * Pagination Schema
90
- *
91
- * Validates pagination query parameters.
92
- */
93
- export const PaginationSchema = z.object({
94
- page: z
95
- .coerce
96
- .number()
97
- .int()
98
- .min(1, 'Page must be at least 1')
99
- .default(1),
100
- limit: z
101
- .coerce
102
- .number()
103
- .int()
104
- .min(1, 'Limit must be at least 1')
105
- .max(100, 'Limit must not exceed 100')
106
- .default(10),
107
- });
108
-
109
- /**
110
- * Resource Response Schema (for type validation)
111
- *
112
- * Defines the structure of resource objects in API responses.
113
- */
114
- export const ResourceResponseSchema = z.object({
115
- id: z.string().uuid(),
116
- ownerId: z.string().uuid(),
117
- title: z.string(),
118
- summary: z.string().nullable(),
119
- price: z.number(),
120
- status: z.string(),
121
- createdAt: z.string().datetime(),
122
- updatedAt: z.string().datetime(),
123
- });
124
-
125
- /**
126
- * Resource with Owner Response Schema (for type validation)
127
- */
128
- export const ResourceWithOwnerResponseSchema = ResourceResponseSchema.extend({
129
- owner: z.object({
130
- id: z.string().uuid(),
131
- email: z.string().email(),
132
- name: z.string().nullable(),
133
- }),
134
- });
135
-
136
- /**
137
- * Type inference from schemas
138
- *
139
- * These types can be used in controllers and services.
140
- */
141
- export type CreateResourceInput = z.infer<typeof CreateResourceSchema>;
142
- export type UpdateResourceInput = z.infer<typeof UpdateResourceSchema>;
143
- export type ResourceFiltersInput = z.infer<typeof ResourceFiltersSchema>;
144
- export type PaginationInput = z.infer<typeof PaginationSchema>;
145
- export type ResourceResponse = z.infer<typeof ResourceResponseSchema>;
146
- export type ResourceWithOwnerResponse = z.infer<typeof ResourceWithOwnerResponseSchema>;
@@ -1,218 +0,0 @@
1
- /**
2
- * Resources Service
3
- *
4
- * Business logic for resources operations.
5
- * Handles CRUD operations with authorization checks.
6
- *
7
- * @see /mnt/project/02-general-rules.md
8
- */
9
-
10
- import logger from '@/libs/logger';
11
- import { NotFoundError, ForbiddenError } from '@/utils/errors';
12
- import * as resourceRepo from './resources.repo';
13
- import type { ResourceFilters, Resource, ResourceWithOwner } from './resources.types';
14
- import type { CreateResourceInput, UpdateResourceInput } from './resources.schemas';
15
-
16
- /**
17
- * Service response for paginated data
18
- */
19
- interface PaginatedResult<T> {
20
- items: T[];
21
- totalItems: number;
22
- }
23
-
24
- /**
25
- * Get resources with filters and pagination
26
- *
27
- * @param filters - Filter criteria
28
- * @param page - Page number (1-indexed)
29
- * @param limit - Items per page
30
- * @returns Items and total count
31
- */
32
- export async function getResources(
33
- filters: ResourceFilters,
34
- page: number,
35
- limit: number
36
- ): Promise<PaginatedResult<Resource>> {
37
- try {
38
- // Get items with filters and pagination
39
- const items = await resourceRepo.findMany(filters, page, limit);
40
-
41
- // Get total count with same filters
42
- const totalItems = await resourceRepo.count(filters);
43
-
44
- logger.info(
45
- { filters, page, limit, count: items.length, totalItems },
46
- 'Resources retrieved'
47
- );
48
-
49
- return { items, totalItems };
50
- } catch (error) {
51
- logger.error({ error, filters, page, limit }, 'Failed to get resources');
52
- throw error;
53
- }
54
- }
55
-
56
- /**
57
- * Get resource by ID
58
- *
59
- * @param id - Resource ID
60
- * @returns Resource with owner relation
61
- * @throws NotFoundError if resource not found
62
- */
63
- export async function getResource(id: string): Promise<ResourceWithOwner> {
64
- try {
65
- const resource = await resourceRepo.findById(id);
66
-
67
- if (!resource) {
68
- throw new NotFoundError('Resource not found');
69
- }
70
-
71
- logger.info({ resourceId: id }, 'Resource retrieved');
72
-
73
- return resource;
74
- } catch (error) {
75
- logger.error({ error, resourceId: id }, 'Failed to get resource');
76
- throw error;
77
- }
78
- }
79
-
80
- /**
81
- * Create a new resource
82
- *
83
- * @param data - Resource creation data
84
- * @param userId - Owner user ID
85
- * @returns Created resource
86
- */
87
- export async function createResource(
88
- data: CreateResourceInput,
89
- userId: string
90
- ): Promise<Resource> {
91
- try {
92
- const resource = await resourceRepo.create(data, userId);
93
-
94
- logger.info(
95
- { resourceId: resource.id, userId, title: resource.title },
96
- 'Resource created'
97
- );
98
-
99
- return resource;
100
- } catch (error) {
101
- logger.error({ error, userId, data }, 'Failed to create resource');
102
- throw error;
103
- }
104
- }
105
-
106
- /**
107
- * Update a resource
108
- *
109
- * @param id - Resource ID
110
- * @param userId - User ID (for ownership verification)
111
- * @param data - Update data
112
- * @returns Updated resource
113
- * @throws NotFoundError if resource not found
114
- * @throws ForbiddenError if user is not the owner
115
- */
116
- export async function updateResource(
117
- id: string,
118
- userId: string,
119
- data: UpdateResourceInput
120
- ): Promise<Resource> {
121
- try {
122
- // Check if resource exists
123
- const exists = await resourceRepo.exists(id);
124
- if (!exists) {
125
- throw new NotFoundError('Resource not found');
126
- }
127
-
128
- // Verify ownership
129
- const isOwner = await resourceRepo.isOwner(id, userId);
130
- if (!isOwner) {
131
- throw new ForbiddenError('You do not have permission to update this resource');
132
- }
133
-
134
- // Update resource
135
- const resource = await resourceRepo.update(id, data);
136
-
137
- logger.info(
138
- { resourceId: id, userId, updates: Object.keys(data) },
139
- 'Resource updated'
140
- );
141
-
142
- return resource;
143
- } catch (error) {
144
- logger.error({ error, resourceId: id, userId, data }, 'Failed to update resource');
145
- throw error;
146
- }
147
- }
148
-
149
- /**
150
- * Delete a resource (soft delete)
151
- *
152
- * @param id - Resource ID
153
- * @param userId - User ID (for ownership verification)
154
- * @returns Deleted resource
155
- * @throws NotFoundError if resource not found
156
- * @throws ForbiddenError if user is not the owner
157
- */
158
- export async function deleteResource(
159
- id: string,
160
- userId: string
161
- ): Promise<Resource> {
162
- try {
163
- // Check if resource exists
164
- const exists = await resourceRepo.exists(id);
165
- if (!exists) {
166
- throw new NotFoundError('Resource not found');
167
- }
168
-
169
- // Verify ownership
170
- const isOwner = await resourceRepo.isOwner(id, userId);
171
- if (!isOwner) {
172
- throw new ForbiddenError('You do not have permission to delete this resource');
173
- }
174
-
175
- // Soft delete resource
176
- const resource = await resourceRepo.deleteResource(id);
177
-
178
- logger.info({ resourceId: id, userId }, 'Resource deleted');
179
-
180
- return resource;
181
- } catch (error) {
182
- logger.error({ error, resourceId: id, userId }, 'Failed to delete resource');
183
- throw error;
184
- }
185
- }
186
-
187
- /**
188
- * Get user's resources
189
- *
190
- * @param userId - User ID
191
- * @param page - Page number (1-indexed)
192
- * @param limit - Items per page
193
- * @returns User's resources and total count
194
- */
195
- export async function getMyResources(
196
- userId: string,
197
- page: number,
198
- limit: number
199
- ): Promise<PaginatedResult<Resource>> {
200
- try {
201
- // Filter by owner ID
202
- const filters: ResourceFilters = { ownerId: userId };
203
-
204
- // Get items and count
205
- const items = await resourceRepo.findMany(filters, page, limit);
206
- const totalItems = await resourceRepo.count(filters);
207
-
208
- logger.info(
209
- { userId, page, limit, count: items.length, totalItems },
210
- 'User resources retrieved'
211
- );
212
-
213
- return { items, totalItems };
214
- } catch (error) {
215
- logger.error({ error, userId, page, limit }, 'Failed to get user resources');
216
- throw error;
217
- }
218
- }
@@ -1,73 +0,0 @@
1
- /**
2
- * Resources Types
3
- *
4
- * TypeScript types and interfaces for resources module.
5
- * Resources are the main CRUD entity in this application.
6
- */
7
-
8
- /**
9
- * Resource Status Enum
10
- */
11
- export type ResourceStatus = 'active' | 'inactive' | 'deleted';
12
-
13
- /**
14
- * Resource object
15
- *
16
- * Main entity representing a resource in the system.
17
- */
18
- export interface Resource {
19
- id: string;
20
- ownerId: string;
21
- title: string;
22
- summary: string | null;
23
- price: number;
24
- status: string;
25
- createdAt: Date;
26
- updatedAt: Date;
27
- }
28
-
29
- /**
30
- * Create Resource Request Payload
31
- */
32
- export interface CreateResourceRequest {
33
- title: string;
34
- summary?: string;
35
- price: number;
36
- }
37
-
38
- /**
39
- * Update Resource Request Payload
40
- *
41
- * All fields are optional (partial update)
42
- */
43
- export interface UpdateResourceRequest {
44
- title?: string;
45
- summary?: string;
46
- price?: number;
47
- }
48
-
49
- /**
50
- * Resource Filters
51
- *
52
- * Used for filtering and searching resources.
53
- */
54
- export interface ResourceFilters {
55
- status?: ResourceStatus;
56
- minPrice?: number;
57
- maxPrice?: number;
58
- ownerId?: string;
59
- search?: string; // Search in title
60
- }
61
-
62
- /**
63
- * Resource with Owner
64
- *
65
- * Resource with owner relation populated.
66
- */
67
- export interface ResourceWithOwner extends Resource {
68
- owner: {
69
- id: string;
70
- email: string;
71
- name: string | null;
72
- };
73
- }
@@ -1,21 +0,0 @@
1
- import { FastifyInstance } from 'fastify';
2
- import rateLimit from '@fastify/rate-limit';
3
- import { redis } from '@/libs/redis';
4
- import { env } from '@/config/env';
5
-
6
- export async function registerRateLimit(app: FastifyInstance) {
7
- await app.register(rateLimit, {
8
- global: true,
9
- max: env.RATE_LIMIT_MAX,
10
- timeWindow: env.RATE_LIMIT_WINDOW,
11
- redis,
12
- nameSpace: 'rate-limit:',
13
- errorResponseBuilder: (request, context) => ({
14
- success: false,
15
- error: {
16
- code: 'RATE_LIMIT_EXCEEDED',
17
- message: `Too many requests. Please try again in ${Math.ceil(context.ttl / 1000)}s.`,
18
- },
19
- }),
20
- });
21
- }
@@ -1,21 +0,0 @@
1
- import { FastifyInstance } from 'fastify';
2
- import cors from '@fastify/cors';
3
- import helmet from '@fastify/helmet';
4
- import compress from '@fastify/compress';
5
- import { env } from '@/config/env';
6
-
7
- export async function registerSecurityPlugins(app: FastifyInstance) {
8
- // CORS - Cross-Origin Resource Sharing
9
- await app.register(cors, {
10
- origin: env.NODE_ENV === 'development' ? true : env.CORS_ALLOWED_ORIGINS.split(','),
11
- credentials: true,
12
- });
13
-
14
- // Helmet - Security headers
15
- await app.register(helmet, {
16
- contentSecurityPolicy: env.NODE_ENV === 'production',
17
- });
18
-
19
- // Compression - Gzip/Deflate
20
- await app.register(compress, { global: true });
21
- }
@@ -1,31 +0,0 @@
1
- import { FastifyInstance } from 'fastify';
2
- import { prisma } from '@/libs/db';
3
- import { redis } from '@/libs/redis';
4
-
5
- export async function healthRoutes(app: FastifyInstance) {
6
- app.get('/health', async (request, reply) => {
7
- const health = {
8
- status: 'healthy',
9
- timestamp: new Date().toISOString(),
10
- uptime: process.uptime(),
11
- database: 'connected',
12
- redis: 'connected',
13
- };
14
-
15
- try {
16
- await prisma.$queryRaw`SELECT 1`;
17
- } catch (error) {
18
- health.database = 'disconnected';
19
- health.status = 'unhealthy';
20
- }
21
-
22
- try {
23
- await redis.ping();
24
- } catch (error) {
25
- health.redis = 'disconnected';
26
- health.status = 'unhealthy';
27
- }
28
-
29
- return reply.status(health.status === 'healthy' ? 200 : 503).send(health);
30
- });
31
- }
@@ -1,36 +0,0 @@
1
- import { FastifyReply, FastifyRequest } from 'fastify';
2
- import { JwtPayload } from '@/modules/auth/auth.types';
3
- import { UserRole } from '@/libs/auth/rbac.middleware';
4
-
5
- /**
6
- * TypeScript Declaration Merging
7
- *
8
- * Extends Fastify types with custom properties.
9
- */
10
- declare module 'fastify' {
11
- interface FastifyRequest {
12
- user?: JwtPayload;
13
- startTime: number;
14
- }
15
- interface FastifyInstance {
16
- authenticate: (
17
- request: FastifyRequest,
18
- reply: FastifyReply
19
- ) => Promise<void>;
20
- requireRole: (
21
- ...roles: UserRole[]
22
- ) => (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
23
- requireAdmin: () => (
24
- request: FastifyRequest,
25
- reply: FastifyReply
26
- ) => Promise<void>;
27
- requireUser: () => (
28
- request: FastifyRequest,
29
- reply: FastifyReply
30
- ) => Promise<void>;
31
- requireAny: () => (
32
- request: FastifyRequest,
33
- reply: FastifyReply
34
- ) => Promise<void>;
35
- }
36
- }