diff --git a/server.js b/server.js index 371be70..1837365 100644 --- a/server.js +++ b/server.js @@ -38,13 +38,12 @@ const pool = new Pool({ }); // ========================================== -// 🛠️ RUTA DE INSTALACIÓN (Ejecutar una vez) +// 🛠️ RUTA DE INSTALACIÓN (ACTUALIZADA) // ========================================== -// Esta ruta crea las tablas si no existen. -// Úsala entrando a: https://tu-api.com/setup-db app.get("/setup-db", async (req, res) => { + const client = await pool.connect(); try { - const client = await pool.connect(); + // 1. TABLAS ORIGINALES await client.query(` CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, @@ -57,7 +56,6 @@ app.get("/setup-db", async (req, res) => { is_verified BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT NOW() ); - CREATE TABLE IF NOT EXISTS login_codes ( id SERIAL PRIMARY KEY, user_id INT REFERENCES users(id) ON DELETE CASCADE, @@ -68,7 +66,6 @@ app.get("/setup-db", async (req, res) => { expires_at TIMESTAMP NOT NULL, created_at TIMESTAMP DEFAULT NOW() ); - CREATE TABLE IF NOT EXISTS services ( id SERIAL PRIMARY KEY, user_id INT REFERENCES users(id) ON DELETE CASCADE, @@ -80,11 +77,35 @@ app.get("/setup-db", async (req, res) => { created_at TIMESTAMP DEFAULT NOW() ); `); - client.release(); - res.send("✅ ¡Base de datos configurada correctamente! Tablas creadas."); + + // 2. NUEVAS TABLAS (Gremios y Roles) + await client.query(` + CREATE TABLE IF NOT EXISTS guilds ( + id SERIAL PRIMARY KEY, + name TEXT UNIQUE NOT NULL, + created_at TIMESTAMP DEFAULT NOW() + ); + + CREATE TABLE IF NOT EXISTS user_guilds ( + user_id INT REFERENCES users(id) ON DELETE CASCADE, + guild_id INT REFERENCES guilds(id) ON DELETE CASCADE, + PRIMARY KEY (user_id, guild_id) + ); + `); + + // 3. ACTUALIZAR USUARIOS (Añadir columna role si no existe) + try { + await client.query(`ALTER TABLE users ADD COLUMN role TEXT DEFAULT 'operario'`); + } catch (e) { + // Ignoramos error si la columna ya existe + } + + res.send("✅ ¡Base de datos actualizada! Tablas y Roles listos."); } catch (e) { console.error(e); - res.status(500).send("❌ Error creando tablas: " + e.message); + res.status(500).send("❌ Error configurando DB: " + e.message); + } finally { + client.release(); } }); @@ -112,8 +133,9 @@ function genCode6() { } function signToken(user) { + // AÑADIDO: Incluimos el 'role' en el token return jwt.sign( - { sub: user.id, phone: user.phone, email: user.email }, + { sub: user.id, phone: user.phone, email: user.email, role: user.role || 'operario' }, JWT_SECRET, { expiresIn: "30d" } ); @@ -168,7 +190,7 @@ async function sendWhatsAppCode(phone, code) { } // ========================= -// RUTAS +// RUTAS ORIGINALES // ========================= // 1. REGISTRO @@ -237,7 +259,7 @@ app.post("/auth/verify", async (req, res) => { const p = normalizePhone(phone); const q = await pool.query(` - SELECT lc.*, u.id as uid, u.email + SELECT lc.*, u.id as uid, u.email, u.role FROM login_codes lc JOIN users u ON lc.user_id = u.id WHERE lc.phone=$1 AND lc.consumed_at IS NULL AND lc.expires_at > NOW() @@ -255,7 +277,8 @@ app.post("/auth/verify", async (req, res) => { await pool.query("UPDATE login_codes SET consumed_at=NOW() WHERE id=$1", [row.id]); await pool.query("UPDATE users SET is_verified=TRUE WHERE id=$1", [row.uid]); - const token = signToken({ id: row.uid, email: row.email, phone: p }); + // Incluimos role en el token + const token = signToken({ id: row.uid, email: row.email, phone: p, role: row.role }); res.json({ ok: true, token }); } catch (e) { console.error("Error Verify:", e); @@ -294,6 +317,78 @@ app.get("/services", authMiddleware, async (req, res) => { } }); +// ========================= +// NUEVAS RUTAS: CONFIGURACIÓN +// ========================= + +// 5. GESTIÓN DE GREMIOS +app.get("/guilds", authMiddleware, async (req, res) => { + try { + const q = await pool.query("SELECT * FROM guilds ORDER BY name ASC"); + res.json({ ok: true, guilds: q.rows }); + } catch (e) { res.status(500).json({ ok: false, error: e.message }); } +}); + +app.post("/guilds", authMiddleware, async (req, res) => { + try { + const { name } = req.body; + if (!name) return res.status(400).json({ ok: false, error: "Falta nombre" }); + const q = await pool.query("INSERT INTO guilds (name) VALUES ($1) RETURNING *", [name.toUpperCase()]); + res.json({ ok: true, guild: q.rows[0] }); + } catch (e) { res.status(500).json({ ok: false, error: "Error o gremio duplicado" }); } +}); + +app.delete("/guilds/:id", authMiddleware, async (req, res) => { + try { + await pool.query("DELETE FROM guilds WHERE id = $1", [req.params.id]); + res.json({ ok: true }); + } catch (e) { res.status(500).json({ ok: false, error: e.message }); } +}); + +// 6. CREAR USUARIO (ADMIN) +app.post("/admin/users", authMiddleware, async (req, res) => { + const client = await pool.connect(); + try { + const { fullName, email, password, role, guilds } = req.body; + + // Validación + if (!email || !password || !fullName) { + return res.status(400).json({ ok: false, error: "Faltan datos obligatorios" }); + } + + const passwordHash = await bcrypt.hash(password, 10); + // Generamos un teléfono dummy porque la base de datos lo exige (UNIQUE) + const dummyPhone = `manual_${Date.now()}`; + + await client.query('BEGIN'); + + // Insertar usuario + const insert = await client.query( + `INSERT INTO users (full_name, email, password_hash, role, phone, is_verified) + VALUES ($1, $2, $3, $4, $5, TRUE) RETURNING id`, + [fullName, email, passwordHash, role || 'operario', dummyPhone] + ); + const userId = insert.rows[0].id; + + // Asignar Gremios + if (guilds && Array.isArray(guilds)) { + for (const guildId of guilds) { + await client.query("INSERT INTO user_guilds (user_id, guild_id) VALUES ($1, $2)", [userId, guildId]); + } + } + + await client.query('COMMIT'); + res.json({ ok: true, msg: "Usuario creado correctamente" }); + + } catch (e) { + await client.query('ROLLBACK'); + console.error(e); + res.status(500).json({ ok: false, error: "Error creando usuario (Email duplicado?)" }); + } finally { + client.release(); + } +}); + // ARRANCAR const port = process.env.PORT || 3000; app.listen(port, "0.0.0.0", () => console.log(`🚀 Server OK en puerto ${port}`)); \ No newline at end of file