← back to Costa Rica

routes/build.js

61 lines

'use strict';
// Build / ops dashboard API — behind the basic-auth gate. One aggregate status
// endpoint for the whole build: integration live-state, data pipeline, go-live
// checklist, recent ingest runs + commits.
const express = require('express');
const { execSync } = require('child_process');
const path = require('path');
const { pool } = require('../lib/db');
const router = express.Router();

const liveState = () => ({
  payment: { provider: require('../lib/payments').getProvider().name, live: require('../lib/payments').getProvider().liveMode },
  whatsapp: { live: require('../lib/whatsapp').liveMode },
  plaid: { live: require('../lib/plaid').liveMode, env: require('../lib/plaid').ENV },
  apple: { configured: true }, // verifier always available; needs App ID capability at build
});

router.get('/status', async (_req, res) => {
  const data = {};
  try {
    const { rows: [d] } = await pool.query(`
      SELECT (SELECT count(*) FROM places) places,
             (SELECT count(*) FROM places WHERE lat IS NOT NULL) geo,
             (SELECT count(*) FROM places WHERE hacienda_enriched_at IS NOT NULL) hacienda,
             (SELECT count(*) FROM places WHERE cedula_juridica IS NOT NULL AND hacienda_enriched_at IS NULL) hacienda_pending,
             (SELECT count(*) FROM places WHERE hacienda_situacion='Inscrito') active_biz,
             (SELECT count(*) FROM place_booking WHERE is_active) bookable,
             (SELECT count(*) FROM bookings) bookings,
             (SELECT COALESCE(SUM(platform_fee),0) FROM bookings WHERE status IN ('confirmed','completed')) revenue_minor,
             (SELECT count(*) FROM app_users) users,
             (SELECT count(*) FROM hosts) hosts`);
    Object.assign(data, d);
  } catch (e) { data.error = e.message; }

  let bySource = [], runs = [], commits = [];
  try { ({ rows: bySource } = await pool.query(`SELECT source, count(*) FROM places GROUP BY source ORDER BY 2 DESC LIMIT 12`)); } catch {}
  try { ({ rows: runs } = await pool.query(`SELECT source, status, rows_in, rows_added, started_at, finished_at FROM ingest_runs ORDER BY id DESC LIMIT 8`)); } catch {}
  try {
    commits = execSync('git log --oneline -8 --format="%h|%s|%cr"', { cwd: path.join(__dirname, '..') })
      .toString().trim().split('\n').map(l => { const [h, s, w] = l.split('|'); return { hash: h, subject: s, when: w }; });
  } catch {}

  const checklist = [
    { id: 'schema', label: 'Marketplace schema (migration 004/005)', done: true },
    { id: 'payments', label: 'Payment layer (Tilopay/ONVO, sandbox)', done: true },
    { id: 'whatsapp', label: 'WhatsApp client + webhook (sandbox)', done: true },
    { id: 'apple', label: 'Sign in with Apple (backend + app)', done: true },
    { id: 'app', label: 'Expo native app scaffold', done: true },
    { id: 'admin', label: 'Admin dashboard', done: true },
    { id: 'gate_wa', label: '🔴 GATED: Meta WABA number + token (Steve)', done: false },
    { id: 'gate_tilopay', label: '🔴 GATED: Tilopay merchant keys (Steve)', done: false },
    { id: 'gate_plaid', label: '🔴 GATED: Plaid production keys (Steve)', done: false },
    { id: 'gate_eas', label: '🔴 GATED: eas login → build/submit (Steve)', done: false },
    { id: 'gate_deploy', label: '🔴 GATED: Kamatera deploy + DNS (Steve)', done: false },
  ];

  res.json({ ok: true, live: liveState(), data, bySource, runs, commits, checklist, ts: new Date().toISOString() });
});

module.exports = router;