/** * Settings API Routes * Untuk manage settings lewat API dengan security & validation */ const express = require('express'); const router = express.Router(); const { logger } = require('../config/logger'); const { getSetting, getSettings, saveSettings } = require('../config/settingsManager'); const { getMaskedSettings } = require('../config/settingsEncryption'); const { getAllRules } = require('../config/settingsValidator'); const { getChangeHistory, getAuditStats, exportAuditLog } = require('../config/settingsAudit'); const { requireSuperAdmin, validateSettingsMiddleware, maskSensitiveValues, logSettingsAccess, rateLimitSettings } = require('../middleware/settingsMiddleware'); // Apply middleware router.use(rateLimitSettings); router.use(maskSensitiveValues); router.use(logSettingsAccess); /** * GET /api/settings * Get all settings (masked) */ router.get('/', requireSuperAdmin, (req, res) => { try { const settings = getSettings(); const masked = getMaskedSettings(settings); return res.json({ success: true, settings: masked }); } catch (error) { logger.error(`[settings-api] Error getting settings: ${error.message}`); return res.status(500).json({ error: 'Internal server error' }); } }); /** * GET /api/settings/rules * Get validation rules untuk setiap field */ router.get('/rules', requireSuperAdmin, (req, res) => { try { const rules = getAllRules(); return res.json({ success: true, rules }); } catch (error) { logger.error(`[settings-api] Error getting rules: ${error.message}`); return res.status(500).json({ error: 'Internal server error' }); } }); /** * GET /api/settings/:field * Get single setting (masked) */ router.get('/:field', requireSuperAdmin, (req, res) => { try { const { field } = req.params; const value = getSetting(field); if (value === null || value === undefined) { return res.status(404).json({ error: 'Setting not found' }); } const { getMaskedSettings } = require('../config/settingsEncryption'); const masked = getMaskedSettings({ [field]: value }); return res.json({ success: true, field, value: masked[field] }); } catch (error) { logger.error(`[settings-api] Error getting setting: ${error.message}`); return res.status(500).json({ error: 'Internal server error' }); } }); /** * POST /api/settings * Update settings dengan validation & encryption */ router.post('/', requireSuperAdmin, validateSettingsMiddleware, (req, res) => { try { const settings = req.validatedSettings; const actor = req.session?.username || 'unknown'; const metadata = { ip: req.ip, userAgent: req.get('user-agent') }; const result = saveSettings(settings, actor, metadata); if (!result.success) { return res.status(400).json({ error: 'Failed to save settings', errors: result.errors }); } return res.json({ success: true, message: 'Settings saved successfully', changes: result.changes }); } catch (error) { logger.error(`[settings-api] Error saving settings: ${error.message}`); return res.status(500).json({ error: 'Internal server error' }); } }); /** * PUT /api/settings/:field * Update single setting */ router.put('/:field', requireSuperAdmin, (req, res) => { try { const { field } = req.params; const { value } = req.body; if (value === undefined) { return res.status(400).json({ error: 'Value is required' }); } const settings = { [field]: value }; const actor = req.session?.username || 'unknown'; const metadata = { ip: req.ip, userAgent: req.get('user-agent') }; const result = saveSettings(settings, actor, metadata); if (!result.success) { return res.status(400).json({ error: 'Failed to save setting', errors: result.errors }); } return res.json({ success: true, message: `Setting ${field} updated successfully` }); } catch (error) { logger.error(`[settings-api] Error updating setting: ${error.message}`); return res.status(500).json({ error: 'Internal server error' }); } }); /** * GET /api/settings/audit/history * Get settings change history */ router.get('/audit/history', requireSuperAdmin, (req, res) => { try { const limit = Math.min(parseInt(req.query.limit) || 100, 1000); const history = getChangeHistory(limit); return res.json({ success: true, count: history.length, history }); } catch (error) { logger.error(`[settings-api] Error getting history: ${error.message}`); return res.status(500).json({ error: 'Internal server error' }); } }); /** * GET /api/settings/audit/stats * Get audit statistics */ router.get('/audit/stats', requireSuperAdmin, (req, res) => { try { const stats = getAuditStats(); return res.json({ success: true, stats }); } catch (error) { logger.error(`[settings-api] Error getting stats: ${error.message}`); return res.status(500).json({ error: 'Internal server error' }); } }); /** * GET /api/settings/audit/export * Export audit log */ router.get('/audit/export', requireSuperAdmin, (req, res) => { try { const format = req.query.format || 'json'; const result = exportAuditLog(format); if (!result) { return res.status(500).json({ error: 'Failed to export audit log' }); } if (format === 'csv') { res.setHeader('Content-Type', 'text/csv'); res.setHeader('Content-Disposition', 'attachment; filename="settings-audit.csv"'); return res.send(result.data); } return res.json({ success: true, format: 'json', count: result.count, data: result.data }); } catch (error) { logger.error(`[settings-api] Error exporting audit: ${error.message}`); return res.status(500).json({ error: 'Internal server error' }); } }); /** * POST /api/settings/test-connection/:service * Test connection ke external service */ router.post('/test-connection/:service', requireSuperAdmin, async (req, res) => { try { const { service } = req.params; let result = { success: false, message: '' }; switch (service) { case 'genieacs': result = await testGenieACSConnection(); break; case 'mikrotik': result = await testMikroTikConnection(); break; default: return res.status(400).json({ error: 'Unknown service' }); } return res.json(result); } catch (error) { logger.error(`[settings-api] Error testing connection: ${error.message}`); return res.status(500).json({ error: 'Internal server error' }); } }); /** * Test GenieACS connection */ async function testGenieACSConnection() { try { const axios = require('axios'); const url = getSetting('genieacs_url'); const username = getSetting('genieacs_username'); const password = getSetting('genieacs_password'); if (!url || !username || !password) { return { success: false, message: 'GenieACS credentials not configured' }; } const response = await axios.get(`${url}/devices`, { auth: { username, password }, timeout: 5000 }); return { success: true, message: 'GenieACS connection successful' }; } catch (error) { return { success: false, message: `GenieACS connection failed: ${error.message}` }; } } /** * Test MikroTik connection */ async function testMikroTikConnection() { try { const { RouterOSClient } = require('routeros-client'); const host = getSetting('mikrotik_host'); const user = getSetting('mikrotik_user'); const password = getSetting('mikrotik_password'); const port = getSetting('mikrotik_port') || 8728; if (!host || !user || !password) { return { success: false, message: 'MikroTik credentials not configured' }; } const useTls = Number(port) === 8729 || getSetting('mikrotik_tls') === true; const api = new RouterOSClient({ host, port: Number(port), user, password, tls: useTls ? { rejectUnauthorized: false } : false, // dukung self-signed cert (8729) timeout: 5000 }); const client = await api.connect(); await client.close(); return { success: true, message: 'MikroTik connection successful' }; } catch (error) { return { success: false, message: `MikroTik connection failed: ${error.message}` }; } } /** * Test Tripay connection */ /** * Test Midtrans connection */ module.exports = router;