lapeh 2.3.6 → 2.3.8

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/bin/index.js +39 -56
  2. package/dist/generated/prisma/browser.d.ts +80 -0
  3. package/dist/generated/prisma/browser.d.ts.map +1 -0
  4. package/dist/generated/prisma/browser.js +56 -0
  5. package/dist/generated/prisma/client.d.ts +97 -0
  6. package/dist/generated/prisma/client.d.ts.map +1 -0
  7. package/dist/generated/prisma/client.js +68 -0
  8. package/dist/generated/prisma/commonInputTypes.d.ts +486 -0
  9. package/dist/generated/prisma/commonInputTypes.d.ts.map +1 -0
  10. package/dist/generated/prisma/commonInputTypes.js +11 -0
  11. package/dist/generated/prisma/enums.d.ts +2 -0
  12. package/dist/generated/prisma/enums.d.ts.map +1 -0
  13. package/dist/generated/prisma/enums.js +11 -0
  14. package/dist/generated/prisma/internal/class.d.ts +281 -0
  15. package/dist/generated/prisma/internal/class.d.ts.map +1 -0
  16. package/dist/generated/prisma/internal/class.js +76 -0
  17. package/dist/generated/prisma/internal/prismaNamespace.d.ts +1734 -0
  18. package/dist/generated/prisma/internal/prismaNamespace.d.ts.map +1 -0
  19. package/dist/generated/prisma/internal/prismaNamespace.js +260 -0
  20. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +200 -0
  21. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts.map +1 -0
  22. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +231 -0
  23. package/dist/generated/prisma/models/cache.d.ts +986 -0
  24. package/dist/generated/prisma/models/cache.d.ts.map +1 -0
  25. package/dist/generated/prisma/models/cache.js +2 -0
  26. package/dist/generated/prisma/models/cache_locks.d.ts +976 -0
  27. package/dist/generated/prisma/models/cache_locks.d.ts.map +1 -0
  28. package/dist/generated/prisma/models/cache_locks.js +2 -0
  29. package/dist/generated/prisma/models/failed_jobs.d.ts +1098 -0
  30. package/dist/generated/prisma/models/failed_jobs.d.ts.map +1 -0
  31. package/dist/generated/prisma/models/failed_jobs.js +2 -0
  32. package/dist/generated/prisma/models/job_batches.d.ts +1212 -0
  33. package/dist/generated/prisma/models/job_batches.d.ts.map +1 -0
  34. package/dist/generated/prisma/models/job_batches.js +2 -0
  35. package/dist/generated/prisma/models/jobs.d.ts +1112 -0
  36. package/dist/generated/prisma/models/jobs.d.ts.map +1 -0
  37. package/dist/generated/prisma/models/jobs.js +2 -0
  38. package/dist/generated/prisma/models/migrations.d.ts +979 -0
  39. package/dist/generated/prisma/models/migrations.d.ts.map +1 -0
  40. package/dist/generated/prisma/models/migrations.js +2 -0
  41. package/dist/generated/prisma/models/password_reset_tokens.d.ts +941 -0
  42. package/dist/generated/prisma/models/password_reset_tokens.d.ts.map +1 -0
  43. package/dist/generated/prisma/models/password_reset_tokens.js +2 -0
  44. package/dist/generated/prisma/models/permissions.d.ts +1333 -0
  45. package/dist/generated/prisma/models/permissions.d.ts.map +1 -0
  46. package/dist/generated/prisma/models/permissions.js +2 -0
  47. package/dist/generated/prisma/models/personal_access_tokens.d.ts +1178 -0
  48. package/dist/generated/prisma/models/personal_access_tokens.d.ts.map +1 -0
  49. package/dist/generated/prisma/models/personal_access_tokens.js +2 -0
  50. package/dist/generated/prisma/models/role_permissions.d.ts +1291 -0
  51. package/dist/generated/prisma/models/role_permissions.d.ts.map +1 -0
  52. package/dist/generated/prisma/models/role_permissions.js +2 -0
  53. package/dist/generated/prisma/models/roles.d.ts +1333 -0
  54. package/dist/generated/prisma/models/roles.d.ts.map +1 -0
  55. package/dist/generated/prisma/models/roles.js +2 -0
  56. package/dist/generated/prisma/models/sessions.d.ts +1073 -0
  57. package/dist/generated/prisma/models/sessions.d.ts.map +1 -0
  58. package/dist/generated/prisma/models/sessions.js +2 -0
  59. package/dist/generated/prisma/models/user_permissions.d.ts +1291 -0
  60. package/dist/generated/prisma/models/user_permissions.d.ts.map +1 -0
  61. package/dist/generated/prisma/models/user_permissions.js +2 -0
  62. package/dist/generated/prisma/models/user_roles.d.ts +1291 -0
  63. package/dist/generated/prisma/models/user_roles.d.ts.map +1 -0
  64. package/dist/generated/prisma/models/user_roles.js +2 -0
  65. package/dist/generated/prisma/models/users.d.ts +1513 -0
  66. package/dist/generated/prisma/models/users.d.ts.map +1 -0
  67. package/dist/generated/prisma/models/users.js +2 -0
  68. package/dist/generated/prisma/models.d.ts +17 -0
  69. package/dist/generated/prisma/models.d.ts.map +1 -0
  70. package/dist/generated/prisma/models.js +2 -0
  71. package/dist/lib/bootstrap.d.ts +2 -0
  72. package/dist/lib/bootstrap.d.ts.map +1 -0
  73. package/dist/lib/bootstrap.js +133 -0
  74. package/dist/lib/core/database.d.ts +3 -0
  75. package/dist/lib/core/database.d.ts.map +1 -0
  76. package/dist/lib/core/database.js +34 -0
  77. package/dist/lib/core/realtime.d.ts +3 -0
  78. package/dist/lib/core/realtime.d.ts.map +1 -0
  79. package/dist/lib/core/realtime.js +36 -0
  80. package/dist/lib/core/redis.d.ts +8 -0
  81. package/dist/lib/core/redis.d.ts.map +1 -0
  82. package/dist/lib/core/redis.js +123 -0
  83. package/dist/lib/core/serializer.d.ts +43 -0
  84. package/dist/lib/core/serializer.d.ts.map +1 -0
  85. package/dist/lib/core/serializer.js +66 -0
  86. package/dist/lib/core/server.d.ts +2 -0
  87. package/dist/lib/core/server.d.ts.map +1 -0
  88. package/dist/lib/core/server.js +60 -0
  89. package/dist/lib/middleware/auth.d.ts +4 -0
  90. package/dist/lib/middleware/auth.d.ts.map +1 -0
  91. package/dist/lib/middleware/auth.js +55 -0
  92. package/dist/lib/middleware/error.d.ts +3 -0
  93. package/dist/lib/middleware/error.d.ts.map +1 -0
  94. package/dist/lib/middleware/error.js +60 -0
  95. package/dist/lib/middleware/multipart.d.ts +4 -0
  96. package/dist/lib/middleware/multipart.d.ts.map +1 -0
  97. package/dist/lib/middleware/multipart.js +17 -0
  98. package/dist/lib/middleware/rateLimit.d.ts +2 -0
  99. package/dist/lib/middleware/rateLimit.d.ts.map +1 -0
  100. package/dist/lib/middleware/rateLimit.js +19 -0
  101. package/dist/lib/middleware/requestLogger.d.ts +3 -0
  102. package/dist/lib/middleware/requestLogger.d.ts.map +1 -0
  103. package/dist/lib/middleware/requestLogger.js +22 -0
  104. package/dist/lib/middleware/visitor.d.ts +3 -0
  105. package/dist/lib/middleware/visitor.d.ts.map +1 -0
  106. package/dist/lib/middleware/visitor.js +144 -0
  107. package/dist/lib/utils/logger.d.ts +11 -0
  108. package/dist/lib/utils/logger.d.ts.map +1 -0
  109. package/dist/lib/utils/logger.js +81 -0
  110. package/dist/lib/utils/pagination.d.ts +19 -0
  111. package/dist/lib/utils/pagination.d.ts.map +1 -0
  112. package/dist/lib/utils/pagination.js +34 -0
  113. package/dist/lib/utils/response.d.ts +11 -0
  114. package/dist/lib/utils/response.d.ts.map +1 -0
  115. package/dist/lib/utils/response.js +57 -0
  116. package/dist/lib/utils/validator.d.ts +38 -0
  117. package/dist/lib/utils/validator.d.ts.map +1 -0
  118. package/dist/lib/utils/validator.js +369 -0
  119. package/dist/prisma/seed.d.ts +2 -0
  120. package/dist/prisma/seed.d.ts.map +1 -0
  121. package/dist/prisma/seed.js +381 -0
  122. package/dist/src/controllers/authController.d.ts +11 -0
  123. package/dist/src/controllers/authController.d.ts.map +1 -0
  124. package/dist/src/controllers/authController.js +414 -0
  125. package/dist/src/controllers/petController.d.ts +7 -0
  126. package/dist/src/controllers/petController.d.ts.map +1 -0
  127. package/dist/src/controllers/petController.js +163 -0
  128. package/dist/src/controllers/rbacController.d.ts +16 -0
  129. package/dist/src/controllers/rbacController.d.ts.map +1 -0
  130. package/dist/src/controllers/rbacController.js +437 -0
  131. package/dist/src/core/database.d.ts +3 -0
  132. package/dist/src/core/database.d.ts.map +1 -0
  133. package/dist/src/core/database.js +34 -0
  134. package/dist/src/core/realtime.d.ts +3 -0
  135. package/dist/src/core/realtime.d.ts.map +1 -0
  136. package/dist/src/core/realtime.js +36 -0
  137. package/dist/src/core/redis.d.ts +8 -0
  138. package/dist/src/core/redis.d.ts.map +1 -0
  139. package/dist/src/core/redis.js +123 -0
  140. package/dist/src/core/serializer.d.ts +43 -0
  141. package/dist/src/core/serializer.d.ts.map +1 -0
  142. package/dist/src/core/serializer.js +66 -0
  143. package/dist/src/core/server.d.ts +2 -0
  144. package/dist/src/core/server.d.ts.map +1 -0
  145. package/dist/src/core/server.js +60 -0
  146. package/dist/src/index.d.ts +2 -0
  147. package/dist/src/index.d.ts.map +1 -0
  148. package/dist/src/index.js +98 -0
  149. package/dist/src/middleware/auth.d.ts +4 -0
  150. package/dist/src/middleware/auth.d.ts.map +1 -0
  151. package/dist/src/middleware/auth.js +48 -0
  152. package/dist/src/middleware/error.d.ts +3 -0
  153. package/dist/src/middleware/error.d.ts.map +1 -0
  154. package/dist/src/middleware/error.js +60 -0
  155. package/dist/src/middleware/multipart.d.ts +4 -0
  156. package/dist/src/middleware/multipart.d.ts.map +1 -0
  157. package/dist/src/middleware/multipart.js +17 -0
  158. package/dist/src/middleware/rateLimit.d.ts +2 -0
  159. package/dist/src/middleware/rateLimit.d.ts.map +1 -0
  160. package/dist/src/middleware/rateLimit.js +19 -0
  161. package/dist/src/middleware/requestLogger.d.ts +3 -0
  162. package/dist/src/middleware/requestLogger.d.ts.map +1 -0
  163. package/dist/src/middleware/requestLogger.js +22 -0
  164. package/dist/src/middleware/visitor.d.ts +3 -0
  165. package/dist/src/middleware/visitor.d.ts.map +1 -0
  166. package/dist/src/middleware/visitor.js +144 -0
  167. package/dist/src/prisma.d.ts +3 -0
  168. package/dist/src/prisma.d.ts.map +1 -0
  169. package/dist/src/prisma.js +34 -0
  170. package/dist/src/realtime.d.ts +3 -0
  171. package/dist/src/realtime.d.ts.map +1 -0
  172. package/dist/src/realtime.js +36 -0
  173. package/dist/src/redis.d.ts +8 -0
  174. package/dist/src/redis.d.ts.map +1 -0
  175. package/dist/src/redis.js +122 -0
  176. package/dist/src/routes/auth.d.ts +2 -0
  177. package/dist/src/routes/auth.d.ts.map +1 -0
  178. package/dist/src/routes/auth.js +45 -0
  179. package/dist/src/routes/index.d.ts +2 -0
  180. package/dist/src/routes/index.d.ts.map +1 -0
  181. package/dist/src/routes/index.js +14 -0
  182. package/dist/src/routes/pets.d.ts +3 -0
  183. package/dist/src/routes/pets.d.ts.map +1 -0
  184. package/dist/src/routes/pets.js +45 -0
  185. package/dist/src/routes/rbac.d.ts +2 -0
  186. package/dist/src/routes/rbac.d.ts.map +1 -0
  187. package/dist/src/routes/rbac.js +23 -0
  188. package/dist/src/schema/auth-schema.d.ts +76 -0
  189. package/dist/src/schema/auth-schema.d.ts.map +1 -0
  190. package/dist/src/schema/auth-schema.js +63 -0
  191. package/dist/src/schema/pet-schema.d.ts +28 -0
  192. package/dist/src/schema/pet-schema.d.ts.map +1 -0
  193. package/dist/src/schema/pet-schema.js +14 -0
  194. package/dist/src/server.d.ts +2 -0
  195. package/dist/src/server.d.ts.map +1 -0
  196. package/dist/src/server.js +31 -0
  197. package/dist/src/utils/logger.d.ts +11 -0
  198. package/dist/src/utils/logger.d.ts.map +1 -0
  199. package/dist/src/utils/logger.js +81 -0
  200. package/dist/src/utils/pagination.d.ts +19 -0
  201. package/dist/src/utils/pagination.d.ts.map +1 -0
  202. package/dist/src/utils/pagination.js +34 -0
  203. package/dist/src/utils/response.d.ts +11 -0
  204. package/dist/src/utils/response.d.ts.map +1 -0
  205. package/dist/src/utils/response.js +57 -0
  206. package/dist/src/utils/validator.d.ts +38 -0
  207. package/dist/src/utils/validator.d.ts.map +1 -0
  208. package/dist/src/utils/validator.js +369 -0
  209. package/lib/bootstrap.ts +6 -0
  210. package/package.json +26 -14
  211. package/.env.example +0 -19
  212. package/doc/ARCHITECTURE_GUIDE.md +0 -73
  213. package/doc/CHANGELOG.md +0 -77
  214. package/doc/CHEATSHEET.md +0 -94
  215. package/doc/CLI.md +0 -139
  216. package/doc/CONTRIBUTING.md +0 -105
  217. package/doc/DEPLOYMENT.md +0 -122
  218. package/doc/FAQ.md +0 -81
  219. package/doc/FEATURES.md +0 -165
  220. package/doc/GETTING_STARTED.md +0 -108
  221. package/doc/INTRODUCTION.md +0 -60
  222. package/doc/PACKAGES.md +0 -66
  223. package/doc/PERFORMANCE.md +0 -91
  224. package/doc/ROADMAP.md +0 -93
  225. package/doc/SECURITY.md +0 -93
  226. package/doc/STRUCTURE.md +0 -90
  227. package/doc/TUTORIAL.md +0 -192
  228. package/docker-compose.yml +0 -24
  229. package/eslint.config.mjs +0 -26
  230. package/framework.md +0 -168
  231. package/nodemon.json +0 -6
  232. package/prisma.config.ts +0 -15
  233. package/src/controllers/authController.ts +0 -469
  234. package/src/controllers/petController.ts +0 -194
  235. package/src/controllers/rbacController.ts +0 -478
  236. package/src/models/core.prisma +0 -163
  237. package/src/models/pets.prisma +0 -9
  238. package/src/routes/auth.ts +0 -74
  239. package/src/routes/index.ts +0 -10
  240. package/src/routes/pets.ts +0 -13
  241. package/src/routes/rbac.ts +0 -42
  242. package/storage/logs/.gitkeep +0 -0
  243. package/tsconfig.json +0 -30
