import express from "express"; import { insert, select, update } from "../db/queryHelper/index.js"; const router = express.Router(); // 1. CREATE User router.post("/", async (req, res) => { try { // Parse and extract user data from request body const { id, // Optional: if provided, use this UUID; otherwise generate one name, phone_number, avatar_url, language, timezone, country_code = "+91", } = req.body; // Validate required fields if (!name || !phone_number) { return res.status(400).json({ error: "name and phone_number are required fields", }); } // Build user data object using JSON-based structure const userData = { name: name.trim(), phone_number: phone_number.trim(), avatar_url: avatar_url || null, language: language || null, timezone: timezone || null, country_code: country_code || "+91", }; // If id is provided, include it; otherwise let the database generate one if (id) { userData.id = id; } // Use queryHelper insert with JSON-based approach const user = await insert({ table: 'users', data: userData, returning: '*', }); res.status(201).json(user); } catch (err) { console.error("Error creating user:", err); if (err.code === "23505") { // Unique constraint violation return res.status(400).json({ error: err.detail || "A user with this phone number or ID already exists", }); } if (err.code === "23503") { // Foreign key violation return res.status(400).json({ error: err.detail || "Foreign key constraint violation", }); } res.status(500).json({ error: err.message || "Internal server error" }); } }); // 2. GET All Users router.get("/", async (req, res) => { try { // Parse and validate query parameters const limit = Math.min(parseInt(req.query.limit) || 100, 100); const offset = parseInt(req.query.offset) || 0; const { is_active, phone_number, name } = req.query; // Build where conditions from query parameters const where = { deleted: false }; if (is_active !== undefined) { where.is_active = is_active === 'true' || is_active === true; } if (phone_number) { where.phone_number = phone_number; } if (name) { where.name = { op: 'ilike', value: `%${name}%` }; } const users = await select({ table: 'users', columns: ['id', 'name', 'phone_number', 'avatar_url', 'language', 'timezone', 'country_code', 'is_active', 'created_at', 'updated_at'], where, orderBy: { column: 'created_at', direction: 'desc', }, limit, offset, }); res.json(users); } catch (err) { console.error("Error fetching users:", err); res.status(500).json({ error: "Internal server error" }); } }); // 3. GET Single User router.get("/:id", async (req, res) => { try { const { id } = req.params; // Use queryHelper select with JSON-based where conditions const user = await select({ table: 'users', columns: ['id', 'name', 'phone_number', 'avatar_url', 'language', 'timezone', 'country_code', 'is_active', 'created_at', 'updated_at'], where: { id, deleted: false, }, limit: 1, }); if (user.length === 0) { return res.status(404).json({ error: "User not found" }); } res.json(user[0]); } catch (err) { console.error("Error fetching user:", err); res.status(500).json({ error: "Internal server error" }); } }); // 4. UPDATE User router.put("/:id", async (req, res) => { try { const { id } = req.params; // Parse and extract update data from request body const { name, phone_number, avatar_url, language, timezone, country_code, is_active } = req.body; // Build update data object using JSON-based structure (only include fields that are provided) const updateData = {}; if (name !== undefined) updateData.name = name.trim(); if (phone_number !== undefined) updateData.phone_number = phone_number.trim(); if (avatar_url !== undefined) updateData.avatar_url = avatar_url || null; if (language !== undefined) updateData.language = language || null; if (timezone !== undefined) updateData.timezone = timezone || null; if (country_code !== undefined) updateData.country_code = country_code; if (is_active !== undefined) updateData.is_active = is_active === true || is_active === 'true'; // Validate that at least one field is being updated if (Object.keys(updateData).length === 0) { return res.status(400).json({ error: "At least one field must be provided for update" }); } // Use queryHelper update with JSON-based where conditions const updated = await update({ table: 'users', data: updateData, where: { id, deleted: false, }, returning: '*', }); if (updated.length === 0) { return res.status(404).json({ error: "User not found" }); } res.json(updated[0]); } catch (err) { console.error("Error updating user:", err); if (err.code === "23505") { // Unique constraint violation return res.status(400).json({ error: err.detail || "A user with this phone number already exists", }); } res.status(500).json({ error: err.message || "Internal server error" }); } }); // 5. DELETE User (Soft Delete) router.delete("/:id", async (req, res) => { try { const { id } = req.params; // Use queryHelper update with JSON-based where conditions for soft delete const deleted = await update({ table: 'users', data: { deleted: true, }, where: { id, deleted: false, // Only delete if not already deleted }, returning: ['id'], }); if (deleted.length === 0) { return res.status(404).json({ error: "User not found or already deleted" }); } res.json({ message: "User deleted successfully", id: deleted[0].id }); } catch (err) { console.error("Error deleting user:", err); res.status(500).json({ error: "Internal server error" }); } }); export default router;