api: introduce routes and error handling middleware
Pedro Lucas Porcellis porcellis@eletrotupi.com · 7 months ago
3bcf802a5ee2d0a9efe349a6250e92f62e9f27ee Browse filesParents
a6b6444Changed files
5
Additions
+54
Deletions
-728
/dev/null → api/src/index.ts
1 +
import express from 'express';
2 +
import userRouter from '@app/routes/users'
3 +
import { errorHandler } from '@app/middleware/errorHandler';
4 +
5 +
const port = process.env.PORT || 3000;
6 +
7 +
const app = express();
8 +
app.use(express.json())
9 +
10 +
app.use("/users", userRouter);
11 +
12 +
app.use(errorHandler); // always last
13 +
14 +
app.listen(port, () => {
15 +
console.log(`Server booted on port ${port}`)
16 +
})
17 +
/dev/null → api/src/middleware/errorHandler.ts
1 +
import { Request, Response, NextFunction } from 'express';
2 +
import { Prisma } from '@prisma/client' // Bleh
3 +
import { isDomainError } from "@app/lib/errors/base";
4 +
5 +
export const errorHandler = (
6 +
err: unknown,
7 +
_req: Request,
8 +
res: Response,
9 +
_next: NextFunction
10 +
) => {
11 +
// TODO: Rig some improved, centralized error logging here
12 +
console.error(`[ErrorHandler]: ${err}`);
13 +
14 +
if (isDomainError(err)) {
15 +
return res.status(err.status)
16 +
.json({ error: err.message });
17 +
}
18 +
19 +
// Prisma errors as a fallback (or map these to domain errors in the repo)
20 +
if (err instanceof Prisma.PrismaClientKnownRequestError) {
21 +
if (err.code === "P2025") return res.status(404)
22 +
.json({ error: "Record not found" });
23 +
24 +
if (err.code === "P2002") return res.status(409)
25 +
.json({ error: "Unique constraint violation" });
26 +
}
27 +
28 +
res.status(500).json({ error: "Internal server error" });
29 +
}
api/src/routes/mood.js → /dev/null
1 -
import express from "express";
2 -
3 -
import {
4 -
validateMoodComponents,
5 -
isValidLevel,
6 -
isValidRating,
7 -
calculateEmotionStats
8 -
} from "../utils/emotions.js";
9 -
10 -
import {
11 -
validateRequiredFields,
12 -
isValidDate,
13 -
isNotFutureDate,
14 -
isValidDateRange,
15 -
sanitizeString,
16 -
validatePagination
17 -
} from "../utils/validators.js";
18 -
19 -
const router = express.Router();
20 -
21 -
router.get('/', (req, res) => {
22 -
23 -
});
24 -
25 -
// Create new mood entry with components
26 -
router.post("/", (req, res) => {
27 -
const {
28 -
user_id,
29 -
rating,
30 -
stress_level,
31 -
anxiety_level,
32 -
energy_level,
33 -
title,
34 -
description,
35 -
recorded_at,
36 -
mood_components
37 -
} = req.body;
38 -
39 -
// Validate required fields
40 -
const requiredValidation = validateRequiredFields(req.body, [
41 -
'user_id', 'stress_level', 'anxiety_level', 'energy_level', 'recorded_at'
42 -
]);
43 -
44 -
if (!requiredValidation.valid) {
45 -
return res.status(400).json({
46 -
error: "Campos faltantes",
47 -
missing: requiredValidation.missing
48 -
});
49 -
}
50 -
51 -
// Validate levels
52 -
if (!isValidLevel(stress_level)) {
53 -
return res.status(400).json({ error: "stress_level deve ser entre 1 e 10" });
54 -
}
55 -
if (!isValidLevel(anxiety_level)) {
56 -
return res.status(400).json({ error: "anxiety_level deve ser entre 1 e 10" });
57 -
}
58 -
if (!isValidLevel(energy_level)) {
59 -
return res.status(400).json({ error: "energy_level deve ser entre 1 e 10" });
60 -
}
61 -
62 -
// Validate optional rating
63 -
if (rating && !isValidRating(rating)) {
64 -
return res.status(400).json({ error: "rating deve ser entre 1 e 10" });
65 -
}
66 -
67 -
// Validate date
68 -
if (!isValidDate(recorded_at)) {
69 -
return res.status(400).json({ error: "recorded_at deve ser uma data válida" });
70 -
}
71 -
72 -
if (!isNotFutureDate(recorded_at)) {
73 -
return res.status(400).json({ error: "recorded_at não pode ser no futuro" });
74 -
}
75 -
76 -
// Validate mood components if provided
77 -
if (mood_components) {
78 -
const componentValidation = validateMoodComponents(mood_components);
79 -
if (!componentValidation.valid) {
80 -
return res.status(400).json({ error: componentValidation.error });
81 -
}
82 -
}
83 -
84 -
const db = req.db;
85 -
86 -
try {
87 -
// Start transaction
88 -
db.prepare("BEGIN").run();
89 -
90 -
// Insert mood entry
91 -
const moodStmt = db.prepare(`
92 -
INSERT INTO mood (
93 -
user_id, rating, stress_level, anxiety_level, energy_level,
94 -
title, description, recorded_at
95 -
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
96 -
`);
97 -
98 -
const moodResult = moodStmt.run(
99 -
user_id,
100 -
rating || null,
101 -
stress_level,
102 -
anxiety_level,
103 -
energy_level,
104 -
sanitizeString(title) || null,
105 -
sanitizeString(description) || null,
106 -
recorded_at
107 -
);
108 -
109 -
const moodId = moodResult.lastInsertRowid;
110 -
111 -
// Insert mood components if provided
112 -
if (mood_components && mood_components.length > 0) {
113 -
const componentStmt = db.prepare(`
114 -
INSERT INTO mood_components (mood_id, emotion, intensity)
115 -
VALUES (?, ?, ?)
116 -
`);
117 -
118 -
for (const component of mood_components) {
119 -
componentStmt.run(
120 -
moodId,
121 -
component.emotion.toLowerCase(),
122 -
component.intensity
123 -
);
124 -
}
125 -
}
126 -
127 -
// Commit transaction
128 -
db.prepare("COMMIT").run();
129 -
130 -
res.status(201).json({ id: moodId });
131 -
} catch (err) {
132 -
// Rollback on error
133 -
db.prepare("ROLLBACK").run();
134 -
console.error('Error creating mood:', err);
135 -
res.status(500).json({ error: "Erro ao criar registro de humor" });
136 -
}
137 -
});
138 -
139 -
// Get single mood entry with components
140 -
router.get("/:mood_id", (req, res) => {
141 -
const moodId = req.params.mood_id;
142 -
const db = req.db;
143 -
144 -
const moodStmt = db.prepare(`
145 -
SELECT m.*, u.first_name, u.last_name
146 -
FROM mood m
147 -
JOIN users u ON m.user_id = u.id
148 -
WHERE m.id = ?
149 -
`);
150 -
151 -
const mood = moodStmt.get(moodId);
152 -
153 -
if (!mood) {
154 -
return res.status(404).json({ error: "Humor não encontrado" });
155 -
}
156 -
157 -
// Get mood components
158 -
const componentsStmt = db.prepare(`
159 -
SELECT emotion, intensity
160 -
FROM mood_components
161 -
WHERE mood_id = ?
162 -
`);
163 -
164 -
const components = componentsStmt.all(moodId);
165 -
mood.mood_components = components;
166 -
167 -
// Calculate emotion stats
168 -
if (components.length > 0) {
169 -
mood.emotion_stats = calculateEmotionStats(components);
170 -
}
171 -
172 -
res.json(mood);
173 -
});
174 -
175 -
// Update mood entry
176 -
router.put("/:mood_id", (req, res) => {
177 -
const moodId = req.params.mood_id;
178 -
const {
179 -
rating,
180 -
stress_level,
181 -
anxiety_level,
182 -
energy_level,
183 -
title,
184 -
description,
185 -
recorded_at,
186 -
mood_components
187 -
} = req.body;
188 -
189 -
const db = req.db;
190 -
191 -
// Check if mood exists
192 -
const existingMood = db.prepare("SELECT id FROM mood WHERE id = ?").get(moodId);
193 -
if (!existingMood) {
194 -
return res.status(404).json({ error: "Humor não encontrado" });
195 -
}
196 -
197 -
// Build update query dynamically
198 -
const updates = [];
199 -
const values = [];
200 -
201 -
if (rating !== undefined) {
202 -
if (!isValidRating(rating)) {
203 -
return res.status(400).json({ error: "rating deve ser entre 1 e 10" });
204 -
}
205 -
updates.push("rating = ?");
206 -
values.push(rating);
207 -
}
208 -
209 -
if (stress_level !== undefined) {
210 -
if (!isValidLevel(stress_level)) {
211 -
return res.status(400).json({ error: "stress_level deve ser entre 1 e 10" });
212 -
}
213 -
updates.push("stress_level = ?");
214 -
values.push(stress_level);
215 -
}
216 -
217 -
if (anxiety_level !== undefined) {
218 -
if (!isValidLevel(anxiety_level)) {
219 -
return res.status(400).json({ error: "anxiety_level deve ser entre 1 e 10" });
220 -
}
221 -
updates.push("anxiety_level = ?");
222 -
values.push(anxiety_level);
223 -
}
224 -
225 -
if (energy_level !== undefined) {
226 -
if (!isValidLevel(energy_level)) {
227 -
return res.status(400).json({ error: "energy_level deve ser entre 1 e 10" });
228 -
}
229 -
updates.push("energy_level = ?");
230 -
values.push(energy_level);
231 -
}
232 -
233 -
if (title !== undefined) {
234 -
updates.push("title = ?");
235 -
values.push(sanitizeString(title) || null);
236 -
}
237 -
238 -
if (description !== undefined) {
239 -
updates.push("description = ?");
240 -
values.push(sanitizeString(description) || null);
241 -
}
242 -
243 -
if (recorded_at !== undefined) {
244 -
if (!isValidDate(recorded_at)) {
245 -
return res.status(400).json({ error: "recorded_at deve ser uma data válida" });
246 -
}
247 -
if (!isNotFutureDate(recorded_at)) {
248 -
return res.status(400).json({ error: "recorded_at não pode ser no futuro" });
249 -
}
250 -
updates.push("recorded_at = ?");
251 -
values.push(recorded_at);
252 -
}
253 -
254 -
updates.push("updated_at = CURRENT_TIMESTAMP");
255 -
256 -
try {
257 -
db.prepare("BEGIN").run();
258 -
259 -
// Update mood if there are changes
260 -
if (updates.length > 0) {
261 -
const updateQuery = `UPDATE mood SET ${updates.join(', ')} WHERE id = ?`;
262 -
values.push(moodId);
263 -
db.prepare(updateQuery).run(...values);
264 -
}
265 -
266 -
// Update mood components if provided
267 -
if (mood_components !== undefined) {
268 -
const componentValidation = validateMoodComponents(mood_components);
269 -
if (!componentValidation.valid) {
270 -
db.prepare("ROLLBACK").run();
271 -
return res.status(400).json({ error: componentValidation.error });
272 -
}
273 -
274 -
// Delete existing components
275 -
db.prepare("DELETE FROM mood_components WHERE mood_id = ?").run(moodId);
276 -
277 -
// Insert new components
278 -
if (mood_components.length > 0) {
279 -
const componentStmt = db.prepare(`
280 -
INSERT INTO mood_components (mood_id, emotion, intensity)
281 -
VALUES (?, ?, ?)
282 -
`);
283 -
284 -
for (const component of mood_components) {
285 -
componentStmt.run(
286 -
moodId,
287 -
component.emotion.toLowerCase(),
288 -
component.intensity
289 -
);
290 -
}
291 -
}
292 -
}
293 -
294 -
db.prepare("COMMIT").run();
295 -
res.json({ success: true, id: moodId });
296 -
} catch (err) {
297 -
db.prepare("ROLLBACK").run();
298 -
console.error('Error updating mood:', err);
299 -
res.status(500).json({ error: "Erro ao atualizar humor" });
300 -
}
301 -
});
302 -
303 -
// Delete mood entry
304 -
router.delete("/:mood_id", (req, res) => {
305 -
const moodId = req.params.mood_id;
306 -
const db = req.db;
307 -
308 -
const result = db.prepare("DELETE FROM mood WHERE id = ?").run(moodId);
309 -
310 -
if (result.changes === 0) {
311 -
return res.status(404).json({ error: "Humor não encontrado" });
312 -
}
313 -
314 -
res.json({ success: true, deleted: moodId });
315 -
});
316 -
317 -
// Get user's moods with optional date range
318 -
router.get("/user/:user_id", (req, res) => {
319 -
const userId = req.params.user_id;
320 -
const { start_date, end_date, page, limit } = req.query;
321 -
const db = req.db;
322 -
323 -
// Validate pagination
324 -
const { page: validPage, limit: validLimit, offset } = validatePagination(page, limit);
325 -
326 -
let query = `
327 -
SELECT m.*,
328 -
COUNT(mc.id) as component_count
329 -
FROM mood m
330 -
LEFT JOIN mood_components mc ON m.id = mc.mood_id
331 -
WHERE m.user_id = ?
332 -
`;
333 -
const params = [userId];
334 -
335 -
// Add date range filtering
336 -
if (start_date && end_date) {
337 -
if (!isValidDateRange(start_date, end_date)) {
338 -
return res.status(400).json({ error: "Intervalo de datas inválido" });
339 -
}
340 -
query += " AND m.recorded_at BETWEEN ? AND ?";
341 -
params.push(start_date, end_date);
342 -
} else if (start_date) {
343 -
if (!isValidDate(start_date)) {
344 -
return res.status(400).json({ error: "Data de início inválida" });
345 -
}
346 -
query += " AND m.recorded_at >= ?";
347 -
params.push(start_date);
348 -
} else if (end_date) {
349 -
if (!isValidDate(end_date)) {
350 -
return res.status(400).json({ error: "Data de fim inválida" });
351 -
}
352 -
query += " AND m.recorded_at <= ?";
353 -
params.push(end_date);
354 -
}
355 -
356 -
query += " GROUP BY m.id ORDER BY m.recorded_at DESC LIMIT ? OFFSET ?";
357 -
params.push(validLimit, offset);
358 -
359 -
const moods = db.prepare(query).all(...params);
360 -
361 -
// Get components for each mood
362 -
const moodIds = moods.map(m => m.id);
363 -
if (moodIds.length > 0) {
364 -
const componentsQuery = `
365 -
SELECT mood_id, emotion, intensity
366 -
FROM mood_components
367 -
WHERE mood_id IN (${moodIds.map(() => '?').join(',')})
368 -
`;
369 -
370 -
const components = db.prepare(componentsQuery).all(...moodIds);
371 -
372 -
// Group components by mood_id
373 -
const componentsByMood = {};
374 -
components.forEach(c => {
375 -
if (!componentsByMood[c.mood_id]) {
376 -
componentsByMood[c.mood_id] = [];
377 -
}
378 -
componentsByMood[c.mood_id].push({
379 -
emotion: c.emotion,
380 -
intensity: c.intensity
381 -
});
382 -
});
383 -
384 -
// Attach components to moods
385 -
moods.forEach(mood => {
386 -
mood.mood_components = componentsByMood[mood.id] || [];
387 -
if (mood.mood_components.length > 0) {
388 -
mood.emotion_stats = calculateEmotionStats(mood.mood_components);
389 -
}
390 -
});
391 -
}
392 -
393 -
// Get total count for pagination
394 -
let countQuery = "SELECT COUNT(*) as total FROM mood WHERE user_id = ?";
395 -
const countParams = [userId];
396 -
397 -
if (start_date && end_date) {
398 -
countQuery += " AND recorded_at BETWEEN ? AND ?";
399 -
countParams.push(start_date, end_date);
400 -
} else if (start_date) {
401 -
countQuery += " AND recorded_at >= ?";
402 -
countParams.push(start_date);
403 -
} else if (end_date) {
404 -
countQuery += " AND recorded_at <= ?";
405 -
countParams.push(end_date);
406 -
}
407 -
408 -
const { total } = db.prepare(countQuery).get(...countParams);
409 -
410 -
res.json({
411 -
moods,
412 -
pagination: {
413 -
page: validPage,
414 -
limit: validLimit,
415 -
total,
416 -
total_pages: Math.ceil(total / validLimit)
417 -
}
418 -
});
419 -
});
420 -
421 -
// Get mood statistics for a user
422 -
router.get("/user/:user_id/stats", (req, res) => {
423 -
const userId = req.params.user_id;
424 -
const { start_date, end_date } = req.query;
425 -
const db = req.db;
426 -
427 -
let baseQuery = "FROM mood WHERE user_id = ?";
428 -
const params = [userId];
429 -
430 -
if (start_date && end_date) {
431 -
if (!isValidDateRange(start_date, end_date)) {
432 -
return res.status(400).json({ error: "Intervalo de datas inválido" });
433 -
}
434 -
baseQuery += " AND recorded_at BETWEEN ? AND ?";
435 -
params.push(start_date, end_date);
436 -
}
437 -
438 -
// Get aggregated stats
439 -
const statsQuery = `
440 -
SELECT
441 -
COUNT(*) as total_entries,
442 -
AVG(rating) as avg_rating,
443 -
AVG(stress_level) as avg_stress,
444 -
AVG(anxiety_level) as avg_anxiety,
445 -
AVG(energy_level) as avg_energy,
446 -
MIN(stress_level) as min_stress,
447 -
MAX(stress_level) as max_stress,
448 -
MIN(anxiety_level) as min_anxiety,
449 -
MAX(anxiety_level) as max_anxiety,
450 -
MIN(energy_level) as min_energy,
451 -
MAX(energy_level) as max_energy
452 -
${baseQuery}
453 -
`;
454 -
455 -
const stats = db.prepare(statsQuery).get(...params);
456 -
457 -
// Get emotion frequency
458 -
const emotionQuery = `
459 -
SELECT
460 -
mc.emotion,
461 -
COUNT(*) as frequency,
462 -
AVG(mc.intensity) as avg_intensity
463 -
FROM mood_components mc
464 -
JOIN mood m ON mc.mood_id = m.id
465 -
WHERE m.user_id = ?
466 -
${start_date && end_date ? 'AND m.recorded_at BETWEEN ? AND ?' : ''}
467 -
GROUP BY mc.emotion
468 -
ORDER BY frequency DESC
469 -
`;
470 -
471 -
const emotionParams = [...params];
472 -
const emotions = db.prepare(emotionQuery).all(...emotionParams);
473 -
474 -
// Get mood trends by day of week
475 -
const dayTrendsQuery = `
476 -
SELECT
477 -
CASE CAST(strftime('%w', recorded_at) AS INTEGER)
478 -
WHEN 0 THEN 'Sunday'
479 -
WHEN 1 THEN 'Monday'
480 -
WHEN 2 THEN 'Tuesday'
481 -
WHEN 3 THEN 'Wednesday'
482 -
WHEN 4 THEN 'Thursday'
483 -
WHEN 5 THEN 'Friday'
484 -
WHEN 6 THEN 'Saturday'
485 -
END as day_of_week,
486 -
AVG(stress_level) as avg_stress,
487 -
AVG(anxiety_level) as avg_anxiety,
488 -
AVG(energy_level) as avg_energy,
489 -
COUNT(*) as entries
490 -
${baseQuery}
491 -
GROUP BY strftime('%w', recorded_at)
492 -
`;
493 -
494 -
const dayTrends = db.prepare(dayTrendsQuery).all(...params);
495 -
496 -
res.json({
497 -
overall: stats,
498 -
emotions: emotions,
499 -
trends: {
500 -
by_day_of_week: dayTrends
501 -
},
502 -
date_range: {
503 -
start: start_date || 'all time',
504 -
end: end_date || 'all time'
505 -
}
506 -
});
507 -
});
508 -
509 -
export default router;
api/src/routes/users.js → /dev/null
1 -
import express from "express";
2 -
import bcrypt from "bcryptjs";
3 -
4 -
import {
5 -
isValidEmail,
6 -
isValidPassword,
7 -
sanitizeString,
8 -
validateRequiredFields
9 -
} from "../utils/validators.js";
10 -
11 -
const router = express.Router();
12 -
13 -
// Create user
14 -
router.post("/", (req, res) => {
15 -
const { email, password, first_name, last_name, timezone } = req.body;
16 -
17 -
// Validate required fields
18 -
const requiredValidation = validateRequiredFields(req.body, [
19 -
'email', 'password', 'first_name', 'last_name'
20 -
]);
21 -
22 -
if (!requiredValidation.valid) {
23 -
return res.status(400).json({
24 -
error: "Campos faltantes",
25 -
missing: requiredValidation.missing
26 -
});
27 -
}
28 -
29 -
// Validate email format
30 -
if (!isValidEmail(email)) {
31 -
return res.status(400).json({ error: "Email inválido" });
32 -
}
33 -
34 -
// Validate password strength
35 -
if (!isValidPassword(password)) {
36 -
return res.status(400).json({ error: "Senha deve ter pelo menos 6 caracteres" });
37 -
}
38 -
39 -
const hashed = bcrypt.hashSync(password, 10);
40 -
const db = req.db;
41 -
42 -
try {
43 -
const stmt = db.prepare(`
44 -
INSERT INTO users (email, password, first_name, last_name, timezone)
45 -
VALUES (?, ?, ?, ?, ?)
46 -
`);
47 -
48 -
const result = stmt.run(
49 -
email.toLowerCase(),
50 -
hashed,
51 -
sanitizeString(first_name),
52 -
sanitizeString(last_name),
53 -
timezone || 'UTC'
54 -
);
55 -
56 -
res.status(201).json({ id: result.lastInsertRowid });
57 -
} catch (err) {
58 -
if (err.message.includes('UNIQUE')) {
59 -
res.status(400).json({ error: "Email já cadastrado" });
60 -
} else {
61 -
console.error('Error creating user:', err);
62 -
res.status(500).json({ error: "Erro ao criar usuário" });
63 -
}
64 -
}
65 -
});
66 -
67 -
// User login
68 -
router.post("/login", (req, res) => {
69 -
const { email, password } = req.body;
70 -
71 -
// Validate required fields
72 -
if (!email || !password) {
73 -
return res.status(400).json({ error: "Email e senha são obrigatórios" });
74 -
}
75 -
76 -
const db = req.db;
77 -
78 -
const user = db.prepare(`
79 -
SELECT id, email, password, first_name, last_name, timezone
80 -
FROM users
81 -
WHERE email = ?
82 -
`).get(email.toLowerCase());
83 -
84 -
if (!user) {
85 -
return res.status(401).json({ error: "Credenciais inválidas" });
86 -
}
87 -
88 -
const passwordMatch = bcrypt.compareSync(password, user.password);
89 -
90 -
if (!passwordMatch) {
91 -
return res.status(401).json({ error: "Credenciais inválidas" });
92 -
}
93 -
94 -
// Don't send password in response
95 -
delete user.password;
96 -
97 -
res.json({
98 -
success: true,
99 -
user
100 -
});
101 -
});
102 -
103 -
// Get user profile
104 -
router.get("/:user_id", (req, res) => {
105 -
const userId = req.params.user_id;
106 -
const db = req.db;
107 -
108 -
const user = db.prepare(`
109 -
SELECT id, email, first_name, last_name, timezone, created_at, updated_at
110 -
FROM users
111 -
WHERE id = ?
112 -
`).get(userId);
113 -
114 -
if (!user) {
115 -
return res.status(404).json({ error: "Usuário não encontrado" });
116 -
}
117 -
118 -
// Get user's mood count
119 -
const moodStats = db.prepare(`
120 -
SELECT COUNT(*) as total_moods
121 -
FROM mood
122 -
WHERE user_id = ?
123 -
`).get(userId);
124 -
125 -
user.total_moods = moodStats.total_moods;
126 -
127 -
res.json(user);
128 -
});
129 -
130 -
// Update user
131 -
router.put("/:user_id", (req, res) => {
132 -
const userId = req.params.user_id;
133 -
const { email, password, first_name, last_name, timezone } = req.body;
134 -
const db = req.db;
135 -
136 -
// Check if user exists
137 -
const existingUser = db.prepare("SELECT id FROM users WHERE id = ?").get(userId);
138 -
if (!existingUser) {
139 -
return res.status(404).json({ error: "Usuário não encontrado" });
140 -
}
141 -
142 -
const updates = [];
143 -
const values = [];
144 -
145 -
if (email !== undefined) {
146 -
if (!isValidEmail(email)) {
147 -
return res.status(400).json({ error: "Email inválido" });
148 -
}
149 -
150 -
// Check if email is already taken by another user
151 -
const emailCheck = db.prepare("SELECT id FROM users WHERE email = ? AND id != ?")
152 -
.get(email.toLowerCase(), userId);
153 -
154 -
if (emailCheck) {
155 -
return res.status(400).json({ error: "Email já está em uso" });
156 -
}
157 -
158 -
updates.push("email = ?");
159 -
values.push(email.toLowerCase());
160 -
}
161 -
162 -
if (password !== undefined) {
163 -
if (!isValidPassword(password)) {
164 -
return res.status(400).json({ error: "Senha deve ter pelo menos 6 caracteres" });
165 -
}
166 -
updates.push("password = ?");
167 -
values.push(bcrypt.hashSync(password, 10));
168 -
}
169 -
170 -
if (first_name !== undefined) {
171 -
updates.push("first_name = ?");
172 -
values.push(sanitizeString(first_name));
173 -
}
174 -
175 -
if (last_name !== undefined) {
176 -
updates.push("last_name = ?");
177 -
values.push(sanitizeString(last_name));
178 -
}
179 -
180 -
if (timezone !== undefined) {
181 -
updates.push("timezone = ?");
182 -
values.push(timezone);
183 -
}
184 -
185 -
if (updates.length === 0) {
186 -
return res.status(400).json({ error: "Nenhum campo para atualizar" });
187 -
}
188 -
189 -
updates.push("updated_at = CURRENT_TIMESTAMP");
190 -
191 -
try {
192 -
const updateQuery = `UPDATE users SET ${updates.join(', ')} WHERE id = ?`;
193 -
values.push(userId);
194 -
195 -
db.prepare(updateQuery).run(...values);
196 -
197 -
res.json({ success: true, id: userId });
198 -
} catch (err) {
199 -
console.error('Error updating user:', err);
200 -
res.status(500).json({ error: "Erro ao atualizar usuário" });
201 -
}
202 -
});
203 -
204 -
// Delete user
205 -
router.delete("/:user_id", (req, res) => {
206 -
const userId = req.params.user_id;
207 -
const db = req.db;
208 -
209 -
// This will cascade delete all moods and mood_components
210 -
const result = db.prepare("DELETE FROM users WHERE id = ?").run(userId);
211 -
212 -
if (result.changes === 0) {
213 -
return res.status(404).json({ error: "Usuário não encontrado" });
214 -
}
215 -
216 -
res.json({ success: true, deleted: userId });
217 -
});
218 -
219 -
export default router;
/dev/null → api/src/routes/users.ts
1 +
import { Router } from "express";
2 +
import { UsersController } from '@app/controllers/users'
3 +
4 +
const router = Router();
5 +
6 +
router.post('/', UsersController.create);
7 +
8 +
export default router;