feat(prod): deploy web dashboard, migrate mongodb configuration, resolve server components render error and fix logo static asset paths

This commit is contained in:
Rafif-Riqullah-Siregar committed 2026-07-15 00:27:55 +07:00
1 parent 4882108068
commit b2ea883601
119 files changed
+7123 -2011

No files matched your search

+18 -16
View File
@@ -11,7 +11,7 @@ const config = {
password: 'htEo7x6LsBQiEHHH'
};
const DEPLOY_DIR = 'backone-production';
const DEPLOY_DIR = 'web/demoplace.my.id/public_html';
async function createZip() {
console.log("Packaging application...");
@@ -30,11 +30,15 @@ async function createZip() {
// 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');
archive.directory(path.join(__dirname, '../public'), false);
// Add backend and proxy
archive.directory(path.join(__dirname, '../backend'), 'backend');
archive.directory(path.join(__dirname, '../proxy'), 'proxy');
// Add PM2 ecosystem config
archive.file(path.join(__dirname, '../ecosystem.config.js'), { name: 'ecosystem.config.js' });
// Add production env
archive.file(path.join(__dirname, '../.env.production'), { name: '.env.production' });
@@ -81,26 +85,24 @@ async function deploy() {
await sftp.put(zipPath, `${targetDir}/deploy.zip`);
console.log("Extracting package on server...");
await runSSH(`cd ${targetDir} && unzip -o deploy.zip && rm deploy.zip`);
await runSSH(`cd ${targetDir} && rm -rf .next && 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("Creating symlink for static files...");
await runSSH(`cd ${targetDir} && rm -rf _next && ln -s .next _next`);
console.log("Installing production dependencies for root, backend & proxy...");
await runSSH(`. ~/.nvm/nvm.sh && nvm use 20 && cd ${targetDir} && npm install --production`);
await runSSH(`. ~/.nvm/nvm.sh && nvm use 20 && cd ${targetDir}/backend && npm install --production`);
await runSSH(`. ~/.nvm/nvm.sh && nvm use 20 && 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`);
// PM2 ecosystem is now packaged in deploy.zip directly, no need to generate via bash echo.
console.log("Restarting PM2 processes...");
const pm2Result = await runSSH(`cd ${targetDir} && pm2 start ecosystem.config.js || pm2 restart ecosystem.config.js`);
// Clean delete old PM2 processes if they exist to apply new paths, then start fresh
await runSSH(`. ~/.nvm/nvm.sh && nvm use 20 && npx pm2 delete backone-frontend backone-backend backone-proxy || true`);
const pm2Result = await runSSH(`. ~/.nvm/nvm.sh && nvm use 20 && cd ${targetDir} && npx pm2 start ecosystem.config.js`);
console.log(pm2Result);
console.log("Deployment completed successfully!");
+123
View File
@@ -0,0 +1,123 @@
// scripts/migrate_production_config.js
const { MongoClient } = require('mongodb');
const path = require('path');
const LOCAL_URI = 'mongodb://127.0.0.1:27017';
const REMOTE_URI = 'mongodb://backone_user:SusuKudaLiar@mongodb.prod.proit.id:27017/backone_dpi?authSource=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();