import { Router, type Router as ExpressRouter } from 'express'; import { supabaseAdmin } from '../lib/supabase.js'; import { requireAdmin } from '../middleware/requireAdmin.js'; import type { UserRole } from '@naturcalabacera/shared'; const router: ExpressRouter = Router(); const VALID_ROLES: UserRole[] = ['admin', 'internal_staff', 'external_availability_viewer']; // GET /api/users — listar todos los perfiles router.get('/', requireAdmin, async (_req, res) => { const { data, error } = await supabaseAdmin .from('user_profiles') .select('id, email, role, display_name, created_at') .order('created_at', { ascending: false }); if (error) { return res.status(500).json({ error: error.message }); } return res.json({ users: data ?? [] }); }); // POST /api/users/invite — invitar un usuario nuevo router.post('/invite', requireAdmin, async (req, res) => { const { email, role, display_name } = req.body as { email?: string; role?: UserRole; display_name?: string; }; if (!email || !role) { return res.status(400).json({ error: 'email y role son requeridos' }); } if (!VALID_ROLES.includes(role)) { return res.status(400).json({ error: 'Rol inválido' }); } // 1. Invitar al usuario por email (envía correo con magic link) const { data: invited, error: inviteError } = await supabaseAdmin.auth.admin.inviteUserByEmail(email); if (inviteError || !invited.user) { return res.status(400).json({ error: inviteError?.message ?? 'Error al invitar usuario' }); } // 2. Crear el perfil con el rol seleccionado const { data: profile, error: profileError } = await supabaseAdmin .from('user_profiles') .insert({ id: invited.user.id, email, role, display_name: display_name ?? null, }) .select() .single(); if (profileError) { // Best-effort cleanup: eliminar el usuario auth si el perfil falló await supabaseAdmin.auth.admin.deleteUser(invited.user.id).catch(() => {}); return res.status(500).json({ error: profileError.message }); } return res.status(201).json({ user: profile }); }); // PATCH /api/users/:id — actualizar rol o display_name router.patch('/:id', requireAdmin, async (req, res) => { const { id } = req.params; const { role, display_name } = req.body as { role?: UserRole; display_name?: string; }; if (role !== undefined && !VALID_ROLES.includes(role)) { return res.status(400).json({ error: 'Rol inválido' }); } const updates: Record = {}; if (role !== undefined) updates.role = role; if (display_name !== undefined) updates.display_name = display_name; if (Object.keys(updates).length === 0) { return res.status(400).json({ error: 'Nada que actualizar' }); } const { data, error } = await supabaseAdmin .from('user_profiles') .update(updates) .eq('id', id) .select() .single(); if (error) { return res.status(500).json({ error: error.message }); } return res.json({ user: data }); }); // DELETE /api/users/:id — eliminar usuario auth + perfil router.delete('/:id', requireAdmin, async (req, res) => { const id = req.params.id as string; const callerId = res.locals.callerId as string; if (id === callerId) { return res.status(400).json({ error: 'No puedes eliminar tu propia cuenta' }); } // ON DELETE CASCADE en user_profiles.id elimina el perfil al eliminar el usuario auth. const { error } = await supabaseAdmin.auth.admin.deleteUser(id); if (error) { return res.status(500).json({ error: error.message }); } return res.json({ ok: true }); }); export { router as usersRouter };