From f45b83e14c7a2e3bc49c61f15bcf79f163a7e815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ortiz?= Date: Mon, 16 Mar 2026 13:17:15 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20agregar=20soporte=20para=20JSON=20en=20?= =?UTF-8?q?la=20aplicaci=C3=B3n=20y=20mejorar=20la=20gesti=C3=B3n=20de=20c?= =?UTF-8?q?laves=20de=20Supabase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extract-all-jobs.js | 34 +++++++++++++++ src/app.js | 2 + .../v2/peruserver/trucky/top-km/index.js | 6 +-- .../v2/peruserver/trucky/top-km/monthly.js | 42 ++++++++++++++----- src/routes/v2/peruserver/trucky/webhook.js | 14 ++++--- 5 files changed, 77 insertions(+), 21 deletions(-) create mode 100644 extract-all-jobs.js diff --git a/extract-all-jobs.js b/extract-all-jobs.js new file mode 100644 index 0000000..b1b9224 --- /dev/null +++ b/extract-all-jobs.js @@ -0,0 +1,34 @@ +// Script para extraer todos los trabajos de jobs_webhooks sin filtros de fecha +// y mostrarlos por consola para depuración + + +require('dotenv').config(); +const fetch = require('node-fetch'); + +const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL || process.env.SUPABASE_URL || ''; +const SUPABASE_ANON_KEY = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || process.env.SUPABASE_ANON_KEY || process.env.SUPABASE_SERVICE_ROLE_KEY || ''; + +async function main() { + if (!SUPABASE_URL || !SUPABASE_ANON_KEY) { + console.error('Faltan variables de entorno SUPABASE_URL o SUPABASE_ANON_KEY'); + process.exit(1); + } + const url = `${SUPABASE_URL.replace(/\/+$/, '')}/rest/v1/jobs_webhooks?select=job_id,company_id,driver_id,driven_distance_km,status,created_at`; + const res = await fetch(url, { + headers: { + 'Content-Type': 'application/json', + apikey: SUPABASE_ANON_KEY, + Authorization: `Bearer ${SUPABASE_ANON_KEY}`, + }, + }); + const jobs = await res.json(); + console.log('Total trabajos encontrados:', jobs.length); + for (const job of jobs) { + console.log(job); + } +} + +main().catch(err => { + console.error('Error al obtener trabajos:', err); + process.exit(1); +}); diff --git a/src/app.js b/src/app.js index 0838b67..38fa3ea 100644 --- a/src/app.js +++ b/src/app.js @@ -11,6 +11,8 @@ const errorHandler = require(join(__dirname, 'utils', 'api', 'error-handler')); const app = express(); +app.use(express.json()); // <--- ¡Esto es obligatorio para JSON! + const port = process.env.PORT || 3000; global.__basedir = __dirname; diff --git a/src/routes/v2/peruserver/trucky/top-km/index.js b/src/routes/v2/peruserver/trucky/top-km/index.js index 329bf37..c16c316 100644 --- a/src/routes/v2/peruserver/trucky/top-km/index.js +++ b/src/routes/v2/peruserver/trucky/top-km/index.js @@ -10,11 +10,7 @@ const router = Router(); const PERUSERVER_COMPANIES_URL = 'https://peruserver.pe/wp-json/psv/v1/companies'; const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL || process.env.SUPABASE_URL || ''; -const SUPABASE_ANON_KEY = - process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || - process.env.SUPABASE_ANON_KEY || - process.env.SUPABASE_SERVICE_ROLE_KEY || - ''; +const SUPABASE_ANON_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || process.env.SUPABASE_ANON_KEY || ''; const DEFAULT_LIMIT = 50; const MAX_LIMIT = 200; const CACHE_TTL_CURRENT_MONTH_MS = 30 * 60 * 1000; // 30 minutos diff --git a/src/routes/v2/peruserver/trucky/top-km/monthly.js b/src/routes/v2/peruserver/trucky/top-km/monthly.js index fd4baae..d7c2c17 100644 --- a/src/routes/v2/peruserver/trucky/top-km/monthly.js +++ b/src/routes/v2/peruserver/trucky/top-km/monthly.js @@ -10,11 +10,7 @@ const router = Router(); const PERUSERVER_COMPANIES_URL = 'https://peruserver.pe/wp-json/psv/v1/companies'; const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL || process.env.SUPABASE_URL || ''; -const SUPABASE_ANON_KEY = - process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || - process.env.SUPABASE_ANON_KEY || - process.env.SUPABASE_SERVICE_ROLE_KEY || - ''; +const SUPABASE_ANON_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || process.env.SUPABASE_ANON_KEY || ''; const DEFAULT_LIMIT = 50; const MAX_LIMIT = 200; const CACHE_TTL_MS = 30 * 60 * 1000; @@ -548,24 +544,48 @@ router.get('/', async (req, res) => { // Obtener trabajos del mes desde jobs_webhooks const startDate = new Date(Date.UTC(year, month - 1, 1)); const endDate = new Date(Date.UTC(year, month, 1)); + console.log(`Obteniendo trabajos desde jobs_webhooks para ${year}-${month} (desde ${startDate.toISOString()} hasta ${endDate.toISOString()})`); const url = `${SUPABASE_URL}/rest/v1/jobs_webhooks?created_at=gte.${startDate.toISOString()}&created_at=lt.${endDate.toISOString()}&select=driver_id,driven_distance_km,driver_id,company_id,job_id,status,created_at`; const response = await fetch(url, { headers: { 'Content-Type': 'application/json', apikey: SUPABASE_ANON_KEY, + Authorization: `Bearer ${SUPABASE_ANON_KEY}`, }, }); const jobs = await response.json(); - // Agrupar por driver_id y sumar driven_distance_km + console.log(`Obtenidos ${Array.isArray(jobs) ? jobs.length : 0} trabajos desde jobs_webhooks para ${year}-${month}`); + // Agrupar por company_id y sumar driven_distance_km const ranking = {}; for (const job of jobs) { - if (!job.driver_id) continue; - if (!ranking[job.driver_id]) ranking[job.driver_id] = { driver_id: job.driver_id, total_km: 0, jobs: 0 }; - ranking[job.driver_id].total_km += Number(job.driven_distance_km) || 0; - ranking[job.driver_id].jobs++; + if (!job.company_id) continue; + if (!ranking[job.company_id]) ranking[job.company_id] = { company_id: job.company_id, total_km: 0, jobs: 0 }; + ranking[job.company_id].total_km += Number(job.driven_distance_km) || 0; + ranking[job.company_id].jobs++; + } + // Obtener datos de empresa para los company_id únicos + const companyIds = [...new Set(Object.values(ranking).map(r => r.company_id).filter(Boolean))]; + let companies = []; + if (companyIds.length > 0) { + const companiesUrl = `${SUPABASE_URL}/rest/v1/trucky_companies?company_id=in.(${companyIds.join(',')})&select=company_id,name,tag,members_count`; + const companiesRes = await fetch(companiesUrl, { + headers: { + 'Content-Type': 'application/json', + apikey: SUPABASE_ANON_KEY, + }, + }); + companies = await companiesRes.json(); } // Convertir a array y ordenar - const result = Object.values(ranking).sort((a, b) => b.total_km - a.total_km).slice(0, limit); + const result = Object.values(ranking).sort((a, b) => b.total_km - a.total_km).slice(0, limit).map(r => { + const company = companies.find(c => c.company_id === r.company_id) || {}; + return { + ...r, + company_name: company.name || null, + company_tag: company.tag || null, + company_members: company.members_count || null, + }; + }); return res.json({ ok: true, month, year, ranking: result }); } catch (error) { return res.status(500).json({ ok: false, error: error.message || 'Error interno', timestamp: Math.floor(Date.now() / 1000) }); diff --git a/src/routes/v2/peruserver/trucky/webhook.js b/src/routes/v2/peruserver/trucky/webhook.js index a6b2615..11c6c06 100644 --- a/src/routes/v2/peruserver/trucky/webhook.js +++ b/src/routes/v2/peruserver/trucky/webhook.js @@ -359,10 +359,10 @@ router.post("/", async (req, res) => { const jobData = { job_id: jobId, company_id: req.headers["x-trucky-company-id"], - driver_id: req.body.data.driver.id, - event_type: req.body.type, - profile_id: req.body.data.in_game_profile_id, - vehicle_id: req.body.data.vehicle_id, + driver_id: req.body.data.driver?.id || null, + event_type: req.body.event, + profile_id: req.body.data.in_game_profile_id || null, + vehicle_id: req.body.data.vehicle_in_game_id, vehicle_brand_id: req.body.data.vehicle_in_game_brand_id, market: req.body.data.market, status: req.body.data.status, @@ -392,7 +392,7 @@ router.post("/", async (req, res) => { deleted_by_user_id: req.body.data.deleted_by_user_id, points: req.body.data.points, total_damage: req.body.data.total_damage, - events: req.body.data.events, + events: req.body.data.events || [], raw: JSON.stringify(req.body), updated_at: new Date(), }; @@ -402,6 +402,7 @@ router.post("/", async (req, res) => { headers: { "Content-Type": "application/json", apikey: SUPABASE_SERVICE_ROLE_KEY, + Authorization: `Bearer ${SUPABASE_SERVICE_ROLE_KEY}`, }, }); const jobExists = await jobRes.json(); @@ -413,6 +414,7 @@ router.post("/", async (req, res) => { headers: { "Content-Type": "application/json", apikey: SUPABASE_SERVICE_ROLE_KEY, + Authorization: `Bearer ${SUPABASE_SERVICE_ROLE_KEY}`, }, body: JSON.stringify(jobData), }); @@ -429,11 +431,13 @@ router.post("/", async (req, res) => { headers: { "Content-Type": "application/json", apikey: SUPABASE_SERVICE_ROLE_KEY, + Authorization: `Bearer ${SUPABASE_SERVICE_ROLE_KEY}`, }, body: JSON.stringify(jobData), }); if (!insertRes.ok) { const error = await insertRes.text(); + console.error("Failed to insert job data:", error); return res.status(500).json({ error: "Failed to insert", details: error }); } return res.status(200).json({ ok: true, inserted: true });