feat(site): implement dynamic site selection dropdown in sidebar and enable multi-site monitoring

This commit is contained in:
Rafif-Riqullah-Siregar committed 2026-07-13 16:57:45 +07:00
1 parent be6bc14190
commit 4882108068
125 files changed
+5666 -4305

No files matched your search

+11 -2
View File
@@ -77,9 +77,17 @@ function requireAdmin(req, res, next) {
}
}
function getUploadsDir() {
if (fs.existsSync('/home/adminbackend/web/demoplace.my.id/public_html')) {
return '/home/adminbackend/web/demoplace.my.id/public_html/api/uploads';
} else {
return path.join(__dirname, '..', '..', 'public', 'api', 'uploads');
}
}
const storage = multer.diskStorage({
destination: (req, file, cb) => {
const dir = path.join(__dirname, '..', '..', 'uploads');
const dir = getUploadsDir();
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
cb(null, dir);
},
@@ -96,5 +104,6 @@ module.exports = {
setCookieToken,
requireAuth,
requireAdmin,
upload
upload,
getUploadsDir
};
+2 -2
View File
@@ -4,7 +4,7 @@ const bcrypt = require('bcryptjs');
const path = require('path');
const fs = require('fs');
const User = require('../../models/User');
const { requireAuth, makeToken, setCookieToken, upload } = require('./helpers');
const { requireAuth, makeToken, setCookieToken, upload, getUploadsDir } = require('./helpers');
const router = express.Router();
@@ -128,7 +128,7 @@ router.post('/remove-profile-picture', requireAuth, async (req, res) => {
if (!user) return res.status(404).json({ error: 'User not found' });
if (user.profile_picture) {
const filePath = path.join(__dirname, '..', '..', 'uploads', user.profile_picture);
const filePath = path.join(getUploadsDir(), user.profile_picture);
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
}
+38
View File
@@ -115,4 +115,42 @@ router.post('/admin/upload-agent-picture/:id', requireAdmin, blockAnalyst, uploa
}
});
// POST /api/auth/admin/create-external-user — buat akun Eksternal (SOC Analyst, Engineer, dll)
router.post('/admin/create-external-user', requireAdmin, blockAnalyst, upload.single('profile_picture'), async (req, res) => {
try {
const { username, password, account_name, role } = req.body;
if (!username || !password || !role) {
return res.status(400).json({ ok: false, error: 'Username, password, dan role wajib diisi' });
}
// Validasi role (hanya boleh role tertentu, tidak boleh SUPER_ADMIN baru atau AGENT_VIEWER)
const validRoles = ['SOC_ANALYST', 'ENGINEER', 'TENANT_ADMIN'];
if (!validRoles.includes(role)) {
return res.status(400).json({ ok: false, error: 'Role tidak valid untuk pembuatan akun eksternal' });
}
const existing = await User.findOne({ username: username.trim() });
if (existing) {
return res.status(400).json({ ok: false, error: 'Username sudah digunakan' });
}
const passwordHash = bcrypt.hashSync(password, 10);
const siteUuid = process.env.BACKONE_SITE_UUID || process.env.NETIFY_SITE_UUID || null;
const newUser = await User.create({
username: username.trim(),
password_hash: passwordHash,
account_name: account_name?.trim() || null,
role: role,
site_uuid: siteUuid,
profile_picture: req.file ? req.file.filename : null
});
res.json({ ok: true, message: 'Akun eksternal berhasil dibuat', userId: newUser._id.toString() });
} catch (err) {
const msg = err.code === 11000 ? 'Username sudah digunakan' : err.message;
res.status(400).json({ ok: false, error: msg });
}
});
module.exports = router;