feat(source2): lock dark mode, sans-serif typography, and all recent updates
This commit is contained in:
1 parent
dd4c8f6876
commit
9f24b56e97
259 files changed
+15596
-8966
No files matched your search
@@ -3,17 +3,11 @@ const express = require('express');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const User = require('../../models/User');
|
||||
const { requireAdmin, upload } = require('./helpers');
|
||||
const { blockAnalyst, resolveSiteUuidForAgent, mapUserData } = require('./usersHelper');
|
||||
const { handleCreateExternalUser } = require('./usersCreateExternal');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Helper to block SOC_ANALYST from write actions
|
||||
function blockAnalyst(req, res, next) {
|
||||
if (req.adminUser.role === 'SOC_ANALYST') {
|
||||
return res.status(403).json({ ok: false, error: 'Aksi ini tidak diizinkan untuk peran SOC Analyst' });
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
// GET /api/auth/admin/users — daftar semua users (admin & analyst)
|
||||
router.get('/admin/users', requireAdmin, async (req, res) => {
|
||||
try {
|
||||
@@ -25,20 +19,37 @@ router.get('/admin/users', requireAdmin, async (req, res) => {
|
||||
{ created_by: req.adminUser.username }
|
||||
]
|
||||
};
|
||||
} else if (req.adminUser.role === 'COMPANY_ADMIN') {
|
||||
query = { company_name: req.adminUser.company_name };
|
||||
const users = await User.find(query, '-password_hash').sort({ created_at: 1 });
|
||||
return res.json({ ok: true, data: users.map(mapUserData) });
|
||||
}
|
||||
query.username = { $ne: req.adminUser.username };
|
||||
const users = await User.find(query, '-password_hash').sort({ created_at: 1 });
|
||||
const data = users.map(u => ({
|
||||
id: u._id.toString(),
|
||||
username: u.username,
|
||||
account_name: u.account_name,
|
||||
profile_picture: u.profile_picture,
|
||||
role: u.role,
|
||||
site_uuid: u.site_uuid,
|
||||
agent_uuid: u.agent_uuid,
|
||||
is_active: u.is_active,
|
||||
}));
|
||||
res.json({ ok: true, data });
|
||||
res.json({ ok: true, data: users.map(mapUserData) });
|
||||
} catch (err) {
|
||||
res.status(500).json({ ok: false, error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/auth/admin/unlock-user — unlock akun yang terkunci
|
||||
router.post('/admin/unlock-user', requireAdmin, blockAnalyst, async (req, res) => {
|
||||
try {
|
||||
const { user_id } = req.body;
|
||||
if (!user_id) return res.status(400).json({ ok: false, error: 'user_id wajib diisi' });
|
||||
|
||||
const target = await User.findById(user_id);
|
||||
if (!target) return res.status(404).json({ ok: false, error: 'User tidak ditemukan' });
|
||||
|
||||
if (req.adminUser.role !== 'SUPER_ADMIN' && target.site_uuid !== req.adminUser.site_uuid) {
|
||||
return res.status(403).json({ ok: false, error: 'Unauthorized: Account does not belong to your tenant.' });
|
||||
}
|
||||
|
||||
target.login_attempts = 0;
|
||||
target.lockout_until = null;
|
||||
await target.save();
|
||||
|
||||
res.json({ ok: true, message: 'Akun berhasil di-unlock' });
|
||||
} catch (err) {
|
||||
res.status(500).json({ ok: false, error: err.message });
|
||||
}
|
||||
@@ -52,21 +63,7 @@ router.post('/admin/create-agent-user', requireAdmin, blockAnalyst, async (req,
|
||||
return res.status(400).json({ ok: false, error: 'Username dan password wajib diisi' });
|
||||
}
|
||||
const passwordHash = bcrypt.hashSync(password, 10);
|
||||
|
||||
let siteUuid = null;
|
||||
if (agent_uuid) {
|
||||
const { Summary } = require('../../models/Schemas');
|
||||
const summaryDoc = await Summary.findOne({ agent_uuid: agent_uuid.trim() });
|
||||
if (summaryDoc) {
|
||||
siteUuid = summaryDoc.site_uuid;
|
||||
}
|
||||
}
|
||||
|
||||
if (!siteUuid) {
|
||||
siteUuid = req.adminUser.role === 'SUPER_ADMIN'
|
||||
? (req.body.site_uuid || process.env.BACKONE_SITE_UUID || process.env.NETIFY_SITE_UUID || null)
|
||||
: req.adminUser.site_uuid;
|
||||
}
|
||||
const siteUuid = await resolveSiteUuidForAgent(agent_uuid, null, req.adminUser, req.body.site_uuid);
|
||||
|
||||
const newUser = await User.create({
|
||||
username: username.trim(),
|
||||
@@ -85,17 +82,30 @@ router.post('/admin/create-agent-user', requireAdmin, blockAnalyst, async (req,
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/auth/admin/update-agent-user — update akun Network Agent
|
||||
router.post('/admin/update-agent-user', requireAdmin, blockAnalyst, upload.single('profile_picture'), async (req, res) => {
|
||||
// POST /api/auth/admin/update-agent-user — update akun Network Agent / Company User
|
||||
router.post('/admin/update-agent-user', requireAdmin, blockAnalyst, async (req, res) => {
|
||||
try {
|
||||
const { user_id, username, password, account_name, agent_uuid } = req.body;
|
||||
const { user_id, username, password, account_name, agent_uuid, company_name } = req.body;
|
||||
let agent_uuids = null;
|
||||
if (req.body.agent_uuids) {
|
||||
try {
|
||||
agent_uuids = typeof req.body.agent_uuids === 'string' ? JSON.parse(req.body.agent_uuids) : req.body.agent_uuids;
|
||||
} catch {
|
||||
agent_uuids = [req.body.agent_uuids];
|
||||
}
|
||||
}
|
||||
|
||||
if (!user_id) return res.status(400).json({ ok: false, error: 'user_id wajib diisi' });
|
||||
|
||||
const target = await User.findById(user_id).select('+password_hash');
|
||||
if (!target) return res.status(404).json({ ok: false, error: 'User tidak ditemukan' });
|
||||
if (target.role === 'SUPER_ADMIN') return res.status(403).json({ ok: false, error: 'Tidak bisa mengubah akun SUPER_ADMIN dari sini' });
|
||||
|
||||
if (req.adminUser.role !== 'SUPER_ADMIN' && target.site_uuid !== req.adminUser.site_uuid) {
|
||||
if (req.adminUser.role === 'COMPANY_ADMIN') {
|
||||
if (target.company_name !== req.adminUser.company_name) {
|
||||
return res.status(403).json({ ok: false, error: 'Access Denied: Akun ini bukan milik perusahaan Anda.' });
|
||||
}
|
||||
} else if (req.adminUser.role !== 'SUPER_ADMIN' && target.site_uuid !== req.adminUser.site_uuid) {
|
||||
return res.status(403).json({ ok: false, error: 'Unauthorized: This account does not belong to your tenant.' });
|
||||
}
|
||||
|
||||
@@ -106,14 +116,26 @@ router.post('/admin/update-agent-user', requireAdmin, blockAnalyst, upload.singl
|
||||
}
|
||||
if (password) target.password_hash = bcrypt.hashSync(password, 10);
|
||||
if (account_name != null) target.account_name = account_name?.trim() || null;
|
||||
|
||||
if (company_name !== undefined && req.adminUser.role === 'SUPER_ADMIN') {
|
||||
target.company_name = company_name?.trim() || null;
|
||||
}
|
||||
|
||||
if (agent_uuids != null) {
|
||||
if (req.adminUser.role === 'COMPANY_ADMIN') {
|
||||
const allowedAgents = req.adminUser.agent_uuids || [];
|
||||
const invalidAgents = agent_uuids.filter(uuid => !allowedAgents.includes(uuid));
|
||||
if (invalidAgents.length > 0) {
|
||||
return res.status(403).json({ ok: false, error: 'Akses ditolak: Anda tidak memiliki wewenang untuk menetapkan agen tersebut.' });
|
||||
}
|
||||
}
|
||||
target.agent_uuids = agent_uuids;
|
||||
}
|
||||
|
||||
if (agent_uuid != null) {
|
||||
target.agent_uuid = agent_uuid?.trim() || null;
|
||||
if (agent_uuid.trim()) {
|
||||
const { Summary } = require('../../models/Schemas');
|
||||
const summaryDoc = await Summary.findOne({ agent_uuid: agent_uuid.trim() });
|
||||
if (summaryDoc) {
|
||||
target.site_uuid = summaryDoc.site_uuid;
|
||||
}
|
||||
target.site_uuid = await resolveSiteUuidForAgent(agent_uuid, target.site_uuid, req.adminUser, req.body.site_uuid);
|
||||
}
|
||||
}
|
||||
if (req.file) target.profile_picture = req.file.filename;
|
||||
@@ -133,7 +155,11 @@ router.delete('/admin/delete-agent-user/:id', requireAdmin, blockAnalyst, async
|
||||
if (!target) return res.status(404).json({ ok: false, error: 'User tidak ditemukan' });
|
||||
if (target.role === 'SUPER_ADMIN') return res.status(403).json({ ok: false, error: 'Tidak bisa menghapus SUPER_ADMIN' });
|
||||
|
||||
if (req.adminUser.role !== 'SUPER_ADMIN' && target.site_uuid !== req.adminUser.site_uuid) {
|
||||
if (req.adminUser.role === 'COMPANY_ADMIN') {
|
||||
if (target.company_name !== req.adminUser.company_name) {
|
||||
return res.status(403).json({ ok: false, error: 'Access Denied: Akun ini bukan milik perusahaan Anda.' });
|
||||
}
|
||||
} else if (req.adminUser.role !== 'SUPER_ADMIN' && target.site_uuid !== req.adminUser.site_uuid) {
|
||||
return res.status(403).json({ ok: false, error: 'Unauthorized: This account does not belong to your tenant.' });
|
||||
}
|
||||
await User.findByIdAndDelete(req.params.id);
|
||||
@@ -150,7 +176,11 @@ router.post('/admin/upload-agent-picture/:id', requireAdmin, blockAnalyst, uploa
|
||||
const target = await User.findById(req.params.id);
|
||||
if (!target) return res.status(404).json({ ok: false, error: 'User tidak ditemukan' });
|
||||
|
||||
if (req.adminUser.role !== 'SUPER_ADMIN' && target.site_uuid !== req.adminUser.site_uuid) {
|
||||
if (req.adminUser.role === 'COMPANY_ADMIN') {
|
||||
if (target.company_name !== req.adminUser.company_name) {
|
||||
return res.status(403).json({ ok: false, error: 'Access Denied: Akun ini bukan milik perusahaan Anda.' });
|
||||
}
|
||||
} else if (req.adminUser.role !== 'SUPER_ADMIN' && target.site_uuid !== req.adminUser.site_uuid) {
|
||||
return res.status(403).json({ ok: false, error: 'Unauthorized: This account does not belong to your tenant.' });
|
||||
}
|
||||
target.profile_picture = req.file.filename;
|
||||
@@ -162,48 +192,6 @@ 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 = req.adminUser.role === 'SUPER_ADMIN'
|
||||
? (req.body.site_uuid || process.env.BACKONE_SITE_UUID || process.env.NETIFY_SITE_UUID || null)
|
||||
: req.adminUser.site_uuid;
|
||||
|
||||
const createdBy = req.adminUser.role === 'SUPER_ADMIN'
|
||||
? (req.body.created_by || req.adminUser.username)
|
||||
: req.adminUser.username;
|
||||
|
||||
const newUser = await User.create({
|
||||
username: username.trim(),
|
||||
password_hash: passwordHash,
|
||||
account_name: account_name?.trim() || null,
|
||||
role: role,
|
||||
site_uuid: siteUuid,
|
||||
created_by: createdBy,
|
||||
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 });
|
||||
}
|
||||
});
|
||||
router.post('/admin/create-external-user', requireAdmin, blockAnalyst, handleCreateExternalUser);
|
||||
|
||||
module.exports = router;
|
||||
Reference in new issue
Block a user