feat: complete deployment codebase for BackOne DPI dashboard with dynamic telemetry, rebranding, database size tracking, and modular design

This commit is contained in:
Rafif-Riqullah-Siregar committed 2026-07-10 11:52:58 +07:00
1 parent 6cf3c6c7e5
commit c316f3171b
226 files changed
+21152 -8397

No files matched your search

+211
View File
@@ -0,0 +1,211 @@
// proxy/index.js
// ─────────────────────────────────────────────────────────────────────────────
// BackOne Proxy Server - Entry Point
//
// Responsibilities:
// 1. Connect to MongoDB (dengan retry otomatis)
// 2. Start DPI data collection scheduler (setiap 5 menit)
// 3. Expose REST API untuk manual trigger dan health check
// 4. Lakukan immediate collect saat startup (tidak perlu tunggu cron pertama)
//
// Environment Variables:
// MONGODB_URI - MongoDB connection string (default: localhost:27017)
// PROXY_COLLECT_MODE - 'all' (default) | 'agent'
// PROXY_AGENT_UUID - Required if PROXY_COLLECT_MODE=agent
// PROXY_CRON_SCHEDULE - Default: '*/5 * * * *' (setiap 5 menit)
// PROXY_PORT - Port untuk REST API (default: 4000)
// ─────────────────────────────────────────────────────────────────────────────
const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '..', '.env.local') });
const express = require('express');
const cors = require('cors');
const mongoose = require('mongoose');
const scheduler = require('./scheduler');
const PORT = parseInt(process.env.PROXY_PORT || '4000');
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017/backone_dpi';
// ─── Connect to MongoDB (retry-based, tidak exit prematurely) ──────────────────
async function connectDB() {
const MAX_RETRIES = 10;
const RETRY_DELAYS = [2000, 3000, 5000, 5000, 10000, 10000, 10000, 15000, 15000, 30000];
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
try {
console.log(`[MongoDB] Connection attempt ${attempt}/${MAX_RETRIES} → ${MONGODB_URI}`);
await mongoose.connect(MONGODB_URI, {
serverSelectionTimeoutMS: 8000,
connectTimeoutMS: 8000,
socketTimeoutMS: 30000,
});
console.log('[MongoDB] ✓ Connected successfully to', MONGODB_URI);
const { logCapacityStats } = require('../backend/db/capacityTracker');
await logCapacityStats('[MongoDB]');
return true;
} catch (err) {
console.error(`[MongoDB] ✗ Attempt ${attempt} failed: ${err.message}`);
if (attempt < MAX_RETRIES) {
const delay = RETRY_DELAYS[attempt - 1] || 15000;
console.log(`[MongoDB] Retrying in ${delay / 1000}s...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
console.error('[MongoDB] All connection attempts failed. Check if MongoDB is running on', MONGODB_URI);
console.error('[MongoDB] Proxy REST API will still run. Fix MongoDB and restart.');
return false;
}
// ─── REST API ──────────────────────────────────────────────────────────────────
const app = express();
app.use(express.json());
app.use(cors({ origin: '*' })); // Proxy hanya dikonsumsi oleh backend, open CORS ok
/**
* GET /health
* Liveness check
*/
app.get('/health', (req, res) => {
const dbState = mongoose.connection.readyState;
const dbLabel = ['disconnected', 'connected', 'connecting', 'disconnecting'][dbState] || 'unknown';
res.json({
ok: dbState === 1,
service: 'BackOne Proxy Server',
db: dbLabel,
mode: process.env.PROXY_COLLECT_MODE || 'all',
time: new Date().toISOString(),
});
});
/**
* GET /status
* Full scheduler + DB status
*/
app.get('/status', (req, res) => {
const dbState = mongoose.connection.readyState;
res.json({
ok: true,
db_state: ['disconnected', 'connected', 'connecting', 'disconnecting'][dbState] || 'unknown',
...scheduler.getStatus(),
time: new Date().toISOString(),
});
});
/**
* POST /collect/all
* Manual trigger — collect ALL agents sekarang
*/
app.post('/collect/all', async (req, res) => {
if (mongoose.connection.readyState !== 1) {
return res.status(503).json({ ok: false, error: 'MongoDB not connected. Cannot collect.' });
}
const { collectAllAgents } = require('./collector');
console.log('[Proxy API] Manual trigger: collect ALL agents');
try {
const result = await collectAllAgents();
res.json({ ok: result.success, ...result });
} catch (err) {
res.status(500).json({ ok: false, error: err.message });
}
});
/**
* POST /collect/:agentUuid
* Manual trigger — collect ONE specific agent
*/
app.post('/collect/:agentUuid', async (req, res) => {
if (mongoose.connection.readyState !== 1) {
return res.status(503).json({ ok: false, error: 'MongoDB not connected. Cannot collect.' });
}
const { agentUuid } = req.params;
const { collectSpecificAgent } = require('./collector');
console.log(`[Proxy API] Manual trigger: collect agent ${agentUuid}`);
try {
const result = await collectSpecificAgent(agentUuid);
res.json({ ok: result.success, ...result });
} catch (err) {
res.status(500).json({ ok: false, error: err.message });
}
});
/**
* GET /agents
* List all agent UUIDs yang sudah tersimpan di MongoDB
*/
app.get('/agents', async (req, res) => {
try {
if (mongoose.connection.readyState !== 1) {
return res.json({ ok: false, agents: [], count: 0, error: 'MongoDB not connected' });
}
const { Summary } = require('./models/Schemas');
const agents = await Summary.distinct('agent_uuid');
res.json({ ok: true, count: agents.length, agents });
} catch (err) {
res.status(500).json({ ok: false, error: err.message });
}
});
/**
* GET /latest
* Tampilkan data terbaru dari semua collection (untuk debug)
*/
app.get('/latest', async (req, res) => {
try {
const { Summary, AppStat, DeviceStat, Flow, Threat, Event, AppCategoryStat } = require('./models/Schemas');
const [summary, apps, devices, flows, threats, events, categories] = await Promise.all([
Summary.findOne().sort({ timestamp: -1 }).lean(),
AppStat.find().sort({ timestamp: -1 }).limit(5).lean(),
DeviceStat.find().sort({ timestamp: -1 }).limit(5).lean(),
Flow.find().sort({ timestamp: -1 }).limit(5).lean(),
Threat.find().sort({ timestamp: -1 }).limit(5).lean(),
Event.find().sort({ timestamp: -1 }).limit(5).lean(),
AppCategoryStat.find().sort({ timestamp: -1 }).limit(5).lean(),
]);
res.json({ ok: true, data: { summary, apps, devices, flows, threats, events, categories } });
} catch (err) {
res.status(500).json({ ok: false, error: err.message });
}
});
// ─── Boot Sequence ─────────────────────────────────────────────────────────────
async function main() {
console.log('\n╔════════════════════════════════════════════════╗');
console.log('║ BackOne Proxy Server - Starting ║');
console.log('╚════════════════════════════════════════════════╝\n');
console.log(`[Proxy] Mode: ${process.env.PROXY_COLLECT_MODE || 'all'}`);
if (process.env.PROXY_COLLECT_MODE === 'agent') {
console.log(`[Proxy] Agent UUID: ${process.env.PROXY_AGENT_UUID || '(not set!)'}`);
}
// 1. Start REST API server FIRST (so /health is always available)
app.listen(PORT, '0.0.0.0', () => {
console.log(`\n🚀 Proxy REST API running at http://0.0.0.0:${PORT}`);
console.log(` GET /health → liveness check`);
console.log(` GET /status → scheduler + DB status`);
console.log(` GET /agents → list agent UUIDs in MongoDB`);
console.log(` POST /collect/all → trigger manual collect all`);
console.log(` POST /collect/:agentUuid → trigger manual collect specific\n`);
});
// 2. Connect to MongoDB (retry in background)
const connected = await connectDB();
// 3. Start scheduler only if DB connected
if (connected) {
scheduler.startScheduler();
console.log('\n[Proxy] ✓ Scheduler started. Data collection is active.');
console.log('[Proxy] ✓ First collection will run in 2 seconds after MongoDB is ready.\n');
} else {
console.error('\n[Proxy] ✗ Could not connect to MongoDB. Scheduler NOT started.');
console.error('[Proxy] ✗ To fix: ensure MongoDB is running at', MONGODB_URI);
console.error('[Proxy] ✗ Then restart the proxy server.\n');
}
}
main().catch(err => {
console.error('[Proxy] Fatal startup error:', err);
// Don't exit — let REST API still serve /health
});