import express from 'express'; import pg from 'pg'; const { Pool } = pg; import cors from 'cors'; const app = express(); app.use(cors()); app.use(express.json()); const PORT = 7072; // PostgreSQL connection const pool = new Pool({ host: 'localhost', port: 5432, database: 'amos', user: 'postgres', password: 'quixzo…2026' }); // Health check app.get('/health', async (req, res) => { res.json({ status: 'ok', service: 'landvex-admin-api', port: PORT }); }); // ═══════════════════════════════════════════════════════════════ // QUIXZOOM ENDPOINTS // ═══════════════════════════════════════════════════════════════ // GET /api/v1/quixzoom/stats — dashboard stats app.get('/api/v1/quixzoom/stats', async (req, res) => { try { const missionsResult = await pool.query('SELECT COUNT(*) FROM quixzoom.missions'); const activeResult = await pool.query("SELECT COUNT(*) FROM quixzoom.missions WHERE status = 'active'"); const submissionsResult = await pool.query('SELECT COUNT(*) FROM quixzoom.submissions'); const contributorsResult = await pool.query('SELECT COUNT(DISTINCT user_id) FROM quixzoom.submissions'); res.json({ missions_total: parseInt(missionsResult.rows[0].count), missions_active: parseInt(activeResult.rows[0].count), submissions_total: parseInt(submissionsResult.rows[0].count), contributors_total: parseInt(contributorsResult.rows[0].count) }); } catch (e) { res.status(500).json({ error: e.message }); } }); // GET /api/v1/quixzoom/missions — list missions app.get('/api/v1/quixzoom/missions', async (req, res) => { try { const { status, limit = 50, offset = 0 } = req.query; let query = ` SELECT m.id, m.title, m.description, m.latitude as lat, m.longitude as lon, m.area_name as address, m.status, m.reward_credits as reward_sek, m.required_photos, m.created_at, m.expires_at, COUNT(s.id) as submission_count FROM quixzoom.missions m LEFT JOIN quixzoom.submissions s ON s.mission_id = m.id `; const params = []; if (status) { query += ' WHERE m.status = $1'; params.push(status); } query += ` GROUP BY m.id ORDER BY m.created_at DESC LIMIT $${params.length + 1} OFFSET $${params.length + 2}`; params.push(limit, offset); const result = await pool.query(query, params); res.json({ missions: result.rows, count: result.rows.length, total: parseInt((await pool.query('SELECT COUNT(*) FROM quixzoom.missions')).rows[0].count) }); } catch (e) { res.status(500).json({ error: e.message }); } }); // GET /api/v1/quixzoom/contributors — list contributors app.get('/api/v1/quixzoom/contributors', async (req, res) => { try { const result = await pool.query(` SELECT u.id, u.email, u.display_name as name, u.city, u.status, COUNT(DISTINCT s.id) as submission_count, COALESCE(SUM(s.reward_credits), 0) as total_earnings FROM quixzoom.users u LEFT JOIN quixzoom.submissions s ON s.user_id = u.id GROUP BY u.id ORDER BY total_earnings DESC LIMIT 50 `); res.json({ contributors: result.rows, count: result.rows.length }); } catch (e) { res.status(500).json({ error: e.message }); } }); // ═══════════════════════════════════════════════════════════════ // LEDGER ENDPOINTS // ═══════════════════════════════════════════════════════════════ // GET /api/v1/ledger/accounts — list accounts app.get('/api/v1/ledger/accounts', async (req, res) => { try { // Proxy to aamos-ledger const response = await fetch('http://localhost:3250/api/ledger/accounts'); const data = await response.json(); res.json(data); } catch (e) { res.status(500).json({ error: e.message }); } }); // GET /api/v1/ledger/trial-balance — trial balance app.get('/api/v1/ledger/trial-balance', async (req, res) => { try { const response = await fetch('http://localhost:3250/api/ledger/trial-balance'); const data = await response.json(); res.json(data); } catch (e) { res.status(500).json({ error: e.message }); } }); // ═══════════════════════════════════════════════════════════════ // SYSTEM ENDPOINTS // ═══════════════════════════════════════════════════════════════ // GET /api/v1/system/services — list running services app.get('/api/v1/system/services', async (req, res) => { try { const services = [ { name: 'aamos-ledger', port: 3250, status: 'running' }, { name: 'quixzoom-mission', port: 7060, status: 'running' }, { name: 'quixzoom-api', port: 8080, status: 'running' }, { name: 'landvex-api', port: 8081, status: 'running' }, { name: 'aamos-command-center', port: 7071, status: 'running' }, { name: 'nginx', port: 80, status: 'running' } ]; res.json({ services }); } catch (e) { res.status(500).json({ error: e.message }); } }); // Start server app.listen(PORT, () => { console.log(`LandveX Admin API running on port ${PORT}`); console.log(`Endpoints:`); console.log(` GET /api/v1/quixzoom/stats`); console.log(` GET /api/v1/quixzoom/missions`); console.log(` GET /api/v1/quixzoom/contributors`); console.log(` GET /api/v1/ledger/accounts`); console.log(` GET /api/v1/system/services`); });