@reverso/api 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +47 -0
  2. package/dist/index.d.ts +41 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +56 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/plugins/auth.d.ts +72 -0
  7. package/dist/plugins/auth.d.ts.map +1 -0
  8. package/dist/plugins/auth.js +173 -0
  9. package/dist/plugins/auth.js.map +1 -0
  10. package/dist/plugins/database.d.ts +19 -0
  11. package/dist/plugins/database.d.ts.map +1 -0
  12. package/dist/plugins/database.js +23 -0
  13. package/dist/plugins/database.js.map +1 -0
  14. package/dist/plugins/index.d.ts +5 -0
  15. package/dist/plugins/index.d.ts.map +1 -0
  16. package/dist/plugins/index.js +5 -0
  17. package/dist/plugins/index.js.map +1 -0
  18. package/dist/routes/auth.d.ts +6 -0
  19. package/dist/routes/auth.d.ts.map +1 -0
  20. package/dist/routes/auth.js +258 -0
  21. package/dist/routes/auth.js.map +1 -0
  22. package/dist/routes/content.d.ts +8 -0
  23. package/dist/routes/content.d.ts.map +1 -0
  24. package/dist/routes/content.js +339 -0
  25. package/dist/routes/content.js.map +1 -0
  26. package/dist/routes/forms.d.ts +8 -0
  27. package/dist/routes/forms.d.ts.map +1 -0
  28. package/dist/routes/forms.js +953 -0
  29. package/dist/routes/forms.js.map +1 -0
  30. package/dist/routes/index.d.ts +19 -0
  31. package/dist/routes/index.d.ts.map +1 -0
  32. package/dist/routes/index.js +31 -0
  33. package/dist/routes/index.js.map +1 -0
  34. package/dist/routes/media.d.ts +8 -0
  35. package/dist/routes/media.d.ts.map +1 -0
  36. package/dist/routes/media.js +400 -0
  37. package/dist/routes/media.js.map +1 -0
  38. package/dist/routes/pages.d.ts +8 -0
  39. package/dist/routes/pages.d.ts.map +1 -0
  40. package/dist/routes/pages.js +220 -0
  41. package/dist/routes/pages.js.map +1 -0
  42. package/dist/routes/redirects.d.ts +8 -0
  43. package/dist/routes/redirects.d.ts.map +1 -0
  44. package/dist/routes/redirects.js +462 -0
  45. package/dist/routes/redirects.js.map +1 -0
  46. package/dist/routes/schema.d.ts +8 -0
  47. package/dist/routes/schema.d.ts.map +1 -0
  48. package/dist/routes/schema.js +151 -0
  49. package/dist/routes/schema.js.map +1 -0
  50. package/dist/routes/sitemap.d.ts +8 -0
  51. package/dist/routes/sitemap.d.ts.map +1 -0
  52. package/dist/routes/sitemap.js +144 -0
  53. package/dist/routes/sitemap.js.map +1 -0
  54. package/dist/server.d.ts +47 -0
  55. package/dist/server.d.ts.map +1 -0
  56. package/dist/server.js +218 -0
  57. package/dist/server.js.map +1 -0
  58. package/dist/types.d.ts +91 -0
  59. package/dist/types.d.ts.map +1 -0
  60. package/dist/types.js +5 -0
  61. package/dist/types.js.map +1 -0
  62. package/dist/utils/index.d.ts +5 -0
  63. package/dist/utils/index.d.ts.map +1 -0
  64. package/dist/utils/index.js +5 -0
  65. package/dist/utils/index.js.map +1 -0
  66. package/dist/utils/security.d.ts +32 -0
  67. package/dist/utils/security.d.ts.map +1 -0
  68. package/dist/utils/security.js +154 -0
  69. package/dist/utils/security.js.map +1 -0
  70. package/dist/validation.d.ts +402 -0
  71. package/dist/validation.d.ts.map +1 -0
  72. package/dist/validation.js +308 -0
  73. package/dist/validation.js.map +1 -0
  74. package/package.json +76 -0
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Authentication routes.
3
+ */
4
+ import bcrypt from 'bcrypt';
5
+ import crypto from 'node:crypto';
6
+ import { getUserByEmail, getAccountByUserId, createUser, createAccount, createSession, deleteSessionByToken, getSessionWithUser, isLockedOut, recordFailedLoginAttempt, clearLoginAttempts, } from '@reverso/db';
7
+ import { z } from 'zod';
8
+ const SALT_ROUNDS = 12;
9
+ const SESSION_DURATION_DAYS = 30;
10
+ /**
11
+ * Generate a secure session token.
12
+ */
13
+ function generateSessionToken() {
14
+ return crypto.randomBytes(32).toString('hex');
15
+ }
16
+ // Validation schemas
17
+ const loginSchema = z.object({
18
+ email: z.string().email('Invalid email format'),
19
+ password: z.string().min(1, 'Password is required'),
20
+ });
21
+ const registerSchema = z.object({
22
+ email: z.string().email('Invalid email format'),
23
+ password: z.string().min(8, 'Password must be at least 8 characters'),
24
+ name: z.string().min(1, 'Name is required'),
25
+ });
26
+ export default async function authRoutes(fastify) {
27
+ /**
28
+ * POST /auth/login - Login with email and password.
29
+ */
30
+ fastify.post('/auth/login', async (request, reply) => {
31
+ const db = request.db;
32
+ const ip = request.ip;
33
+ // Check lockout (persistent in database)
34
+ const lockoutKey = `login:${ip}`;
35
+ const lockoutStatus = await isLockedOut(db, lockoutKey);
36
+ if (lockoutStatus.locked) {
37
+ return reply.status(429).send({
38
+ success: false,
39
+ error: 'Too many failed attempts',
40
+ message: `Account temporarily locked. Try again in ${Math.ceil(lockoutStatus.remainingSeconds / 60)} minutes.`,
41
+ retryAfter: lockoutStatus.remainingSeconds,
42
+ });
43
+ }
44
+ // Validate input
45
+ const parseResult = loginSchema.safeParse(request.body);
46
+ if (!parseResult.success) {
47
+ return reply.status(400).send({
48
+ success: false,
49
+ error: 'Validation error',
50
+ details: parseResult.error.issues,
51
+ });
52
+ }
53
+ const { email, password } = parseResult.data;
54
+ // Find user
55
+ const user = await getUserByEmail(db, email);
56
+ if (!user) {
57
+ await recordFailedLoginAttempt(db, lockoutKey);
58
+ return reply.status(401).send({
59
+ success: false,
60
+ error: 'Invalid credentials',
61
+ message: 'Email or password is incorrect',
62
+ });
63
+ }
64
+ // Get account with password
65
+ const account = await getAccountByUserId(db, user.id, 'credential');
66
+ if (!account || !account.password) {
67
+ await recordFailedLoginAttempt(db, lockoutKey);
68
+ return reply.status(401).send({
69
+ success: false,
70
+ error: 'Invalid credentials',
71
+ message: 'Email or password is incorrect',
72
+ });
73
+ }
74
+ // Verify password
75
+ const passwordValid = await bcrypt.compare(password, account.password);
76
+ if (!passwordValid) {
77
+ await recordFailedLoginAttempt(db, lockoutKey);
78
+ return reply.status(401).send({
79
+ success: false,
80
+ error: 'Invalid credentials',
81
+ message: 'Email or password is incorrect',
82
+ });
83
+ }
84
+ // Clear failed attempts on successful login
85
+ await clearLoginAttempts(db, lockoutKey);
86
+ // Create session
87
+ const token = generateSessionToken();
88
+ const expiresAt = new Date(Date.now() + SESSION_DURATION_DAYS * 24 * 60 * 60 * 1000);
89
+ const session = await createSession(db, {
90
+ id: crypto.randomUUID(),
91
+ userId: user.id,
92
+ token,
93
+ expiresAt,
94
+ ipAddress: ip,
95
+ userAgent: request.headers['user-agent'] ?? null,
96
+ createdAt: new Date(),
97
+ updatedAt: new Date(),
98
+ });
99
+ // Set cookie
100
+ reply.setCookie('reverso_session', token, {
101
+ httpOnly: true,
102
+ secure: process.env.NODE_ENV === 'production',
103
+ sameSite: 'lax',
104
+ path: '/',
105
+ expires: expiresAt,
106
+ });
107
+ return reply.send({
108
+ success: true,
109
+ user: {
110
+ id: user.id,
111
+ email: user.email,
112
+ name: user.name,
113
+ role: user.role,
114
+ image: user.image,
115
+ },
116
+ session: {
117
+ token,
118
+ expiresAt: session.expiresAt.toISOString(),
119
+ },
120
+ });
121
+ });
122
+ /**
123
+ * POST /auth/logout - Logout and invalidate session.
124
+ */
125
+ fastify.post('/auth/logout', async (request, reply) => {
126
+ const db = request.db;
127
+ // Get token from cookie or header
128
+ const token = request.cookies.reverso_session ||
129
+ request.headers.authorization?.replace('Bearer ', '');
130
+ if (token) {
131
+ await deleteSessionByToken(db, token);
132
+ }
133
+ // Clear cookie
134
+ reply.clearCookie('reverso_session', { path: '/' });
135
+ return reply.send({
136
+ success: true,
137
+ message: 'Logged out successfully',
138
+ });
139
+ });
140
+ /**
141
+ * GET /auth/me - Get current authenticated user.
142
+ */
143
+ fastify.get('/auth/me', async (request, reply) => {
144
+ const db = request.db;
145
+ // Get token from cookie or header
146
+ const token = request.cookies.reverso_session ||
147
+ request.headers.authorization?.replace('Bearer ', '');
148
+ if (!token) {
149
+ return reply.status(401).send({
150
+ success: false,
151
+ error: 'Not authenticated',
152
+ message: 'No session token provided',
153
+ });
154
+ }
155
+ const result = await getSessionWithUser(db, token);
156
+ if (!result) {
157
+ reply.clearCookie('reverso_session', { path: '/' });
158
+ return reply.status(401).send({
159
+ success: false,
160
+ error: 'Invalid session',
161
+ message: 'Session expired or invalid',
162
+ });
163
+ }
164
+ return reply.send({
165
+ success: true,
166
+ user: {
167
+ id: result.user.id,
168
+ email: result.user.email,
169
+ name: result.user.name,
170
+ role: result.user.role,
171
+ image: result.user.image,
172
+ },
173
+ });
174
+ });
175
+ /**
176
+ * POST /auth/register - Register a new user (admin only or first user).
177
+ */
178
+ fastify.post('/auth/register', async (request, reply) => {
179
+ const db = request.db;
180
+ // Validate input
181
+ const parseResult = registerSchema.safeParse(request.body);
182
+ if (!parseResult.success) {
183
+ return reply.status(400).send({
184
+ success: false,
185
+ error: 'Validation error',
186
+ details: parseResult.error.issues,
187
+ });
188
+ }
189
+ const { email, password, name } = parseResult.data;
190
+ // Check if user already exists
191
+ const existingUser = await getUserByEmail(db, email);
192
+ if (existingUser) {
193
+ return reply.status(409).send({
194
+ success: false,
195
+ error: 'User exists',
196
+ message: 'A user with this email already exists',
197
+ });
198
+ }
199
+ // Hash password
200
+ const hashedPassword = await bcrypt.hash(password, SALT_ROUNDS);
201
+ // Create user
202
+ const userId = crypto.randomUUID();
203
+ const now = new Date();
204
+ const user = await createUser(db, {
205
+ id: userId,
206
+ email,
207
+ name,
208
+ role: 'admin', // First user is admin
209
+ createdAt: now,
210
+ updatedAt: now,
211
+ });
212
+ // Create credential account
213
+ await createAccount(db, {
214
+ id: crypto.randomUUID(),
215
+ userId: user.id,
216
+ accountId: user.id,
217
+ providerId: 'credential',
218
+ password: hashedPassword,
219
+ createdAt: now,
220
+ updatedAt: now,
221
+ });
222
+ // Auto-login: create session
223
+ const token = generateSessionToken();
224
+ const expiresAt = new Date(Date.now() + SESSION_DURATION_DAYS * 24 * 60 * 60 * 1000);
225
+ await createSession(db, {
226
+ id: crypto.randomUUID(),
227
+ userId: user.id,
228
+ token,
229
+ expiresAt,
230
+ ipAddress: request.ip,
231
+ userAgent: request.headers['user-agent'] ?? null,
232
+ createdAt: now,
233
+ updatedAt: now,
234
+ });
235
+ // Set cookie
236
+ reply.setCookie('reverso_session', token, {
237
+ httpOnly: true,
238
+ secure: process.env.NODE_ENV === 'production',
239
+ sameSite: 'lax',
240
+ path: '/',
241
+ expires: expiresAt,
242
+ });
243
+ return reply.status(201).send({
244
+ success: true,
245
+ user: {
246
+ id: user.id,
247
+ email: user.email,
248
+ name: user.name,
249
+ role: user.role,
250
+ },
251
+ session: {
252
+ token,
253
+ expiresAt: expiresAt.toISOString(),
254
+ },
255
+ });
256
+ });
257
+ }
258
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,kBAAkB,EAClB,WAAW,EACX,wBAAwB,EACxB,kBAAkB,GAEnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,WAAW,GAAG,EAAE,CAAC;AACvB,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC;;GAEG;AACH,SAAS,oBAAoB;IAC3B,OAAO,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAChD,CAAC;AAED,qBAAqB;AACrB,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC;IAC/C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,sBAAsB,CAAC;CACpD,CAAC,CAAC;AAEH,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC;IAC/C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,wCAAwC,CAAC;IACrE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,kBAAkB,CAAC;CAC5C,CAAC,CAAC;AAEH,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,UAAU,CAAC,OAAwB;IAC/D;;OAEG;IACH,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,OAAuB,EAAE,KAAmB,EAAE,EAAE;QACjF,MAAM,EAAE,GAAI,OAA8C,CAAC,EAAE,CAAC;QAC9D,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC;QAEtB,yCAAyC;QACzC,MAAM,UAAU,GAAG,SAAS,EAAE,EAAE,CAAC;QACjC,MAAM,aAAa,GAAG,MAAM,WAAW,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;QACxD,IAAI,aAAa,CAAC,MAAM,EAAE,CAAC;YACzB,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC5B,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,0BAA0B;gBACjC,OAAO,EAAE,4CAA4C,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,gBAAgB,GAAG,EAAE,CAAC,WAAW;gBAC9G,UAAU,EAAE,aAAa,CAAC,gBAAgB;aAC3C,CAAC,CAAC;QACL,CAAC;QAED,iBAAiB;QACjB,MAAM,WAAW,GAAG,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxD,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;YACzB,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC5B,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,kBAAkB;gBACzB,OAAO,EAAE,WAAW,CAAC,KAAK,CAAC,MAAM;aAClC,CAAC,CAAC;QACL,CAAC;QAED,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,WAAW,CAAC,IAAI,CAAC;QAE7C,YAAY;QACZ,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,wBAAwB,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YAC/C,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC5B,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,qBAAqB;gBAC5B,OAAO,EAAE,gCAAgC;aAC1C,CAAC,CAAC;QACL,CAAC;QAED,4BAA4B;QAC5B,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;QACpE,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YAClC,MAAM,wBAAwB,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YAC/C,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC5B,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,qBAAqB;gBAC5B,OAAO,EAAE,gCAAgC;aAC1C,CAAC,CAAC;QACL,CAAC;QAED,kBAAkB;QAClB,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QACvE,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,wBAAwB,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YAC/C,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC5B,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,qBAAqB;gBAC5B,OAAO,EAAE,gCAAgC;aAC1C,CAAC,CAAC;QACL,CAAC;QAED,4CAA4C;QAC5C,MAAM,kBAAkB,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;QAEzC,iBAAiB;QACjB,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;QACrC,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,qBAAqB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAErF,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,EAAE,EAAE;YACtC,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;YACvB,MAAM,EAAE,IAAI,CAAC,EAAE;YACf,KAAK;YACL,SAAS;YACT,SAAS,EAAE,EAAE;YACb,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,IAAI;YAChD,SAAS,EAAE,IAAI,IAAI,EAAE;YACrB,SAAS,EAAE,IAAI,IAAI,EAAE;SACtB,CAAC,CAAC;QAEH,aAAa;QACb,KAAK,CAAC,SAAS,CAAC,iBAAiB,EAAE,KAAK,EAAE;YACxC,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;YAC7C,QAAQ,EAAE,KAAK;YACf,IAAI,EAAE,GAAG;YACT,OAAO,EAAE,SAAS;SACnB,CAAC,CAAC;QAEH,OAAO,KAAK,CAAC,IAAI,CAAC;YAChB,OAAO,EAAE,IAAI;YACb,IAAI,EAAE;gBACJ,EAAE,EAAE,IAAI,CAAC,EAAE;gBACX,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,KAAK,EAAE,IAAI,CAAC,KAAK;aAClB;YACD,OAAO,EAAE;gBACP,KAAK;gBACL,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,WAAW,EAAE;aAC3C;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH;;OAEG;IACH,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,EAAE,OAAuB,EAAE,KAAmB,EAAE,EAAE;QAClF,MAAM,EAAE,GAAI,OAA8C,CAAC,EAAE,CAAC;QAE9D,kCAAkC;QAClC,MAAM,KAAK,GACT,OAAO,CAAC,OAAO,CAAC,eAAe;YAC/B,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAExD,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,oBAAoB,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACxC,CAAC;QAED,eAAe;QACf,KAAK,CAAC,WAAW,CAAC,iBAAiB,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QAEpD,OAAO,KAAK,CAAC,IAAI,CAAC;YAChB,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,yBAAyB;SACnC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH;;OAEG;IACH,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,OAAuB,EAAE,KAAmB,EAAE,EAAE;QAC7E,MAAM,EAAE,GAAI,OAA8C,CAAC,EAAE,CAAC;QAE9D,kCAAkC;QAClC,MAAM,KAAK,GACT,OAAO,CAAC,OAAO,CAAC,eAAe;YAC/B,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAExD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC5B,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,mBAAmB;gBAC1B,OAAO,EAAE,2BAA2B;aACrC,CAAC,CAAC;QACL,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACnD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,KAAK,CAAC,WAAW,CAAC,iBAAiB,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;YACpD,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC5B,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,iBAAiB;gBACxB,OAAO,EAAE,4BAA4B;aACtC,CAAC,CAAC;QACL,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC;YAChB,OAAO,EAAE,IAAI;YACb,IAAI,EAAE;gBACJ,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE;gBAClB,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK;gBACxB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI;gBACtB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI;gBACtB,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK;aACzB;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH;;OAEG;IACH,OAAO,CAAC,IAAI,CAAC,gBAAgB,EAAE,KAAK,EAAE,OAAuB,EAAE,KAAmB,EAAE,EAAE;QACpF,MAAM,EAAE,GAAI,OAA8C,CAAC,EAAE,CAAC;QAE9D,iBAAiB;QACjB,MAAM,WAAW,GAAG,cAAc,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC3D,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;YACzB,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC5B,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,kBAAkB;gBACzB,OAAO,EAAE,WAAW,CAAC,KAAK,CAAC,MAAM;aAClC,CAAC,CAAC;QACL,CAAC;QAED,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,WAAW,CAAC,IAAI,CAAC;QAEnD,+BAA+B;QAC/B,MAAM,YAAY,GAAG,MAAM,cAAc,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACrD,IAAI,YAAY,EAAE,CAAC;YACjB,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC5B,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,aAAa;gBACpB,OAAO,EAAE,uCAAuC;aACjD,CAAC,CAAC;QACL,CAAC;QAED,gBAAgB;QAChB,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QAEhE,cAAc;QACd,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QAEvB,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,EAAE,EAAE;YAChC,EAAE,EAAE,MAAM;YACV,KAAK;YACL,IAAI;YACJ,IAAI,EAAE,OAAO,EAAE,sBAAsB;YACrC,SAAS,EAAE,GAAG;YACd,SAAS,EAAE,GAAG;SACf,CAAC,CAAC;QAEH,4BAA4B;QAC5B,MAAM,aAAa,CAAC,EAAE,EAAE;YACtB,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;YACvB,MAAM,EAAE,IAAI,CAAC,EAAE;YACf,SAAS,EAAE,IAAI,CAAC,EAAE;YAClB,UAAU,EAAE,YAAY;YACxB,QAAQ,EAAE,cAAc;YACxB,SAAS,EAAE,GAAG;YACd,SAAS,EAAE,GAAG;SACf,CAAC,CAAC;QAEH,6BAA6B;QAC7B,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;QACrC,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,qBAAqB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAErF,MAAM,aAAa,CAAC,EAAE,EAAE;YACtB,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;YACvB,MAAM,EAAE,IAAI,CAAC,EAAE;YACf,KAAK;YACL,SAAS;YACT,SAAS,EAAE,OAAO,CAAC,EAAE;YACrB,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,IAAI;YAChD,SAAS,EAAE,GAAG;YACd,SAAS,EAAE,GAAG;SACf,CAAC,CAAC;QAEH,aAAa;QACb,KAAK,CAAC,SAAS,CAAC,iBAAiB,EAAE,KAAK,EAAE;YACxC,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;YAC7C,QAAQ,EAAE,KAAK;YACf,IAAI,EAAE,GAAG;YACT,OAAO,EAAE,SAAS;SACnB,CAAC,CAAC;QAEH,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAC5B,OAAO,EAAE,IAAI;YACb,IAAI,EAAE;gBACJ,EAAE,EAAE,IAAI,CAAC,EAAE;gBACX,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,IAAI,CAAC,IAAI;aAChB;YACD,OAAO,EAAE;gBACP,KAAK;gBACL,SAAS,EAAE,SAAS,CAAC,WAAW,EAAE;aACnC;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Content routes.
3
+ * Handles content CRUD operations.
4
+ */
5
+ import type { FastifyPluginAsync } from 'fastify';
6
+ declare const contentRoutes: FastifyPluginAsync;
7
+ export default contentRoutes;
8
+ //# sourceMappingURL=content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/routes/content.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,OAAO,KAAK,EAAmB,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAUnE,QAAA,MAAM,aAAa,EAAE,kBAgXpB,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -0,0 +1,339 @@
1
+ /**
2
+ * Content routes.
3
+ * Handles content CRUD operations.
4
+ */
5
+ import { bulkUpdateContent, getContentByPath, getContentByPathPrefix, getFieldByPath, parseContentValue, publishContent, unpublishContent, upsertContent, } from '@reverso/db';
6
+ import { bulkContentUpdateSchema, contentUpdateSchema, localeQuerySchema, pathParamSchema, slugParamSchema, } from '../validation.js';
7
+ const contentRoutes = async (fastify) => {
8
+ /**
9
+ * GET /content/:path
10
+ * Get content by field path.
11
+ * Requires: viewer, editor, or admin role
12
+ */
13
+ fastify.get('/content/:path', {
14
+ preHandler: fastify.requireAuth(['viewer', 'editor', 'admin']),
15
+ }, async (request, reply) => {
16
+ try {
17
+ const paramResult = pathParamSchema.safeParse(request.params);
18
+ if (!paramResult.success) {
19
+ return reply.status(400).send({
20
+ success: false,
21
+ error: 'Validation error',
22
+ message: 'Invalid path format. Use dot notation (e.g., home.hero.title)',
23
+ });
24
+ }
25
+ const queryResult = localeQuerySchema.safeParse(request.query);
26
+ if (!queryResult.success) {
27
+ return reply.status(400).send({
28
+ success: false,
29
+ error: 'Validation error',
30
+ message: 'Invalid locale format',
31
+ });
32
+ }
33
+ const { path } = paramResult.data;
34
+ const { locale } = queryResult.data;
35
+ const db = request.db;
36
+ const content = await getContentByPath(db, path, locale);
37
+ if (!content) {
38
+ // Check if field exists
39
+ const field = await getFieldByPath(db, path);
40
+ if (!field) {
41
+ return reply.status(404).send({
42
+ success: false,
43
+ error: 'Not found',
44
+ message: `Field "${path}" not found`,
45
+ });
46
+ }
47
+ // Field exists but no content yet
48
+ return {
49
+ success: true,
50
+ data: {
51
+ path,
52
+ locale,
53
+ value: null,
54
+ published: false,
55
+ },
56
+ };
57
+ }
58
+ return {
59
+ success: true,
60
+ data: {
61
+ id: content.id,
62
+ path,
63
+ locale: content.locale,
64
+ value: parseContentValue(content),
65
+ published: content.published,
66
+ publishedAt: content.publishedAt,
67
+ updatedAt: content.updatedAt,
68
+ },
69
+ };
70
+ }
71
+ catch (error) {
72
+ fastify.log.error(error, 'Failed to get content');
73
+ return reply.status(500).send({
74
+ success: false,
75
+ error: 'Internal error',
76
+ message: 'Failed to get content',
77
+ });
78
+ }
79
+ });
80
+ /**
81
+ * PUT /content/:path
82
+ * Update content by field path.
83
+ * Requires: editor or admin role
84
+ */
85
+ fastify.put('/content/:path', {
86
+ preHandler: fastify.requireAuth(['editor', 'admin']),
87
+ }, async (request, reply) => {
88
+ try {
89
+ const paramResult = pathParamSchema.safeParse(request.params);
90
+ if (!paramResult.success) {
91
+ return reply.status(400).send({
92
+ success: false,
93
+ error: 'Validation error',
94
+ message: 'Invalid path format. Use dot notation (e.g., home.hero.title)',
95
+ });
96
+ }
97
+ const bodyResult = contentUpdateSchema.safeParse(request.body);
98
+ if (!bodyResult.success) {
99
+ return reply.status(400).send({
100
+ success: false,
101
+ error: 'Validation error',
102
+ message: bodyResult.error.issues[0]?.message ?? 'Invalid request body',
103
+ });
104
+ }
105
+ const { path } = paramResult.data;
106
+ const { value, locale, publish } = bodyResult.data;
107
+ const db = request.db;
108
+ const field = await getFieldByPath(db, path);
109
+ if (!field) {
110
+ return reply.status(404).send({
111
+ success: false,
112
+ error: 'Not found',
113
+ message: `Field "${path}" not found`,
114
+ });
115
+ }
116
+ // Get user ID from auth if available
117
+ const changedBy = request.user?.id;
118
+ const content = await upsertContent(db, {
119
+ fieldId: field.id,
120
+ locale,
121
+ value: value,
122
+ published: publish,
123
+ changedBy,
124
+ });
125
+ return {
126
+ success: true,
127
+ data: {
128
+ id: content.id,
129
+ path,
130
+ locale: content.locale,
131
+ value: parseContentValue(content),
132
+ published: content.published,
133
+ updatedAt: content.updatedAt,
134
+ },
135
+ };
136
+ }
137
+ catch (error) {
138
+ fastify.log.error(error, 'Failed to update content');
139
+ return reply.status(500).send({
140
+ success: false,
141
+ error: 'Internal error',
142
+ message: 'Failed to update content',
143
+ });
144
+ }
145
+ });
146
+ /**
147
+ * POST /content/bulk
148
+ * Bulk update content.
149
+ * Requires: editor or admin role
150
+ */
151
+ fastify.post('/content/bulk', {
152
+ preHandler: fastify.requireAuth(['editor', 'admin']),
153
+ }, async (request, reply) => {
154
+ try {
155
+ const bodyResult = bulkContentUpdateSchema.safeParse(request.body);
156
+ if (!bodyResult.success) {
157
+ return reply.status(400).send({
158
+ success: false,
159
+ error: 'Validation error',
160
+ message: bodyResult.error.issues[0]?.message ?? 'Invalid request body',
161
+ });
162
+ }
163
+ const { updates } = bodyResult.data;
164
+ const db = request.db;
165
+ const changedBy = request.user?.id;
166
+ const results = await bulkUpdateContent(db, updates.map((u) => ({
167
+ path: u.path,
168
+ value: u.value,
169
+ locale: u.locale,
170
+ changedBy,
171
+ })));
172
+ return {
173
+ success: true,
174
+ data: {
175
+ updated: results.length,
176
+ items: results.map((c) => ({
177
+ id: c.id,
178
+ locale: c.locale,
179
+ updatedAt: c.updatedAt,
180
+ })),
181
+ },
182
+ };
183
+ }
184
+ catch (error) {
185
+ fastify.log.error(error, 'Failed to bulk update content');
186
+ return reply.status(500).send({
187
+ success: false,
188
+ error: 'Internal error',
189
+ message: 'Failed to bulk update content',
190
+ });
191
+ }
192
+ });
193
+ /**
194
+ * GET /content/page/:slug
195
+ * Get all content for a page.
196
+ * Requires: viewer, editor, or admin role
197
+ */
198
+ fastify.get('/content/page/:slug', {
199
+ preHandler: fastify.requireAuth(['viewer', 'editor', 'admin']),
200
+ }, async (request, reply) => {
201
+ try {
202
+ const paramResult = slugParamSchema.safeParse(request.params);
203
+ if (!paramResult.success) {
204
+ return reply.status(400).send({
205
+ success: false,
206
+ error: 'Validation error',
207
+ message: 'Invalid slug format',
208
+ });
209
+ }
210
+ const queryResult = localeQuerySchema.safeParse(request.query);
211
+ if (!queryResult.success) {
212
+ return reply.status(400).send({
213
+ success: false,
214
+ error: 'Validation error',
215
+ message: 'Invalid locale format',
216
+ });
217
+ }
218
+ const { slug } = paramResult.data;
219
+ const { locale } = queryResult.data;
220
+ const db = request.db;
221
+ const results = await getContentByPathPrefix(db, `${slug}.`, locale);
222
+ const contentMap = {};
223
+ for (const { path, content } of results) {
224
+ contentMap[path] = parseContentValue(content);
225
+ }
226
+ return {
227
+ success: true,
228
+ data: {
229
+ page: slug,
230
+ locale,
231
+ content: contentMap,
232
+ },
233
+ };
234
+ }
235
+ catch (error) {
236
+ fastify.log.error(error, 'Failed to get page content');
237
+ return reply.status(500).send({
238
+ success: false,
239
+ error: 'Internal error',
240
+ message: 'Failed to get page content',
241
+ });
242
+ }
243
+ });
244
+ /**
245
+ * POST /content/:path/publish
246
+ * Publish content.
247
+ * Requires: editor or admin role
248
+ */
249
+ fastify.post('/content/:path/publish', {
250
+ preHandler: fastify.requireAuth(['editor', 'admin']),
251
+ }, async (request, reply) => {
252
+ try {
253
+ const paramResult = pathParamSchema.safeParse(request.params);
254
+ if (!paramResult.success) {
255
+ return reply.status(400).send({
256
+ success: false,
257
+ error: 'Validation error',
258
+ message: 'Invalid path format',
259
+ });
260
+ }
261
+ const { path } = paramResult.data;
262
+ const db = request.db;
263
+ const content = await getContentByPath(db, path);
264
+ if (!content) {
265
+ return reply.status(404).send({
266
+ success: false,
267
+ error: 'Not found',
268
+ message: `Content for "${path}" not found`,
269
+ });
270
+ }
271
+ const published = await publishContent(db, content.id);
272
+ return {
273
+ success: true,
274
+ data: {
275
+ id: published?.id,
276
+ path,
277
+ published: published?.published,
278
+ publishedAt: published?.publishedAt,
279
+ },
280
+ };
281
+ }
282
+ catch (error) {
283
+ fastify.log.error(error, 'Failed to publish content');
284
+ return reply.status(500).send({
285
+ success: false,
286
+ error: 'Internal error',
287
+ message: 'Failed to publish content',
288
+ });
289
+ }
290
+ });
291
+ /**
292
+ * POST /content/:path/unpublish
293
+ * Unpublish content.
294
+ * Requires: editor or admin role
295
+ */
296
+ fastify.post('/content/:path/unpublish', {
297
+ preHandler: fastify.requireAuth(['editor', 'admin']),
298
+ }, async (request, reply) => {
299
+ try {
300
+ const paramResult = pathParamSchema.safeParse(request.params);
301
+ if (!paramResult.success) {
302
+ return reply.status(400).send({
303
+ success: false,
304
+ error: 'Validation error',
305
+ message: 'Invalid path format',
306
+ });
307
+ }
308
+ const { path } = paramResult.data;
309
+ const db = request.db;
310
+ const content = await getContentByPath(db, path);
311
+ if (!content) {
312
+ return reply.status(404).send({
313
+ success: false,
314
+ error: 'Not found',
315
+ message: `Content for "${path}" not found`,
316
+ });
317
+ }
318
+ const unpublished = await unpublishContent(db, content.id);
319
+ return {
320
+ success: true,
321
+ data: {
322
+ id: unpublished?.id,
323
+ path,
324
+ published: unpublished?.published,
325
+ },
326
+ };
327
+ }
328
+ catch (error) {
329
+ fastify.log.error(error, 'Failed to unpublish content');
330
+ return reply.status(500).send({
331
+ success: false,
332
+ error: 'Internal error',
333
+ message: 'Failed to unpublish content',
334
+ });
335
+ }
336
+ });
337
+ };
338
+ export default contentRoutes;
339
+ //# sourceMappingURL=content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/routes/content.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Forms routes.
3
+ * Handles forms, form fields, and form submissions CRUD operations.
4
+ */
5
+ import type { FastifyPluginAsync } from 'fastify';
6
+ declare const formsRoutes: FastifyPluginAsync;
7
+ export default formsRoutes;
8
+ //# sourceMappingURL=forms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forms.d.ts","sourceRoot":"","sources":["../../src/routes/forms.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAiCH,OAAO,KAAK,EAAmB,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAiBnE,QAAA,MAAM,WAAW,EAAE,kBAihClB,CAAC;AAYF,eAAe,WAAW,CAAC"}