package/prisma.config.ts DELETED
@@ -1,15 +0,0 @@
1
- // This file was generated by Prisma, and assumes you have installed the following:
2
- // npm install --save-dev prisma dotenv
3
- import "dotenv/config";
4
- import { defineConfig, env } from "prisma/config";
5
-
6
- export default defineConfig({
7
- schema: "prisma/schema.prisma",
8
- migrations: {
9
- path: "prisma/migrations",
10
- seed: "ts-node -r tsconfig-paths/register prisma/seed.ts",
11
- },
12
- datasource: {
13
- url: env("DATABASE_URL"),
14
- },
15
- });
@@ -1,469 +0,0 @@
1
- import { Request, Response } from "express";
2
- import bcrypt from "bcryptjs";
3
- import jwt from "jsonwebtoken";
4
- import { v4 as uuidv4 } from "uuid";
5
- import { prisma } from "@lapeh/core/database";
6
- import { sendError, sendFastSuccess } from "@lapeh/utils/response";
7
- import { Validator } from "@lapeh/utils/validator";
8
- import { getSerializer, createResponseSchema } from "@lapeh/core/serializer";
9
-
10
- export const ACCESS_TOKEN_EXPIRES_IN_SECONDS = 7 * 24 * 60 * 60;
11
-
12
- // --- Serializers ---
13
-
14
- const registerSchema = {
15
- type: "object",
16
- properties: {
17
- id: { type: "string" },
18
- email: { type: "string" },
19
- name: { type: "string" },
20
- role: { type: "string" },
21
- },
22
- };
23
-
24
- const loginSchema = {
25
- type: "object",
26
- properties: {
27
- token: { type: "string" },
28
- refreshToken: { type: "string" },
29
- expiresIn: { type: "integer" },
30
- expiresAt: { type: "string" },
31
- name: { type: "string" },
32
- role: { type: "string" },
33
- },
34
- };
35
-
36
- const userProfileSchema = {
37
- type: "object",
38
- properties: {
39
- id: { type: "string" },
40
- name: { type: "string" },
41
- email: { type: "string" },
42
- role: { type: "string" },
43
- avatar: { type: "string", nullable: true },
44
- avatar_url: { type: "string", nullable: true },
45
- email_verified_at: { type: "string", format: "date-time", nullable: true },
46
- created_at: { type: "string", format: "date-time", nullable: true },
47
- updated_at: { type: "string", format: "date-time", nullable: true },
48
- },
49
- };
50
-
51
- const refreshTokenSchema = {
52
- type: "object",
53
- properties: {
54
- token: { type: "string" },
55
- expiresIn: { type: "integer" },
56
- expiresAt: { type: "string" },
57
- name: { type: "string" },
58
- role: { type: "string" },
59
- },
60
- };
61
-
62
- const registerSerializer = getSerializer(
63
- "auth-register",
64
- createResponseSchema(registerSchema)
65
- );
66
- const loginSerializer = getSerializer(
67
- "auth-login",
68
- createResponseSchema(loginSchema)
69
- );
70
- const userProfileSerializer = getSerializer(
71
- "auth-profile",
72
- createResponseSchema(userProfileSchema)
73
- );
74
- const refreshTokenSerializer = getSerializer(
75
- "auth-refresh",
76
- createResponseSchema(refreshTokenSchema)
77
- );
78
-
79
- const voidSerializer = getSerializer(
80
- "void",
81
- createResponseSchema({ type: "null" })
82
- );
83
-
84
- // --- Controllers ---
85
-
86
- export async function register(req: Request, res: Response) {
87
- const validator = Validator.make(req.body || {}, {
88
- email: "required|email|unique:users,email",
89
- name: "required|min:1",
90
- password: "required|min:4",
91
- confirmPassword: "required|min:4|same:password",
92
- });
93
-
94
- if (await validator.fails()) {
95
- sendError(res, 422, "Validation error", validator.errors());
96
- return;
97
- }
98
- const { email, name, password } = await validator.validated();
99
- // Manual unique check removed as it is handled by validator
100
- const hash = await bcrypt.hash(password, 10);
101
- const user = await prisma.users.create({
102
- data: {
103
- email,
104
- name,
105
- password: hash,
106
- uuid: uuidv4(),
107
- created_at: new Date(),
108
- updated_at: new Date(),
109
- },
110
- });
111
-
112
- const defaultRole = await prisma.roles.findUnique({
113
- where: { slug: "user" },
114
- });
115
- if (defaultRole) {
116
- await prisma.user_roles.create({
117
- data: {
118
- user_id: user.id,
119
- role_id: defaultRole.id,
120
- created_at: new Date(),
121
- },
122
- });
123
- }
124
-
125
- sendFastSuccess(res, 200, registerSerializer, {
126
- status: "success",
127
- message: "Registration successful",
128
- data: {
129
- id: user.id.toString(),
130
- email: user.email,
131
- name: user.name,
132
- role: defaultRole ? defaultRole.slug : "user",
133
- },
134
- });
135
- }
136
-
137
- export async function login(req: Request, res: Response) {
138
- const validator = Validator.make(req.body || {}, {
139
- email: "required|email",
140
- password: "required|min:4",
141
- });
142
-
143
- if (await validator.fails()) {
144
- sendError(res, 422, "Validation error", validator.errors());
145
- return;
146
- }
147
- const { email, password } = await validator.validated();
148
- const user = await prisma.users.findUnique({
149
- where: { email },
150
- include: {
151
- user_roles: {
152
- include: {
153
- role: true,
154
- },
155
- },
156
- },
157
- });
158
- if (!user) {
159
- sendError(res, 401, "Email not registered", {
160
- field: "email",
161
- message: "Email is not registered, please register first",
162
- });
163
- return;
164
- }
165
- const ok = await bcrypt.compare(password, user.password);
166
- if (!ok) {
167
- sendError(res, 401, "Invalid credentials", {
168
- field: "password",
169
- message: "The password you entered is incorrect",
170
- });
171
- return;
172
- }
173
- const secret = process.env.JWT_SECRET;
174
- if (!secret) {
175
- sendError(res, 500, "Server misconfigured");
176
- return;
177
- }
178
- const primaryUserRole =
179
- user.user_roles && user.user_roles.length > 0 && user.user_roles[0].role
180
- ? user.user_roles[0].role.slug
181
- : "user";
182
- const accessExpiresInSeconds = ACCESS_TOKEN_EXPIRES_IN_SECONDS;
183
- const accessExpiresAt = new Date(
184
- Date.now() + accessExpiresInSeconds * 1000
185
- ).toISOString();
186
- const token = jwt.sign(
187
- { userId: user.id.toString(), role: primaryUserRole },
188
- secret,
189
- { expiresIn: accessExpiresInSeconds }
190
- );
191
- const refreshExpiresInSeconds = 30 * 24 * 60 * 60;
192
- const refreshToken = jwt.sign(
193
- {
194
- userId: user.id.toString(),
195
- role: primaryUserRole,
196
- tokenType: "refresh",
197
- },
198
- secret,
199
- { expiresIn: refreshExpiresInSeconds }
200
- );
201
- sendFastSuccess(res, 200, loginSerializer, {
202
- status: "success",
203
- message: "Login successful",
204
- data: {
205
- token,
206
- refreshToken,
207
- expiresIn: accessExpiresInSeconds,
208
- expiresAt: accessExpiresAt,
209
- name: user.name,
210
- role: primaryUserRole,
211
- },
212
- });
213
- }
214
-
215
- export async function me(req: Request, res: Response) {
216
- const payload = (req as any).user as { userId: string; role: string };
217
- if (!payload || !payload.userId) {
218
- sendError(res, 401, "Unauthorized");
219
- return;
220
- }
221
- const user = await prisma.users.findUnique({
222
- where: { id: BigInt(payload.userId) },
223
- include: {
224
- user_roles: {
225
- include: {
226
- role: true,
227
- },
228
- },
229
- },
230
- });
231
- if (!user) {
232
- sendError(res, 404, "User not found");
233
- return;
234
- }
235
- const { password, remember_token, ...rest } = user as any;
236
- sendFastSuccess(res, 200, userProfileSerializer, {
237
- status: "success",
238
- message: "User profile",
239
- data: {
240
- ...rest,
241
- id: user.id.toString(),
242
- role:
243
- user.user_roles && user.user_roles.length > 0 && user.user_roles[0].role
244
- ? user.user_roles[0].role.slug
245
- : "user",
246
- },
247
- });
248
- }
249
-
250
- export async function logout(_req: Request, res: Response) {
251
- // In a stateless JWT setup, logout is client-side (delete token).
252
- // If using a whitelist/blacklist in Redis, invalidate the token here.
253
- // For now, just return success.
254
- sendFastSuccess(res, 200, voidSerializer, {
255
- status: "success",
256
- message: "Logout successful",
257
- data: null,
258
- });
259
- }
260
-
261
- export async function refreshToken(req: Request, res: Response) {
262
- const validator = Validator.make(req.body || {}, {
263
- refreshToken: "required|min:1",
264
- });
265
- if (await validator.fails()) {
266
- sendError(res, 422, "Validation error", validator.errors());
267
- return;
268
- }
269
- const secret = process.env.JWT_SECRET;
270
- if (!secret) {
271
- sendError(res, 500, "Server misconfigured");
272
- return;
273
- }
274
- try {
275
- const validatedData = await validator.validated();
276
- const decoded = jwt.verify(validatedData.refreshToken, secret) as {
277
- userId: string;
278
- role: string;
279
- tokenType?: string;
280
- iat: number;
281
- exp: number;
282
- };
283
- if (decoded.tokenType !== "refresh") {
284
- sendError(res, 401, "Invalid refresh token");
285
- return;
286
- }
287
- const user = await prisma.users.findUnique({
288
- where: { id: BigInt(decoded.userId) },
289
- include: {
290
- user_roles: {
291
- include: {
292
- role: true,
293
- },
294
- },
295
- },
296
- });
297
- if (!user) {
298
- sendError(res, 401, "Invalid refresh token");
299
- return;
300
- }
301
- const primaryUserRole =
302
- user.user_roles && user.user_roles.length > 0 && user.user_roles[0].role
303
- ? user.user_roles[0].role.slug
304
- : "user";
305
- const accessExpiresInSeconds = ACCESS_TOKEN_EXPIRES_IN_SECONDS;
306
- const accessExpiresAt = new Date(
307
- Date.now() + accessExpiresInSeconds * 1000
308
- ).toISOString();
309
- const token = jwt.sign(
310
- { userId: user.id.toString(), role: primaryUserRole },
311
- secret,
312
- { expiresIn: accessExpiresInSeconds }
313
- );
314
- sendFastSuccess(res, 200, refreshTokenSerializer, {
315
- status: "success",
316
- message: "Token refreshed",
317
- data: {
318
- token,
319
- expiresIn: accessExpiresInSeconds,
320
- expiresAt: accessExpiresAt,
321
- name: user.name,
322
- role: primaryUserRole,
323
- },
324
- });
325
- } catch {
326
- sendError(res, 401, "Invalid refresh token");
327
- }
328
- }
329
-
330
- export async function updateAvatar(req: Request, res: Response) {
331
- const payload = (req as any).user as { userId: string; role: string };
332
- if (!payload || !payload.userId) {
333
- sendError(res, 401, "Unauthorized");
334
- return;
335
- }
336
-
337
- const data = {
338
- avatar: (req as any).file,
339
- };
340
-
341
- const validator = Validator.make(data, {
342
- avatar: "nullable|image|mimes:jpeg,png,jpg,gif|max:2048",
343
- });
344
-
345
- if (await validator.fails()) {
346
- sendError(res, 422, "Validation error", validator.errors());
347
- return;
348
- }
349
-
350
- const { avatar: file } = await validator.validated();
351
-
352
- if (!file) {
353
- sendError(res, 400, "Avatar file is required");
354
- return;
355
- }
356
- const userId = BigInt(payload.userId);
357
- const avatar = file.filename;
358
- const avatar_url =
359
- process.env.AVATAR_BASE_URL || `/uploads/avatars/${file.filename}`;
360
- const updated = await prisma.users.update({
361
- where: { id: userId },
362
- data: {
363
- avatar,
364
- avatar_url,
365
- updated_at: new Date(),
366
- },
367
- });
368
- const { password, remember_token, ...rest } = updated as any;
369
- // Note: user_roles might not be fetched in update, so role defaults to "user" or fetched if needed.
370
- // Ideally we should refetch or pass existing role.
371
- // For now assuming role is preserved or handled by frontend state, but API should return it.
372
- // Let's rely on nullable role or simple "user" fallback if not present in `updated`.
373
- // Actually `update` returns what was updated. Relations are not included unless specified.
374
- // For now we will return it compatible with userProfileSchema.
375
-
376
- sendFastSuccess(res, 200, userProfileSerializer, {
377
- status: "success",
378
- message: "Avatar updated successfully",
379
- data: {
380
- ...rest,
381
- id: updated.id.toString(),
382
- role: payload.role, // Use role from JWT payload as it shouldn't change here
383
- },
384
- });
385
- }
386
-
387
- export async function updatePassword(req: Request, res: Response) {
388
- const payload = (req as any).user as { userId: string; role: string };
389
- if (!payload || !payload.userId) {
390
- sendError(res, 401, "Unauthorized");
391
- return;
392
- }
393
- const validator = Validator.make(req.body || {}, {
394
- currentPassword: "required|min:4",
395
- newPassword: "required|min:4",
396
- confirmPassword: "required|min:4|same:newPassword",
397
- });
398
- if (await validator.fails()) {
399
- sendError(res, 422, "Validation error", validator.errors());
400
- return;
401
- }
402
- const { currentPassword, newPassword } = await validator.validated();
403
- const user = await prisma.users.findUnique({
404
- where: { id: BigInt(payload.userId) },
405
- });
406
- if (!user) {
407
- sendError(res, 404, "User not found");
408
- return;
409
- }
410
- const ok = await bcrypt.compare(currentPassword, user.password);
411
- if (!ok) {
412
- sendError(res, 401, "Invalid credentials", {
413
- field: "currentPassword",
414
- message: "Current password is incorrect",
415
- });
416
- return;
417
- }
418
- const hash = await bcrypt.hash(newPassword, 10);
419
- await prisma.users.update({
420
- where: { id: user.id },
421
- data: {
422
- password: hash,
423
- updated_at: new Date(),
424
- },
425
- });
426
- sendFastSuccess(res, 200, voidSerializer, {
427
- status: "success",
428
- message: "Password updated successfully",
429
- data: null,
430
- });
431
- }
432
-
433
- export async function updateProfile(req: Request, res: Response) {
434
- const payload = (req as any).user as { userId: string; role: string };
435
- if (!payload || !payload.userId) {
436
- sendError(res, 401, "Unauthorized");
437
- return;
438
- }
439
- const validator = Validator.make(req.body || {}, {
440
- name: "required|min:1",
441
- email: `required|email|unique:users,email,${payload.userId}`,
442
- });
443
- if (await validator.fails()) {
444
- sendError(res, 422, "Validation error", validator.errors());
445
- return;
446
- }
447
- const { name, email } = await validator.validated();
448
- const userId = BigInt(payload.userId);
449
- // Manual unique check removed as it is handled by validator
450
-
451
- const updated = await prisma.users.update({
452
- where: { id: userId },
453
- data: {
454
- name,
455
- email,
456
- updated_at: new Date(),
457
- },
458
- });
459
- const { password, remember_token, ...rest } = updated as any;
460
- sendFastSuccess(res, 200, userProfileSerializer, {
461
- status: "success",
462
- message: "Profile updated successfully",
463
- data: {
464
- ...rest,
465
- id: updated.id.toString(),
466
- role: payload.role, // Use role from JWT payload
467
- },
468
- });
469
- }
@@ -1,194 +0,0 @@
1
- import { Request, Response } from "express";
2
- import { prisma } from "@lapeh/core/database";
3
- import { sendSuccess, sendError, sendFastSuccess } from "@lapeh/utils/response";
4
- import { getPagination, buildPaginationMeta } from "@lapeh/utils/pagination";
5
- import { Validator } from "@lapeh/utils/validator";
6
- import {
7
- getSerializer,
8
- createResponseSchema,
9
- createPaginatedResponseSchema,
10
- } from "@lapeh/core/serializer";
11
-
12
- // 1. Definisikan Schema Output untuk performa tinggi
13
- const petSchema = {
14
- type: "object",
15
- properties: {
16
- id: { type: "string" }, // BigInt dikonversi ke string
17
- name: { type: "string" },
18
- species: { type: "string" },
19
- age: { type: "integer" },
20
- created_at: { type: "string", format: "date-time" },
21
- updated_at: { type: "string", format: "date-time" },
22
- },
23
- };
24
-
25
- // 2. Compile Serializer
26
- // Untuk Single Item
27
- const petSerializer = getSerializer(
28
- "pet-single",
29
- createResponseSchema(petSchema)
30
- );
31
-
32
- // Untuk List Item (Paginated)
33
- const petListSerializer = getSerializer(
34
- "pet-list",
35
- createPaginatedResponseSchema(petSchema)
36
- );
37
-
38
- export async function index(req: Request, res: Response) {
39
- const { page, perPage, skip, take } = getPagination(req.query);
40
- const search = req.query.search as string;
41
-
42
- const where: any = {};
43
- if (search) {
44
- where.OR = [
45
- { name: { contains: search, mode: "insensitive" } },
46
- { species: { contains: search, mode: "insensitive" } },
47
- ];
48
- }
49
-
50
- const [data, total] = await Promise.all([
51
- prisma.pets.findMany({
52
- where,
53
- skip,
54
- take,
55
- orderBy: { created_at: "desc" },
56
- }),
57
- prisma.pets.count({ where }),
58
- ]);
59
-
60
- // Kita perlu convert BigInt ke string sebelum masuk serializer
61
- // Karena fast-json-stringify mengharapkan tipe data yang sesuai dengan schema
62
- const serialized = data.map((item: any) => ({
63
- ...item,
64
- id: item.id.toString(),
65
- }));
66
-
67
- const meta = buildPaginationMeta(page, perPage, total);
68
-
69
- // Gunakan sendFastSuccess untuk performa maksimal
70
- // Struktur data disesuaikan dengan createPaginatedResponseSchema: { data: [], meta: {} }
71
- sendFastSuccess(res, 200, petListSerializer, {
72
- status: "success",
73
- message: "Pets retrieved successfully",
74
- data: {
75
- data: serialized,
76
- meta,
77
- },
78
- });
79
- }
80
-
81
- export async function show(req: Request, res: Response) {
82
- const { id } = req.params;
83
- const pet = await prisma.pets.findUnique({
84
- where: { id: BigInt(id) },
85
- });
86
-
87
- if (!pet) {
88
- sendError(res, 404, "Pet not found");
89
- return;
90
- }
91
-
92
- // Gunakan sendFastSuccess
93
- sendFastSuccess(res, 200, petSerializer, {
94
- status: "success",
95
- message: "Pet retrieved successfully",
96
- data: {
97
- ...pet,
98
- id: pet.id.toString(),
99
- },
100
- });
101
- }
102
-
103
- export async function store(req: Request, res: Response) {
104
- const validator = Validator.make(req.body || {}, {
105
- name: "required|string",
106
- species: "required|string",
107
- age: "required|integer|min:1",
108
- });
109
-
110
- if (await validator.fails()) {
111
- sendError(res, 422, "Validation error", validator.errors());
112
- return;
113
- }
114
-
115
- const validatedData = await validator.validated();
116
- const pet = await prisma.pets.create({
117
- data: {
118
- ...validatedData,
119
- created_at: new Date(),
120
- updated_at: new Date(),
121
- },
122
- });
123
-
124
- // Gunakan sendFastSuccess
125
- sendFastSuccess(res, 201, petSerializer, {
126
- status: "success",
127
- message: "Pet created successfully",
128
- data: {
129
- ...pet,
130
- id: pet.id.toString(),
131
- },
132
- });
133
- }
134
-
135
- export async function update(req: Request, res: Response) {
136
- const { id } = req.params;
137
- const validator = Validator.make(req.body || {}, {
138
- name: "string",
139
- species: "string",
140
- age: "integer|min:1",
141
- });
142
-
143
- if (await validator.fails()) {
144
- sendError(res, 422, "Validation error", validator.errors());
145
- return;
146
- }
147
-
148
- const existing = await prisma.pets.findUnique({
149
- where: { id: BigInt(id) },
150
- });
151
-
152
- if (!existing) {
153
- sendError(res, 404, "Pet not found");
154
- return;
155
- }
156
-
157
- const validatedData = await validator.validated();
158
- const updated = await prisma.pets.update({
159
- where: { id: BigInt(id) },
160
- data: {
161
- ...validatedData,
162
- updated_at: new Date(),
163
- },
164
- });
165
-
166
- // Gunakan sendFastSuccess
167
- sendFastSuccess(res, 200, petSerializer, {
168
- status: "success",
169
- message: "Pet updated successfully",
170
- data: {
171
- ...updated,
172
- id: updated.id.toString(),
173
- },
174
- });
175
- }
176
-
177
- export async function destroy(req: Request, res: Response) {
178
- const { id } = req.params;
179
-
180
- const existing = await prisma.pets.findUnique({
181
- where: { id: BigInt(id) },
182
- });
183
-
184
- if (!existing) {
185
- sendError(res, 404, "Pet not found");
186
- return;
187
- }
188
-
189
- await prisma.pets.delete({
190
- where: { id: BigInt(id) },
191
- });
192
-
193
- sendSuccess(res, 200, "Pet deleted successfully", null);
194
- }