Actualizar server.js
This commit is contained in:
69
server.js
69
server.js
@@ -263,6 +263,13 @@ async function autoUpdateDB() {
|
|||||||
|
|
||||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='scraped_services' AND column_name='automation_status') THEN ALTER TABLE scraped_services ADD COLUMN automation_status TEXT DEFAULT 'manual'; END IF;
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='scraped_services' AND column_name='automation_status') THEN ALTER TABLE scraped_services ADD COLUMN automation_status TEXT DEFAULT 'manual'; END IF;
|
||||||
|
|
||||||
|
-- AÑADIDO: Token mágico para el Portal del Cliente
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='clients' AND column_name='portal_token') THEN
|
||||||
|
ALTER TABLE clients ADD COLUMN portal_token TEXT UNIQUE;
|
||||||
|
UPDATE clients SET portal_token = substr(md5(random()::text || id::text), 1, 12) WHERE portal_token IS NULL;
|
||||||
|
ALTER TABLE clients ALTER COLUMN portal_token SET DEFAULT substr(md5(random()::text || clock_timestamp()::text), 1, 12);
|
||||||
|
END IF;
|
||||||
|
|
||||||
BEGIN ALTER TABLE users DROP CONSTRAINT IF EXISTS users_phone_key; EXCEPTION WHEN OTHERS THEN NULL; END;
|
BEGIN ALTER TABLE users DROP CONSTRAINT IF EXISTS users_phone_key; EXCEPTION WHEN OTHERS THEN NULL; END;
|
||||||
BEGIN ALTER TABLE users DROP CONSTRAINT IF EXISTS users_email_key; EXCEPTION WHEN OTHERS THEN NULL; END;
|
BEGIN ALTER TABLE users DROP CONSTRAINT IF EXISTS users_email_key; EXCEPTION WHEN OTHERS THEN NULL; END;
|
||||||
END $$;
|
END $$;
|
||||||
@@ -457,6 +464,68 @@ app.post("/public/assignment/respond", async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 🌐 RUTAS PÚBLICAS: PORTAL DEL CLIENTE (SIN FRICCIÓN)
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
app.get("/public/portal/:token", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { token } = req.params;
|
||||||
|
const clientQ = await pool.query(`
|
||||||
|
SELECT c.id, c.full_name, c.phone, c.addresses, c.owner_id,
|
||||||
|
u.company_slug, u.full_name as company_name
|
||||||
|
FROM clients c
|
||||||
|
JOIN users u ON c.owner_id = u.id
|
||||||
|
WHERE c.portal_token = $1
|
||||||
|
`, [token]);
|
||||||
|
|
||||||
|
if (clientQ.rowCount === 0) return res.status(404).json({ ok: false, error: "Enlace no válido o caducado" });
|
||||||
|
const clientData = clientQ.rows[0];
|
||||||
|
|
||||||
|
const servicesQ = await pool.query(`
|
||||||
|
SELECT s.id, s.title, s.description, s.scheduled_date, s.scheduled_time, s.created_at,
|
||||||
|
st.name as status_name, st.color as status_color, st.is_final,
|
||||||
|
u.full_name as assigned_worker
|
||||||
|
FROM services s
|
||||||
|
LEFT JOIN service_statuses st ON s.status_id = st.id
|
||||||
|
LEFT JOIN users u ON s.assigned_to = u.id
|
||||||
|
WHERE s.client_id = $1
|
||||||
|
ORDER BY s.created_at DESC
|
||||||
|
`, [clientData.id]);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
ok: true,
|
||||||
|
client: { name: clientData.full_name, phone: clientData.phone, addresses: clientData.addresses },
|
||||||
|
company: { name: clientData.company_name, slug: clientData.company_slug },
|
||||||
|
services: servicesQ.rows
|
||||||
|
});
|
||||||
|
} catch (e) { res.status(500).json({ ok: false, error: "Error de servidor" }); }
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/public/portal/:token/request", async (req, res) => {
|
||||||
|
const client = await pool.connect();
|
||||||
|
try {
|
||||||
|
const { token } = req.params;
|
||||||
|
const { description, address } = req.body;
|
||||||
|
await client.query('BEGIN');
|
||||||
|
const clientQ = await client.query("SELECT id, owner_id, full_name, phone FROM clients WHERE portal_token = $1", [token]);
|
||||||
|
if (clientQ.rowCount === 0) throw new Error("Token inválido");
|
||||||
|
const cData = clientQ.rows[0];
|
||||||
|
const statusQ = await client.query("SELECT id FROM service_statuses WHERE owner_id=$1 AND is_default=TRUE LIMIT 1", [cData.owner_id]);
|
||||||
|
const statusId = statusQ.rows[0]?.id;
|
||||||
|
const insertSvc = await client.query(`
|
||||||
|
INSERT INTO services (owner_id, client_id, status_id, contact_name, contact_phone, address, description, title, import_source)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 'PORTAL_CLIENTE') RETURNING id
|
||||||
|
`, [cData.owner_id, cData.id, statusId, cData.full_name, cData.phone, address, description, "Nuevo Aviso desde App Cliente"]);
|
||||||
|
await client.query("INSERT INTO service_logs (service_id, new_status_id, comment) VALUES ($1, $2, 'Aviso reportado por el cliente desde su portal')", [insertSvc.rows[0].id, statusId]);
|
||||||
|
await client.query('COMMIT');
|
||||||
|
res.json({ ok: true, message: "Aviso recibido", service_id: insertSvc.rows[0].id });
|
||||||
|
} catch (e) {
|
||||||
|
await client.query('ROLLBACK');
|
||||||
|
res.status(500).json({ ok: false, error: e.message });
|
||||||
|
} finally { client.release(); }
|
||||||
|
});
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// 🔐 RUTAS AUTH Y PRIVADAS ( CRM ORIGINAL )
|
// 🔐 RUTAS AUTH Y PRIVADAS ( CRM ORIGINAL )
|
||||||
// ==========================================
|
// ==========================================
|
||||||
|
|||||||
Reference in New Issue
Block a user