feat(prod): production deployment fixes - env loader, mongoose module, CSS static files, security hardening, PDF export, new UI components

This commit is contained in:
Rafif-Riqullah-Siregar committed 2026-07-16 13:14:34 +07:00
1 parent a403f752f3
commit 46f33eb088
69 files changed
+7367 -744

No files matched your search

+71 -31
View File
@@ -1,50 +1,90 @@
// test/check_remote_env.js
// Fix 502: Write Nginx custom config to proxy directly to Next.js port 3000
'use strict';
const { Client } = require('ssh2');
const sshConfig = {
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH'
password: 'htEo7x6LsBQiEHHH',
readyTimeout: 60000
};
async function runSSHCommands(commands) {
function runSSHCommand(conn, cmd) {
return new Promise((resolve, reject) => {
const conn = new Client();
conn.on('ready', () => {
let combinedCmd = commands.join(' && echo "=== CMD_SEPARATOR ===" && ');
conn.exec(combinedCmd, (err, stream) => {
if (err) {
conn.end();
return reject(err);
}
let out = '';
stream.on('close', (code, signal) => {
conn.end();
resolve(out);
}).on('data', (data) => {
out += data;
}).stderr.on('data', (data) => {
out += data;
});
});
}).on('error', reject).connect(sshConfig);
conn.exec(cmd, (err, stream) => {
if (err) return reject(err);
let out = '';
stream.on('close', () => resolve(out))
.on('data', d => { out += d; process.stdout.write(d.toString()); })
.stderr.on('data', d => { out += d; process.stdout.write(d.toString()); });
});
});
}
async function main() {
const conn = new Client();
await new Promise((resolve, reject) => {
conn.on('ready', resolve).on('error', reject).connect(sshConfig);
});
console.log('[SSH] Connected!\n');
const CONF = '/home/adminbackend/conf/web/demoplace.my.id';
// The Nginx config includes nginx.conf_custom and nginx.ssl.conf_custom
// We override the location / block to proxy directly to Next.js port 3000
const httpCustom = [
'# BackOne DPI — Direct proxy to Next.js',
'location / {',
' proxy_pass http://127.0.0.1:3000;',
' proxy_http_version 1.1;',
' proxy_set_header Upgrade $http_upgrade;',
' proxy_set_header Connection "upgrade";',
' proxy_set_header Host $host;',
' proxy_set_header X-Real-IP $remote_addr;',
' proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;',
' proxy_set_header X-Forwarded-Proto $scheme;',
' proxy_read_timeout 86400;',
'}',
].join('\n');
const sslCustom = [
'# BackOne DPI — Direct SSL proxy to Next.js',
'location / {',
' proxy_pass http://127.0.0.1:3000;',
' proxy_http_version 1.1;',
' proxy_set_header Upgrade $http_upgrade;',
' proxy_set_header Connection "upgrade";',
' proxy_set_header Host $host;',
' proxy_set_header X-Real-IP $remote_addr;',
' proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;',
' proxy_set_header X-Forwarded-Proto https;',
' proxy_read_timeout 86400;',
'}',
].join('\n');
const commands = [
'echo "=== FLUSH LOGS ===" && . ~/.nvm/nvm.sh && nvm use 20 && npx pm2 flush',
'echo "=== HIT AGENTS ===" && curl -s https://demoplace.my.id/agents > /dev/null || echo "Hit failed"',
'echo "=== LATEST FRONTEND ERR LOGS ===" && cat /home/adminbackend/.pm2/logs/backone-frontend-error.log'
// Write HTTP custom config
`printf '%s\n' ${JSON.stringify(httpCustom)} > ${CONF}/nginx.conf_custom && echo "[OK] HTTP custom config written"`,
// Write SSL custom config
`printf '%s\n' ${JSON.stringify(sslCustom)} > ${CONF}/nginx.ssl.conf_custom && echo "[OK] SSL custom config written"`,
// Verify files
`echo "=== HTTP CUSTOM ===" && cat ${CONF}/nginx.conf_custom`,
`echo "=== SSL CUSTOM ===" && cat ${CONF}/nginx.ssl.conf_custom`,
// Reload Nginx via Hestia
`echo "=== RELOADING ===" && sudo /usr/local/hestia/bin/v-restart-web 2>&1 | tail -5 || nginx -s reload 2>&1 | tail -5 || echo "Reload attempt done"`,
// Test after reload
`sleep 6 && echo "=== DOMAIN TEST ===" && curl -sk -o /dev/null -w "HTTPS Status: %{http_code}" https://demoplace.my.id/login`,
];
try {
const output = await runSSHCommands(commands);
console.log(output);
} catch (err) {
console.error("SSH Commands failed:", err.message);
for (const cmd of commands) {
console.log(`\n$ ${cmd.substring(0, 100)}${cmd.length > 100 ? '...' : ''}`);
await runSSHCommand(conn, cmd);
}
conn.end();
console.log('\n\n✅ Selesai! Cek https://demoplace.my.id/login di browser.\n');
}
main();
main().catch(err => { console.error('[FATAL]', err.message); process.exit(1); });
-63
View File
@@ -1,63 +0,0 @@
// test/get_db_date_range.js
const mongoose = require('mongoose');
const MONGODB_URI = 'mongodb://backone_user:SusuKudaLiar@mongodb.prod.proit.id:27017/backone_dpi?authSource=backone_dpi';
async function main() {
try {
await mongoose.connect(MONGODB_URI);
console.log('✓ Connected to Production MongoDB');
const db = mongoose.connection.db;
const summariesCol = db.collection('summaries');
// Get list of agents
const agents = await summariesCol.distinct('agent_uuid', { agent_uuid: { $ne: null } });
console.log('Agents found in database:', agents);
console.log('\n--- DATE RANGE ANALYSIS PER AGENT ---');
for (const agentUuid of agents) {
// Find earliest and latest timestamp in summaries for this agent
const bounds = await summariesCol.aggregate([
{ $match: { agent_uuid: agentUuid } },
{
$group: {
_id: '$agent_uuid',
minDate: { $min: '$timestamp' },
maxDate: { $max: '$timestamp' },
count: { $sum: 1 }
}
}
]).toArray();
if (bounds.length > 0) {
const { minDate, maxDate, count } = bounds[0];
console.log(`Agent: ${agentUuid}`);
console.log(` Earliest Date: ${minDate ? new Date(minDate).toLocaleString('id-ID', { timeZone: 'Asia/Jakarta' }) : 'N/A'}`);
console.log(` Latest Date : ${maxDate ? new Date(maxDate).toLocaleString('id-ID', { timeZone: 'Asia/Jakarta' }) : 'N/A'}`);
console.log(` Total Summaries Documents: ${count}`);
} else {
console.log(`Agent: ${agentUuid} (No summary documents found)`);
}
}
// Also get overall collections bounds
console.log('\n--- OVERALL COLLECTION BOUNDS ---');
const colsToCheck = ['flows', 'appstats', 'threats', 'events'];
for (const colName of colsToCheck) {
const col = db.collection(colName);
const firstDoc = await col.find().sort({ timestamp: 1 }).limit(1).toArray();
const lastDoc = await col.find().sort({ timestamp: -1 }).limit(1).toArray();
console.log(`Collection: ${colName}`);
console.log(` Earliest Doc: ${firstDoc[0]?.timestamp ? new Date(firstDoc[0].timestamp).toLocaleString('id-ID', { timeZone: 'Asia/Jakarta' }) : 'N/A'}`);
console.log(` Latest Doc : ${lastDoc[0]?.timestamp ? new Date(lastDoc[0].timestamp).toLocaleString('id-ID', { timeZone: 'Asia/Jakarta' }) : 'N/A'}`);
}
await mongoose.disconnect();
} catch (err) {
console.error('Error:', err.message);
}
}
main();
+153
View File
@@ -0,0 +1,153 @@
// test/tunnel_migrate.js
const { Client } = require('ssh2');
const net = require('net');
const { MongoClient } = require('mongodb');
const sshConfig = {
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH',
readyTimeout: 60000
};
const LOCAL_PORT = 27018;
const REMOTE_MONGO_HOST = 'mongodb.prod.proit.id';
const REMOTE_MONGO_PORT = 27017;
const conn = new Client();
const server = net.createServer((sock) => {
conn.forwardOut(
'127.0.0.1',
sock.remotePort,
REMOTE_MONGO_HOST,
REMOTE_MONGO_PORT,
(err, stream) => {
if (err) {
sock.destroy();
return;
}
sock.pipe(stream);
stream.pipe(sock);
}
);
});
conn.on('ready', () => {
server.listen(LOCAL_PORT, '127.0.0.1', async () => {
console.log(`✓ SSH Tunnel established: 127.0.0.1:${LOCAL_PORT} -> ${REMOTE_MONGO_HOST}:${REMOTE_MONGO_PORT}`);
try {
await runMigration();
} catch (e) {
console.error("✗ Migration failed:", e);
} finally {
server.close();
conn.end();
console.log("Tunnel closed.");
}
});
}).on('error', (err) => {
console.error("SSH Connection error:", err.message);
}).connect(sshConfig);
async function runMigration() {
const LOCAL_URI = 'mongodb://127.0.0.1:27017';
const REMOTE_URI = `mongodb://backone_user:SusuKudaLiar@127.0.0.1:${LOCAL_PORT}/backone_dpi?authSource=backone_dpi`;
const DB_NAME = 'backone_dpi';
console.log("Connecting to local MongoDB...");
const localClient = new MongoClient(LOCAL_URI);
await localClient.connect();
const localDb = localClient.db(DB_NAME);
console.log("✓ Connected to local MongoDB.");
console.log("Connecting to production MongoDB via tunnel...");
const remoteClient = new MongoClient(REMOTE_URI, { serverSelectionTimeoutMS: 5000 });
await remoteClient.connect();
const remoteDb = remoteClient.db(DB_NAME);
console.log("✓ Connected to production MongoDB.\n");
// 1. Sync users
console.log("--- Syncing 'users' collection ---");
const localUsersColl = localDb.collection('users');
const remoteUsersColl = remoteDb.collection('users');
const localUsers = await localUsersColl.find({}).toArray();
console.log(`Found ${localUsers.length} users in local database.`);
let userUpsertCount = 0;
for (const user of localUsers) {
const { _id, ...userData } = user;
const res = await remoteUsersColl.updateOne(
{ username: user.username },
{ $set: userData },
{ upsert: true }
);
if (res.upsertedCount > 0 || res.modifiedCount > 0) {
userUpsertCount++;
}
}
console.log(`✓ Synchronized ${userUpsertCount} users to production MongoDB.`);
// 2. Sync tenantconfigs
console.log("\n--- Syncing 'tenantconfigs' collection ---");
const localConfigColl = localDb.collection('tenantconfigs');
const remoteConfigColl = remoteDb.collection('tenantconfigs');
const localConfigs = await localConfigColl.find({}).toArray();
console.log(`Found ${localConfigs.length} tenant configurations locally.`);
if (localConfigs.length > 0) {
await remoteConfigColl.deleteMany({});
const cleanedConfigs = localConfigs.map(c => {
const { _id, ...rest } = c;
return rest;
});
await remoteConfigColl.insertMany(cleanedConfigs);
console.log(`✓ Synchronized ${localConfigs.length} configurations to production.`);
}
// 3. Sync customagentlocations
console.log("\n--- Syncing 'customagentlocations' collection ---");
const localLocColl = localDb.collection('customagentlocations');
const remoteLocColl = remoteDb.collection('customagentlocations');
const localLocs = await localLocColl.find({}).toArray();
console.log(`Found ${localLocs.length} agent locations locally.`);
if (localLocs.length > 0) {
await remoteLocColl.deleteMany({});
const cleanedLocs = localLocs.map(l => {
const { _id, ...rest } = l;
return rest;
});
await remoteLocColl.insertMany(cleanedLocs);
console.log(`✓ Synchronized ${localLocs.length} agent locations to production.`);
}
// 4. Sync blacklistrules
console.log("\n--- Syncing 'blacklistrules' collection ---");
const localRulesColl = localDb.collection('blacklistrules');
const remoteRulesColl = remoteDb.collection('blacklistrules');
const localColls = await localDb.listCollections({ name: 'blacklistrules' }).toArray();
if (localColls.length > 0) {
const localRules = await localRulesColl.find({}).toArray();
console.log(`Found ${localRules.length} blacklist rules locally.`);
if (localRules.length > 0) {
await remoteRulesColl.deleteMany({});
const cleanedRules = localRules.map(r => {
const { _id, ...rest } = r;
return rest;
});
await remoteRulesColl.insertMany(cleanedRules);
console.log(`✓ Synchronized ${localRules.length} blacklist rules to production.`);
}
} else {
console.log("No blacklist rules found locally.");
}
await localClient.close();
await remoteClient.close();
console.log("\n✓ Database configuration migration completed successfully!");
}