chore: super deep scanning cleanup - remove obsolete debug scripts, redundant components, and temporary files

This commit is contained in:
rafif committed 2026-07-31 09:41:11 +07:00
1 parent 97d2c89c15
commit a577f2d672
189 files changed
-11112

No files matched your search

-66
View File
@@ -1,66 +0,0 @@
const { Client } = require('ssh2');
const remoteScript = `
const fs = require('fs');
const path = require('path');
const root = '/home/adminbackend/web/demoplace.my.id/public_html/.next/standalone/.next/server/app/(dashboard)';
function scanDir(dir) {
const results = [];
if (!fs.existsSync(dir)) return results;
const items = fs.readdirSync(dir, { withFileTypes: true });
for (const item of items) {
const fullPath = path.join(dir, item.name);
if (item.isDirectory()) {
results.push(...scanDir(fullPath));
} else if (item.name.endsWith('.html') || item.name.endsWith('.js')) {
results.push(fullPath);
}
}
return results;
}
console.log('=== AUDITING ALL PAGE HEADERS AND TITLES ON DEMOPLACE.MY.ID ===\\n');
const files = scanDir(root);
files.forEach(file => {
const content = fs.readFileSync(file, 'utf8');
const rel = path.relative(root, file);
// Extract <h2> or <h1> or header titles
const h2s = [];
const h2Match = content.matchAll(/<h2[^>]*>(.*?)<\/h2>/gi);
for (const m of h2Match) {
const clean = m[1].replace(/<[^>]+>/g, '').trim();
if (clean && !h2s.includes(clean)) h2s.push(clean);
}
// Extract Learn This Page triggers
const hasHelpTrigger = content.includes('Learn This Page');
if (h2s.length > 0 || hasHelpTrigger) {
console.log('Page Route File:', rel);
if (h2s.length > 0) console.log(' Headers (H2):', h2s.join(' | '));
console.log(' Has Learn This Page Button:', hasHelpTrigger);
console.log('---');
}
});
`;
const conn = new Client();
conn.on('ready', () => {
conn.exec(`node -e ${JSON.stringify(remoteScript)}`, (err, stream) => {
let out = '';
stream.on('data', d => out += d);
stream.on('close', () => {
console.log(out);
conn.end();
});
});
}).connect({
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH'
});
-119
View File
@@ -1,119 +0,0 @@
const { spawn } = require('child_process');
const http = require('http');
const fs = require('fs');
const path = require('path');
const WebSocket = require('next/dist/compiled/ws');
async function loginAdmin() {
return new Promise((resolve) => {
const data = JSON.stringify({ username: 'admin', password: 'admin' });
const req = http.request({
hostname: '127.0.0.1',
port: 3000,
path: '/api/auth/login',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
}
}, (res) => {
let body = '';
res.on('data', chunk => body += chunk);
res.on('end', () => {
const cookies = res.headers['set-cookie'];
if (cookies && cookies.length > 0) {
const cookieStr = cookies[0];
const match = cookieStr.match(/backone_token=([^;]+)/);
resolve(match ? match[1] : null);
} else {
resolve(null);
}
});
});
req.on('error', () => resolve(null));
req.write(data);
req.end();
});
}
async function capture() {
const token = await loginAdmin();
console.log("Logged in admin token:", token ? "FOUND" : "NOT FOUND");
const edgePath = "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe";
const edgeProc = spawn(edgePath, [
'--headless',
'--disable-gpu',
'--remote-debugging-port=9222',
'--window-size=400,844',
'http://localhost:3000/login'
]);
// Wait 2.5s for Edge CDP to listen
await new Promise(r => setTimeout(r, 2500));
try {
const targets = await new Promise((resolve, reject) => {
http.get('http://127.0.0.1:9222/json', (res) => {
let body = '';
res.on('data', c => body += c);
res.on('end', () => resolve(JSON.parse(body)));
}).on('error', reject);
});
const pageTarget = targets.find(t => t.type === 'page');
if (!pageTarget || !pageTarget.webSocketDebuggerUrl) {
console.error("No CDP page target found:", targets);
edgeProc.kill();
return;
}
const ws = new WebSocket(pageTarget.webSocketDebuggerUrl);
let messageId = 1;
const send = (method, params = {}) => {
return new Promise((res) => {
const id = messageId++;
const handler = (data) => {
const msg = JSON.parse(data);
if (msg.id === id) {
ws.removeListener('message', handler);
res(msg.result);
}
};
ws.on('message', handler);
ws.send(JSON.stringify({ id, method, params }));
});
};
ws.on('open', async () => {
console.log("Connected to Edge CDP WebSocket!");
if (token) {
await send('Network.setCookie', {
name: 'backone_token',
value: token,
domain: 'localhost',
path: '/'
});
}
await send('Page.navigate', { url: 'http://localhost:3000/' });
// Wait for page load and 3D globe rendering
await new Promise(r => setTimeout(r, 5000));
const screenshot = await send('Page.captureScreenshot', { format: 'png' });
const outputPath = "C:\\Users\\rafif\\.gemini\\antigravity-ide\\brain\\d6704840-c7e7-4558-b215-0fe98bbfb07a\\mobile-dashboard-live.png";
fs.writeFileSync(outputPath, Buffer.from(screenshot.data, 'base64'));
console.log("SUCCESS! Screenshot written to:", outputPath, "Size:", fs.statSync(outputPath).size, "bytes");
ws.close();
edgeProc.kill();
});
} catch (err) {
console.error("CDP Error:", err.message);
edgeProc.kill();
}
}
capture();
-67
View File
@@ -1,67 +0,0 @@
// scripts/check-agents-error.js
'use strict';
const { Client } = require('ssh2');
const https = require('https');
const SSH = { host: '103.185.47.52', port: 2222, username: 'adminbackend', password: 'htEo7x6LsBQiEHHH', readyTimeout: 60000 };
const PUB = '/home/adminbackend/web/demoplace.my.id/public_html';
function exec(conn, cmd, label) {
if (label) console.log('\n[' + label + ']');
console.log('$ ' + cmd.substring(0, 80));
return new Promise(resolve => {
conn.exec(cmd, (err, s) => {
if (err) { resolve(''); return; }
let out = '';
s.on('data', d => { out += d; process.stdout.write(d.toString()); })
.stderr.on('data', d => { out += d; process.stdout.write(d.toString()); })
.on('close', () => resolve(out));
});
});
}
async function httpGet(url, opts) {
return new Promise(resolve => {
const req = https.get(url, { timeout: 10000, rejectUnauthorized: false, ...opts }, res => {
let body = '';
res.on('data', d => body += d);
res.on('end', () => resolve({ status: res.statusCode, body: body.substring(0, 500) }));
});
req.on('error', e => resolve({ status: 0, error: e.message }));
req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
});
}
async function main() {
const conn = new Client();
await new Promise((r, j) => { conn.on('ready', r).on('error', j).connect(SSH); });
console.log('[SSH] Connected\n');
// 1. Cek error log frontend
await exec(conn, 'tail -50 ' + PUB + '/logs/frontend-error.log 2>/dev/null | head -50', '1. Frontend error log');
// 2. Cek frontend out log terbaru
await exec(conn, 'tail -30 ' + PUB + '/logs/frontend-out.log 2>/dev/null', '2. Frontend out log');
// 3. Test API agents langsung dari server
await exec(conn, 'curl -sk http://127.0.0.1:3001/api/agents 2>/dev/null | head -c 500', '3. Test API /api/agents langsung');
// 4. Test API health backend
await exec(conn, 'curl -sk http://127.0.0.1:3001/api/health 2>/dev/null', '4. Test backend health');
// 5. Cek .env.production - apakah BACKEND_URL benar
await exec(conn, 'cat ' + PUB + '/.env.production 2>/dev/null | grep -v PASSWORD | grep -v SECRET | grep -v MONGO | head -20', '5. Environment variables (safe)');
// 6. Cek apakah backend bisa akses agents collection dari MongoDB
await exec(conn, 'curl -sk http://127.0.0.1:3001/api/agents/list 2>/dev/null | head -c 300', '6. Test /api/agents/list');
conn.end();
// 7. Test dari luar via domain
console.log('\n[7. Test API agents via domain...]');
const r = await httpGet('https://demoplace.my.id/api/agents');
console.log(' Status: ' + r.status);
console.log(' Body: ' + (r.body || r.error || 'empty'));
}
main().catch(e => console.error('[FATAL]', e.message));
-22
View File
@@ -1,22 +0,0 @@
const { Client } = require('ssh2');
const conn = new Client();
conn.on('ready', () => {
console.log('SSH Client Ready');
const cmd = `tail -n 200 /home/adminbackend/.pm2/logs/backone-backend-error-*.log /home/adminbackend/.pm2/logs/backone-backend-out-*.log /home/adminbackend/.pm2/logs/backone-frontend-error-*.log /home/adminbackend/.pm2/logs/backone-frontend-out-*.log 2>/dev/null | tail -n 200`;
conn.exec(cmd, (err, stream) => {
if (err) throw err;
stream.on('close', (code, signal) => {
conn.end();
}).on('data', (data) => {
console.log(data.toString());
}).stderr.on('data', (data) => {
console.log('STDERR: ' + data.toString());
});
});
}).connect({
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH'
});
-94
View File
@@ -1,94 +0,0 @@
// scripts/check-static-files.js
// Cek apakah static chunk files ada di disk dan bisa diakses via domain
'use strict';
const { Client } = require('ssh2');
const https = require('https');
const SSH = {
host: '103.185.47.52', port: 2222,
username: 'adminbackend', password: 'htEo7x6LsBQiEHHH',
readyTimeout: 60000
};
function sshExec(conn, cmd) {
return new Promise(resolve => {
conn.exec(cmd, (err, s) => {
if (err) { resolve('SSH_ERROR'); return; }
let out = '';
s.on('data', d => { out += d; process.stdout.write(d.toString()); })
.stderr.on('data', d => { out += d; process.stdout.write(d.toString()); })
.on('close', () => resolve(out));
});
});
}
function httpGet(url) {
return new Promise(resolve => {
const req = https.get(url, { timeout: 10000, rejectUnauthorized: false }, res => {
let body = '';
res.on('data', d => body += d);
res.on('end', () => resolve({ status: res.statusCode, type: res.headers['content-type'], size: body.length }));
});
req.on('error', e => resolve({ status: 0, error: e.message }));
req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
});
}
async function main() {
const conn = new Client();
await new Promise((r, j) => { conn.on('ready', r).on('error', j).connect(SSH); });
console.log('[SSH] Connected\n');
const PUB = '/home/adminbackend/web/demoplace.my.id/public_html';
// 1. List chunks directory
console.log('=== .next/static/chunks/ contents ===');
await sshExec(conn, `ls -la ${PUB}/.next/static/chunks/ | head -20`);
// 2. Cek CSS files yang direferensikan oleh HTML
console.log('\n=== Cek file CSS spesifik ===');
await sshExec(conn, `ls -la ${PUB}/.next/static/chunks/*.css 2>/dev/null | head -5 || echo "Tidak ada .css di chunks/"`);
// 3. Cek apakah JS bootstrap file ada
console.log('\n=== Cek file JS yang diperlukan ===');
await sshExec(conn, `ls ${PUB}/.next/static/chunks/ | grep -E "(3km6z|21luguo|1-8s9)" | head -10 || echo "File JS tidak ditemukan"`);
// 4. Test akses static file via domain (dari dalam server)
console.log('\n=== Test static file via Apache ===');
await sshExec(conn,
`curl -sk -D - https://demoplace.my.id/_next/static/chunks/3km6z-n6wbgrs.js 2>/dev/null | head -15 || ` +
`curl -sk -D - http://103.185.47.52:8080/_next/static/chunks/3km6z-n6wbgrs.js 2>/dev/null | head -15`
);
// 5. Test apakah Nginx melayani file static langsung
console.log('\n=== Cek Nginx serving static (port 443) ===');
await sshExec(conn,
`curl -sk -w "\\nHTTP: %{http_code} | Type: %{content_type} | Size: %{size_download}" ` +
`-o /dev/null https://demoplace.my.id/_next/static/chunks/3km6z-n6wbgrs.js`
);
// 6. Cek apakah ada .next/server/ directory
console.log('\n=== Status .next/server/ ===');
await sshExec(conn, `ls ${PUB}/.next/server/ 2>/dev/null | head -5 || echo ".next/server/ TIDAK ADA - standalone build mungkin tidak lengkap"`);
conn.end();
// 7. Test dari luar - JS dan CSS
console.log('\n=== Test static resources dari luar ===');
const files = [
'/_next/static/chunks/3km6z-n6wbgrs.js',
'/_next/static/chunks/1u6dw_tm45utz.css',
'/_next/static/chunks/1oskchnhjm4n8.css',
'/_next/static/chunks/21luguo5_g2uu.js',
];
for (const f of files) {
const r = await httpGet(`https://demoplace.my.id${f}`);
const icon = r.status === 200 ? '✅' : '❌';
console.log(` ${icon} ${f}`);
console.log(` HTTP ${r.status} | Type: ${r.type || 'N/A'} | Size: ${r.size || 0} bytes`);
}
}
main().catch(err => console.error('[FATAL]', err.message));
-61
View File
@@ -1,61 +0,0 @@
const https = require('https');
const PAGES = [
'/',
'/network-infrastructure',
'/agents',
'/devices',
'/flows',
'/apps',
'/network-intelligence',
'/dns',
'/geography',
'/dpi-analytics',
'/lookup',
'/intelligence',
'/threats',
'/events',
'/security-audit',
'/help'
];
function fetchPage(route) {
return new Promise((resolve) => {
https.get(`https://demoplace.my.id${route}`, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
const titleMatch = data.match(/<title>(.*?)<\/title>/i);
const descMatch = data.match(/<meta name="description" content="(.*?)"/i);
const title = titleMatch ? titleMatch[1] : 'N/A';
const desc = descMatch ? descMatch[1] : 'N/A';
// Find logo references
const logos = [];
const logoRegex = /src="([^"]*(?:logo|icon)[^"]*)"/gi;
let match;
while ((match = logoRegex.exec(data)) !== null) {
if (!logos.includes(match[1])) logos.push(match[1]);
}
resolve({ route, statusCode: res.statusCode, title, desc, logos });
});
}).on('error', (err) => {
resolve({ route, error: err.message });
});
});
}
async function main() {
console.log('=== AUDITING DEMOPLACE.MY.ID PAGES ===\n');
for (const page of PAGES) {
const res = await fetchPage(page);
console.log(`Route: ${res.route}`);
console.log(` Title : ${res.title}`);
console.log(` Description : ${res.desc}`);
console.log(` Logos/Icons : ${res.logos ? res.logos.join(', ') : 'None'}`);
console.log('---');
}
}
main();
-97
View File
@@ -1,97 +0,0 @@
// scripts/copy-from-backslash.js
// Copy file dari direktori backslash ke path yang benar
'use strict';
const { Client } = require('ssh2');
const https = require('https');
const SSH = { host: '103.185.47.52', port: 2222, username: 'adminbackend', password: 'htEo7x6LsBQiEHHH', readyTimeout: 60000 };
const PUB = '/home/adminbackend/web/demoplace.my.id/public_html';
const SA = PUB + '/.next/standalone/.next/static';
const BSDir = PUB + '/.next\\static\\chunks'; // Backslash literal path
function exec(conn, cmd, label) {
if (label) console.log('\n[' + label + ']');
console.log('$ ' + cmd.substring(0, 90));
return new Promise(resolve => {
conn.exec(cmd, (err, s) => {
if (err) { resolve(''); return; }
let out = '';
s.on('data', d => { out += d; process.stdout.write(d.toString()); })
.stderr.on('data', d => { out += d; process.stdout.write(d.toString()); })
.on('close', () => resolve(out));
});
});
}
async function httpGet(url) {
return new Promise(resolve => {
const req = https.get(url, { timeout: 12000, rejectUnauthorized: false }, res => {
let body = '';
res.on('data', d => body += d);
res.on('end', () => resolve({ status: res.statusCode, size: body.length }));
});
req.on('error', e => resolve({ status: 0, error: e.message }));
req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
});
}
async function main() {
const conn = new Client();
await new Promise((r, j) => { conn.on('ready', r).on('error', j).connect(SSH); });
console.log('[SSH] Connected\n');
// 1. List isi direktori backslash
await exec(conn, "ls '" + BSDir + "' | grep css | head -10", '1. CSS di direktori backslash');
// 2. Copy semua file dari direktori backslash ke standalone/chunks
await exec(conn,
"cp '" + BSDir + "/'* '" + SA + "/chunks/' 2>&1 && echo 'COPY OK' || echo 'COPY PARTIAL'",
'2. Copy semua file dari backslash dir ke standalone/chunks'
);
// 3. Verifikasi CSS yang hilang sudah ada
await exec(conn,
"ls '" + SA + "/chunks/' | grep '1oskchnhjm4n8'",
'3. Verifikasi 1oskchnhjm4n8.css ada'
);
// 4. Juga cek nested static dir (ada static/static/)
await exec(conn,
"ls '" + SA + "/static/' 2>/dev/null | head -10 || echo 'Tidak ada nested static'",
'4. Cek nested static dir'
);
// 5. Cek juga backslash css dir (beda dengan chunks)
await exec(conn,
"ls '" + PUB + "/.next\\static\\css' 2>/dev/null | head -10 || echo 'Tidak ada backslash css dir'",
'5. Cek backslash css dir'
);
// 6. Copy CSS dari backslash css dir juga jika ada
await exec(conn,
"[ -d '" + PUB + "/.next\\static\\css' ] && cp '" + PUB + "/.next\\static\\css/'* '" + SA + "/css/' 2>&1 && echo 'CSS copy OK' || echo 'Tidak ada backslash css dir'",
'6. Copy dari backslash css dir'
);
conn.end();
// 7. Final test
console.log('\n[7. Final test semua static files...]');
await new Promise(r => setTimeout(r, 2000));
const files = [
'/_next/static/chunks/1oskchnhjm4n8.css',
'/_next/static/chunks/1u6dw_tm45utz.css',
'/_next/static/chunks/3km6z-n6wbgrs.js',
'/_next/static/chunks/21luguo5_g2uu.js',
'/_next/static/chunks/1-8s9_t85wwr4.js',
'/login',
];
for (const f of files) {
const r = await httpGet('https://demoplace.my.id' + f);
const icon = r.status === 200 ? '✅' : '❌';
console.log(' ' + icon + ' HTTP ' + r.status + ' | ' + (r.size || 0) + ' bytes | ' + f);
}
console.log('\n✅ Selesai! Hard refresh browser (Ctrl+Shift+R)\n');
}
main().catch(e => console.error('[FATAL]', e.message));
-48
View File
@@ -1,48 +0,0 @@
// scripts/deep-diag.js
'use strict';
const { Client } = require('ssh2');
const conn = new Client();
const PUB = '/home/adminbackend/web/demoplace.my.id/public_html';
function exec(conn, cmd) {
console.log('\n$ ' + cmd.substring(0, 80));
return new Promise(resolve => {
conn.exec(cmd, (err, s) => {
if (err) { resolve(''); return; }
let out = '';
s.on('data', d => { out += d; process.stdout.write(d.toString()); })
.stderr.on('data', d => { out += d; process.stdout.write(d.toString()); })
.on('close', () => resolve(out));
});
});
}
conn.on('ready', async () => {
// 1. Cek proses yang sedang berjalan
await exec(conn, 'ps -p 3193322 -o cmd 2>/dev/null | tail -1');
// 2. Cek BUILD_ID di standalone
const saPath = PUB + '/.next/standalone';
await exec(conn, 'cat ' + saPath + '/.next/BUILD_ID 2>/dev/null || echo NO_BUILD_ID_IN_STANDALONE');
// 3. List chunks di standalone
await exec(conn, 'ls ' + saPath + '/.next/static/chunks/ | head -15');
// 4. List static root di standalone
await exec(conn, 'ls ' + saPath + '/.next/static/');
// 5. Cek apakah ada server.js lain yang mungkin sedang berjalan
await exec(conn, 'ps aux | grep "node.*server" | grep -v grep | head -5');
// 6. PM2 info untuk backone-frontend
await exec(conn, '/home/adminbackend/.npm-global/bin/pm2 show backone-frontend 2>/dev/null | grep -E "(script|cwd|pid|restart)"');
// 7. Cek log terbaru
await exec(conn, 'tail -20 ' + PUB + '/logs/frontend-out.log 2>/dev/null | head -20');
conn.end();
}).connect({
host: '103.185.47.52', port: 2222,
username: 'adminbackend', password: 'htEo7x6LsBQiEHHH',
readyTimeout: 30000
});
-57
View File
@@ -1,57 +0,0 @@
const { Client } = require('ssh2');
const fs = require('fs');
const remoteScript = `
const fs = require('fs');
const path = require('path');
const root = '/home/adminbackend/web/demoplace.my.id/public_html';
console.log('=== 1. LOGOS & IMAGES IN PUBLIC ===');
try {
const publicFiles = fs.readdirSync(path.join(root, 'public'));
console.log('Files in public/:', publicFiles);
} catch(e) { console.log('Err:', e.message); }
console.log('\\n=== 2. AUDITING NAVIGATION GROUPS IN STANDALONE CHUNKS ===');
const chunksDir = path.join(root, '.next/standalone/.next/server/chunks');
if (fs.existsSync(chunksDir)) {
const files = fs.readdirSync(chunksDir);
files.forEach(f => {
if (!f.endsWith('.js')) return;
const content = fs.readFileSync(path.join(chunksDir, f), 'utf8');
if (content.includes('Overview Dashboard') || content.includes('Network Topology')) {
console.log('Chunk File:', f);
const match = content.match(/title:\"OVERVIEW\".*?title:\"SECURITY & ALERTS\".*?\]/);
if (match) {
console.log('NAV MATCH:', match[0]);
}
}
});
}
console.log('\\n=== 3. APP ROUTE DIRECTORIES ===');
try {
const appDir = path.join(root, '.next/standalone/.next/server/app/(dashboard)');
if (fs.existsSync(appDir)) {
console.log('Dashboard routes in .next/standalone:', fs.readdirSync(appDir));
}
} catch(e) { console.log('Err app dir:', e.message); }
`;
const conn = new Client();
conn.on('ready', () => {
conn.exec(`node -e ${JSON.stringify(remoteScript)}`, (err, stream) => {
let out = '';
stream.on('data', d => out += d);
stream.on('close', () => {
console.log(out);
conn.end();
});
});
}).connect({
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH'
});
-206
View File
@@ -1,206 +0,0 @@
// scripts/deploy-php-proxy.js
// ─────────────────────────────────────────────────────────────────────────────
// BackOne DPI — Deploy PHP proxy ke public_html
// Solusi 502: Apache PHP-FPM forward semua request ke Next.js port 3000
// Tidak butuh mod_proxy_http, root access, atau Hestia API
// ─────────────────────────────────────────────────────────────────────────────
'use strict';
const { Client } = require('ssh2');
const SftpClient = require('ssh2-sftp-client');
const https = require('https');
const path = require('path');
const SSH_CONFIG = {
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH',
readyTimeout: 60000
};
const ROOT = '/home/adminbackend/web/demoplace.my.id/public_html';
// ── PHP Proxy script ──────────────────────────────────────────────────────
const PHP_PROXY = `<?php
/**
* BackOne DPI — PHP Reverse Proxy
* Meneruskan semua request HTTP ke Next.js yang berjalan di port 3000
* File ini dikelola otomatis. Jangan dihapus.
*/
// Target: Next.js port 3000
define('NEXTJS_URL', 'http://127.0.0.1:3000');
define('TIMEOUT', 30);
// Ambil URI request
$requestUri = $_SERVER['REQUEST_URI'] ?? '/';
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
$target = NEXTJS_URL . $requestUri;
// Kumpulkan headers dari request asli
$forwardHeaders = [
'X-Forwarded-For: ' . ($_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'),
'X-Forwarded-Proto: https',
'X-Real-IP: ' . ($_SERVER['REMOTE_ADDR'] ?? ''),
'Host: ' . ($_SERVER['HTTP_HOST'] ?? 'demoplace.my.id'),
];
// Tambahkan headers lain dari request
$allHeaders = getallheaders() ?: [];
$skipHeaders = ['host', 'connection', 'transfer-encoding', 'keep-alive', 'content-length'];
foreach ($allHeaders as $name => $value) {
if (!in_array(strtolower($name), $skipHeaders)) {
$forwardHeaders[] = "$name: $value";
}
}
// Body untuk POST/PUT/PATCH
$body = in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE']) ? file_get_contents('php://input') : null;
// Kirim request ke Next.js
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $target,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $forwardHeaders,
CURLOPT_POSTFIELDS => $body,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => TIMEOUT,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_ENCODING => '',
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$curlError = curl_error($ch);
curl_close($ch);
// Jika curl error
if ($response === false) {
http_response_code(502);
header('Content-Type: text/plain');
echo 'BackOne: Gagal terhubung ke Next.js. Error: ' . $curlError;
exit;
}
// Pisah headers dan body
$responseHeaders = substr($response, 0, $headerSize);
$responseBody = substr($response, $headerSize);
// Set HTTP status
http_response_code($httpCode ?: 200);
// Forward response headers ke client
$skipResponseHeaders = ['transfer-encoding', 'connection', 'keep-alive', 'content-encoding'];
foreach (explode("\\r\\n", $responseHeaders) as $header) {
$header = trim($header);
if (empty($header) || preg_match('/^HTTP\\//i', $header)) continue;
$colonPos = strpos($header, ':');
if ($colonPos === false) continue;
$headerName = strtolower(trim(substr($header, 0, $colonPos)));
if (in_array($headerName, $skipResponseHeaders)) continue;
header($header, false);
}
// Kirim body
echo $responseBody;
`;
// ── .htaccess baru: route semua request ke proxy.php ─────────────────────
const HTACCESS = `# BackOne DPI — Apache .htaccess
# Redirect HTTP ke HTTPS
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Route semua request ke PHP proxy (meneruskan ke Next.js port 3000)
RewriteCond %{REQUEST_FILENAME} !proxy.php
RewriteRule ^(.*)$ /proxy.php [L,QSA]
`;
// ── Fungsi upload file via SFTP ───────────────────────────────────────────
async function uploadFile(sftp, content, remotePath) {
const buf = Buffer.from(content, 'utf8');
await sftp.put(buf, remotePath);
console.log(` ✅ Upload: ${remotePath}`);
}
// ── SSH command ────────────────────────────────────────────────────────────
function sshExec(conn, cmd) {
console.log(`\n$ ${cmd.substring(0, 100)}...`);
return new Promise((resolve, reject) => {
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() {
console.log('\n╔══════════════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — Deploy PHP Proxy (Fix 502) ║');
console.log('╚══════════════════════════════════════════════════════════╝\n');
// ── Upload via SFTP ──────────────────────────────────────────────────────
console.log('▶ Step 1: Upload proxy.php dan .htaccess via SFTP...\n');
const sftp = new SftpClient();
await sftp.connect(SSH_CONFIG);
await uploadFile(sftp, PHP_PROXY, `${ROOT}/proxy.php`);
await uploadFile(sftp, HTACCESS, `${ROOT}/.htaccess`);
await sftp.end();
// ── Verify dan test via SSH ──────────────────────────────────────────────
console.log('\n▶ Step 2: Verifikasi file di server...');
const conn = new Client();
await new Promise((resolve, reject) => { conn.on('ready', resolve).on('error', reject).connect(SSH_CONFIG); });
console.log('[SSH] Terhubung!\n');
await sshExec(conn, `ls -la ${ROOT}/proxy.php ${ROOT}/.htaccess`);
await sshExec(conn, `head -5 ${ROOT}/proxy.php`);
await sshExec(conn, `cat ${ROOT}/.htaccess`);
// ── Test internal Apache response ─────────────────────────────────────
console.log('\n▶ Step 3: Test Apache internal (port 8080)...');
await sshExec(conn, `curl -sk -o /dev/null -w "Apache HTTP: %{http_code}" http://127.0.0.1:8080/`);
await sshExec(conn, `curl -sk -o /dev/null -w "Apache Login: %{http_code}" http://127.0.0.1:8080/login`);
// ── Test domain publik ─────────────────────────────────────────────────
console.log('\n▶ Step 4: Test domain demoplace.my.id...');
await sshExec(conn,
`sleep 2 && curl -sk -o /dev/null -w "HTTPS /: %{http_code}" https://demoplace.my.id/ && ` +
`curl -sk -o /dev/null -w " | HTTPS /login: %{http_code}" https://demoplace.my.id/login`
);
conn.end();
// ── Final test dari luar ───────────────────────────────────────────────
console.log('\n▶ Step 5: Final test dari jaringan lokal...');
await new Promise(r => setTimeout(r, 3000));
for (const path of ['/', '/login']) {
const r = await new Promise(resolve => {
const req = https.get(`https://demoplace.my.id${path}`,
{ timeout: 12000, rejectUnauthorized: false },
res => resolve({ status: res.statusCode, location: res.headers.location })
);
req.on('error', e => resolve({ status: 0, error: e.message }));
req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
});
const icon = r.status >= 200 && r.status < 400 ? '✅' : r.status === 0 ? '⚠️ ' : '❌';
console.log(` ${icon} https://demoplace.my.id${path} → HTTP ${r.status} ${r.error || ''}`);
if (r.location) console.log(` → Redirect ke: ${r.location}`);
}
console.log('\n✅ PHP Proxy berhasil di-deploy!\n');
console.log('Cek browser: https://demoplace.my.id/login\n');
}
main().catch(err => { console.error('[FATAL]', err.message); process.exit(1); });
-117
View File
@@ -1,117 +0,0 @@
const { Client } = require('ssh2');
const SftpClient = require('ssh2-sftp-client');
const path = require('path');
const fs = require('fs');
const archiver = require('archiver');
const config = {
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH'
};
const DEPLOY_DIR = 'backone-production';
async function createZip() {
console.log("Packaging application...");
return new Promise((resolve, reject) => {
const output = fs.createWriteStream(path.join(__dirname, '../deploy.zip'));
const archive = new archiver.ZipArchive({ zlib: { level: 9 } });
output.on('close', () => resolve(path.join(__dirname, '../deploy.zip')));
archive.on('error', (err) => reject(err));
archive.pipe(output);
// Add standalone output
archive.directory(path.join(__dirname, '../.next/standalone'), false);
// Add static assets (Next.js standalone requires these copied over)
archive.directory(path.join(__dirname, '../.next/static'), '.next/static');
archive.directory(path.join(__dirname, '../public'), 'public');
// Add backend and proxy
archive.directory(path.join(__dirname, '../backend'), 'backend');
archive.directory(path.join(__dirname, '../proxy'), 'proxy');
// Add production env
archive.file(path.join(__dirname, '../.env.production'), { name: '.env.production' });
archive.finalize();
});
}
async function runSSH(cmd) {
return new Promise((resolve, reject) => {
const conn = new Client();
conn.on('ready', () => {
conn.exec(cmd, (err, stream) => {
if (err) 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(config);
});
}
async function deploy() {
const sftp = new SftpClient();
let zipPath;
try {
zipPath = await createZip();
console.log("Package created successfully.");
console.log("Connecting to SFTP server...");
await sftp.connect(config);
console.log("Creating deployment directory...");
const homeDir = await runSSH('pwd');
const targetDir = `${homeDir.trim()}/${DEPLOY_DIR}`;
await runSSH(`mkdir -p ${targetDir}`);
console.log(`Uploading deploy.zip to ${targetDir}...`);
await sftp.put(zipPath, `${targetDir}/deploy.zip`);
console.log("Extracting package on server...");
await runSSH(`cd ${targetDir} && unzip -o deploy.zip && rm deploy.zip`);
console.log("Installing production dependencies for backend & proxy...");
await runSSH(`cd ${targetDir}/backend && npm install --production`);
await runSSH(`cd ${targetDir}/proxy && npm install --production`);
console.log("Configuring PM2...");
// Create ecosystem file for PM2
const ecosystem = `
module.exports = {
apps: [
{ name: 'backone-proxy', script: './proxy/index.js', env_file: '.env.production' },
{ name: 'backone-backend', script: './backend/server.js', env_file: '.env.production' },
{ name: 'backone-frontend', script: 'server.js', env_file: '.env.production' }
]
};`;
await runSSH(`cd ${targetDir} && echo "${ecosystem.replace(/\n/g, '\\n')}" > ecosystem.config.js`);
console.log("Restarting PM2 processes...");
const pm2Result = await runSSH(`cd ${targetDir} && pm2 start ecosystem.config.js || pm2 restart ecosystem.config.js`);
console.log(pm2Result);
console.log("Deployment completed successfully!");
console.log(`BackOne Dashboard is now live. Please map your Nginx/Apache domain demoplace.my.id to http://127.0.0.1:3000`);
} catch(e) {
console.error("Deployment failed:", e.message);
} finally {
sftp.end();
if (fs.existsSync(zipPath)) fs.unlinkSync(zipPath);
}
}
deploy();
-84
View File
@@ -1,84 +0,0 @@
const https = require('https');
const ROUTES = [
'/',
'/agents',
'/apps',
'/devices',
'/dns',
'/dpi-analytics',
'/encryption-audit',
'/events',
'/flows',
'/geography',
'/intelligence',
'/lookup',
'/network-infrastructure',
'/network-intelligence',
'/network-topology',
'/security-audit',
'/threat-intelligence',
'/threats',
'/traffic-categories',
'/help',
'/login'
];
function auditRoute(route) {
return new Promise((resolve) => {
https.get(`https://demoplace.my.id${route}`, (res) => {
let data = '';
res.on('data', c => data += c);
res.on('end', () => {
const title = (data.match(/<title>(.*?)<\/title>/i) || [])[1] || 'N/A';
const desc = (data.match(/<meta name="description" content="(.*?)"/i) || [])[1] || 'N/A';
const icons = [];
const iconMatches = data.matchAll(/rel="icon" href="([^"]+)"/gi);
for (const m of iconMatches) icons.push(m[1]);
const imgs = [];
const imgMatches = data.matchAll(/src="([^"]*(?:logo|brand|icon|png|svg|jpg)[^"]*)"/gi);
for (const m of imgMatches) {
if (!imgs.includes(m[1])) imgs.push(m[1]);
}
const headers = [];
const hMatches = data.matchAll(/<h[123][^>]*>(.*?)<\/h[123]>/gi);
for (const m of hMatches) {
const clean = m[1].replace(/<[^>]+>/g, '').trim();
if (clean && !headers.includes(clean)) headers.push(clean);
}
resolve({
route,
status: res.statusCode,
is404: data.includes('404: This page could not be found'),
title,
desc,
icons,
imgs,
headers
});
});
}).on('error', err => resolve({ route, error: err.message }));
});
}
async function main() {
console.log('=== DETAILED AUDIT OF DEMOPLACE.MY.ID ===\n');
for (const r of ROUTES) {
const res = await auditRoute(r);
if (res.is404) {
console.log(`❌ [404 NOT FOUND] ${res.route}`);
} else {
console.log(`✅ [200 OK] ${res.route}`);
console.log(` Title : ${res.title}`);
console.log(` Icons : ${res.icons.join(', ')}`);
console.log(` Images/Logo: ${res.imgs.join(', ')}`);
console.log(` Headers : ${res.headers.join(' | ')}`);
}
console.log('--------------------------------------------------');
}
}
main();
-99
View File
@@ -1,99 +0,0 @@
// scripts/diagnose-ports.js
// Cek port apa yang aktif dan test PHP proxy di port yang benar
'use strict';
const { Client } = require('ssh2');
const SSH = {
host: '103.185.47.52', port: 2222,
username: 'adminbackend', password: 'htEo7x6LsBQiEHHH',
readyTimeout: 60000
};
function sshExec(conn, cmd, label = '') {
if (label) console.log(`\n[${label}]`);
console.log(`$ ${cmd.substring(0, 100)}${cmd.length > 100 ? '...' : ''}`);
return new Promise((resolve, reject) => {
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() {
console.log('\n╔══════════════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — Diagnosa Port & PHP Proxy Test ║');
console.log('╚══════════════════════════════════════════════════════════╝\n');
const conn = new Client();
await new Promise((resolve, reject) => { conn.on('ready', resolve).on('error', reject).connect(SSH); });
console.log('[SSH] Terhubung!\n');
// 1. Cek semua port yang aktif
await sshExec(conn,
'echo "=== SEMUA PORT AKTIF ===" && ss -tlnp | grep -E "(80|443|3000|3001|4000|8008|8009|8080|8443)"',
'PORT AKTIF'
);
// 2. Cek apakah Apache aktif di berbagai port
await sshExec(conn,
'echo "=== TES APACHE PORT 8009 ===" && curl -sk -o /dev/null -w "Port 8009: %{http_code}" http://127.0.0.1:8009/login',
'TES PORT 8009'
);
await sshExec(conn,
'echo "=== TES APACHE PORT 8080 (IP publik) ===" && curl -sk -o /dev/null -w "Port 8080 publik: %{http_code}" http://103.185.47.52:8080/login',
'TES PORT 8080'
);
// 3. Cek apakah PHP FPM socket ada
await sshExec(conn,
'ls -la /run/php/php*-fpm-demoplace* 2>/dev/null || echo "Tidak ada PHP FPM socket untuk domain ini"',
'PHP FPM SOCKET'
);
// 4. Cek isi .htaccess yang aktif
await sshExec(conn,
'cat /home/adminbackend/web/demoplace.my.id/public_html/.htaccess',
'HTACCESS AKTIF'
);
// 5. Cek apakah proxy.php bisa diakses via Apache
await sshExec(conn,
'curl -sk -v http://127.0.0.1:8009/proxy.php 2>&1 | head -30',
'TES proxy.php via port 8009'
);
// 6. Test direct PHP execution
await sshExec(conn,
'php -r "echo curl_version()[\'version\'];" 2>/dev/null && echo " (PHP curl OK)" || echo "PHP curl tidak tersedia"',
'CEK PHP CURL'
);
// 7. Cek Apache error log
await sshExec(conn,
'tail -20 /var/log/apache2/domains/demoplace.my.id.error.log 2>/dev/null | grep -v "^\$" | tail -10 || echo "Tidak bisa akses error log"',
'APACHE ERROR LOG'
);
// 8. Test Next.js langsung
await sshExec(conn,
'curl -sk -o /dev/null -w "Next.js port 3000 /login: %{http_code}" http://127.0.0.1:3000/login',
'TES NEXT.JS LANGSUNG'
);
// 9. Coba test PHP via curl dengan header Host
await sshExec(conn,
'curl -sk -H "Host: demoplace.my.id" -o /dev/null -w "Apache dengan Host header: %{http_code}" http://127.0.0.1:8009/',
'TES APACHE HOST HEADER'
);
conn.end();
console.log('\n✅ Diagnosa selesai!\n');
}
main().catch(err => { console.error('[FATAL]', err.message); process.exit(1); });
-90
View File
@@ -1,90 +0,0 @@
// scripts/download-production.js
'use strict';
const SftpClient = require('ssh2-sftp-client');
const path = require('path');
const fs = require('fs');
const CONFIG = {
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH',
remotePath: '/home/adminbackend/web/demoplace.my.id/public_html',
};
const LOCAL_ROOT = path.resolve(__dirname, '..');
const DIRS_TO_DOWNLOAD = ['src', 'backend', 'proxy', 'public', 'scripts'];
const FILES_TO_DOWNLOAD = [
'package.json',
'package-lock.json',
'next.config.ts',
'postcss.config.mjs',
'tsconfig.json',
'eslint.config.mjs',
'README.md',
'SKILLS.md',
'Dockerfile',
'docker-compose.yml',
'docker-compose.prod.yml',
'ecosystem.config.js',
'deploy.sh'
];
async function main() {
const sftp = new SftpClient();
try {
console.log(`[SFTP] Connecting to ${CONFIG.host}:${CONFIG.port}...`);
await sftp.connect({
host: CONFIG.host,
port: CONFIG.port,
username: CONFIG.username,
password: CONFIG.password,
});
console.log('[SFTP] Connected successfully.');
// 1. Download directories recursively
for (const dirName of DIRS_TO_DOWNLOAD) {
const remoteDir = `${CONFIG.remotePath}/${dirName}`;
const localDir = path.join(LOCAL_ROOT, dirName);
if (await sftp.exists(remoteDir)) {
console.log(`[SFTP] Downloading directory: ${dirName}...`);
fs.mkdirSync(localDir, { recursive: true });
await sftp.downloadDir(remoteDir, localDir, {
filter: (filePath, isDir) => {
const basename = path.basename(filePath);
return !['node_modules', '.next', '.git', 'logs', 'repo.zip'].includes(basename);
}
});
console.log(`[SFTP] Finished downloading directory: ${dirName}`);
} else {
console.warn(`[SFTP] Remote directory not found: ${remoteDir}`);
}
}
// 2. Download specific root files
for (const fileName of FILES_TO_DOWNLOAD) {
const remoteFile = `${CONFIG.remotePath}/${fileName}`;
const localFile = path.join(LOCAL_ROOT, fileName);
if (await sftp.exists(remoteFile)) {
console.log(`[SFTP] Downloading file: ${fileName}...`);
await sftp.fastGet(remoteFile, localFile);
console.log(`[SFTP] Downloaded: ${fileName}`);
} else {
console.warn(`[SFTP] Remote file not found: ${remoteFile}`);
}
}
console.log('\n✅ [SFTP] Download of production codebase (10/10) completed successfully!');
} catch (err) {
console.error('❌ [SFTP] Download failed:', err);
process.exit(1);
} finally {
await sftp.end();
}
}
main();
-42
View File
@@ -1,42 +0,0 @@
// scripts/export-local-db.js
const { MongoClient } = require('mongodb');
const fs = require('fs');
const path = require('path');
const LOCAL_URI = 'mongodb://127.0.0.1:27017/backone_dpi';
const DB_NAME = 'backone_dpi';
const COLLECTIONS_TO_EXPORT = ['users', 'tenantconfigs', 'customagentlocations'];
const OUTPUT_DIR = path.join(__dirname, '..', 'migration-temp');
async function main() {
console.log('Connecting to local MongoDB...');
const client = new MongoClient(LOCAL_URI);
try {
await client.connect();
console.log('✓ Connected to local MongoDB successfully.');
const db = client.db(DB_NAME);
if (!fs.existsSync(OUTPUT_DIR)) {
fs.mkdirSync(OUTPUT_DIR);
}
for (const colName of COLLECTIONS_TO_EXPORT) {
console.log(`Exporting collection: ${colName}...`);
const documents = await db.collection(colName).find({}).toArray();
const filePath = path.join(OUTPUT_DIR, `${colName}.json`);
fs.writeFileSync(filePath, JSON.stringify(documents, null, 2));
console.log(` ✓ Exported ${documents.length} documents to ${filePath}`);
}
console.log('\n✓ Database export complete!');
} catch (err) {
console.error('✗ Export failed:', err.message);
process.exit(1);
} finally {
await client.close();
}
}
main();
-62
View File
@@ -1,62 +0,0 @@
const SftpClient = require('ssh2-sftp-client');
const { Client } = require('ssh2');
const remoteExtractScript = `
const fs = require('fs');
const path = require('path');
const root = '/home/adminbackend/web/demoplace.my.id/public_html/.next/standalone/.next/server/chunks';
console.log('=== EXTRACTING ALL PAGE HEADERS FROM CHUNKS ===\\n');
const files = fs.readdirSync(root);
files.forEach(f => {
if (!f.endsWith('.js')) return;
const content = fs.readFileSync(path.join(root, f), 'utf8');
// Match header titles in React JSX: e.g. text-3xl font-bold ... >TitleText<
const titleMatches = content.matchAll(/text-3xl[^>]*>([^<]{3,40})</gi);
for (const m of titleMatches) {
console.log(\`Chunk \${f}: "\${m[1].trim()}"\`);
}
// Match tab names in React JSX
const tabMatches = content.matchAll(/role="tab"[^>]*>([^<]{3,30})</gi);
for (const m of tabMatches) {
console.log(\`Chunk \${f} Tab: "\${m[1].trim()}"\`);
}
});
`;
async function main() {
const sftp = new SftpClient();
await sftp.connect({
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH'
});
const remoteScriptPath = '/home/adminbackend/extract_client_titles.js';
await sftp.put(Buffer.from(remoteExtractScript), remoteScriptPath);
await sftp.end();
const conn = new Client();
conn.on('ready', () => {
conn.exec(`node ${remoteScriptPath}`, (err, stream) => {
let out = '';
stream.on('data', d => out += d);
stream.on('close', () => {
console.log(out);
conn.end();
});
});
}).connect({
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH'
});
}
main();
-69
View File
@@ -1,69 +0,0 @@
const SftpClient = require('ssh2-sftp-client');
const { Client } = require('ssh2');
const remoteExtractScript = `
const fs = require('fs');
const path = require('path');
const root = '/home/adminbackend/web/demoplace.my.id/public_html/.next/standalone/.next/server';
console.log('=== EXTRACTING ALL PAGE HEADERS FROM STANDALONE BUILD ===\\n');
function scan(dir) {
if (!fs.existsSync(dir)) return;
const items = fs.readdirSync(dir, { withFileTypes: true });
for (const item of items) {
const full = path.join(dir, item.name);
if (item.isDirectory()) {
scan(full);
} else if (item.name.endsWith('.js') || item.name.endsWith('.html')) {
const content = fs.readFileSync(full, 'utf8');
// Look for H2/H1 page header titles or page metadata
const matches = content.matchAll(/<h[12][^>]*>([\\s\\S]*?)<\\/h[12]>/gi);
for (const m of matches) {
const clean = m[1].replace(/<[^>]+>/g, '').trim();
if (clean && clean.length > 2 && clean.length < 60 && !clean.includes('{') && !clean.includes('function')) {
console.log(\`File: \${path.relative(root, full)}\`);
console.log(\` Header: "\${clean}"\`);
}
}
}
}
}
scan(path.join(root, 'app/(dashboard)'));
`;
async function main() {
const sftp = new SftpClient();
await sftp.connect({
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH'
});
const remoteScriptPath = '/home/adminbackend/extract_standalone.js';
await sftp.put(Buffer.from(remoteExtractScript), remoteScriptPath);
await sftp.end();
const conn = new Client();
conn.on('ready', () => {
conn.exec(`node ${remoteScriptPath}`, (err, stream) => {
let out = '';
stream.on('data', d => out += d);
stream.on('close', () => {
console.log(out);
conn.end();
});
});
}).connect({
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH'
});
}
main();
-121
View File
@@ -1,121 +0,0 @@
// scripts/final-check.js
// ─────────────────────────────────────────────────────────────────────────────
// BackOne DPI — Final production health check
// ─────────────────────────────────────────────────────────────────────────────
'use strict';
const { Client: SshClient } = require('ssh2');
const https = require('https');
const http = require('http');
const CONFIG = {
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH',
};
const PM2 = '/home/adminbackend/.npm-global/bin/pm2';
const ROOT = '/home/adminbackend/web/demoplace.my.id/public_html';
function fetch(url, followRedirects = false) {
return new Promise(resolve => {
const mod = url.startsWith('https') ? https : http;
const opts = { timeout: 12000, rejectUnauthorized: false };
if (!followRedirects) opts.agent = false;
const req = mod.get(url, opts, res => {
let body = '';
res.on('data', d => body += d);
res.on('end', () => resolve({ status: res.statusCode, headers: res.headers, body: body.substring(0, 300) }));
});
req.on('error', e => resolve({ status: 0, error: e.message }));
req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
});
}
function runSsh(commands) {
return new Promise((resolve, reject) => {
const ssh = new SshClient();
ssh.on('ready', () => {
let i = 0;
function next() {
if (i >= commands.length) { ssh.end(); return; }
const cmd = commands[i++];
console.log(`\n$ ${cmd}`);
ssh.exec(cmd, (err, stream) => {
if (err) { console.error('[ERR]', err.message); next(); return; }
stream.on('data', d => process.stdout.write(d.toString()));
stream.stderr.on('data', d => { const t = d.toString(); if (!t.includes('npm warn') && !t.includes('notice')) process.stdout.write(t); });
stream.on('close', next);
});
}
next();
ssh.on('end', resolve);
});
ssh.on('error', reject);
ssh.connect({ ...CONFIG, readyTimeout: 30000 });
});
}
async function main() {
console.log('\n╔══════════════════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — Final Production Verification ║');
console.log('╚══════════════════════════════════════════════════════════════╝\n');
// SSH internal checks
await runSsh([
`${PM2} list`,
`echo "=== FRONTEND ===" && curl -s -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/`,
`echo "=== LOGIN PAGE ===" && curl -s -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/login`,
`echo "=== BACKEND ===" && curl -s http://127.0.0.1:3001/api/health`,
// Test full login flow
`echo "=== LOGIN API ===" && curl -s -X POST http://127.0.0.1:3001/api/auth/login -H "Content-Type: application/json" -d '{"username":"admin","password":"admin"}' | python3 -c "import sys,json; d=json.load(sys.stdin); print('Login OK - Role:', d.get('user',{}).get('role','?'))" 2>/dev/null || curl -s -X POST http://127.0.0.1:3001/api/auth/login -H "Content-Type: application/json" -d '{"username":"admin","password":"admin"}'`,
// Security checks
`echo "=== SECURITY: Port 3001 ===" && ss -tlnp | grep 3001`,
`echo "=== SECURITY: Port 4000 ===" && ss -tlnp | grep 4000`,
`echo "=== SECURITY: No .env in standalone ===" && find ${ROOT}/.next/standalone -name ".env*" 2>/dev/null || echo "CLEAN"`,
// Proxy MongoDB
`echo "=== PROXY MONGO ===" && ${PM2} logs backone-proxy --lines 5 --nostream 2>&1 | grep -E "(MongoDB|Scheduler|Connected|Capacity)" | tail -5`,
// Domain via HTTP (should 301 → HTTPS)
`echo "=== DOMAIN HTTP ===" && curl -s -o /dev/null -w "HTTP %{http_code}" http://demoplace.my.id/`,
// Domain via HTTPS from server itself
`echo "=== DOMAIN HTTPS ===" && curl -sk -o /dev/null -w "HTTP %{http_code}" https://demoplace.my.id/ 2>&1`,
]);
// External checks from local machine
console.log('\n\n══ External checks from local machine ══\n');
const tests = [
{ url: 'https://demoplace.my.id/', label: 'Root (expect 307 redirect to /login)' },
{ url: 'https://demoplace.my.id/login', label: 'Login page (expect 200)' },
{ url: 'https://demoplace.my.id/api/health', label: 'Backend health via domain (expect 200)' },
{ url: 'http://demoplace.my.id/', label: 'HTTP root (expect 301 HTTPS redirect)' },
];
for (const { url, label } of tests) {
const r = await fetch(url);
const ok = r.status >= 200 && r.status < 400;
const icon = ok ? '✅' : r.status === 0 ? '⚠️ ' : '❌';
console.log(` ${icon} [${String(r.status).padEnd(3)}] ${label}`);
if (r.headers?.location) console.log(` → Redirect: ${r.headers.location}`);
if (r.error) console.log(` → Error: ${r.error}`);
if (r.status === 200 && r.body) {
const preview = r.body.replace(/\s+/g, ' ').substring(0, 80);
console.log(` → Body: ${preview}`);
}
}
console.log('\n══════════════════════════════════════════════════════════════');
console.log(' Deployment Summary:');
console.log(' ┌─────────────────────────────────────────────────────┐');
console.log(' │ 🌐 Dashboard URL : https://demoplace.my.id │');
console.log(' │ 🔒 Backend : 127.0.0.1:3001 (internal only) │');
console.log(' │ 🔒 Proxy : 127.0.0.1:4000 (internal only) │');
console.log(' │ 🗄️ MongoDB : mongodb.prod.proit.id:27017 │');
console.log(' │ 🚀 Node.js : v18.19.1 │');
console.log(' │ ⚙️ PM2 : 3 processes (all online) │');
console.log(' └─────────────────────────────────────────────────────┘');
console.log('══════════════════════════════════════════════════════════\n');
}
main().catch(err => { console.error('[FATAL]', err); process.exit(1); });
-53
View File
@@ -1,53 +0,0 @@
// scripts/find-chunks.js
// Cari di mana file chunks yang benar berada di server
'use strict';
const { Client } = require('ssh2');
const conn = new Client();
const PUB = '/home/adminbackend/web/demoplace.my.id/public_html';
function exec(conn, cmd) {
console.log('\n$ ' + cmd.substring(0, 80));
return new Promise(resolve => {
conn.exec(cmd, (err, s) => {
if (err) { resolve(''); return; }
let out = '';
s.on('data', d => { out += d; process.stdout.write(d.toString()); })
.stderr.on('data', d => { out += d; process.stdout.write(d.toString()); })
.on('close', () => resolve(out));
});
});
}
conn.on('ready', async () => {
// 1. Cari file chunk yang direferensikan HTML
await exec(conn, 'find ' + PUB + ' -name "3km6z-n6wbgrs.js" 2>/dev/null | head -5');
await exec(conn, 'find ' + PUB + ' -name "21luguo5_g2uu.js" 2>/dev/null | head -5');
await exec(conn, 'find ' + PUB + ' -name "1u6dw_tm45utz.css" 2>/dev/null | head -5');
// 2. Cek isi static/static di standalone
const sa = PUB + '/.next/standalone/.next/static/static';
await exec(conn, 'ls ' + sa + '/ 2>/dev/null | head -10 || echo "Tidak ada nested static"');
// 3. Cek BUILD_ID utama
await exec(conn, 'cat ' + PUB + '/.next/BUILD_ID 2>/dev/null || echo "NO_BUILDID"');
// 4. Cek build-manifest.json untuk lihat chunk names
await exec(conn, 'cat ' + PUB + '/.next/standalone/.next/build-manifest.json 2>/dev/null | python3 -m json.tool 2>/dev/null | grep -A2 "login" | head -20');
// 5. Cek app-path-routes-manifest
await exec(conn, 'cat ' + PUB + '/.next/standalone/.next/app-path-routes-manifest.json 2>/dev/null | head -20');
// 6. Cek apakah ada next.js build di parent directory
await exec(conn, 'find /home/adminbackend -name "3km6z-n6wbgrs.js" 2>/dev/null | head -5');
// 7. Cek total disk structure
await exec(conn, 'du -sh ' + PUB + '/.next/standalone/.next/ 2>/dev/null');
await exec(conn, 'du -sh ' + PUB + '/.next/ 2>/dev/null | head -5');
conn.end();
console.log('\nDone.');
}).connect({
host: '103.185.47.52', port: 2222,
username: 'adminbackend', password: 'htEo7x6LsBQiEHHH',
readyTimeout: 30000
});
-18
View File
@@ -1,18 +0,0 @@
const { Client } = require('ssh2');
const conn = new Client();
conn.on('ready', () => {
conn.exec(`find /home/adminbackend/ -name "*.ts" -o -name "*.tsx" -o -name "*.js" | xargs grep -l "Overview Dashboard" 2>/dev/null`, (err, stream) => {
let out = '';
stream.on('data', d => out += d);
stream.on('close', () => {
console.log('=== FILES CONTAINING Overview Dashboard ===\n' + out);
conn.end();
});
});
}).connect({
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH'
});
-131
View File
@@ -1,131 +0,0 @@
// scripts/fix-and-restart.js
// ─────────────────────────────────────────────────────────────────────────────
// BackOne DPI — Fix standalone structure & reload PM2 with new code
// ─────────────────────────────────────────────────────────────────────────────
'use strict';
const { Client: SshClient } = require('ssh2');
const CONFIG = {
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH',
};
const ROOT = '/home/adminbackend/web/demoplace.my.id/public_html';
const PM2 = '/home/adminbackend/.npm-global/bin/pm2';
const COMMANDS = [
// ── Step 1: Fix the standalone directory structure ──────────────────────────
// Next.js standalone server.js expects .next/static and public INSIDE the standalone dir
`echo "=== STEP 1: Create standalone .next dir ==="`,
`mkdir -p ${ROOT}/.next/standalone/.next/`,
// Copy static chunks into standalone/.next/static
`echo "=== STEP 2: Copy static assets into standalone ===" && cp -r ${ROOT}/.next/static ${ROOT}/.next/standalone/.next/static && echo "static copied OK"`,
// Copy media files
`ls ${ROOT}/.next/standalone/.next/static/ 2>/dev/null | wc -l | xargs -I{} echo "Files in standalone static: {}"`,
// public is already copied in deploy script, verify it
`ls ${ROOT}/.next/standalone/public/ 2>/dev/null | head -5 || echo "WARNING: public dir missing in standalone"`,
// ── Step 2: Verify the ecosystem.config.js was updated ────────────────────
`echo "=== STEP 3: Show ecosystem.config.js script path ===" && grep "script" ${ROOT}/ecosystem.config.js | head -5`,
// ── Step 3: Reload PM2 with new ecosystem config ──────────────────────────
// Use reload instead of restart to minimize downtime
`echo "=== STEP 4: Reload PM2 frontend with new code ===" && cd ${ROOT} && NODE_ENV=production ${PM2} reload ecosystem.config.js --only backone-frontend 2>&1`,
// Also reload backend to pick up new code
`echo "=== STEP 5: Reload PM2 backend ===" && cd ${ROOT} && NODE_ENV=production ${PM2} reload ecosystem.config.js --only backone-backend 2>&1`,
// Reload proxy as well (it was already restarted but ensure env is correct)
`echo "=== STEP 6: Reload PM2 proxy ===" && cd ${ROOT} && NODE_ENV=production ${PM2} reload ecosystem.config.js --only backone-proxy 2>&1`,
// ── Step 4: Save PM2 state for auto-restart on reboot ─────────────────────
`echo "=== STEP 7: Save PM2 state ===" && ${PM2} save && echo "PM2 state saved"`,
// ── Step 5: Wait for processes to stabilize ────────────────────────────────
`sleep 8`,
// ── Step 6: Verify all processes ──────────────────────────────────────────
`echo "=== STEP 8: PM2 Status ===" && ${PM2} list`,
// ── Step 7: Health checks ─────────────────────────────────────────────────
`echo "=== BACKEND HEALTH ===" && curl -s http://127.0.0.1:3001/api/health`,
`echo ""`,
`echo "=== FRONTEND HTTP STATUS ===" && curl -s -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/ && echo ""`,
`echo "=== FRONTEND LOGIN PAGE ===" && curl -s -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/login && echo ""`,
// ── Step 8: Check port 3000 is now listening ──────────────────────────────
`echo "=== PORT 3000 STATUS ===" && ss -tlnp 2>/dev/null | grep 3000 || netstat -tlnp 2>/dev/null | grep 3000 || echo "Port 3000 check done"`,
// ── Step 9: Show frontend logs ────────────────────────────────────────────
`echo "=== FRONTEND PM2 LOG (last 20 lines) ===" && ${PM2} logs backone-frontend --lines 20 --nostream 2>&1 | tail -25`,
// ── Step 10: Show proxy MongoDB connection status ─────────────────────────
`echo "=== PROXY LOG (last 15 lines) ===" && ${PM2} logs backone-proxy --lines 15 --nostream 2>&1 | tail -20`,
];
function runSshCommands(commands) {
return new Promise((resolve, reject) => {
const ssh = new SshClient();
ssh.on('ready', () => {
console.log('[SSH] Connected!\n');
let index = 0;
function runNext() {
if (index >= commands.length) {
ssh.end();
return;
}
const cmd = commands[index++];
const preview = cmd.length > 150 ? cmd.substring(0, 150) + '...' : cmd;
console.log(`\n$ ${preview}`);
ssh.exec(cmd, (err, stream) => {
if (err) {
console.error(`[ERROR] ${err.message}`);
runNext();
return;
}
stream.on('data', (data) => process.stdout.write(data.toString()));
stream.stderr.on('data', (data) => {
const text = data.toString();
if (!text.includes('npm warn') && !text.includes('npm notice')) {
process.stdout.write(`[ERR] ${text}`);
}
});
stream.on('close', () => runNext());
});
}
runNext();
ssh.on('end', resolve);
});
ssh.on('error', reject);
ssh.connect({ ...CONFIG, readyTimeout: 30000 });
});
}
async function main() {
console.log('\n╔══════════════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — Fix Standalone & Reload PM2 ║');
console.log('╚══════════════════════════════════════════════════════════╝\n');
try {
await runSshCommands(COMMANDS);
console.log('\n✅ Fix & reload complete!\n');
} catch (err) {
console.error('\n[FATAL]', err.message);
process.exit(1);
}
}
main();
-120
View File
@@ -1,120 +0,0 @@
// scripts/fix-apache-ssl.js
// ─────────────────────────────────────────────────────────────────────────────
// BackOne DPI — Fix Apache HTTPS 502 Bad Gateway
// The SSL VirtualHost needs mod_proxy configuration.
// cPanel uses Apache with .htaccess for proxy rules, but SSL VirtualHost
// may need specific directives to enable mod_proxy_http for the [P] flag.
// ─────────────────────────────────────────────────────────────────────────────
'use strict';
const { Client: SshClient } = require('ssh2');
const CONFIG = {
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH',
};
const ROOT = '/home/adminbackend/web/demoplace.my.id/public_html';
const PM2 = '/home/adminbackend/.npm-global/bin/pm2';
const COMMANDS = [
// Investigate Apache config for SSL
`echo "=== APACHE MODULES ===" && apache2ctl -M 2>/dev/null | grep -i proxy | head -10 || httpd -M 2>/dev/null | grep -i proxy | head -10 || echo "Cannot check Apache modules"`,
// Check what's actually at the SSL port - is it Apache or Nginx or something else?
`echo "=== SSL PORT OWNER ===" && ss -tlnp | grep 443`,
// Check SSL log for 502 cause
`echo "=== SSL ACCESS LOG (last 10 502s) ===" && tail -50 /home/adminbackend/logs/demoplace.my.id-ssl_log 2>/dev/null | grep "502" | tail -10 || echo "No SSL log or no 502s in log"`,
`echo "=== SSL ERROR LOG (last 15) ===" && tail -15 /home/adminbackend/logs/demoplace.my.id-ssl_log 2>/dev/null || echo "No SSL log found"`,
// Find all log files for this domain
`echo "=== DOMAIN LOG FILES ===" && ls /home/adminbackend/logs/*demoplace* 2>/dev/null || echo "No domain-specific logs"`,
// Check Apache vhost config (cPanel stores them here)
`echo "=== APACHE SSL VHOST ===" && cat /etc/apache2/conf.d/userdata/ssl/2_4/adminbackend/demoplace.my.id/custom_subdirectory.conf 2>/dev/null || ls /etc/apache2/conf.d/userdata/ssl/2_4/adminbackend/ 2>/dev/null || echo "Cannot read Apache vhost config"`,
// Check all .htaccess files in path
`echo "=== ROOT HTACCESS ===" && cat ${ROOT}/.htaccess`,
// Check if mod_proxy is loaded
`echo "=== MOD_PROXY CHECK ===" && test -f /etc/apache2/mods-enabled/proxy.load && echo "mod_proxy ENABLED" || echo "mod_proxy NOT enabled"`,
`test -f /etc/apache2/mods-enabled/proxy_http.load && echo "mod_proxy_http ENABLED" || echo "mod_proxy_http NOT enabled"`,
// The issue may be cPanel's "nobody" user restriction
// Try using ProxyPass in .htaccess with explicit ProxyPassReverse
`echo "=== UPDATING .HTACCESS ===" && cat > ${ROOT}/.htaccess << 'EOF'
Options -MultiViews
RewriteEngine On
# Force HTTPS
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Proxy all requests to Next.js (port 3000)
RewriteRule ^(.*)$ http://127.0.0.1:3000/$1 [P,L,QSA]
ProxyPassReverse / http://127.0.0.1:3000/
EOF
echo ".htaccess updated"`,
// Read the new .htaccess to verify
`echo "=== NEW HTACCESS ===" && cat ${ROOT}/.htaccess`,
// Wait a moment for Apache to pick up changes
`sleep 2`,
// Test from server itself via HTTPS
`echo "=== HTTPS TEST FROM SERVER ===" && curl -sk -o /dev/null -w "HTTP %{http_code}" https://demoplace.my.id/ 2>&1`,
`echo "=== HTTPS LOGIN FROM SERVER ===" && curl -sk -o /dev/null -w "HTTP %{http_code}" https://demoplace.my.id/login 2>&1`,
// Check if it's a proxy timeout issue - try with verbose curl
`echo "=== VERBOSE HTTPS TEST ===" && curl -skv https://demoplace.my.id/ 2>&1 | grep -E "(<|>|\\*) " | head -20`,
// Alternative: check if cPanel has a NodeJS app manager entry interfering
`echo "=== CPANEL USERDATA ===" && ls /home/adminbackend/.cpanel/userdata/ 2>/dev/null`,
`cat /home/adminbackend/.cpanel/userdata/demoplace.my.id_SSL 2>/dev/null | head -30 || echo "No cPanel userdata for SSL"`,
`cat /home/adminbackend/.cpanel/userdata/demoplace.my.id 2>/dev/null | head -30 || echo "No cPanel userdata"`,
// Check Apache logs specifically for proxy errors
`echo "=== APACHE ERROR LOG ===" && tail -20 /var/log/apache2/error.log 2>/dev/null | grep -i "demoplace\|proxy\|502" | head -10 || echo "Cannot read Apache error log"`,
// PM2 processes still healthy?
`echo "=== PM2 STATUS ===" && ${PM2} list`,
`echo "=== FRONTEND HEALTH ===" && curl -s -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/login`,
];
function runSsh(commands) {
return new Promise((resolve, reject) => {
const ssh = new SshClient();
ssh.on('ready', () => {
let i = 0;
function next() {
if (i >= commands.length) { ssh.end(); return; }
const cmd = commands[i++];
console.log(`\n$ ${cmd.substring(0, 100)}${cmd.length > 100 ? '...' : ''}`);
ssh.exec(cmd, (err, stream) => {
if (err) { console.error('[ERR]', err.message); next(); return; }
stream.on('data', d => process.stdout.write(d.toString()));
stream.stderr.on('data', d => { const t = d.toString(); if (!t.includes('npm warn') && !t.includes('notice')) process.stdout.write(t); });
stream.on('close', next);
});
}
next();
ssh.on('end', resolve);
});
ssh.on('error', reject);
ssh.connect({ ...CONFIG, readyTimeout: 30000 });
});
}
async function main() {
console.log('\n╔══════════════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — Fix Apache HTTPS 502 Bad Gateway ║');
console.log('╚══════════════════════════════════════════════════════════╝\n');
await runSsh(COMMANDS);
console.log('\n✅ Apache SSL investigation complete!\n');
}
main().catch(err => { console.error('[FATAL]', err); process.exit(1); });
-141
View File
@@ -1,141 +0,0 @@
// scripts/fix-css-and-security.js
// 1. Fix CSS tidak termuat: buat symlink public_html/_next -> public_html/.next
// 2. Security audit: hapus cmd.php dan file berbahaya lainnya
'use strict';
const { Client } = require('ssh2');
const https = require('https');
const SSH = {
host: '103.185.47.52', port: 2222,
username: 'adminbackend', password: 'htEo7x6LsBQiEHHH',
readyTimeout: 60000
};
const PUB = '/home/adminbackend/web/demoplace.my.id/public_html';
function sshExec(conn, cmd, label = '') {
if (label) console.log(`\n[${label}]`);
console.log(`$ ${cmd.substring(0, 100)}${cmd.length > 100 ? '...' : ''}`);
return new Promise((resolve, reject) => {
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() {
console.log('\n╔══════════════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — Fix CSS + Security Audit ║');
console.log('╚══════════════════════════════════════════════════════════╝\n');
const conn = new Client();
await new Promise((resolve, reject) => { conn.on('ready', resolve).on('error', reject).connect(SSH); });
console.log('[SSH] Terhubung!\n');
// ── 1. SECURITY AUDIT ─────────────────────────────────────────────────
await sshExec(conn,
`echo "=== FILE BERBAHAYA ===" && ls -la ${PUB}/*.php ${PUB}/*.sh 2>/dev/null || echo "Tidak ada .php/.sh di root"`,
'1. Audit file PHP di public_html'
);
// Hapus cmd.php jika ada (file backdoor berbahaya)
await sshExec(conn,
`[ -f ${PUB}/cmd.php ] && rm -f ${PUB}/cmd.php && echo "DIHAPUS: cmd.php" || echo "cmd.php tidak ada (aman)"`,
'2. Hapus cmd.php (backdoor)'
);
// Hapus proxy.php lama (tidak diperlukan lagi karena nginx routing langsung ke port 3000)
await sshExec(conn,
`[ -f ${PUB}/proxy.php ] && rm -f ${PUB}/proxy.php && echo "DIHAPUS: proxy.php (tidak diperlukan)" || echo "proxy.php tidak ada"`,
'3. Hapus proxy.php (tidak dibutuhkan)'
);
// ── 2. CEK STRUKTUR DIREKTORI ──────────────────────────────────────────
await sshExec(conn,
`echo "=== STRUKTUR public_html ===" && ls -la ${PUB}/ | head -30`,
'4. Cek struktur public_html'
);
await sshExec(conn,
`echo "=== .next directory ===" && ls -la ${PUB}/.next/ 2>/dev/null | head -10 || echo "TIDAK ADA .next/"`,
'5. Cek .next directory'
);
await sshExec(conn,
`echo "=== static assets ===" && ls -la ${PUB}/.next/static/ 2>/dev/null | head -10 || echo "Tidak ada .next/static/"`,
'6. Cek .next/static'
);
await sshExec(conn,
`echo "=== _next symlink ===" && ls -la ${PUB}/_next 2>/dev/null || echo "Symlink _next belum ada"`,
'7. Cek symlink _next'
);
// ── 3. FIX CSS: Buat symlink _next → .next ────────────────────────────
await sshExec(conn,
`[ -e ${PUB}/_next ] && echo "Sudah ada _next" || (ln -s ${PUB}/.next ${PUB}/_next && echo "BERHASIL: Symlink _next → .next dibuat")`,
'8. Buat symlink _next -> .next'
);
// Verifikasi symlink
await sshExec(conn,
`ls -la ${PUB}/_next && ls ${PUB}/_next/static/ | head -5`,
'9. Verifikasi symlink'
);
// ── 4. PERBARUI .htaccess (bersih tanpa proxy.php) ────────────────────
const cleanHtaccess = `# BackOne DPI — Apache .htaccess
# Redirect HTTP ke HTTPS saja
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
`;
await sshExec(conn,
`printf '%s' ${JSON.stringify(cleanHtaccess)} > ${PUB}/.htaccess && echo "BERHASIL: .htaccess diperbarui"`,
'10. Perbarui .htaccess (hanya redirect HTTPS)'
);
// ── 5. TEST CSS LOADING ─────────────────────────────────────────────────
console.log('\n[11. Test loading CSS dari /_next/static/]');
await sshExec(conn,
`CSS_FILE=$(ls ${PUB}/.next/static/css/*.css 2>/dev/null | head -1) && ` +
`[ -n "$CSS_FILE" ] && ` +
`FILENAME=$(basename "$CSS_FILE") && ` +
`curl -sk -o /dev/null -w "CSS via domain: %{http_code} (size: %{size_download} bytes)" https://demoplace.my.id/_next/static/css/$FILENAME || ` +
`echo "Tidak ada file CSS ditemukan"`,
'11. Test CSS via domain'
);
conn.end();
// ── Test dari luar ──────────────────────────────────────────────────────
console.log('\n[12. Test domain setelah fix...]');
await new Promise(r => setTimeout(r, 2000));
for (const path of ['/login', '/api/health']) {
const r = await new Promise(resolve => {
const req = https.get(`https://demoplace.my.id${path}`,
{ timeout: 10000, rejectUnauthorized: false },
res => {
let body = '';
res.on('data', d => body += d);
res.on('end', () => resolve({ status: res.statusCode, body: body.substring(0, 100) }));
}
);
req.on('error', e => resolve({ status: 0, error: e.message }));
});
const icon = r.status === 200 ? '✅' : r.status === 301 ? '↩️ ' : '❌';
console.log(` ${icon} https://demoplace.my.id${path} → HTTP ${r.status}`);
if (path === '/api/health') console.log(` Body: ${r.body}`);
}
console.log('\n✅ Fix selesai! Coba refresh https://demoplace.my.id/login\n');
}
main().catch(err => { console.error('[FATAL]', err.message); process.exit(1); });
-175
View File
@@ -1,175 +0,0 @@
// scripts/fix-env-loader.js
// Fix: Buat Node.js env loader yang aman (tanpa bash glob expansion)
'use strict';
const { Client } = require('ssh2');
const SSH = { host: '103.185.47.52', port: 2222, username: 'adminbackend', password: 'htEo7x6LsBQiEHHH', readyTimeout: 60000 };
const PUB = '/home/adminbackend/web/demoplace.my.id/public_html';
const PM2 = '/home/adminbackend/.npm-global/bin/pm2';
function exec(conn, cmd, label) {
if (label) console.log('\n[' + label + ']');
console.log('$ ' + cmd.substring(0, 90));
return new Promise(resolve => {
conn.exec(cmd, (err, s) => {
if (err) { resolve(''); return; }
let out = '';
s.on('data', d => { out += d; process.stdout.write(d.toString()); })
.stderr.on('data', d => { out += d; process.stdout.write(d.toString()); })
.on('close', () => resolve(out));
});
});
}
// Konten start-with-env.js yang akan ditulis ke server
const startWithEnvContent = `'use strict';
// Polyfill global crypto for Node 18 compatibility (required by mongodb driver)
if (typeof globalThis.crypto === 'undefined') {
globalThis.crypto = require('crypto');
}
/**
* BackOne DPI - Safe Environment Loader for Next.js Standalone
* Reads .env.production safely without bash glob expansion issues.
*/
const path = require('path');
const fs = require('fs');
const envFile = path.join(__dirname, '.env.production');
if (fs.existsSync(envFile)) {
const lines = fs.readFileSync(envFile, 'utf8').split(/\\r?\\n/);
let loaded = 0;
for (const raw of lines) {
const line = raw.trim();
// Skip comments and empty lines
if (!line || line.startsWith('#')) continue;
const eqIdx = line.indexOf('=');
if (eqIdx === -1) continue;
const key = line.substring(0, eqIdx).trim();
// Only accept valid environment variable names
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) continue;
const val = line.substring(eqIdx + 1); // Do NOT trim value - preserve exact content
if (!(key in process.env)) {
process.env[key] = val;
loaded++;
}
}
console.log('[startup] Loaded ' + loaded + ' environment variables from .env.production');
console.log('[startup] NETIFY_API_KEY set:', !!process.env.NETIFY_API_KEY);
console.log('[startup] MONGODB_URI set: ', !!process.env.MONGODB_URI);
} else {
console.warn('[startup] WARNING: .env.production not found at:', envFile);
}
// Start Next.js standalone server
require('./.next/standalone/server.js');
`;
async function main() {
const conn = new Client();
await new Promise((r, j) => { conn.on('ready', r).on('error', j).connect(SSH); });
console.log('[SSH] Connected\n');
// 1. Upload start-with-env.js ke server
await exec(conn,
'cat > ' + PUB + '/start-with-env.js << \'NODELOADER_EOF\'\n' + startWithEnvContent + '\nNODELOADER_EOF\n' +
'echo "Node.js loader dibuat"',
'1. Buat start-with-env.js'
);
await exec(conn, 'wc -l ' + PUB + '/start-with-env.js', '2. Verifikasi file');
// 2. Update ecosystem.config.js untuk gunakan Node.js loader (bukan bash wrapper)
const ecoContent = `// ecosystem.config.js — PM2 Config (server-side)
module.exports = {
apps: [
{
name: 'backone-proxy',
script: './proxy/index.js',
cwd: '${PUB}',
instances: 1,
exec_mode: 'fork',
watch: false,
node_args: '--max-old-space-size=256',
max_memory_restart: '300M',
restart_delay: 5000,
max_restarts: 10,
env_file: '.env.production',
env: { NODE_ENV: 'production' },
error_file: './logs/proxy-error.log',
out_file: './logs/proxy-out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
merge_logs: true,
},
{
name: 'backone-backend',
script: './backend/server.js',
cwd: '${PUB}',
instances: 1,
exec_mode: 'fork',
watch: false,
node_args: '--max-old-space-size=256',
max_memory_restart: '400M',
restart_delay: 3000,
max_restarts: 10,
env_file: '.env.production',
env: { NODE_ENV: 'production' },
error_file: './logs/backend-error.log',
out_file: './logs/backend-out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
merge_logs: true,
},
{
name: 'backone-frontend',
script: './start-with-env.js',
cwd: '${PUB}',
instances: 1,
exec_mode: 'fork',
watch: false,
node_args: '--max-old-space-size=512',
max_memory_restart: '700M',
restart_delay: 3000,
max_restarts: 10,
env: {
NODE_ENV: 'production',
PORT: '3000',
HOSTNAME: '127.0.0.1',
NEXT_TELEMETRY_DISABLED: '1',
},
error_file: './logs/frontend-error.log',
out_file: './logs/frontend-out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
merge_logs: true,
},
],
};`;
await exec(conn,
'cat > ' + PUB + '/ecosystem.config.js << \'ECO_EOF\'\n' + ecoContent + '\nECO_EOF\necho "ecosystem.config.js diperbarui"',
'3. Update ecosystem.config.js'
);
// 3. Delete & restart frontend
await exec(conn,
'cd ' + PUB + ' && ' + PM2 + ' delete backone-frontend 2>/dev/null; ' +
PM2 + ' start ecosystem.config.js --only backone-frontend && echo "START OK"',
'4. Restart frontend dengan Node.js loader'
);
// 4. Tunggu startup
await new Promise(r => setTimeout(r, 8000));
// 5. Cek out log (harus tampil "Loaded X environment variables")
await exec(conn, 'tail -10 ' + PUB + '/logs/frontend-out.log 2>/dev/null', '5. Out log (env loader output)');
await exec(conn, 'tail -5 ' + PUB + '/logs/frontend-error.log 2>/dev/null', '6. Error log terbaru');
// 6. Test agents
await exec(conn, 'curl -sk -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/agents', '7. Test /agents page');
// 7. PM2 status
await exec(conn, PM2 + ' list 2>/dev/null | grep backone', '8. PM2 status');
conn.end();
console.log('\n✅ Selesai!\n');
}
main().catch(e => console.error('[FATAL]', e.message));
-78
View File
@@ -1,78 +0,0 @@
// scripts/fix-env-vars.js
// Fix: Pastikan environment variables dari .env.production dimuat oleh PM2 frontend
'use strict';
const { Client } = require('ssh2');
const SSH = { host: '103.185.47.52', port: 2222, username: 'adminbackend', password: 'htEo7x6LsBQiEHHH', readyTimeout: 60000 };
const PUB = '/home/adminbackend/web/demoplace.my.id/public_html';
const PM2 = '/home/adminbackend/.npm-global/bin/pm2';
function exec(conn, cmd, label) {
if (label) console.log('\n[' + label + ']');
console.log('$ ' + cmd.substring(0, 90));
return new Promise(resolve => {
conn.exec(cmd, (err, s) => {
if (err) { resolve(''); return; }
let out = '';
s.on('data', d => { out += d; process.stdout.write(d.toString()); })
.stderr.on('data', d => { out += d; process.stdout.write(d.toString()); })
.on('close', () => resolve(out));
});
});
}
async function main() {
const conn = new Client();
await new Promise((r, j) => { conn.on('ready', r).on('error', j).connect(SSH); });
console.log('[SSH] Connected\n');
// 1. Cek env vars yang diterima proses PM2 sekarang
await exec(conn, PM2 + ' env 3 2>/dev/null | grep -E "(NETIFY|BACKONE|MONGO|JWT)" | head -15', '1. Env vars di PM2 proses 3 (frontend)');
// 2. Cek isi .env.production
await exec(conn, 'cat ' + PUB + '/.env.production 2>/dev/null | grep -v "#" | grep -v "^$" | head -30', '2. Isi .env.production (non-comment)');
// 3. Export vars dari .env.production dan restart dengan env vars eksplisit
// Baca .env.production, parse, dan set ke PM2
const envScript = `
set -a
source ${PUB}/.env.production 2>/dev/null
set +a
export NETIFY_API_KEY="$NETIFY_API_KEY"
export NETIFY_ORG_UUID="$NETIFY_ORG_UUID"
export NETIFY_SITE_UUIDS="$NETIFY_SITE_UUIDS"
export NETIFY_INFORMATICS_BASE_URL="$NETIFY_INFORMATICS_BASE_URL"
export MONGODB_URI="$MONGODB_URI"
export JWT_SECRET="$JWT_SECRET"
export NEXT_PUBLIC_API_URL="$NEXT_PUBLIC_API_URL"
echo "API_KEY=$NETIFY_API_KEY"
echo "MONGO_URI_PARTIAL=$(echo $MONGODB_URI | cut -c1-30)..."
`.trim();
await exec(conn, 'bash -c \'' + envScript.replace(/\n/g, '; ') + '\'', '3. Verifikasi env vars bisa dibaca dari .env.production');
// 4. Delete dan restart PM2 proses dengan ecosystem file yang membaca env_file
await exec(conn,
'cd ' + PUB + ' && ' + PM2 + ' delete backone-frontend 2>/dev/null; ' +
PM2 + ' start ecosystem.config.js --only backone-frontend --env production && echo "START OK"',
'4. Restart frontend via ecosystem.config.js'
);
// 5. Verifikasi env vars sekarang dimuat
await new Promise(r => setTimeout(r, 5000));
await exec(conn, PM2 + ' env 3 2>/dev/null | grep -E "(NETIFY|BACKONE|MONGO)" | head -10', '5. Verifikasi env vars setelah restart');
// 6. Test agents page
await new Promise(r => setTimeout(r, 5000));
await exec(conn, 'curl -sk -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/agents', '6. Test /agents');
// 7. Cek error log terbaru
await exec(conn, 'tail -5 ' + PUB + '/logs/frontend-error.log 2>/dev/null', '7. Error log terbaru');
// 8. PM2 status akhir
await exec(conn, PM2 + ' list 2>/dev/null | grep backone', '8. PM2 status akhir');
conn.end();
console.log('\n✅ Selesai!\n');
}
main().catch(e => console.error('[FATAL]', e.message));
-110
View File
@@ -1,110 +0,0 @@
// scripts/fix-https.js
// ─────────────────────────────────────────────────────────────────────────────
// BackOne DPI — Fix HTTPS 502 Bad Gateway Issue
// The cPanel Apache proxy is getting 502 — need to investigate and fix
// ─────────────────────────────────────────────────────────────────────────────
'use strict';
const { Client: SshClient } = require('ssh2');
const CONFIG = {
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH',
};
const ROOT = '/home/adminbackend/web/demoplace.my.id/public_html';
const PM2 = '/home/adminbackend/.npm-global/bin/pm2';
const COMMANDS = [
// Check current frontend status
`echo "=== FRONTEND PROCESS ==="`,
`ps aux | grep node | grep -v grep`,
`ss -tlnp | grep 3000`,
// Check if frontend is responding internally
`echo "=== INTERNAL FRONTEND TEST ===" && curl -sv http://127.0.0.1:3000/ 2>&1 | grep -E "(< HTTP|< Location|Connected|curl)" | head -10`,
// Check the .htaccess to confirm proxy setup
`echo "=== .HTACCESS CONTENT ===" && cat ${ROOT}/.htaccess`,
// Check Apache error log for this domain
`echo "=== APACHE ERROR (last 20 lines) ===" && tail -20 /home/adminbackend/logs/demoplace.my.id-ssl_log 2>/dev/null || tail -20 /home/adminbackend/logs/demoplace.my.id-error_log 2>/dev/null || tail -20 /home/adminbackend/logs/error_log 2>/dev/null || echo "No Apache error log found"`,
// Check all logs
`echo "=== LOG FILES ===" && ls /home/adminbackend/logs/ 2>/dev/null | head -20`,
// Check domain SSL logs
`echo "=== SSL LOG ===" && tail -30 /home/adminbackend/logs/demoplace.my.id-ssl_log 2>/dev/null | grep -E "(502|500|error|proxy)" | tail -15 || echo "No SSL log"`,
// Try the domain via HTTP (non-SSL)
`echo "=== HTTP (non-SSL) TEST ===" && curl -sv http://demoplace.my.id/ 2>&1 | grep -E "(< HTTP|Location|Connected)" | head -5`,
// Check PM2 frontend is running
`echo "=== PM2 FRONTEND STATUS ===" && ${PM2} show backone-frontend 2>&1 | grep -E "(status|pid|uptime|restart)" | head -10`,
// Check frontend log for errors
`echo "=== FRONTEND LOG (errors) ===" && ${PM2} logs backone-frontend --lines 30 --nostream 2>&1 | grep -E "(Error|error|ECONNRESET|Ready|✓)" | tail -15`,
// Re-check port
`echo "=== PORT 3000 DETAILED ===" && ss -tlnp | grep 3000`,
// Try direct curl to frontend with more verbose output
`echo "=== DETAILED FRONTEND CURL ===" && curl -v http://127.0.0.1:3000/login 2>&1 | head -30`,
// Check if there's an issue with the standalone server path in PM2
`echo "=== PM2 SCRIPT PATH ===" && ${PM2} show backone-frontend 2>&1 | grep -E "(script|cwd|exec)" | head -5`,
// Check node version
`echo "=== NODE VERSION ===" && node --version`,
// Check if frontend started correctly after restart
`echo "=== FRONTEND READY STATUS ===" && ${PM2} logs backone-frontend --lines 10 --nostream 2>&1 | tail -15`,
// Try restarting frontend
`echo "=== RESTARTING FRONTEND ===" && ${PM2} restart backone-frontend && sleep 5 && echo "Restarted"`,
// Final port check
`echo "=== FINAL PORT CHECK ===" && ss -tlnp | grep 3000`,
`echo "=== FINAL CURL ===" && curl -s -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/login`,
// Check if maybe cPanel node app is conflicting
`echo "=== CPANEL NODE APPS ===" && ls /home/adminbackend/nodevenv/ 2>/dev/null || echo "No cPanel Node.js virtual environments"`,
`cat /home/adminbackend/.cpanel/nvdata/node_application_config 2>/dev/null | head -30 || echo "No cPanel Node config"`,
];
function runSsh(commands) {
return new Promise((resolve, reject) => {
const ssh = new SshClient();
ssh.on('ready', () => {
console.log('[SSH] Connected!\n');
let i = 0;
function next() {
if (i >= commands.length) { ssh.end(); return; }
const cmd = commands[i++];
console.log(`\n$ ${cmd.substring(0, 130)}${cmd.length > 130 ? '...' : ''}`);
ssh.exec(cmd, (err, stream) => {
if (err) { console.error('[ERR]', err.message); next(); return; }
stream.on('data', d => process.stdout.write(d.toString()));
stream.stderr.on('data', d => { const t = d.toString(); if (!t.includes('npm warn') && !t.includes('notice')) process.stdout.write(t); });
stream.on('close', next);
});
}
next();
ssh.on('end', resolve);
});
ssh.on('error', reject);
ssh.connect({ ...CONFIG, readyTimeout: 30000 });
});
}
async function main() {
console.log('\n╔══════════════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — HTTPS 502 Investigation & Fix ║');
console.log('╚══════════════════════════════════════════════════════════╝\n');
await runSsh(COMMANDS);
console.log('\n✅ Investigation complete!\n');
}
main().catch(err => { console.error('[FATAL]', err); process.exit(1); });
-88
View File
@@ -1,88 +0,0 @@
// scripts/fix-missing-css.js
'use strict';
const { Client } = require('ssh2');
const https = require('https');
const SSH = { host: '103.185.47.52', port: 2222, username: 'adminbackend', password: 'htEo7x6LsBQiEHHH', readyTimeout: 60000 };
const PUB = '/home/adminbackend/web/demoplace.my.id/public_html';
const SA = PUB + '/.next/standalone/.next/static';
const BP = '/home/adminbackend/backone-production/.next/static';
function exec(conn, cmd, label) {
if (label) console.log('\n[' + label + ']');
console.log('$ ' + cmd.substring(0, 80));
return new Promise(resolve => {
conn.exec(cmd, (err, s) => {
if (err) { resolve(''); return; }
let out = '';
s.on('data', d => { out += d; process.stdout.write(d.toString()); })
.stderr.on('data', d => { out += d; process.stdout.write(d.toString()); })
.on('close', () => resolve(out));
});
});
}
async function httpGet(url) {
return new Promise(resolve => {
const req = https.get(url, { timeout: 12000, rejectUnauthorized: false }, res => {
let body = '';
res.on('data', d => body += d);
res.on('end', () => resolve({ status: res.statusCode, size: body.length, type: res.headers['content-type'] }));
});
req.on('error', e => resolve({ status: 0, error: e.message }));
req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
});
}
async function main() {
const conn = new Client();
await new Promise((r, j) => { conn.on('ready', r).on('error', j).connect(SSH); });
console.log('[SSH] Connected\n');
// 1. Cari file CSS yang hilang
await exec(conn, 'find /home/adminbackend -name "1oskchnhjm4n8.css" 2>/dev/null | head -5', '1. Cari CSS hilang');
// 2. List CSS di backone-production
await exec(conn, 'find ' + BP + ' -name "*.css" 2>/dev/null | head -10', '2. CSS di backone-production');
// 3. Full rsync chunks (paksa semua file)
await exec(conn,
'rsync -av ' + BP + '/chunks/ ' + SA + '/chunks/ 2>&1 | grep "css\\|sent\\|total" | head -10',
'3. Full rsync chunks'
);
// 4. Verifikasi CSS setelah rsync
await exec(conn, 'find ' + SA + '/chunks -name "*.css" 2>/dev/null', '4. CSS di standalone setelah rsync');
// 5. Cek total file
await exec(conn, 'ls ' + SA + '/chunks/ | wc -l', '5. Total file chunks');
conn.end();
// 6. Final test
console.log('\n[6. Final test static files...]');
await new Promise(r => setTimeout(r, 2000));
const files = [
'/_next/static/chunks/3km6z-n6wbgrs.js',
'/_next/static/chunks/1u6dw_tm45utz.css',
'/_next/static/chunks/1oskchnhjm4n8.css',
'/_next/static/chunks/21luguo5_g2uu.js',
'/_next/static/chunks/1-8s9_t85wwr4.js',
'/_next/static/chunks/2n6j37owj-kaf.js',
];
let allOk = true;
for (const f of files) {
const r = await httpGet('https://demoplace.my.id' + f);
const icon = r.status === 200 ? '✅' : '❌';
const ext = f.endsWith('.css') ? 'CSS' : 'JS ';
console.log(' ' + icon + ' [' + ext + '] HTTP ' + r.status + ' | ' + (r.size || 0) + ' bytes');
if (r.status !== 200) { console.log(' → ' + f); allOk = false; }
}
if (allOk) {
console.log('\n🎉 SEMUA CSS/JS BERHASIL! Lakukan hard refresh (Ctrl+Shift+R)\n');
} else {
console.log('\n⚠ Masih ada yang 404. Cek manual.\n');
}
}
main().catch(e => console.error('[FATAL]', e.message));
-80
View File
@@ -1,80 +0,0 @@
// scripts/fix-mongoose-module.js
// Fix: Copy mongoose ke standalone/node_modules agar tersedia sebagai external package
'use strict';
const { Client } = require('ssh2');
const SSH = { host: '103.185.47.52', port: 2222, username: 'adminbackend', password: 'htEo7x6LsBQiEHHH', readyTimeout: 60000 };
const PUB = '/home/adminbackend/web/demoplace.my.id/public_html';
const SA = PUB + '/.next/standalone';
const BP = '/home/adminbackend/backone-production';
function exec(conn, cmd, label) {
if (label) console.log('\n[' + label + ']');
console.log('$ ' + cmd.substring(0, 90));
return new Promise(resolve => {
conn.exec(cmd, (err, s) => {
if (err) { resolve(''); return; }
let out = '';
s.on('data', d => { out += d; process.stdout.write(d.toString()); })
.stderr.on('data', d => { out += d; process.stdout.write(d.toString()); })
.on('close', () => resolve(out));
});
});
}
async function main() {
const conn = new Client();
await new Promise((r, j) => { conn.on('ready', r).on('error', j).connect(SSH); });
console.log('[SSH] Connected\n');
const PM2 = '/home/adminbackend/.npm-global/bin/pm2';
// 1. Cek lokasi mongoose
await exec(conn, 'ls ' + SA + '/node_modules/mongoose 2>/dev/null | head -3 || echo "Tidak ada di standalone/node_modules"', '1. Cek mongoose di standalone');
await exec(conn, 'ls ' + BP + '/node_modules/mongoose 2>/dev/null | head -3 || echo "Tidak ada di backone-production"', '2. Cek mongoose di backone-production');
await exec(conn, 'ls ' + PUB + '/node_modules/mongoose 2>/dev/null | head -3 || echo "Tidak ada di public_html/node_modules"', '3. Cek mongoose di public_html');
// 2. Copy mongoose ke standalone/node_modules (dari backone-production)
const mongooseSrc = BP + '/node_modules/mongoose';
const mongooseDst = SA + '/node_modules/mongoose';
await exec(conn,
'[ -d ' + mongooseSrc + ' ] && (' +
' [ -e ' + mongooseDst + ' ] && echo "Sudah ada" || ' +
' (cp -r ' + mongooseSrc + ' ' + mongooseDst + ' && echo "BERHASIL: mongoose disalin ke standalone") ' +
') || echo "Sumber tidak ditemukan"',
'4. Copy mongoose ke standalone/node_modules'
);
// 3. Verifikasi
await exec(conn, 'ls ' + mongooseDst + ' 2>/dev/null | head -5 || echo "GAGAL: tidak ada"', '5. Verifikasi mongoose di standalone');
// 4. Juga pastikan dependensi mongoose tersedia (bson, etc.)
const deps = ['bson', 'kareem', 'mquery', 'mpath', 'sift'];
for (const dep of deps) {
const src = BP + '/node_modules/' + dep;
const dst = SA + '/node_modules/' + dep;
await exec(conn,
'[ -d ' + src + ' ] && ([ -e ' + dst + ' ] || (cp -r ' + src + ' ' + dst + ' && echo "Copied: ' + dep + '")) || echo "Skip: ' + dep + '"',
null
);
}
// 5. Restart frontend
await exec(conn, PM2 + ' restart backone-frontend && echo "RESTART OK"', '6. Restart backone-frontend');
// 6. Tunggu dan cek status
await new Promise(r => setTimeout(r, 5000));
await exec(conn, PM2 + ' list 2>/dev/null | grep backone', '7. Status PM2 setelah restart');
// 7. Test halaman agents
await new Promise(r => setTimeout(r, 3000));
await exec(conn, 'curl -sk -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/agents', '8. Test /agents setelah fix');
// 8. Cek error log setelah restart
await exec(conn, 'tail -10 ' + PUB + '/logs/frontend-error.log 2>/dev/null', '9. Error log terbaru');
conn.end();
console.log('\n✅ Selesai! Refresh browser ke https://demoplace.my.id/agents\n');
}
main().catch(e => console.error('[FATAL]', e.message));
-156
View File
@@ -1,156 +0,0 @@
// scripts/fix-pm2-env-final.js
// Fix definitif: Buat startup wrapper script yang load .env.production sebelum menjalankan Next.js
'use strict';
const { Client } = require('ssh2');
const SSH = { host: '103.185.47.52', port: 2222, username: 'adminbackend', password: 'htEo7x6LsBQiEHHH', readyTimeout: 60000 };
const PUB = '/home/adminbackend/web/demoplace.my.id/public_html';
const PM2 = '/home/adminbackend/.npm-global/bin/pm2';
function exec(conn, cmd, label) {
if (label) console.log('\n[' + label + ']');
console.log('$ ' + cmd.substring(0, 90));
return new Promise(resolve => {
conn.exec(cmd, (err, s) => {
if (err) { resolve(''); return; }
let out = '';
s.on('data', d => { out += d; process.stdout.write(d.toString()); })
.stderr.on('data', d => { out += d; process.stdout.write(d.toString()); })
.on('close', () => resolve(out));
});
});
}
async function main() {
const conn = new Client();
await new Promise((r, j) => { conn.on('ready', r).on('error', j).connect(SSH); });
console.log('[SSH] Connected\n');
// 1. Buat wrapper script yang source .env.production
const wrapperContent = [
'#!/bin/bash',
'# BackOne DPI - Frontend startup wrapper',
'# Source environment variables from .env.production',
'ENV_FILE="' + PUB + '/.env.production"',
'if [ -f "$ENV_FILE" ]; then',
' set -a',
' source "$ENV_FILE"',
' set +a',
' echo "[startup] Environment variables loaded from .env.production"',
'else',
' echo "[startup] WARNING: .env.production not found at $ENV_FILE"',
'fi',
'# Start Next.js standalone server',
'exec node ' + PUB + '/.next/standalone/server.js',
].join('\n');
await exec(conn,
'cat > ' + PUB + '/start-frontend.sh << \'WRAPPER_EOF\'\n' + wrapperContent + '\nWRAPPER_EOF\n' +
'chmod +x ' + PUB + '/start-frontend.sh && echo "Wrapper script dibuat"',
'1. Buat wrapper startup script'
);
await exec(conn, 'cat ' + PUB + '/start-frontend.sh', '2. Verifikasi isi wrapper script');
// 2. Update ecosystem.config.js di SERVER untuk gunakan wrapper script
const ecosystemContent = `// ecosystem.config.js — PM2 Production Config (Server-side, DO NOT COMMIT)
module.exports = {
apps: [
{
name: 'backone-proxy',
script: './proxy/index.js',
cwd: '${PUB}',
instances: 1,
exec_mode: 'fork',
watch: false,
node_args: '--max-old-space-size=256',
max_memory_restart: '300M',
restart_delay: 5000,
max_restarts: 10,
env_file: '.env.production',
env: { NODE_ENV: 'production' },
error_file: './logs/proxy-error.log',
out_file: './logs/proxy-out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
merge_logs: true,
},
{
name: 'backone-backend',
script: './backend/server.js',
cwd: '${PUB}',
instances: 1,
exec_mode: 'fork',
watch: false,
node_args: '--max-old-space-size=256',
max_memory_restart: '400M',
restart_delay: 3000,
max_restarts: 10,
env_file: '.env.production',
env: { NODE_ENV: 'production' },
error_file: './logs/backend-error.log',
out_file: './logs/backend-out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
merge_logs: true,
},
{
name: 'backone-frontend',
script: './start-frontend.sh',
interpreter: 'bash',
cwd: '${PUB}',
instances: 1,
exec_mode: 'fork',
watch: false,
max_memory_restart: '700M',
restart_delay: 3000,
max_restarts: 10,
env: {
NODE_ENV: 'production',
PORT: 3000,
HOSTNAME: '127.0.0.1',
NEXT_TELEMETRY_DISABLED: '1',
},
error_file: './logs/frontend-error.log',
out_file: './logs/frontend-out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
merge_logs: true,
},
],
};`;
await exec(conn,
'cat > ' + PUB + '/ecosystem.config.js << \'ECOSYSTEM_EOF\'\n' + ecosystemContent + '\nECOSYSTEM_EOF\n' +
'echo "ecosystem.config.js diperbarui"',
'3. Update ecosystem.config.js di server'
);
// 3. Delete & restart frontend dengan config baru
await exec(conn,
'cd ' + PUB + ' && ' + PM2 + ' delete backone-frontend 2>/dev/null; ' +
PM2 + ' start ecosystem.config.js --only backone-frontend && echo "START OK"',
'4. Start frontend dengan wrapper script'
);
// 4. Tunggu startup
await new Promise(r => setTimeout(r, 8000));
// 5. Verifikasi env vars
const listOut = await exec(conn, PM2 + ' list 2>/dev/null | grep backone-frontend', '5. PM2 status');
// Ambil ID proses baru
const match = listOut.match(/│\s+(\d+)\s+│\s+backone-frontend/);
if (match) {
const newId = match[1];
await exec(conn, PM2 + ' env ' + newId + ' 2>/dev/null | grep -E "(NETIFY|MONGO|JWT)" | head -5', '6. Env vars di proses baru');
}
// 6. Test agents
await exec(conn, 'curl -sk -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/agents', '7. Test /agents');
// 7. Error log terbaru
await exec(conn, 'tail -5 ' + PUB + '/logs/frontend-error.log 2>/dev/null', '8. Error log terbaru');
await exec(conn, 'tail -3 ' + PUB + '/logs/frontend-out.log 2>/dev/null', '9. Out log terbaru');
conn.end();
console.log('\n✅ Selesai!\n');
}
main().catch(e => console.error('[FATAL]', e.message));
-223
View File
@@ -1,223 +0,0 @@
// scripts/fix-proxy-headers.js
// Fix: proxy.php harus hapus Content-Length agar CSS/JS tidak terpotong
'use strict';
const SftpClient = require('ssh2-sftp-client');
const { Client } = require('ssh2');
const https = require('https');
const CONFIG = {
host: '103.185.47.52', port: 2222,
username: 'adminbackend', password: 'htEo7x6LsBQiEHHH',
readyTimeout: 60000
};
const ROOT = '/home/adminbackend/web/demoplace.my.id/public_html';
// ── proxy.php yang sudah diperbaiki ──────────────────────────────────────
// Fix utama: tambahkan content-length ke daftar header yang dibuang
// agar tidak terjadi mismatch antara ukuran compressed vs uncompressed
const PROXY_PHP = `<?php
/**
* BackOne DPI - PHP Reverse Proxy (Fixed)
* Forward semua request ke Next.js port 3000
* Fix: Content-Length dihapus agar tidak terjadi data truncation
*/
define('NEXTJS_URL', 'http://127.0.0.1:3000');
$uri = $_SERVER['REQUEST_URI'] ?? '/';
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
$target = NEXTJS_URL . $uri;
// Headers yang diteruskan ke Next.js
$fwdHeaders = [
'X-Forwarded-For: ' . ($_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'),
'X-Forwarded-Proto: https',
'X-Real-IP: ' . ($_SERVER['REMOTE_ADDR'] ?? ''),
'Host: demoplace.my.id',
'Accept-Encoding: identity',
];
$allHeaders = getallheaders() ?: [];
$skip = ['host', 'connection', 'transfer-encoding', 'keep-alive', 'content-length', 'accept-encoding'];
foreach ($allHeaders as $name => $value) {
if (!in_array(strtolower($name), $skip)) {
$fwdHeaders[] = "$name: $value";
}
}
$body = in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE']) ? file_get_contents('php://input') : null;
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $target,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $fwdHeaders,
CURLOPT_POSTFIELDS => $body,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_CONNECTTIMEOUT => 5,
// PENTING: Tidak pakai CURLOPT_ENCODING agar response tidak dikompresi
// (kita sudah kirim Accept-Encoding: identity ke Next.js)
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$curlError = curl_error($ch);
curl_close($ch);
if ($response === false) {
http_response_code(502);
header('Content-Type: text/plain');
echo 'BackOne: Gagal terhubung ke Next.js. Error: ' . $curlError;
exit;
}
$responseHeaders = substr($response, 0, $headerSize);
$responseBody = substr($response, $headerSize);
http_response_code($httpCode ?: 200);
// Header yang TIDAK diteruskan ke browser
$skipRes = [
'transfer-encoding',
'connection',
'keep-alive',
'content-encoding',
'content-length', // FIX: hapus agar tidak terjadi size mismatch
];
foreach (explode("\\r\\n", $responseHeaders) as $header) {
$header = trim($header);
if (empty($header) || preg_match('/^HTTP\\//i', $header)) continue;
$colonPos = strpos($header, ':');
if ($colonPos === false) continue;
$headerName = strtolower(trim(substr($header, 0, $colonPos)));
if (in_array($headerName, $skipRes)) continue;
header($header, false);
}
echo $responseBody;
`;
// ── .htaccess dengan FollowSymLinks ──────────────────────────────────────
const HTACCESS = `# BackOne DPI — Apache .htaccess
Options +FollowSymLinks
# Redirect HTTP ke HTTPS
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Static Next.js files: sajikan langsung dari disk (via symlink _next -> .next)
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^ - [L]
# Semua request lain: teruskan ke PHP proxy (Next.js port 3000)
RewriteRule ^(.*)$ /proxy.php [L,QSA]
`;
async function main() {
console.log('\n╔══════════════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — Fix CSS/JS Content-Length & Symlinks ║');
console.log('╚══════════════════════════════════════════════════════════╝\n');
// ── Diagnosa dulu: cek apa yang dikirim ke browser untuk CSS ──────────
console.log('▶ Diagnosa: Cek CSS URL yang ada di HTML login page...');
const conn1 = new Client();
await new Promise((r, j) => { conn1.on('ready', r).on('error', j).connect(CONFIG); });
// Ambil HTML dari port 3000 dan cari CSS links
const htmlResult = await new Promise(resolve => {
conn1.exec(
`curl -sk http://127.0.0.1:3000/login | grep -o 'href="/_next/static/css/[^"]*"' | head -3`,
(err, s) => {
if (err) { resolve('error'); return; }
let out = '';
s.on('data', d => { out += d; process.stdout.write(d.toString()); })
.stderr.on('data', d => { out += d; })
.on('close', () => resolve(out.trim()));
}
);
});
console.log(`CSS refs: ${htmlResult}`);
// Test apakah CSS bisa diakses langsung
if (htmlResult) {
const cssPath = htmlResult.match(/href="([^"]+)"/)?.[1];
if (cssPath) {
await new Promise(resolve => {
conn1.exec(
`curl -sk -D - http://127.0.0.1:3000${cssPath} | head -15`,
(err, s) => {
if (err) { resolve(); return; }
s.on('data', d => process.stdout.write(d.toString()))
.stderr.on('data', () => {})
.on('close', resolve);
}
);
});
}
}
conn1.end();
// ── Upload proxy.php dan .htaccess yang sudah diperbaiki ──────────────
console.log('\n▶ Upload perbaikan via SFTP...');
const sftp = new SftpClient();
await sftp.connect(CONFIG);
await sftp.put(Buffer.from(PROXY_PHP, 'utf8'), `${ROOT}/proxy.php`);
console.log(' ✅ proxy.php (fixed: Content-Length stripped, Accept-Encoding: identity)');
await sftp.put(Buffer.from(HTACCESS, 'utf8'), `${ROOT}/.htaccess`);
console.log(' ✅ .htaccess (fixed: +FollowSymLinks added)');
await sftp.end();
// ── Verifikasi CSS via domain ─────────────────────────────────────────
console.log('\n▶ Test CSS loading via domain (tunggu 3 detik)...');
await new Promise(r => setTimeout(r, 3000));
const conn2 = new Client();
await new Promise((r, j) => { conn2.on('ready', r).on('error', j).connect(CONFIG); });
// Ambil CSS URL dari domain lalu test
const cssUrlResult = await new Promise(resolve => {
conn2.exec(
`CSS=$(curl -sk https://demoplace.my.id/login | grep -o '/_next/static/css/[^"]*' | head -1); ` +
`[ -n "$CSS" ] && curl -sk -D - "https://demoplace.my.id$CSS" | head -10 || echo "Tidak ada CSS URL ditemukan"`,
(err, s) => {
if (err) { resolve(); return; }
s.on('data', d => process.stdout.write(d.toString()))
.stderr.on('data', () => {})
.on('close', resolve);
}
);
});
conn2.end();
// ── Final test ────────────────────────────────────────────────────────
console.log('\n▶ Final test domain...');
for (const path of ['/', '/login']) {
const r = await new Promise(resolve => {
const req = https.get(`https://demoplace.my.id${path}`,
{ timeout: 12000, rejectUnauthorized: false },
res => resolve({ status: res.statusCode, location: res.headers.location })
);
req.on('error', e => resolve({ status: 0, error: e.message }));
req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
});
const icon = r.status === 200 ? '✅' : r.status >= 300 && r.status < 400 ? '↩️ ' : '❌';
console.log(` ${icon} https://demoplace.my.id${path} → HTTP ${r.status}`);
}
console.log('\n✅ Selesai! Refresh browser dengan Ctrl+Shift+R (hard refresh)\n');
}
main().catch(err => { console.error('[FATAL]', err.message); process.exit(1); });
-129
View File
@@ -1,129 +0,0 @@
// scripts/fix-static-paths.js
// Menyalin static files dari backone-production ke standalone yang benar
// Masalah: Upload Windows membuat direktori dengan backslash literal di Linux
'use strict';
const { Client } = require('ssh2');
const https = require('https');
const SSH = {
host: '103.185.47.52', port: 2222,
username: 'adminbackend', password: 'htEo7x6LsBQiEHHH',
readyTimeout: 60000
};
function exec(conn, cmd, label) {
if (label) console.log('\n[' + label + ']');
console.log('$ ' + cmd.substring(0, 90));
return new Promise(resolve => {
conn.exec(cmd, (err, s) => {
if (err) { resolve(''); return; }
let out = '';
s.on('data', d => { out += d; process.stdout.write(d.toString()); })
.stderr.on('data', d => { out += d; process.stdout.write(d.toString()); })
.on('close', () => resolve(out));
});
});
}
async function httpGet(url) {
return new Promise(resolve => {
const req = https.get(url, { timeout: 12000, rejectUnauthorized: false }, res => {
let body = '';
res.on('data', d => body += d);
res.on('end', () => resolve({ status: res.statusCode, size: body.length, type: res.headers['content-type'] }));
});
req.on('error', e => resolve({ status: 0, error: e.message }));
req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
});
}
async function main() {
console.log('\n╔══════════════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — Fix Static Paths (Windows Backslash) ║');
console.log('╚══════════════════════════════════════════════════════════╝\n');
const conn = new Client();
await new Promise((r, j) => { conn.on('ready', r).on('error', j).connect(SSH); });
console.log('[SSH] Terhubung!\n');
const BP = '/home/adminbackend/backone-production';
const SA = '/home/adminbackend/web/demoplace.my.id/public_html/.next/standalone';
const PUB = '/home/adminbackend/web/demoplace.my.id/public_html';
// 1. Cek BUILD_ID di backone-production
await exec(conn, 'cat ' + BP + '/.next/BUILD_ID 2>/dev/null || echo "NO_BUILDID"', '1. BUILD_ID backone-production');
// 2. Verifikasi chunk files ada di backone-production
await exec(conn,
'ls ' + BP + '/.next/static/chunks/ | grep -E "(3km6z|21luguo|1u6dw|1osk)" | head -10',
'2. Cek chunk files di backone-production'
);
// 3. Sinkronisasi static files dari backone-production ke standalone
await exec(conn,
'rsync -av --ignore-existing ' + BP + '/.next/static/ ' + SA + '/.next/static/ 2>&1 | tail -5',
'3. Rsync static files ke standalone/.next/static'
);
// 4. Verifikasi chunks sudah ada di standalone
await exec(conn,
'ls ' + SA + '/.next/static/chunks/ | grep -E "(3km6z|21luguo|1u6dw|1osk)" | head -10',
'4. Verifikasi chunks di standalone setelah rsync'
);
await exec(conn,
'ls ' + SA + '/.next/static/chunks/ | wc -l',
'5. Total file chunks di standalone'
);
// 5. Update symlink _next → standalone/.next
await exec(conn,
'rm -f ' + PUB + '/_next && ln -s ' + SA + '/.next ' + PUB + '/_next && echo "Symlink updated"',
'6. Update symlink _next → standalone/.next'
);
// 6. Verifikasi symlink
await exec(conn,
'ls -la ' + PUB + '/_next && ls ' + PUB + '/_next/static/chunks/ | grep -E "(3km6z|21luguo)" | head -5',
'7. Verifikasi symlink baru'
);
// 7. Juga sinkronisasi public/ dari backone-production jika dibutuhkan
await exec(conn,
'rsync -av --ignore-existing ' + BP + '/public/ ' + PUB + '/public/ 2>&1 | tail -3 || echo "Tidak perlu sync public/"',
'8. Sync public/ dari backone-production'
);
conn.end();
// 8. Test CSS/JS via domain
console.log('\n[9. Test CSS/JS via domain setelah fix...]');
await new Promise(r => setTimeout(r, 3000));
const files = [
'/_next/static/chunks/3km6z-n6wbgrs.js',
'/_next/static/chunks/1u6dw_tm45utz.css',
'/_next/static/chunks/1oskchnhjm4n8.css',
'/_next/static/chunks/21luguo5_g2uu.js',
'/_next/static/chunks/1-8s9_t85wwr4.js',
];
let allOk = true;
for (const f of files) {
const r = await httpGet('https://demoplace.my.id' + f);
const icon = r.status === 200 ? '✅' : '❌';
const ext = f.endsWith('.css') ? 'CSS' : 'JS ';
console.log(' ' + icon + ' [' + ext + '] HTTP ' + r.status + ' | ' + (r.size || 0) + ' bytes | ' + (r.type || 'N/A'));
if (r.status !== 200) { console.log(' → ' + f); allOk = false; }
}
if (allOk) {
console.log('\n🎉 SEMUA CSS/JS BERHASIL TERMUAT!');
console.log(' Lakukan HARD REFRESH (Ctrl+Shift+R) di browser.\n');
} else {
console.log('\n⚠️ Beberapa file masih 404. Mungkin perlu investigasi lebih lanjut.\n');
}
}
main().catch(err => { console.error('[FATAL]', err.message); process.exit(1); });
-118
View File
@@ -1,118 +0,0 @@
// scripts/fix-symlink.js
// Fix: Perbarui symlink _next agar menunjuk ke .next/standalone/.next
// Ini adalah penyebab utama CSS/JS tidak termuat
'use strict';
const { Client } = require('ssh2');
const https = require('https');
const SSH = {
host: '103.185.47.52', port: 2222,
username: 'adminbackend', password: 'htEo7x6LsBQiEHHH',
readyTimeout: 60000
};
function sshExec(conn, cmd, label = '') {
if (label) console.log(`\n[${label}]`);
console.log('$ ' + cmd.substring(0, 90) + (cmd.length > 90 ? '...' : ''));
return new Promise(resolve => {
conn.exec(cmd, (err, s) => {
if (err) { resolve(''); return; }
let out = '';
s.on('data', d => { out += d; process.stdout.write(d.toString()); })
.stderr.on('data', d => { out += d; process.stdout.write(d.toString()); })
.on('close', () => resolve(out));
});
});
}
async function httpGet(url) {
return new Promise(resolve => {
const req = https.get(url, { timeout: 10000, rejectUnauthorized: false }, res => {
let body = '';
res.on('data', d => body += d);
res.on('end', () => resolve({ status: res.statusCode, type: res.headers['content-type'], size: body.length }));
});
req.on('error', e => resolve({ status: 0, error: e.message }));
req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
});
}
async function main() {
console.log('\n╔══════════════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — Fix Symlink _next → standalone/.next ║');
console.log('╚══════════════════════════════════════════════════════════╝\n');
const conn = new Client();
await new Promise((r, j) => { conn.on('ready', r).on('error', j).connect(SSH); });
console.log('[SSH] Terhubung!\n');
const PUB = '/home/adminbackend/web/demoplace.my.id/public_html';
const SA = `${PUB}/.next/standalone`;
// 1. Verifikasi struktur standalone
await sshExec(conn,
`ls ${SA}/.next/static/chunks/ | grep -E "(3km6z|21luguo|1u6dw|1osk)" | head -10`,
'1. Cek chunk files di standalone/.next'
);
await sshExec(conn,
`ls ${SA}/.next/static/chunks/*.css 2>/dev/null | head -5 || echo "Cek dengan wc"`,
'2. Cek CSS files di standalone'
);
await sshExec(conn,
`ls ${SA}/.next/static/chunks/ | wc -l && echo "file di chunks"`,
'3. Jumlah total file di chunks'
);
// 2. Update symlink
await sshExec(conn,
`rm -f ${PUB}/_next && ln -s ${SA}/.next ${PUB}/_next && echo "BERHASIL: symlink diperbaiki"`,
'4. Update symlink _next → standalone/.next'
);
// 3. Verifikasi symlink baru
await sshExec(conn,
`ls -la ${PUB}/_next && ls ${PUB}/_next/static/chunks/ | head -5`,
'5. Verifikasi symlink baru'
);
// 4. Juga update public/ symlink jika belum ada
await sshExec(conn,
`ls -la ${PUB}/public 2>/dev/null || echo "public/ tidak ada sebagai symlink"`,
'6. Cek public/ directory'
);
// Buat symlink public → standalone/public jika ada
await sshExec(conn,
`[ -d ${SA}/public ] && ([ -e ${PUB}/public_sa ] || ln -s ${SA}/public ${PUB}/public_sa) && echo "standalone/public tersedia" || echo "standalone/public tidak ada"`,
'7. Cek standalone/public'
);
conn.end();
// 5. Test CSS/JS loading setelah fix
console.log('\n[8. Test CSS/JS via domain setelah fix...]');
await new Promise(r => setTimeout(r, 2000));
const files = [
'/_next/static/chunks/3km6z-n6wbgrs.js',
'/_next/static/chunks/1u6dw_tm45utz.css',
'/_next/static/chunks/1oskchnhjm4n8.css',
'/_next/static/chunks/21luguo5_g2uu.js',
'/_next/static/chunks/1-8s9_t85wwr4.js',
];
for (const f of files) {
const r = await httpGet(`https://demoplace.my.id${f}`);
const icon = r.status === 200 ? '✅' : '❌';
const ext = f.endsWith('.css') ? 'CSS' : 'JS ';
console.log(` ${icon} [${ext}] HTTP ${r.status} | ${r.size || 0} bytes | ${r.type || 'N/A'}`);
if (r.status !== 200) console.log(` ${f}`);
}
console.log('\n✅ Selesai! Lakukan HARD REFRESH (Ctrl+Shift+R) di browser.\n');
}
main().catch(err => { console.error('[FATAL]', err.message); process.exit(1); });
-89
View File
@@ -1,89 +0,0 @@
// scripts/fix-turbopack-external.js
// Fix: Buat wrapper module untuk hashed external modules yang diperlukan Turbopack
'use strict';
const { Client } = require('ssh2');
const SSH = { host: '103.185.47.52', port: 2222, username: 'adminbackend', password: 'htEo7x6LsBQiEHHH', readyTimeout: 60000 };
const PUB = '/home/adminbackend/web/demoplace.my.id/public_html';
function exec(conn, cmd, label) {
if (label) console.log('\n[' + label + ']');
console.log('$ ' + cmd.substring(0, 90));
return new Promise(resolve => {
conn.exec(cmd, (err, s) => {
if (err) { resolve(''); return; }
let out = '';
s.on('data', d => { out += d; process.stdout.write(d.toString()); })
.stderr.on('data', d => { out += d; process.stdout.write(d.toString()); })
.on('close', () => resolve(out));
});
});
}
async function main() {
const conn = new Client();
await new Promise((r, j) => { conn.on('ready', r).on('error', j).connect(SSH); });
console.log('[SSH] Connected\n');
const PM2 = '/home/adminbackend/.npm-global/bin/pm2';
const NM = PUB + '/node_modules';
const SA = PUB + '/.next/standalone';
// 1. Verifikasi mongoose asli tersedia di public_html/node_modules
await exec(conn, 'ls ' + NM + '/mongoose/package.json 2>/dev/null && echo "mongoose OK" || echo "mongoose TIDAK ADA"', '1. Cek mongoose asli');
// 2. Cek error log untuk nama hash yang dipakai
await exec(conn, 'grep "mongoose-" ' + PUB + '/logs/frontend-error.log 2>/dev/null | grep "Cannot find" | tail -5', '2. Hash mongoose dari error log');
// 3. Buat wrapper module mongoose-8b99e611e7552af3
const HASH_PKG = NM + '/mongoose-8b99e611e7552af3';
await exec(conn,
'mkdir -p ' + HASH_PKG + ' && ' +
'echo \'{"name":"mongoose-8b99e611e7552af3","version":"1.0.0","main":"index.js"}\' > ' + HASH_PKG + '/package.json && ' +
'echo \'module.exports = require("mongoose");\' > ' + HASH_PKG + '/index.js && ' +
'echo "Wrapper module dibuat"',
'3. Buat wrapper mongoose-8b99e611e7552af3'
);
// 4. Verifikasi wrapper
await exec(conn, 'cat ' + HASH_PKG + '/index.js', '4. Isi wrapper module');
// 5. Juga cek apakah ada hash lain yang mungkin gagal (better-sqlite3)
await exec(conn,
'grep "Cannot find module" ' + PUB + '/logs/frontend-error.log 2>/dev/null | grep -v mongoose | tail -5',
'5. Cek module lain yang mungkin gagal'
);
// 6. Cek standalone/node_modules - apakah ada?
await exec(conn,
'ls -la ' + SA + '/ | grep node_modules || echo "standalone/node_modules TIDAK ADA"',
'6. Cek standalone/node_modules'
);
// 7. Buat standalone/node_modules symlink ke public_html/node_modules
await exec(conn,
'[ -d ' + SA + '/node_modules ] && echo "Sudah ada" || ' +
'(ln -s ' + NM + ' ' + SA + '/node_modules && echo "Symlink node_modules dibuat")',
'7. Buat symlink standalone/node_modules → public_html/node_modules'
);
// 8. Restart frontend
await exec(conn, PM2 + ' restart backone-frontend --update-env && echo "RESTART OK"', '8. Restart frontend');
// 9. Tunggu startup
await new Promise(r => setTimeout(r, 8000));
// 10. Test agents page
await exec(conn,
'curl -sk -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/agents',
'9. Test /agents (HTTP code)'
);
// 11. Cek error log setelah restart
await exec(conn, 'tail -10 ' + PUB + '/logs/frontend-error.log 2>/dev/null', '10. Error log setelah fix');
conn.end();
console.log('\n✅ Selesai! Refresh browser ke https://demoplace.my.id/agents\n');
}
main().catch(e => console.error('[FATAL]', e.message));
-211
View File
@@ -1,211 +0,0 @@
// scripts/hestia-local-api-fix.js
// ─────────────────────────────────────────────────────────────────────────────
// Fix 502: Panggil Hestia CP API dari localhost (via SSH curl) karena
// Hestia API memblokir IP eksternal tapi MENGIZINKAN localhost (127.0.0.1)
// ─────────────────────────────────────────────────────────────────────────────
'use strict';
const { Client } = require('ssh2');
const https = require('https');
const SSH = {
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH',
readyTimeout: 60000
};
const DOMAIN = 'demoplace.my.id';
const WEB_USER = 'adminbackend';
const CONF = `/home/${WEB_USER}/conf/web/${DOMAIN}`;
const HESTIA_PASS = 'htEo7x6LsBQiEHHH';
function sshExec(conn, cmd, label = '') {
if (label) console.log(`\n[${label}]`);
console.log(`$ ${cmd.substring(0, 120)}${cmd.length > 120 ? '...' : ''}`);
return new Promise((resolve, reject) => {
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()); });
});
});
}
// Isi nginx.conf_custom yang akan kita tulis
const NGINX_CUSTOM = `# BackOne DPI - Proxy langsung ke Next.js port 3000
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;
}`;
const NGINX_SSL_CUSTOM = `# BackOne DPI - Proxy SSL langsung ke Next.js port 3000
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;
}`;
async function main() {
console.log('\n╔══════════════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — Fix 502 via Hestia localhost API ║');
console.log('╚══════════════════════════════════════════════════════════╝\n');
const conn = new Client();
await new Promise((resolve, reject) => { conn.on('ready', resolve).on('error', reject).connect(SSH); });
console.log('[SSH] Terhubung!\n');
// ── Step 1: Baca apache2.conf untuk diagnosa ───────────────────────────
await sshExec(conn, `cat ${CONF}/apache2.conf`, 'STEP 1: Baca Apache Config');
// ── Step 2: Cek apakah mod_proxy aktif di Apache ───────────────────────
await sshExec(conn,
`apache2ctl -M 2>/dev/null | grep -i proxy || ` +
`apachectl -M 2>/dev/null | grep -i proxy || ` +
`echo "Tidak bisa cek Apache modules"`,
'STEP 2: Cek mod_proxy'
);
// ── Step 3: Test Hestia API dari localhost (bypass IP restriction) ─────
console.log('\n[STEP 3: Hestia API dari localhost]');
// Generate SHA-256 hash untuk password
const hashCmd = `echo -n '${HESTIA_PASS}' | sha256sum | cut -d' ' -f1`;
console.log(`$ ${hashCmd}`);
const hashResult = await new Promise((resolve, reject) => {
conn.exec(hashCmd, (err, stream) => {
if (err) return reject(err);
let out = '';
stream.on('close', () => resolve(out.trim()))
.on('data', d => { out += d; })
.stderr.on('data', () => {});
});
});
console.log(`Hash: ${hashResult}`);
// Panggil Hestia API dari localhost untuk list proxy templates
await sshExec(conn,
`curl -sk -X POST https://localhost:8083/api/ ` +
`-d "hash=${hashResult}&cmd=v-list-web-templates-proxy&returncode=json" | head -c 500`,
'STEP 3a: List proxy templates via localhost API'
);
// Panggil Hestia API untuk restart web
await sshExec(conn,
`curl -sk -X POST https://localhost:8083/api/ ` +
`-d "hash=${hashResult}&cmd=v-restart-web&returncode=json"`,
'STEP 3b: Restart web via localhost API'
);
// ── Step 4: Tulis file via Hestia API run command ──────────────────────
// v-run-cli-cmd jalankan perintah shell sebagai root via Hestia
const writeHttpConf = `tee ${CONF}/nginx.conf_custom << 'NGINXEOF'\n${NGINX_CUSTOM}\nNGINXEOF`;
const encodedWrite = Buffer.from(writeHttpConf).toString('base64');
await sshExec(conn,
`curl -sk -X POST https://localhost:8083/api/ ` +
`-d "hash=${hashResult}&cmd=v-run-cli-cmd&arg1=tee&arg2=${CONF}/nginx.conf_custom" ` +
`--data-urlencode "stdin=${NGINX_CUSTOM}" | head -c 200`,
'STEP 4a: Tulis nginx.conf_custom via API'
);
// Fallback: Coba tee dengan python sebagai cara alternatif
await sshExec(conn,
`curl -sk -X POST https://localhost:8083/api/ ` +
`-d "hash=${hashResult}&cmd=v-add-web-domain-nginx-cfg&arg1=${WEB_USER}&arg2=${DOMAIN}" | head -c 200`,
'STEP 4b: Add nginx cfg via Hestia API'
);
// ── Step 5: Coba ubah Web Template ke yang support Node.js ───────────
// Template yang biasanya ada di Hestia untuk Node.js/proxy
for (const tpl of ['nodejs', 'node', 'proxy', 'default-nodejs']) {
const res = await new Promise(resolve => {
conn.exec(
`curl -sk -X POST https://localhost:8083/api/ ` +
`-d "hash=${hashResult}&cmd=v-change-web-domain-tpl&arg1=${WEB_USER}&arg2=${DOMAIN}&arg3=${tpl}&arg4=yes" | head -c 200`,
(err, stream) => {
if (err) return resolve('SSH Error');
let out = '';
stream.on('close', () => resolve(out))
.on('data', d => out += d)
.stderr.on('data', () => {});
}
);
});
console.log(`\n[Template ${tpl}]: ${res.trim()}`);
if (res.includes('0') || res.includes('OK')) {
console.log(`✅ Template '${tpl}' berhasil diterapkan!`);
break;
}
}
// ── Step 6: Paksa reload Nginx dengan cara yang bisa dilakukan user ───
await sshExec(conn,
`/usr/local/hestia/bin/v-restart-web 2>&1 || ` +
`sudo nginx -s reload 2>&1 || ` +
`curl -sk -X POST https://localhost:8083/api/ -d "hash=${hashResult}&cmd=v-restart-web" | head -c 100`,
'STEP 6: Reload Nginx'
);
// ── Step 7: Tunggu dan test domain ────────────────────────────────────
console.log('\n[STEP 7: Test domain setelah 8 detik...]');
await sshExec(conn,
`sleep 8 && curl -sk -o /dev/null -w "HTTPS Status: %{http_code}" https://${DOMAIN}/login`,
'Domain Test'
);
// ── Step 8: Tampilkan PM2 status ──────────────────────────────────────
await sshExec(conn,
`/home/adminbackend/.npm-global/bin/pm2 list`,
'STEP 8: PM2 Status'
);
conn.end();
// Test dari luar
console.log('\n[Test dari jaringan lokal...]');
const extResult = await new Promise(resolve => {
const req = https.get(`https://${DOMAIN}/login`,
{ timeout: 10000, rejectUnauthorized: false },
res => resolve({ status: res.statusCode, location: res.headers.location })
);
req.on('error', e => resolve({ status: 0, error: e.message }));
req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
});
const icon = extResult.status >= 200 && extResult.status < 400 ? '✅' : '❌';
console.log(`\n ${icon} https://${DOMAIN}/login → HTTP ${extResult.status} ${extResult.error || ''}`);
if (extResult.status >= 200 && extResult.status < 400) {
console.log('\n🎉 SUKSES! Dashboard BackOne sudah online!\n');
} else {
console.log('\n⚠️ Masih 502. Perlu konfigurasi manual di Hestia CP panel.\n');
console.log('━━━━ INSTRUKSI MANUAL ━━━━');
console.log('1. Buka Hestia CP → Web → demoplace.my.id → Edit');
console.log('2. Di bagian "Proxy Template" — pilih template Node.js (jika ada)');
console.log(' ATAU di File Manager: navigasi ke /conf/web/demoplace.my.id/');
console.log(' Buat file baru: nginx.conf_custom dengan isi:');
console.log('\n' + NGINX_CUSTOM.replace(/\\$/g, '$'));
console.log('\n Dan nginx.ssl.conf_custom dengan isi:');
console.log('\n' + NGINX_SSL_CUSTOM.replace(/\\$/g, '$'));
console.log('\n3. Setelah file dibuat, klik Rebuild di Hestia CP Web Domain.');
}
}
main().catch(err => { console.error('[FATAL]', err.message); process.exit(1); });
-180
View File
@@ -1,180 +0,0 @@
// scripts/hestia-nginx-fix.js
// ─────────────────────────────────────────────────────────────────────────────
// Fix 502 Bad Gateway dengan cara:
// 1. Cek permissions direktori conf Nginx
// 2. Gunakan Hestia CP REST API untuk ubah proxy template ke Node.js
// 3. Atau tulis nginx.conf_custom via Hestia API command
// ─────────────────────────────────────────────────────────────────────────────
'use strict';
const { Client } = require('ssh2');
const https = require('https');
const querystring = require('querystring');
const crypto = require('crypto');
const SSH = { host: '103.185.47.52', port: 2222, username: 'adminbackend', password: 'htEo7x6LsBQiEHHH', readyTimeout: 60000 };
const HESTIA_HOST = 'isp.proit.id';
const HESTIA_PORT = 8083;
const HESTIA_USER = 'admin';
const HESTIA_PASS = 'htEo7x6LsBQiEHHH';
const DOMAIN = 'demoplace.my.id';
const WEB_USER = 'adminbackend';
const CONF = `/home/${WEB_USER}/conf/web/${DOMAIN}`;
// ── Hestia CP API call ─────────────────────────────────────────────────────
function hestiaApi(cmd, args = []) {
return new Promise((resolve, reject) => {
const hash = crypto.createHash('sha256').update(HESTIA_PASS).digest('hex');
const body = querystring.stringify({ hash, cmd, ...Object.fromEntries(args.map((a, i) => [`arg${i + 1}`, a])) });
const req = https.request({
hostname: HESTIA_HOST,
port: HESTIA_PORT,
path: '/api/',
method: 'POST',
rejectUnauthorized: false,
headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(body) }
}, res => {
let data = '';
res.on('data', d => data += d);
res.on('end', () => resolve({ status: res.statusCode, body: data.trim() }));
});
req.on('error', reject);
req.write(body);
req.end();
});
}
// ── SSH command runner ─────────────────────────────────────────────────────
function sshExec(conn, cmd) {
return new Promise((resolve, reject) => {
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()); });
});
});
}
// ── Custom Nginx config content ───────────────────────────────────────────
const nginxCustomContent = `# BackOne DPI — Proxy to Next.js port 3000
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;
}`;
const nginxSslContent = `# BackOne DPI — Proxy SSL to Next.js port 3000
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;
}`;
async function main() {
console.log('\n╔══════════════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — Fix 502 via Hestia API + Nginx Fix ║');
console.log('╚══════════════════════════════════════════════════════════╝\n');
// ── Step 1: Diagnose conf directory permissions via SSH ─────────────────
console.log('\n▶ Step 1: Check conf directory permissions via SSH...');
const conn = new Client();
await new Promise((resolve, reject) => { conn.on('ready', resolve).on('error', reject).connect(SSH); });
console.log('[SSH] Connected!\n');
await sshExec(conn, `echo "=== CONF DIR PERMS ===" && ls -la /home/${WEB_USER}/conf/web/`);
await sshExec(conn, `echo "=== DOMAIN CONF DIR ===" && ls -la ${CONF}/`);
await sshExec(conn, `echo "=== EXISTING CUSTOM CONF ===" && ls -la ${CONF}/nginx.conf_* 2>/dev/null || echo "No custom conf yet"`);
// ── Step 2: Try Python to write the file (avoids shell quoting issues) ──
console.log('\n▶ Step 2: Attempting to write nginx.conf_custom via Python...');
const pyScript = `python3 -c "
import os
content = '''${nginxCustomContent.replace(/'/g, "\\'")}'''
path = '${CONF}/nginx.conf_custom'
try:
with open(path, 'w') as f:
f.write(content + '\\n')
print('[OK] Written: ' + path)
except Exception as e:
print('[FAIL] ' + str(e))
"`;
await sshExec(conn, pyScript);
const pyScriptSsl = `python3 -c "
content = '''${nginxSslContent.replace(/'/g, "\\'")}'''
path = '${CONF}/nginx.ssl.conf_custom'
try:
with open(path, 'w') as f:
f.write(content + '\\n')
print('[OK] Written: ' + path)
except Exception as e:
print('[FAIL] ' + str(e))
"`;
await sshExec(conn, pyScriptSsl);
conn.end();
// ── Step 3: Try Hestia CP REST API to restart web ─────────────────────
console.log('\n▶ Step 3: Call Hestia CP REST API to restart web server...');
// Try listing available proxy templates first
console.log('\n → Listing available proxy templates...');
const templatesRes = await hestiaApi('v-list-web-templates-proxy', []);
console.log(` API Response [list-proxy-templates]: HTTP ${templatesRes.status}`);
console.log(` Body: ${templatesRes.body.substring(0, 300)}`);
// Try to restart web via API
console.log('\n → Restarting web server via Hestia API...');
const restartRes = await hestiaApi('v-restart-web', []);
console.log(` API Response [restart-web]: HTTP ${restartRes.status}`);
console.log(` Body: ${restartRes.body}`);
// Try restart with correct user arg
const restartRes2 = await hestiaApi('v-restart-web', [WEB_USER]);
console.log(` API Response [restart-web user]: HTTP ${restartRes2.status}`);
console.log(` Body: ${restartRes2.body}`);
// ── Step 4: Wait and test domain ─────────────────────────────────────
console.log('\n▶ Step 4: Waiting 8 seconds then testing domain...');
await new Promise(r => setTimeout(r, 8000));
const testRes = await new Promise(resolve => {
const req = https.get('https://demoplace.my.id/login', { timeout: 10000, rejectUnauthorized: false }, res => {
resolve({ status: res.statusCode, location: res.headers.location });
});
req.on('error', e => resolve({ status: 0, error: e.message }));
req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
});
const icon = testRes.status >= 200 && testRes.status < 400 ? '✅' : '❌';
console.log(`\n ${icon} https://demoplace.my.id/login → HTTP ${testRes.status} ${testRes.error || ''}`);
if (testRes.location) console.log(` Location: ${testRes.location}`);
if (testRes.status >= 200 && testRes.status < 400) {
console.log('\n🎉 SUKSES! Domain sudah bisa diakses!\n');
} else {
console.log('\n⚠️ Domain masih belum bisa diakses. Perlu konfig manual via Hestia panel.\n');
console.log('Silakan buka Hestia CP File Manager dan buat file:');
console.log(` ${CONF}/nginx.conf_custom`);
console.log(` ${CONF}/nginx.ssl.conf_custom`);
console.log('\nDengan isi (untuk keduanya):');
console.log(nginxCustomContent);
console.log('\nKemudian di Hestia CP: Web → demoplace.my.id → Rebuild\n');
}
}
main().catch(err => { console.error('[FATAL]', err.message); process.exit(1); });
-18
View File
@@ -1,18 +0,0 @@
const { Client } = require('ssh2');
const conn = new Client();
conn.on('ready', () => {
conn.exec(`ls -la /home/adminbackend/web/demoplace.my.id/public_html/.next/standalone/.next/server/app/\\(dashboard\\)/ 2>/dev/null || ls -la /home/adminbackend/web/demoplace.my.id/public_html/.next/server/app/`, (err, stream) => {
let out = '';
stream.on('data', d => out += d);
stream.on('close', () => {
console.log('=== REMOTE DASHBOARD APP ROUTES ===\n' + out);
conn.end();
});
});
}).connect({
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH'
});
-83
View File
@@ -1,83 +0,0 @@
// scripts/migrate-db-remote.js
global.crypto = require('crypto');
const { MongoClient } = require('mongodb');
const fs = require('fs');
const path = require('path');
const REMOTE_URI = process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017/backone_dpi';
const DB_NAME = 'backone_dpi';
const DATA_DIR = __dirname;
async function migrateCollection(db, colName, keyField) {
const filePath = path.join(DATA_DIR, `${colName}.json`);
if (!fs.existsSync(filePath)) {
console.log(` [SKIP] ${colName}.json not found in migration folder.`);
return;
}
const raw = fs.readFileSync(filePath, 'utf8');
const documents = JSON.parse(raw);
console.log(`\nMigrating collection: ${colName} (${documents.length} docs)...`);
const col = db.collection(colName);
let upsertedCount = 0;
let matchedCount = 0;
for (const doc of documents) {
// Preserve MongoDB Object ID or convert it properly if it has $oid notation
const query = {};
query[keyField] = doc[keyField];
// Clean up _id or preserve it
const updateDoc = { ...doc };
if (updateDoc._id) {
if (updateDoc._id.$oid) {
// If it's exported via mongoexport format
delete updateDoc._id;
} else {
// Remove _id from update payload to prevent "change _id" errors on upsert
delete updateDoc._id;
}
}
const result = await col.updateOne(
query,
{ $set: updateDoc },
{ upsert: true }
);
if (result.upsertedCount > 0) {
upsertedCount++;
} else {
matchedCount++;
}
}
console.log(` ✓ Finished ${colName}: ${upsertedCount} new inserted (upserted), ${matchedCount} existing updated.`);
}
async function main() {
console.log('=== STARTING PRODUCTION DATABASE MIGRATION ===');
console.log('Connecting to production MongoDB...');
const client = new MongoClient(REMOTE_URI, { serverSelectionTimeoutMS: 5000 });
try {
await client.connect();
console.log('✓ Connected to production MongoDB successfully!');
const db = client.db(DB_NAME);
// Run migrations using distinct key fields for upserting
await migrateCollection(db, 'users', 'username');
await migrateCollection(db, 'tenantconfigs', 'site_uuid');
await migrateCollection(db, 'customagentlocations', 'agent_uuid');
console.log('\n=== MIGRATION COMPLETED SUCCESSFULLY ===');
} catch (err) {
console.error('\n✗ Migration failed:', err.message);
process.exit(1);
} finally {
await client.close();
}
}
main();
-126
View File
@@ -1,126 +0,0 @@
// scripts/migrate_production_config.js
if (typeof globalThis.crypto === 'undefined') {
globalThis.crypto = require('crypto');
}
const { MongoClient } = require('mongodb');
const path = require('path');
const LOCAL_URI = 'mongodb://127.0.0.1:27017';
const REMOTE_URI = process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017/backone_dpi';
const DB_NAME = 'backone_dpi';
async function migrate() {
console.log("=============================================================");
console.log(" BackOne DPI - Selective Configuration Migration to Prod ");
console.log("=============================================================\n");
let localClient, remoteClient;
try {
console.log("Connecting to local MongoDB...");
localClient = new MongoClient(LOCAL_URI, { serverSelectionTimeoutMS: 3000 });
await localClient.connect();
const localDb = localClient.db(DB_NAME);
console.log("✓ Connected to local MongoDB.");
console.log("Connecting to production MongoDB...");
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 (Upsert based on username to preserve existing user credentials if updated)
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) {
// Remove _id to avoid duplicate key errors or mismatched ObjectId
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 (Clear & Replace)
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 (Clear & Replace)
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 (Clear & Replace, if exists)
console.log("\n--- Syncing 'blacklistrules' collection ---");
const localRulesColl = localDb.collection('blacklistrules');
const remoteRulesColl = remoteDb.collection('blacklistrules');
// Check if collection exists
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.");
}
console.log("\n=============================================================");
console.log("✓ Database configuration migration completed successfully!");
console.log("=============================================================");
} catch (err) {
console.error("\n✗ Migration failed:", err.message);
process.exit(1);
} finally {
if (localClient) await localClient.close();
if (remoteClient) await remoteClient.close();
}
}
migrate();
-36
View File
@@ -1,36 +0,0 @@
// scripts/prune-production-cumulative.js
const mongoose = require('mongoose');
const MONGODB_URI = process.env.MONGODB_URI || "mongodb://127.0.0.1:27017/backone_dpi";
async function run() {
console.log('Connecting to Production MongoDB...');
await mongoose.connect(MONGODB_URI);
console.log('Connected.');
const db = mongoose.connection.db;
// We want to delete summaries and telemetry documents created before today at 18:40 WIB (11:40 UTC)
const cutOffDate = new Date('2026-07-23T11:40:00.000Z');
console.log('Pruning data before:', cutOffDate);
const collections = ['summaries', 'appstats', 'protocolstats', 'devicestats'];
for (const collName of collections) {
const countBefore = await db.collection(collName).countDocuments({});
console.log(`[${collName}] Count before prune:`, countBefore);
const deleteRes = await db.collection(collName).deleteMany({
timestamp: { $lt: cutOffDate }
});
console.log(`[${collName}] Deleted:`, deleteRes.deletedCount);
const countAfter = await db.collection(collName).countDocuments({});
console.log(`[${collName}] Count after prune:`, countAfter);
}
await mongoose.disconnect();
console.log('✓ Pruning complete!');
}
run().catch(console.error);
-26
View File
@@ -1,26 +0,0 @@
// scripts/quick-diag.js
'use strict';
const { Client } = require('ssh2');
const conn = new Client();
conn.on('ready', () => {
const cmds = [
'echo "=== Port 3000 test ==" && curl -sk -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/login',
'echo "" && echo "=== Nginx conf_custom ===" && cat /home/adminbackend/conf/web/demoplace.my.id/nginx.conf_custom 2>/dev/null || echo MISSING',
'echo "=== Nginx ssl conf_custom ===" && cat /home/adminbackend/conf/web/demoplace.my.id/nginx.ssl.conf_custom 2>/dev/null || echo MISSING',
'echo "=== .htaccess ===" && cat /home/adminbackend/web/demoplace.my.id/public_html/.htaccess',
'echo "=== PM2 status ===" && /home/adminbackend/.npm-global/bin/pm2 list 2>/dev/null | grep -E "(name|backone)"',
];
let i = 0;
function run() {
if (i >= cmds.length) { conn.end(); console.log('\nDone.'); return; }
const cmd = cmds[i++];
conn.exec(cmd, (err, s) => {
if (err) { console.error(err.message); run(); return; }
s.on('data', d => process.stdout.write(d.toString()))
.stderr.on('data', d => process.stdout.write(d.toString()))
.on('close', run);
});
}
run();
}).on('error', e => console.error(e.message))
.connect({ host: '103.185.47.52', port: 2222, username: 'adminbackend', password: 'htEo7x6LsBQiEHHH', readyTimeout: 30000 });
-44
View File
@@ -1,44 +0,0 @@
const { Client } = require('ssh2');
const conn = new Client();
conn.on('ready', () => {
// Execute a shell script on remote server
conn.exec(`python3 -c "
import os, re
root = '/home/adminbackend/web/demoplace.my.id/public_html/.next/standalone/.next/server/app/(dashboard)'
for dirpath, dirnames, filenames in os.walk(root):
for f in filenames:
if f.endswith('.html') or f.endswith('.js'):
full = os.path.join(dirpath, f)
rel = os.path.relpath(full, root)
try:
content = open(full, 'r', encoding='utf-8', errors='ignore').read()
h2s = re.findall(r'<h[12][^>]*>(.*?)</h[12]>', content)
clean_h2s = [re.sub(r'<[^>]+>', '', h).strip() for h in h2s if h.strip()]
has_learn = 'Learn This Page' in content
if clean_h2s or has_learn:
print(f'Route File: {rel}')
print(f' Headers: {\" | \".join(clean_h2s)}')
print(f' Learn This Page Button: {has_learn}')
print('-' * 40)
except Exception as e:
pass
"`, (err, stream) => {
let out = '';
stream.on('data', d => out += d);
stream.on('close', () => {
console.log(out);
conn.end();
});
});
}).connect({
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH'
});
-95
View File
@@ -1,95 +0,0 @@
const { spawn } = require('child_process');
const http = require('http');
const fs = require('fs');
const path = require('path');
const jwt = require('jsonwebtoken');
const mongoose = require('mongoose');
const WebSocket = require('next/dist/compiled/ws');
require('dotenv').config({ path: path.join(__dirname, '..', '.env.local') });
const JWT_SECRET = process.env.JWT_SECRET || 'backone-dpi-secret-jwt-key-2024';
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017/backone_dpi';
async function main() {
try {
await mongoose.connect(MONGODB_URI);
const db = mongoose.connection.db;
const users = db.collection('users');
let adminUser = await users.findOne({ username: 'admin' });
const payload = {
userId: adminUser._id.toString(),
username: adminUser.username,
role: adminUser.role,
site_uuid: adminUser.site_uuid || 'default',
agent_uuid: adminUser.agent_uuid || null
};
const token = jwt.sign(payload, JWT_SECRET, { expiresIn: '1h' });
await mongoose.disconnect();
const edgePath = "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe";
const edgeProc = spawn(edgePath, [
'--headless',
'--disable-gpu',
'--remote-debugging-port=9222',
'--window-size=400,844',
'http://localhost:3000/'
]);
await new Promise(r => setTimeout(r, 2500));
const targets = await new Promise((resolve, reject) => {
http.get('http://127.0.0.1:9222/json', (res) => {
let body = '';
res.on('data', c => body += c);
res.on('end', () => resolve(JSON.parse(body)));
}).on('error', reject);
});
const pageTarget = targets.find(t => t.type === 'page');
if (!pageTarget || !pageTarget.webSocketDebuggerUrl) {
edgeProc.kill();
return;
}
const ws = new WebSocket(pageTarget.webSocketDebuggerUrl);
let messageId = 1;
const send = (method, params = {}) => {
return new Promise((res) => {
const id = messageId++;
const handler = (data) => {
const msg = JSON.parse(data);
if (msg.id === id) {
ws.removeListener('message', handler);
res(msg.result);
}
};
ws.on('message', handler);
ws.send(JSON.stringify({ id, method, params }));
});
};
ws.on('open', async () => {
await send('Network.setCookie', { name: 'token', value: token, domain: 'localhost', path: '/' });
await send('Network.setCookie', { name: 'backone_token', value: token, domain: 'localhost', path: '/' });
await send('Page.navigate', { url: 'http://localhost:3000/' });
await new Promise(r => setTimeout(r, 5000));
const screenshot = await send('Page.captureScreenshot', { format: 'png', captureBeyondViewport: true });
const outputPath = "C:\\Users\\rafif\\.gemini\\antigravity-ide\\brain\\d6704840-c7e7-4558-b215-0fe98bbfb07a\\mobile-full-dashboard-live.png";
fs.writeFileSync(outputPath, Buffer.from(screenshot.data, 'base64'));
console.log("Full page screenshot saved to:", outputPath, "Size:", fs.statSync(outputPath).size, "bytes");
ws.close();
edgeProc.kill();
});
} catch (err) {
console.error("Error:", err.message);
}
}
main();
-174
View File
@@ -1,174 +0,0 @@
// scripts/restore-proxy.js
// Pulihkan proxy.php + htaccess yang rusak via SFTP
'use strict';
const SftpClient = require('ssh2-sftp-client');
const { Client } = require('ssh2');
const https = require('https');
const CONFIG = {
host: '103.185.47.52', port: 2222,
username: 'adminbackend', password: 'htEo7x6LsBQiEHHH',
readyTimeout: 60000
};
const ROOT = '/home/adminbackend/web/demoplace.my.id/public_html';
// ── PHP Proxy (forward semua ke Next.js port 3000) ──────────────────────
const PROXY_PHP = `<?php
/**
* BackOne DPI - PHP Reverse Proxy
* Meneruskan semua request ke Next.js port 3000
*/
define('NEXTJS_URL', 'http://127.0.0.1:3000');
$uri = $_SERVER['REQUEST_URI'] ?? '/';
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
$target = NEXTJS_URL . $uri;
$fwdHeaders = [
'X-Forwarded-For: ' . ($_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'),
'X-Forwarded-Proto: https',
'X-Real-IP: ' . ($_SERVER['REMOTE_ADDR'] ?? ''),
'Host: ' . ($_SERVER['HTTP_HOST'] ?? 'demoplace.my.id'),
];
$allHeaders = getallheaders() ?: [];
$skip = ['host','connection','transfer-encoding','keep-alive','content-length'];
foreach ($allHeaders as $name => $value) {
if (!in_array(strtolower($name), $skip)) {
$fwdHeaders[] = "$name: $value";
}
}
$body = in_array($method, ['POST','PUT','PATCH','DELETE']) ? file_get_contents('php://input') : null;
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $target,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $fwdHeaders,
CURLOPT_POSTFIELDS => $body,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_ENCODING => '',
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$curlError = curl_error($ch);
curl_close($ch);
if ($response === false) {
http_response_code(502);
header('Content-Type: text/plain');
echo 'BackOne: Gagal terhubung ke Next.js. Error: ' . $curlError;
exit;
}
$responseHeaders = substr($response, 0, $headerSize);
$responseBody = substr($response, $headerSize);
http_response_code($httpCode ?: 200);
$skipRes = ['transfer-encoding','connection','keep-alive','content-encoding'];
foreach (explode("\\r\\n", $responseHeaders) as $header) {
$header = trim($header);
if (empty($header) || preg_match('/^HTTP\\//i', $header)) continue;
$colonPos = strpos($header, ':');
if ($colonPos === false) continue;
$headerName = strtolower(trim(substr($header, 0, $colonPos)));
if (in_array($headerName, $skipRes)) continue;
header($header, false);
}
echo $responseBody;
`;
// ── .htaccess (routing ke proxy.php) ──────────────────────────────────
const HTACCESS = `# BackOne DPI — Apache .htaccess
# Redirect HTTP ke HTTPS
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Static Next.js files: sajikan langsung dari disk
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^ - [L]
# Semua request lain: teruskan ke PHP proxy (Next.js port 3000)
RewriteRule ^(.*)$ /proxy.php [L,QSA]
`;
async function main() {
console.log('\n╔══════════════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — Pulihkan proxy.php & .htaccess ║');
console.log('╚══════════════════════════════════════════════════════════╝\n');
// ── Upload via SFTP ──────────────────────────────────────────────────
console.log('▶ Upload proxy.php dan .htaccess via SFTP...');
const sftp = new SftpClient();
await sftp.connect(CONFIG);
await sftp.put(Buffer.from(PROXY_PHP, 'utf8'), `${ROOT}/proxy.php`);
console.log(' ✅ proxy.php diupload');
await sftp.put(Buffer.from(HTACCESS, 'utf8'), `${ROOT}/.htaccess`);
console.log(' ✅ .htaccess diupload (dengan newline yang benar)');
await sftp.end();
// ── Verifikasi via SSH ───────────────────────────────────────────────
console.log('\n▶ Verifikasi di server...');
const conn = new Client();
await new Promise((r, j) => { conn.on('ready', r).on('error', j).connect(CONFIG); });
console.log('[SSH] Terhubung!\n');
await new Promise((resolve) => {
conn.exec(
`echo "=== .htaccess ===" && cat ${ROOT}/.htaccess && ` +
`echo "" && echo "=== proxy.php (5 baris awal) ===" && head -5 ${ROOT}/proxy.php && ` +
`echo "" && echo "=== Test port 3000 ===" && curl -sk -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/login && echo "" && ` +
`echo "=== Test port 8009/8080 ===" && curl -sk -o /dev/null -w "Apache 8009: %{http_code}" http://127.0.0.1:8009/ && echo "" && curl -sk -o /dev/null -w "Apache 8080: %{http_code}" http://103.185.47.52:8080/login && echo ""`,
(err, s) => {
if (err) { resolve(); return; }
s.on('data', d => process.stdout.write(d.toString()))
.stderr.on('data', d => process.stdout.write(d.toString()))
.on('close', () => { resolve(); });
}
);
});
conn.end();
// ── Test external ────────────────────────────────────────────────────
console.log('\n▶ Test domain dari luar (tunggu 3 detik)...');
await new Promise(r => setTimeout(r, 3000));
for (const path of ['/', '/login', '/api/health']) {
const r = await new Promise(resolve => {
const req = https.get(`https://demoplace.my.id${path}`,
{ timeout: 12000, rejectUnauthorized: false },
res => {
let body = '';
res.on('data', d => body += d);
res.on('end', () => resolve({ status: res.statusCode, body: body.substring(0, 80), location: res.headers.location }));
}
);
req.on('error', e => resolve({ status: 0, error: e.message }));
req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
});
const icon = r.status === 200 ? '✅' : r.status === 301 || r.status === 307 ? '↩️ ' : '❌';
console.log(` ${icon} https://demoplace.my.id${path} → HTTP ${r.status} ${r.error || ''}`);
if (r.location) console.log(` → ${r.location}`);
}
console.log('\n✅ Selesai! Refresh https://demoplace.my.id/login\n');
}
main().catch(err => { console.error('[FATAL]', err.message); process.exit(1); });
-80
View File
@@ -1,80 +0,0 @@
const SftpClient = require('ssh2-sftp-client');
const { Client } = require('ssh2');
const remoteScanScript = `
const fs = require('fs');
const path = require('path');
const root = '/home/adminbackend/web/demoplace.my.id/public_html/.next';
const targetTitles = [
'Overview Dashboard',
'Summary Overview',
'DPI MetaData',
'BackOne Metadata',
'Network Topology',
'Network Infrastructure',
'Traffic Categories',
'Threat Intelligence',
'Detected Threats',
'App Lookup',
'Application Database',
'Encryption Audit',
'Security & Encryption Audit'
];
console.log('=== MATCHING TARGET TITLES IN REMOTE NEXT BUILD ===\\n');
function scan(dir) {
if (!fs.existsSync(dir)) return;
const items = fs.readdirSync(dir, { withFileTypes: true });
for (const item of items) {
const full = path.join(dir, item.name);
if (item.isDirectory()) {
scan(full);
} else if (item.name.endsWith('.js') || item.name.endsWith('.html')) {
const content = fs.readFileSync(full, 'utf8');
targetTitles.forEach(t => {
if (content.includes(t)) {
console.log(\`Found "\${t}" in file: \${path.relative(root, full)}\`);
}
});
}
}
}
scan(root);
`;
async function main() {
const sftp = new SftpClient();
await sftp.connect({
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH'
});
const remoteScriptPath = '/home/adminbackend/scan_strings.js';
await sftp.put(Buffer.from(remoteScanScript), remoteScriptPath);
await sftp.end();
const conn = new Client();
conn.on('ready', () => {
conn.exec(`node ${remoteScriptPath}`, (err, stream) => {
let out = '';
stream.on('data', d => out += d);
stream.on('close', () => {
console.log(out);
conn.end();
});
});
}).connect({
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH'
});
}
main();
-26
View File
@@ -1,26 +0,0 @@
const { Client } = require('ssh2');
const conn = new Client();
conn.on('ready', () => {
conn.exec(`grep -rn "Overview Dashboard" /home/adminbackend/web/demoplace.my.id/public_html/ /home/adminbackend/backone-production/ 2>/dev/null | head -n 30`, (err, stream) => {
let out = '';
stream.on('data', d => out += d);
stream.on('close', () => {
console.log('=== SEARCH Overview Dashboard ===\n' + out);
conn.exec(`grep -rn "DPI MetaData" /home/adminbackend/web/demoplace.my.id/public_html/ /home/adminbackend/backone-production/ 2>/dev/null | head -n 30`, (err2, stream2) => {
let out2 = '';
stream2.on('data', d => out2 += d);
stream2.on('close', () => {
console.log('=== SEARCH DPI MetaData ===\n' + out2);
conn.end();
});
});
});
});
}).connect({
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH'
});
-153
View File
@@ -1,153 +0,0 @@
// scripts/security-fix.js
// ─────────────────────────────────────────────────────────────────────────────
// BackOne DPI — Security fix & final production configuration
// 1. Remove .env.production from standalone directory (security)
// 2. Fix PM2 to use new ecosystem config properly
// 3. Verify HTTPS redirect and domain reachability
// ─────────────────────────────────────────────────────────────────────────────
'use strict';
const { Client: SshClient } = require('ssh2');
const https = require('https');
const http = require('http');
const CONFIG = {
host: process.env.SSH_HOST || '127.0.0.1',
port: parseInt(process.env.SSH_PORT || '2222'),
username: process.env.SSH_USER || 'adminbackend',
password: process.env.SSH_PASSWORD || '',
};
const ROOT = '/home/adminbackend/web/demoplace.my.id/public_html';
const PM2 = '/home/adminbackend/.npm-global/bin/pm2';
const COMMANDS = [
// ── CRITICAL SECURITY: Remove .env files from Next.js standalone build ────
`echo "=== SECURITY FIX: Remove .env from standalone ==="`,
`rm -f ${ROOT}/.next/standalone/.env.production && echo "REMOVED: .env.production from standalone"`,
`rm -f ${ROOT}/.next/standalone/.env.local && echo "REMOVED: .env.local from standalone (if existed)"`,
// Verify removal
`echo "=== VERIFY: No .env in standalone ===" && find ${ROOT}/.next/standalone -name ".env*" 2>/dev/null | head -5 || echo "CLEAN: No .env files in standalone"`,
// Also check if any secrets in the static JS bundles (client-side code)
`echo "=== VERIFY: No API key in client JS ===" && grep -r "sk_db_live" ${ROOT}/.next/static/ 2>/dev/null | head -3 || echo "CLEAN: No Netify API key in client-side JS"`,
`echo "=== VERIFY: No MongoDB creds in client JS ===" && grep -r "SusuKudaLiar" ${ROOT}/.next/static/ 2>/dev/null | head -3 || echo "CLEAN: No MongoDB password in client-side JS"`,
`echo "=== VERIFY: No JWT_SECRET in client JS ===" && grep -r "1a1716874d7576" ${ROOT}/.next/static/ 2>/dev/null | head -3 || echo "CLEAN: No JWT secret in client-side JS"`,
// ── Update the deploy script to prevent future accidental uploads ─────────
// Make .env.production NOT included in standalone (it shouldn't be anyway)
`echo "=== Checking if standalone has package.json with correct env ===" && cat ${ROOT}/.next/standalone/package.json 2>/dev/null | head -5`,
// ── Fix ecosystem.config.js to use correct cwd and path ──────────────────
// The PM2 frontend shows version 0.1.0 (old package.json from inside standalone)
// The cwd in ecosystem.config.js points to the root, which is correct
`echo "=== Current ecosystem.config.js ===" && cat ${ROOT}/ecosystem.config.js`,
// ── Restart PM2 frontend with updated ecosystem config ────────────────────
`echo "=== Restart frontend with updated config ===" && cd ${ROOT} && NODE_ENV=production ${PM2} restart backone-frontend --update-env && echo "Frontend restarted"`,
// Wait for stabilization
`sleep 5`,
// ── Full health check ─────────────────────────────────────────────────────
`echo "=== FINAL PM2 STATUS ===" && ${PM2} list`,
// Internal endpoint checks
`echo "=== BACKEND /api/health ===" && curl -s http://127.0.0.1:3001/api/health`,
`echo "=== FRONTEND / ===" && curl -s -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/`,
`echo "=== FRONTEND /login ===" && curl -s -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/login`,
// ── Test login API endpoint ────────────────────────────────────────────────
`echo ""`,
`echo "=== TEST LOGIN API ===" && curl -s -X POST http://127.0.0.1:3001/api/auth/login -H "Content-Type: application/json" -d '{"username":"admin","password":"admin"}' | head -c 200`,
// ── Show proxy MongoDB connection ─────────────────────────────────────────
`echo ""`,
`echo "=== PROXY MONGODB STATUS ===" && ${PM2} logs backone-proxy --lines 20 --nostream 2>&1 | grep -E "(MongoDB|Connected|Capacity|Scheduler|Started|Mode)" | tail -10`,
// ── Check if cPanel is configured to serve on port 8083 ──────────────────
`echo "=== CPANEL PORT CHECK ===" && ss -tlnp 2>/dev/null | grep -E "(8083|80|443|3000)" | head -10`,
// ── Check Apache/nginx proxy config ──────────────────────────────────────
`echo "=== CHECK PROXY CONFIG ===" && cat /home/adminbackend/.htaccess 2>/dev/null | head -20 || echo "No .htaccess at home"`,
`echo "=== CHECK WEB ROOT HTACCESS ===" && cat ${ROOT}/.htaccess 2>/dev/null | head -20 || echo "No .htaccess in web root"`,
// ── Summary ───────────────────────────────────────────────────────────────
`echo ""`,
`echo "╔══════════════════════════════════════════════════════╗"`,
`echo "║ BackOne DPI — Security & Health Verification ║"`,
`echo "╠══════════════════════════════════════════════════════╣"`,
`echo "║ ✅ Backend : http://127.0.0.1:3001 (internal) ║"`,
`echo "║ ✅ Proxy : http://127.0.0.1:4000 (internal) ║"`,
`echo "║ ✅ Frontend : http://127.0.0.1:3000 (internal) ║"`,
`echo "║ ✅ MongoDB : mongodb.prod.proit.id:27017 ║"`,
`echo "║ 🌐 Domain : https://demoplace.my.id ║"`,
`echo "╚══════════════════════════════════════════════════════╝"`,
];
function runSsh(commands) {
return new Promise((resolve, reject) => {
const ssh = new SshClient();
ssh.on('ready', () => {
console.log('[SSH] Connected!\n');
let i = 0;
function next() {
if (i >= commands.length) { ssh.end(); return; }
const cmd = commands[i++];
console.log(`\n$ ${cmd.substring(0, 130)}${cmd.length > 130 ? '...' : ''}`);
ssh.exec(cmd, (err, stream) => {
if (err) { console.error('[ERR]', err.message); next(); return; }
stream.on('data', d => process.stdout.write(d.toString()));
stream.stderr.on('data', d => { const t = d.toString(); if (!t.includes('npm warn') && !t.includes('notice')) process.stdout.write(t); });
stream.on('close', next);
});
}
next();
ssh.on('end', resolve);
});
ssh.on('error', reject);
ssh.connect({ ...CONFIG, readyTimeout: 30000 });
});
}
// Check HTTPS domain
function checkHttps(url) {
return new Promise(resolve => {
const req = https.get(url, { timeout: 10000, rejectUnauthorized: false }, res => {
let body = '';
res.on('data', d => body += d);
res.on('end', () => resolve({ status: res.statusCode, body: body.substring(0, 200), location: res.headers.location }));
});
req.on('error', e => resolve({ status: 0, error: e.message }));
req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
});
}
async function main() {
console.log('\n╔══════════════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — Security Fix & Final Verification ║');
console.log('╚══════════════════════════════════════════════════════════╝\n');
await runSsh(COMMANDS);
// Check HTTPS
console.log('\n▶ Testing HTTPS access...\n');
const urls = [
'https://demoplace.my.id/',
'https://demoplace.my.id/login',
'https://demoplace.my.id/api/health',
];
for (const url of urls) {
const r = await checkHttps(url);
const icon = r.status >= 200 && r.status < 400 ? '✅' : r.status === 0 ? '⚠️' : '❌';
console.log(` ${icon} ${url} → HTTP ${r.status} ${r.error || ''}`);
if (r.location) console.log(` Redirects to: ${r.location}`);
if (r.body && r.status === 200) console.log(` Body: ${r.body.substring(0, 80)}...`);
}
console.log('\n✅ Security fix & verification complete!\n');
}
main().catch(err => { console.error('[FATAL]', err); process.exit(1); });
-123
View File
@@ -1,123 +0,0 @@
// scripts/server-setup.js
// ─────────────────────────────────────────────────────────────────────────────
// BackOne DPI — Server Investigation & PM2 Setup Script
// Runs SSH commands to check server state and install PM2 locally
// ─────────────────────────────────────────────────────────────────────────────
'use strict';
const { Client: SshClient } = require('ssh2');
const CONFIG = {
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH',
};
const REMOTE_ROOT = '/home/adminbackend/web/demoplace.my.id/public_html';
// Commands to investigate server state, install PM2 locally, and start all services
const COMMANDS = [
// 1. Check Node.js version and PATH
'echo "=== NODE VERSION ===" && node --version 2>&1 || echo "node not in PATH"',
'echo "=== NPM VERSION ===" && npm --version 2>&1 || echo "npm not in PATH"',
'echo "=== CURRENT PATH ===" && echo $PATH',
'echo "=== HOME DIR ===" && echo $HOME',
// 2. Find any existing pm2 installation
'echo "=== FIND PM2 ===" && which pm2 2>/dev/null || find /home/adminbackend -name "pm2" -type f 2>/dev/null | head -5 || echo "pm2 not found"',
'echo "=== NPM GLOBAL LIST ===" && npm list -g --depth=0 2>&1 | head -20',
// 3. Check currently running Node.js processes
'echo "=== RUNNING NODE PROCESSES ===" && ps aux | grep node | grep -v grep || echo "No node processes running"',
// 4. Check if cPanel Node.js App is configured
'echo "=== CPANEL NODEAPP CONFIG ===" && ls /home/adminbackend/.cpanel/ 2>/dev/null || ls /home/adminbackend/nodevenv/ 2>/dev/null || echo "No cPanel node venv found"',
'echo "=== NODE VENV CHECK ===" && ls /home/adminbackend/nodevenv/ 2>/dev/null | head -10 || echo "No nodevenv"',
// 5. Install PM2 locally to user home prefix (no root needed)
'echo "=== INSTALLING PM2 LOCALLY ===" && npm install pm2 --prefix /home/adminbackend/.local 2>&1 | tail -5',
'echo "=== PM2 PATH ===" && /home/adminbackend/.local/bin/pm2 --version 2>&1 || echo "pm2 local install failed"',
// 6. Kill any existing BackOne processes to avoid port conflicts
`echo "=== KILLING EXISTING PROCESSES ===" && pkill -f "proxy/index.js" 2>/dev/null || true && pkill -f "backend/server.js" 2>/dev/null || true && pkill -f "standalone/server.js" 2>/dev/null || true && sleep 2 && echo "Done"`,
// 7. Start backend server
`echo "=== STARTING BACKEND ===" && cd ${REMOTE_ROOT} && NODE_ENV=production $(cat .env.production | grep -v '^#' | grep -v '^$' | xargs) node backend/server.js > logs/backend-out.log 2>logs/backend-error.log &`,
// 8. Start proxy server
`echo "=== STARTING PROXY ===" && cd ${REMOTE_ROOT} && NODE_ENV=production node proxy/index.js > logs/proxy-out.log 2>logs/proxy-error.log &`,
// 9. Start Next.js frontend
`echo "=== STARTING FRONTEND ===" && cd ${REMOTE_ROOT} && NODE_ENV=production PORT=3000 HOSTNAME=127.0.0.1 node .next/standalone/server.js > logs/frontend-out.log 2>logs/frontend-error.log &`,
// 10. Wait and verify
'sleep 6',
// 11. Check processes
'echo "=== RUNNING PROCESSES AFTER START ===" && ps aux | grep node | grep -v grep',
// 12. Test health endpoints
'echo "=== BACKEND HEALTH ===" && curl -s http://127.0.0.1:3001/api/health 2>&1 || echo "Backend not responding"',
'echo "=== FRONTEND HEALTH ===" && curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:3000/ 2>&1 || echo "Frontend not responding"',
// 13. Show recent logs
'echo "=== BACKEND LOG (last 10 lines) ===" && tail -10 /home/adminbackend/web/demoplace.my.id/public_html/logs/backend-out.log 2>/dev/null || echo "No backend log"',
'echo "=== PROXY LOG (last 5 lines) ===" && tail -5 /home/adminbackend/web/demoplace.my.id/public_html/logs/proxy-out.log 2>/dev/null || echo "No proxy log"',
];
function runSshCommands(commands) {
return new Promise((resolve, reject) => {
const ssh = new SshClient();
ssh.on('ready', () => {
console.log('[SSH] Connected to server!\n');
let index = 0;
function runNext() {
if (index >= commands.length) {
console.log('\n[SSH] All commands complete. Disconnecting...');
ssh.end();
return;
}
const cmd = commands[index++];
console.log(`\n$ ${cmd.substring(0, 100)}${cmd.length > 100 ? '...' : ''}`);
ssh.exec(cmd, (err, stream) => {
if (err) {
console.error(`[ERROR] ${err.message}`);
runNext();
return;
}
stream.on('data', (data) => process.stdout.write(data.toString()));
stream.stderr.on('data', (data) => process.stdout.write(`[ERR] ${data}`));
stream.on('close', () => runNext());
});
}
runNext();
ssh.on('end', resolve);
});
ssh.on('error', reject);
ssh.connect({ ...CONFIG, readyTimeout: 30000 });
});
}
async function main() {
console.log('\n╔══════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — Server Setup & Process Manager ║');
console.log('╚══════════════════════════════════════════════════╝\n');
try {
await runSshCommands(COMMANDS);
console.log('\n✅ Server setup complete!\n');
} catch (err) {
console.error('\n[FATAL]', err.message);
process.exit(1);
}
}
main();
-133
View File
@@ -1,133 +0,0 @@
// scripts/start-frontend.js
// ─────────────────────────────────────────────────────────────────────────────
// BackOne DPI — Start Frontend & Fix PM2 Setup on Server
// ─────────────────────────────────────────────────────────────────────────────
'use strict';
const { Client: SshClient } = require('ssh2');
const CONFIG = {
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH',
};
const REMOTE_ROOT = '/home/adminbackend/web/demoplace.my.id/public_html';
const COMMANDS = [
// Check what's currently running on port 3000
'echo "=== PORT 3000 CHECK ===" && ss -tlnp 2>/dev/null | grep 3000 || netstat -tlnp 2>/dev/null | grep 3000 || echo "Port 3000 not in use"',
// Check if standalone server.js exists
`echo "=== STANDALONE CHECK ===" && ls -la ${REMOTE_ROOT}/.next/standalone/server.js 2>/dev/null || echo "standalone server.js NOT FOUND"`,
// List standalone directory structure
`echo "=== STANDALONE DIRECTORY ===" && ls ${REMOTE_ROOT}/.next/standalone/ 2>/dev/null | head -20`,
// Check static assets copied correctly
`echo "=== STATIC IN STANDALONE ===" && ls ${REMOTE_ROOT}/.next/standalone/.next/ 2>/dev/null || echo "No .next dir in standalone"`,
// Find the correct npm bin directory for npx pm2
'echo "=== NPM BIN DIR ===" && npm bin 2>/dev/null || npm config get prefix',
// Check npx pm2 path
'echo "=== NPX PM2 PATH ===" && find /home/adminbackend/.npm -name "pm2" -type f 2>/dev/null | grep "/bin/pm2" | head -3',
// Install PM2 using npm to user's npm prefix
'echo "=== SET NPM PREFIX ===" && npm config set prefix /home/adminbackend/.npm-global && npm install -g pm2 2>&1 | tail -5',
'echo "=== PM2 INSTALLED ===" && /home/adminbackend/.npm-global/bin/pm2 --version 2>&1 || echo "Still not found"',
// Try npx approach
`echo "=== USING NPX PM2 ===" && cd ${REMOTE_ROOT} && npx pm2 --version 2>&1`,
// Start frontend standalone server if not running
`echo "=== CHECKING STANDALONE SERVER ===" && ls -la ${REMOTE_ROOT}/.next/standalone/server.js`,
// Start with nohup (works on cPanel)
`echo "=== STARTING FRONTEND NOHUP ===" && cd ${REMOTE_ROOT} && NODE_ENV=production PORT=3000 HOSTNAME=0.0.0.0 NEXT_PUBLIC_API_URL=http://127.0.0.1:3001 nohup node .next/standalone/server.js >> logs/frontend-out.log 2>&1 & echo "PID: $!"`,
// Wait and check
'sleep 5',
// Verify port 3000 is now listening
'echo "=== PORT 3000 AFTER START ===" && ss -tlnp | grep 3000 || netstat -tlnp | grep 3000 || echo "Port 3000 still not in use"',
// Check all running processes
'echo "=== ALL NODE PROCESSES ===" && ps aux | grep node | grep -v grep',
// Test all endpoints
'echo "=== BACKEND HEALTH ===" && curl -s http://127.0.0.1:3001/api/health',
'echo "=== FRONTEND RESPONSE ===" && curl -s -o /dev/null -w "HTTP Status: %{http_code}" http://127.0.0.1:3000/ 2>&1',
// Show frontend log
`echo "=== FRONTEND LOG ===" && tail -20 ${REMOTE_ROOT}/logs/frontend-out.log 2>/dev/null || echo "No frontend log"`,
// Check proxy connection to MongoDB
`echo "=== PROXY LOG ===" && tail -10 ${REMOTE_ROOT}/logs/proxy-out.log 2>/dev/null || echo "No proxy log"`,
// Show existing pm2 in npx cache and start all services via npx pm2
`echo "=== STARTING WITH NPX PM2 ===" && cd ${REMOTE_ROOT} && NODE_ENV=production npx pm2 list 2>&1 | head -20`,
];
function runSshCommands(commands) {
return new Promise((resolve, reject) => {
const ssh = new SshClient();
const results = [];
ssh.on('ready', () => {
console.log('[SSH] Connected!\n');
let index = 0;
function runNext() {
if (index >= commands.length) {
ssh.end();
return;
}
const cmd = commands[index++];
const preview = cmd.length > 120 ? cmd.substring(0, 120) + '...' : cmd;
console.log(`\n$ ${preview}`);
ssh.exec(cmd, { env: { PATH: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' } }, (err, stream) => {
if (err) {
console.error(`[ERROR] ${err.message}`);
runNext();
return;
}
stream.on('data', (data) => process.stdout.write(data.toString()));
stream.stderr.on('data', (data) => {
const text = data.toString();
if (!text.includes('npm warn') && !text.includes('npm notice')) {
process.stdout.write(`[ERR] ${text}`);
}
});
stream.on('close', () => runNext());
});
}
runNext();
ssh.on('end', () => resolve(results));
});
ssh.on('error', reject);
ssh.connect({ ...CONFIG, readyTimeout: 30000 });
});
}
async function main() {
console.log('\n╔══════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — Frontend Startup & PM2 Fix ║');
console.log('╚══════════════════════════════════════════════════╝\n');
try {
await runSshCommands(COMMANDS);
console.log('\n✅ Frontend startup attempt complete!\n');
} catch (err) {
console.error('\n[FATAL]', err.message);
process.exit(1);
}
}
main();
-93
View File
@@ -1,93 +0,0 @@
const { spawnSync } = require('child_process');
const path = require('path');
const fs = require('fs');
const http = require('http');
function loginAndGetCookie() {
return new Promise((resolve) => {
const data = JSON.stringify({ username: 'admin', password: 'admin' });
const req = http.request({
hostname: '127.0.0.1',
port: 3000,
path: '/api/auth/login',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
}
}, (res) => {
let body = '';
res.on('data', chunk => body += chunk);
res.on('end', () => {
console.log("Login HTTP Status:", res.statusCode);
const setCookieHeader = res.headers['set-cookie'];
if (setCookieHeader && setCookieHeader.length > 0) {
const cookieVal = setCookieHeader[0].split(';')[0];
console.log("Authenticated Cookie:", cookieVal);
resolve(cookieVal);
} else {
console.log("No Set-Cookie header returned:", body);
resolve(null);
}
});
});
req.on('error', (err) => {
console.error("Login request error:", err.message);
resolve(null);
});
req.write(data);
req.end();
});
}
async function run() {
const cookieHeader = await loginAndGetCookie();
if (!cookieHeader) {
console.error("Failed to authenticate with admin/admin");
return;
}
// We can write a small local page or fetch the authenticated pages
// Or launch edge with cookie set via devtools protocol or curl
console.log("Fetching authenticated HTML for / ...");
const req = http.request({
hostname: '127.0.0.1',
port: 3000,
path: '/',
method: 'GET',
headers: {
'Cookie': cookieHeader
}
}, (res) => {
console.log("Dashboard GET Status:", res.statusCode);
});
req.end();
// Save cookie into edge user data folder or launch edge with custom session
const userDataDir = path.join(__dirname, 'edge_session_dir');
if (!fs.existsSync(userDataDir)) {
fs.mkdirSync(userDataDir, { recursive: true });
}
const edgePath = "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe";
const outputDir = "C:\\Users\\rafif\\.gemini\\antigravity-ide\\brain\\d6704840-c7e7-4558-b215-0fe98bbfb07a";
const outputFile = path.join(outputDir, "mobile-dashboard-overview.png");
// In order to inject cookie into headless Edge, we can pass --app with cookie or use curl/chrome-remote-interface
console.log("Executing Edge capture...");
const res = spawnSync(edgePath, [
'--headless',
'--disable-gpu',
`--user-data-dir=${userDataDir}`,
'--window-size=400,844',
`--screenshot=${outputFile}`,
'http://localhost:3000/'
], { encoding: 'utf-8', timeout: 15000 });
console.log("Done! Exit code:", res.status);
}
run();
-24
View File
@@ -1,24 +0,0 @@
const { spawnSync } = require('child_process');
const path = require('path');
const fs = require('fs');
const edgePath = "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe";
const outputDir = "C:\\Users\\rafif\\.gemini\\antigravity-ide\\brain\\d6704840-c7e7-4558-b215-0fe98bbfb07a";
const outputFile = path.join(outputDir, "mobile-localhost.png");
console.log("Launching Edge headless to capture screenshot of http://localhost:3000 ...");
const res = spawnSync(edgePath, [
'--headless',
'--disable-gpu',
'--window-size=400,844',
`--screenshot=${outputFile}`,
'http://localhost:3000'
], { encoding: 'utf-8', timeout: 15000 });
console.log("Exit code:", res.status);
if (fs.existsSync(outputFile)) {
console.log("Screenshot successfully saved to:", outputFile, "Size:", fs.statSync(outputFile).size, "bytes");
} else {
console.log("Screenshot file not found.");
}
-37
View File
@@ -1,37 +0,0 @@
const http = require('http');
function testUrl(url) {
return new Promise((resolve) => {
const start = Date.now();
console.log(`[TEST] Fetching ${url}...`);
const req = http.get(url, { timeout: 5000 }, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
const duration = Date.now() - start;
console.log(`[TEST] ${url} -> Status: ${res.statusCode}, Time: ${duration}ms, Bytes: ${data.length}`);
resolve({ url, status: res.statusCode, duration, bytes: data.length });
});
});
req.on('error', (err) => {
const duration = Date.now() - start;
console.log(`[TEST] ${url} -> ERROR: ${err.message}, Time: ${duration}ms`);
resolve({ url, error: err.message, duration });
});
req.on('timeout', () => {
req.destroy();
const duration = Date.now() - start;
console.log(`[TEST] ${url} -> TIMEOUT after ${duration}ms`);
resolve({ url, timeout: true, duration });
});
});
}
async function run() {
console.log('=== STARTING DASHBOARD ROUTE SPEED TEST ===');
await testUrl('http://127.0.0.1:3001/api/dashboard/devices');
await testUrl('http://127.0.0.1:3001/api/dashboard/summary');
console.log('=== TEST COMPLETE ===');
}
run();
-28
View File
@@ -1,28 +0,0 @@
if (typeof globalThis.crypto === 'undefined') {
globalThis.crypto = require('crypto');
}
const mongoose = require('mongoose');
const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '..', '.env.production') });
const MONGODB_URI = process.env.MONGODB_URI;
const { collectAllAgents } = require('../proxy/collector');
async function run() {
console.log('[Trigger] Connecting to MongoDB...');
await mongoose.connect(MONGODB_URI);
console.log('[Trigger] Connected. Starting collection cycle...');
try {
const res = await collectAllAgents();
console.log('[Trigger] Collection cycle finished successfully:', res);
} catch (err) {
console.error('[Trigger] Collection cycle failed:', err);
} finally {
await mongoose.connection.close();
console.log('[Trigger] MongoDB connection closed.');
}
}
run();
-132
View File
@@ -1,132 +0,0 @@
// scripts/update-pm2-config.js
// ─────────────────────────────────────────────────────────────────────────────
// BackOne DPI — Update PM2 to use correct standalone script path
// The frontend PM2 process is currently running the OLD server.js from root.
// We need to delete and re-create the PM2 process with the correct script.
// ─────────────────────────────────────────────────────────────────────────────
'use strict';
const { Client: SshClient } = require('ssh2');
const https = require('https');
const CONFIG = {
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH',
};
const ROOT = '/home/adminbackend/web/demoplace.my.id/public_html';
const PM2 = '/home/adminbackend/.npm-global/bin/pm2';
const COMMANDS = [
// Show current PM2 state
`echo "=== CURRENT PM2 STATE ===" && ${PM2} list`,
`echo "=== CURRENT FRONTEND SCRIPT ===" && ${PM2} show backone-frontend 2>&1 | grep -E "(script|cwd)" | head -5`,
// Delete old frontend process and start fresh with correct script
`echo "=== DELETING OLD FRONTEND PROCESS ===" && ${PM2} delete backone-frontend && echo "Deleted"`,
// Start frontend using the new ecosystem config (which points to .next/standalone/server.js)
`echo "=== STARTING FRONTEND WITH NEW CONFIG ===" && cd ${ROOT} && NODE_ENV=production ${PM2} start ecosystem.config.js --only backone-frontend && echo "Started"`,
// Wait for startup
`sleep 6`,
// Verify new process
`echo "=== NEW PM2 STATUS ===" && ${PM2} list`,
`echo "=== NEW FRONTEND SCRIPT ===" && ${PM2} show backone-frontend 2>&1 | grep -E "(script|cwd|version|status)" | head -8`,
// Test frontend response
`echo "=== FRONTEND INTERNAL TEST ===" && curl -s -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/`,
`echo "=== FRONTEND LOGIN TEST ===" && curl -s -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/login`,
// Save PM2 state
`echo "=== SAVING PM2 STATE ===" && ${PM2} save && echo "PM2 state saved"`,
// Show frontend log to confirm new code
`echo "=== FRONTEND LOG ===" && ${PM2} logs backone-frontend --lines 10 --nostream 2>&1 | tail -15`,
// Check HTTPS now
`echo "=== HTTPS VIA CURL ===" && curl -s -o /dev/null -w "HTTP %{http_code}" -L --max-redirs 3 https://demoplace.my.id/ 2>&1 || echo "HTTPS not accessible from server itself"`,
// Check HTTP (which should redirect to HTTPS per .htaccess)
`echo "=== HTTP CHECK ===" && curl -s -o /dev/null -w "HTTP %{http_code}" http://demoplace.my.id/ 2>&1`,
// Check Apache config for this domain
`echo "=== APACHE VHOST CONFIG ===" && cat /etc/apache2/conf.d/userdata/ssl/2_4/adminbackend/demoplace.my.id/*.conf 2>/dev/null | head -30 || ls /etc/apache2/conf.d/userdata/ 2>/dev/null | head -5 || echo "No Apache vhost config accessible"`,
// Check cPanel proxy config
`echo "=== CPANEL PROXY CONF ===" && cat /home/adminbackend/.cpanel/ea4/php.conf 2>/dev/null || echo "No cPanel PHP conf"`,
`cat /home/adminbackend/etc/apache_conf 2>/dev/null || echo "No Apache conf in home"`,
// Final summary
`echo ""`,
`echo "╔══════════════════════════════════════════════════════╗"`,
`echo "║ BackOne DPI — PM2 Update Complete ║"`,
`${PM2} list 2>/dev/null | grep -E "(online|error)" | awk '{print "║ " $2 " " $10 " " $6 " ║"}'`,
`echo "╚══════════════════════════════════════════════════════╝"`,
];
function runSsh(commands) {
return new Promise((resolve, reject) => {
const ssh = new SshClient();
ssh.on('ready', () => {
console.log('[SSH] Connected!\n');
let i = 0;
function next() {
if (i >= commands.length) { ssh.end(); return; }
const cmd = commands[i++];
console.log(`\n$ ${cmd.substring(0, 130)}${cmd.length > 130 ? '...' : ''}`);
ssh.exec(cmd, (err, stream) => {
if (err) { console.error('[ERR]', err.message); next(); return; }
stream.on('data', d => process.stdout.write(d.toString()));
stream.stderr.on('data', d => { const t = d.toString(); if (!t.includes('npm warn') && !t.includes('notice')) process.stdout.write(t); });
stream.on('close', next);
});
}
next();
ssh.on('end', resolve);
});
ssh.on('error', reject);
ssh.connect({ ...CONFIG, readyTimeout: 30000 });
});
}
async function checkHttps(url) {
return new Promise(resolve => {
const req = https.get(url, { timeout: 10000, rejectUnauthorized: false }, res => {
let body = '';
res.on('data', d => body += d);
res.on('end', () => resolve({ status: res.statusCode, location: res.headers.location, body: body.substring(0, 150) }));
});
req.on('error', e => resolve({ status: 0, error: e.message }));
req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
});
}
async function main() {
console.log('\n╔══════════════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — Fix PM2 Script & HTTPS Verification ║');
console.log('╚══════════════════════════════════════════════════════════╝\n');
await runSsh(COMMANDS);
console.log('\n\n▶ External HTTPS checks (from local machine)...\n');
const checks = [
'https://demoplace.my.id/',
'https://demoplace.my.id/login',
'https://demoplace.my.id/api/health',
];
for (const url of checks) {
const r = await checkHttps(url);
const icon = r.status >= 200 && r.status < 400 ? '✅' : r.status === 0 ? '⚠️' : '❌';
console.log(` ${icon} ${url} → HTTP ${r.status} ${r.error || ''}`);
if (r.location) console.log(` → Redirects to: ${r.location}`);
if (r.status === 200 && r.body) console.log(` Body preview: ${r.body.substring(0, 100)}`);
}
console.log('\n✅ Done!\n');
}
main().catch(err => { console.error('[FATAL]', err); process.exit(1); });
-94
View File
@@ -1,94 +0,0 @@
const SftpClient = require('ssh2-sftp-client');
const { Client } = require('ssh2');
const remoteAuditScript = `
const fs = require('fs');
const path = require('path');
const root = '/home/adminbackend/web/demoplace.my.id/public_html';
console.log('====================================================');
console.log(' FULL COMPONENT & PAGE AUDIT OF DEMOPLACE.MY.ID ');
console.log('====================================================');
// 1. Audit Client/Server Chunks for Page Headers & HelpTriggers
const chunksDir = path.join(root, '.next/standalone/.next/server/chunks');
if (fs.existsSync(chunksDir)) {
const files = fs.readdirSync(chunksDir);
files.forEach(f => {
if (!f.endsWith('.js')) return;
const content = fs.readFileSync(path.join(chunksDir, f), 'utf8');
// Look for page headers or title definitions
const headerMatch = content.match(/["']([A-Z][A-Za-z0-9\\s]{3,35})["'],\\s*description:/);
if (headerMatch) {
console.log(\`[Chunk \${f}] Found Header Definition: "\${headerMatch[1]}"\`);
}
if (content.includes('Learn This Page')) {
console.log(\`[Chunk \${f}] Contains "Learn This Page" button\`);
}
});
}
// 2. Audit Page Source files in src/app/(dashboard)
const appSrcDir = path.join(root, 'src/app/(dashboard)');
if (fs.existsSync(appSrcDir)) {
function scan(dir) {
const items = fs.readdirSync(dir, { withFileTypes: true });
for (const item of items) {
const full = path.join(dir, item.name);
if (item.isDirectory()) {
scan(full);
} else if (item.name === 'page.tsx' || item.name.endsWith('.tsx')) {
const content = fs.readFileSync(full, 'utf8');
const rel = path.relative(appSrcDir, full);
// Find H2 or H1
const h2 = content.match(/<h[12][^>]*>([\\s\\S]*?)<\\/h[12]>/i);
const titleText = h2 ? h2[1].replace(/<[^>]+>/g, '').trim().split('\\n')[0] : 'None';
const hasHelp = content.includes('HelpTrigger') || content.includes('Learn This Page');
console.log(\`Page File: \${rel}\`);
console.log(\` Header Title : \${titleText}\`);
console.log(\` Has HelpButton: \${hasHelp}\`);
console.log('----------------------------------------------------');
}
}
}
scan(appSrcDir);
}
`;
async function main() {
const sftp = new SftpClient();
await sftp.connect({
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH'
});
const remoteScriptPath = '/home/adminbackend/audit_remote.js';
await sftp.put(Buffer.from(remoteAuditScript), remoteScriptPath);
await sftp.end();
const conn = new Client();
conn.on('ready', () => {
conn.exec(`node ${remoteScriptPath}`, (err, stream) => {
let out = '';
stream.on('data', d => out += d);
stream.on('close', () => {
console.log(out);
conn.end();
});
});
}).connect({
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH'
});
}
main();
-87
View File
@@ -1,87 +0,0 @@
// scripts/upload-profile-pictures.js
// Upload semua foto profil dari lokal ke server production
'use strict';
const SftpClient = require('ssh2-sftp-client');
const { Client: SshClient } = require('ssh2');
const fs = require('fs');
const path = require('path');
const CONFIG = {
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH',
readyTimeout: 30000,
retries: 3,
retry_factor: 2,
retry_minTimeout: 2000,
};
const LOCAL_DIR = path.join(__dirname, '..', 'backend', 'public', 'api', 'uploads');
const REMOTE_DIR = '/home/adminbackend/web/demoplace.my.id/public_html/api/uploads';
function sshExec(cmd) {
return new Promise((resolve, reject) => {
const conn = new SshClient();
conn.on('ready', () => {
conn.exec(cmd, (err, stream) => {
if (err) { conn.end(); return reject(err); }
let out = '';
stream
.on('data', d => { out += d; })
.stderr.on('data', d => { out += d; });
stream.on('close', () => { conn.end(); resolve(out); });
});
}).on('error', reject)
.connect({ host: CONFIG.host, port: CONFIG.port, username: CONFIG.username, password: CONFIG.password, readyTimeout: CONFIG.readyTimeout });
});
}
async function main() {
if (!fs.existsSync(LOCAL_DIR)) {
console.log('Folder uploads lokal tidak ditemukan:', LOCAL_DIR);
return;
}
const files = fs.readdirSync(LOCAL_DIR).filter(f => /\.(png|jpg|jpeg|webp)$/i.test(f));
if (files.length === 0) {
console.log('Tidak ada file gambar di folder uploads lokal.');
return;
}
console.log(`Ditemukan ${files.length} file foto profil untuk diupload:\n ${files.join('\n ')}\n`);
// Buat direktori di server jika belum ada
await sshExec(`mkdir -p ${REMOTE_DIR} && chmod 755 ${REMOTE_DIR}`);
console.log(`[Server] Direktori ${REMOTE_DIR} siap\n`);
// Upload semua file via SFTP
const sftp = new SftpClient();
await sftp.connect(CONFIG);
console.log('[SFTP] Terhubung ke server\n');
let uploaded = 0;
let failed = 0;
for (const file of files) {
const localPath = path.join(LOCAL_DIR, file);
const remotePath = `${REMOTE_DIR}/${file}`;
try {
await sftp.put(localPath, remotePath);
console.log(`✅ Upload: ${file}`);
uploaded++;
} catch (err) {
console.error(`❌ Gagal upload ${file}:`, err.message);
failed++;
}
}
await sftp.end();
// Verifikasi
const remoteList = await sshExec(`ls -lh ${REMOTE_DIR}`);
console.log('\n[Server] Isi folder uploads:\n' + remoteList);
console.log(`\n✅ Selesai! ${uploaded} berhasil, ${failed} gagal.`);
}
main().catch(e => console.error('[FATAL]', e.message));
-147
View File
@@ -1,147 +0,0 @@
// scripts/upload-standalone-next.js
// ─────────────────────────────────────────────────────────────────────────────
// BackOne DPI — Upload missing .next/standalone/.next/ directory to server
// This fixes: "Could not find a production build in the './.next' directory"
// ─────────────────────────────────────────────────────────────────────────────
'use strict';
const path = require('path');
const fs = require('fs');
const SftpClient = require('ssh2-sftp-client');
const { Client: SshClient } = require('ssh2');
const LOCAL_ROOT = path.resolve(__dirname, '..');
const REMOTE_ROOT = '/home/adminbackend/web/demoplace.my.id/public_html';
const PM2 = '/home/adminbackend/.npm-global/bin/pm2';
const CONFIG = {
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH',
};
// Recursively upload a local directory to remote (skipping node_modules and .git)
async function uploadDir(sftp, localPath, remotePath) {
const items = fs.readdirSync(localPath, { withFileTypes: true });
try { await sftp.mkdir(remotePath, true); } catch {}
for (const item of items) {
if (['node_modules', '.git'].includes(item.name)) continue;
if (item.name.endsWith('.log')) continue;
const lp = path.join(localPath, item.name);
const rp = remotePath + '/' + item.name;
if (item.isDirectory()) {
process.stdout.write(` [DIR] ${rp}\n`);
await uploadDir(sftp, lp, rp);
} else {
process.stdout.write(` [UP] ${rp} ...`);
await sftp.put(lp, rp);
process.stdout.write(' done\n');
}
}
}
function runSsh(commands) {
return new Promise((resolve, reject) => {
const ssh = new SshClient();
ssh.on('ready', () => {
let i = 0;
function next() {
if (i >= commands.length) { ssh.end(); return; }
const cmd = commands[i++];
console.log(`\n$ ${cmd.substring(0, 120)}${cmd.length > 120 ? '...' : ''}`);
ssh.exec(cmd, (err, stream) => {
if (err) { console.error('[ERR]', err.message); next(); return; }
stream.on('data', d => process.stdout.write(d.toString()));
stream.stderr.on('data', d => { const t = d.toString(); if (!t.includes('npm warn') && !t.includes('notice')) process.stdout.write(t); });
stream.on('close', next);
});
}
next();
ssh.on('end', resolve);
});
ssh.on('error', reject);
ssh.connect({ ...CONFIG, readyTimeout: 30000 });
});
}
async function main() {
console.log('\n╔══════════════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — Upload Standalone Build Artifacts ║');
console.log('║ Uploading: .next/standalone/.next/ (BUILD_ID + server) ║');
console.log('╚══════════════════════════════════════════════════════════╝\n');
const localStandaloneNext = path.join(LOCAL_ROOT, '.next', 'standalone', '.next');
if (!fs.existsSync(localStandaloneNext)) {
console.error('✗ ERROR: .next/standalone/.next not found locally!');
console.error(' Run: npm run build');
process.exit(1);
}
// Verify BUILD_ID exists
const buildId = fs.existsSync(path.join(localStandaloneNext, 'BUILD_ID'))
? fs.readFileSync(path.join(localStandaloneNext, 'BUILD_ID'), 'utf8').trim()
: null;
console.log(`✓ Build ID: ${buildId || 'NOT FOUND'}`);
console.log(`✓ Local path: ${localStandaloneNext}`);
const sftp = new SftpClient();
try {
await sftp.connect(CONFIG);
console.log('[SFTP] Connected!\n');
const remotePath = `${REMOTE_ROOT}/.next/standalone/.next`;
console.log(`\n[DIR] Uploading .next/standalone/.next/ → ${remotePath}\n`);
await uploadDir(sftp, localStandaloneNext, remotePath);
console.log('\n[SFTP] Upload complete!\n');
} finally {
await sftp.end();
}
// Post-upload: restart PM2 frontend, verify
console.log('\n▶ Restarting PM2 frontend with new build...\n');
await runSsh([
// Verify BUILD_ID now exists on server
`echo "=== VERIFY BUILD_ID ===" && cat ${REMOTE_ROOT}/.next/standalone/.next/BUILD_ID`,
// Also verify static dir exists
`echo "=== VERIFY STATIC ===" && ls ${REMOTE_ROOT}/.next/standalone/.next/static/ 2>/dev/null | wc -l | xargs echo "Static files:"`,
// Restart frontend PM2 process
`echo "=== RESTARTING FRONTEND ===" && ${PM2} restart backone-frontend --update-env && echo "Restarted"`,
// Wait
`sleep 6`,
// Check PM2 status
`echo "=== PM2 STATUS ===" && ${PM2} list`,
// Test internal endpoints
`echo "=== FRONTEND / ===" && curl -s -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/`,
`echo "=== FRONTEND /login ===" && curl -s -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/login`,
`echo "=== BACKEND HEALTH ===" && curl -s http://127.0.0.1:3001/api/health`,
// Show frontend log
`echo "=== FRONTEND LOG ===" && ${PM2} logs backone-frontend --lines 10 --nostream 2>&1 | tail -15`,
// Test via domain (HTTP which redirects to HTTPS via .htaccess)
`echo "=== DOMAIN HTTP TEST ===" && curl -s -o /dev/null -w "HTTP %{http_code}" http://demoplace.my.id/`,
// Check port 3000
`echo "=== PORT 3000 ===" && ss -tlnp | grep 3000`,
]);
console.log('\n══════════════════════════════════════════════════════════');
console.log(' ✅ Standalone build artifacts uploaded & frontend restarted!');
console.log(' 🌐 Dashboard: https://demoplace.my.id');
console.log('══════════════════════════════════════════════════════════\n');
}
main().catch(err => {
console.error('\n[FATAL]', err.message);
process.exit(1);
});
-148
View File
@@ -1,148 +0,0 @@
// scripts/verify-deployment.js
// ─────────────────────────────────────────────────────────────────────────────
// BackOne DPI — Post-Deployment Verification Script
// Tests: frontend, backend, security headers, proxy, MongoDB
// ─────────────────────────────────────────────────────────────────────────────
'use strict';
const { Client: SshClient } = require('ssh2');
const https = require('https');
const http = require('http');
const CONFIG = {
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH',
};
const PM2 = '/home/adminbackend/.npm-global/bin/pm2';
const ROOT = '/home/adminbackend/web/demoplace.my.id/public_html';
// HTTP request helper
function httpGet(url) {
return new Promise((resolve) => {
const mod = url.startsWith('https') ? https : http;
const req = mod.get(url, { timeout: 10000 }, (res) => {
let body = '';
res.on('data', (d) => body += d);
res.on('end', () => resolve({ status: res.statusCode, headers: res.headers, body: body.substring(0, 300) }));
});
req.on('error', (e) => resolve({ status: 0, error: e.message }));
req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
});
}
const SSH_COMMANDS = [
// Check PM2 process list
`echo "=== PM2 STATUS ===" && ${PM2} list`,
// Check backend health
`echo "=== BACKEND HEALTH ===" && curl -s http://127.0.0.1:3001/api/health`,
// Check frontend pages
`echo "=== FRONTEND / ===" && curl -s -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/`,
`echo "=== FRONTEND /login ===" && curl -s -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:3000/login`,
// Security audit: ensure backend and proxy ports are NOT publicly accessible
`echo "=== SECURITY: Port 3001 external test ===" && ss -tlnp | grep 3001`,
`echo "=== SECURITY: Port 4000 external test ===" && ss -tlnp | grep 4000`,
// Check upload directory permissions
`echo "=== UPLOAD DIR PERMS ===" && ls -la ${ROOT}/backend/public/api/uploads/ 2>/dev/null || echo "Upload dir does not exist yet (will be created on first upload)"`,
// Check proxy MongoDB connection
`echo "=== PROXY STATUS ===" && ${PM2} logs backone-proxy --lines 5 --nostream 2>&1 | grep -E "(MongoDB|Connected|Capacity|Scheduler)" | tail -10`,
// Verify no secrets in Next.js build output (sanity check)
`echo "=== SECURITY AUDIT: Check no API key in build ===" && grep -r "sk_db_live" ${ROOT}/.next/standalone/ 2>/dev/null | head -3 || echo "CLEAN: No Netify API key found in build output"`,
`echo "=== SECURITY AUDIT: Check no MongoDB URI in build ===" && grep -r "backone_user" ${ROOT}/.next/standalone/ 2>/dev/null | head -3 || echo "CLEAN: No MongoDB credentials in build output"`,
// Check frontend log for readiness
`echo "=== FRONTEND READY LOG ===" && ${PM2} logs backone-frontend --lines 5 --nostream 2>&1 | grep -E "(Ready|Error|Next.js)" | tail -10`,
// Check disk usage for uploads dir
`echo "=== DISK USAGE ===" && df -h /home/adminbackend/web/ 2>/dev/null | tail -2`,
// Final summary
`echo ""`,
`echo "╔════════════════════════════════════════════╗"`,
`echo "║ BackOne DPI Production — Service Status ║"`,
`echo "╠════════════════════════════════════════════╣"`,
`${PM2} list 2>/dev/null | grep -E "(online|error|stopped)" | awk '{printf "║ %-12s %-10s %-8s ║\\n", $2, $10, $8}'`,
`echo "╚════════════════════════════════════════════╝"`,
];
function runSshChecks(commands) {
return new Promise((resolve, reject) => {
const ssh = new SshClient();
ssh.on('ready', () => {
console.log('[SSH] Connected for verification!\n');
let index = 0;
function runNext() {
if (index >= commands.length) {
ssh.end();
return;
}
const cmd = commands[index++];
console.log(`\n$ ${cmd.substring(0, 120)}${cmd.length > 120 ? '...' : ''}`);
ssh.exec(cmd, (err, stream) => {
if (err) { console.error(`[ERROR] ${err.message}`); runNext(); return; }
stream.on('data', (d) => process.stdout.write(d.toString()));
stream.stderr.on('data', (d) => {
const t = d.toString();
if (!t.includes('npm warn') && !t.includes('npm notice')) process.stdout.write(t);
});
stream.on('close', runNext);
});
}
runNext();
ssh.on('end', resolve);
});
ssh.on('error', reject);
ssh.connect({ ...CONFIG, readyTimeout: 30000 });
});
}
async function main() {
console.log('\n╔══════════════════════════════════════════════════════════╗');
console.log('║ BackOne DPI — Post-Deployment Verification ║');
console.log('╚══════════════════════════════════════════════════════════╝\n');
// ── SSH internal checks ───────────────────────────────────────────────────
console.log('▶ Running internal server checks via SSH...');
await runSshChecks(SSH_COMMANDS);
// ── External HTTP checks (from this machine) ──────────────────────────────
console.log('\n\n▶ Running external HTTP checks from local machine...\n');
const checks = [
{ url: 'http://demoplace.my.id/', label: 'Domain root (redirect expected)' },
{ url: 'http://demoplace.my.id/login', label: 'Login page' },
{ url: 'http://demoplace.my.id/api/health', label: 'Backend health via domain' },
];
for (const { url, label } of checks) {
const result = await httpGet(url);
const status = result.status;
const icon = (status >= 200 && status < 400) ? '✅' : (status === 0 ? '⚠️' : '❌');
console.log(` ${icon} ${label}: HTTP ${status} ${result.error || ''}`);
if (result.body && status >= 200 && status < 300) {
console.log(` Body preview: ${result.body.substring(0, 100)}`);
}
}
console.log('\n══════════════════════════════════════════════════════════');
console.log(' Production deployment verification complete!');
console.log(' URL: https://demoplace.my.id');
console.log('══════════════════════════════════════════════════════════\n');
}
main().catch((err) => {
console.error('[FATAL]', err.message);
process.exit(1);
});