api: introduce routes and error handling middleware

Pedro Lucas Porcellis porcellis@eletrotupi.com 3 months ago 3bcf802a5ee2d0a9efe349a6250e92f62e9f27ee
Parents: a6b6444
5 file(s) changed
  • api/src/index.ts +17 -0
  • api/src/middleware/errorHandler.ts +29 -0
  • api/src/routes/mood.js +0 -509
  • api/src/routes/users.js +0 -219
  • api/src/routes/users.ts +8 -0
api/src/index.ts
@@ -0,0 +1,17 @@
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 +
api/src/middleware/errorHandler.ts
@@ -0,0 +1,29 @@
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 + }
@@ -1,509 +0,0 @@
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;
@@ -1,219 +0,0 @@
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;
api/src/routes/users.ts
@@ -0,0 +1,8 @@
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;