/** * SystemSettingController - Controller de Configuración del Sistema * * Endpoints REST para gestión de configuraciones. * * @module Admin */ import { Router, Request, Response, NextFunction } from 'express'; import { DataSource } from 'typeorm'; import { SystemSettingService, CreateSettingDto, UpdateSettingDto, SettingFilters } from '../services/system-setting.service'; import { AuthMiddleware } from '../../auth/middleware/auth.middleware'; import { AuthService } from '../../auth/services/auth.service'; import { SystemSetting } from '../entities/system-setting.entity'; import { User } from '../../core/entities/user.entity'; import { Tenant } from '../../core/entities/tenant.entity'; import { RefreshToken } from '../../auth/entities/refresh-token.entity'; import { ServiceContext } from '../../../shared/services/base.service'; export function createSystemSettingController(dataSource: DataSource): Router { const router = Router(); // Repositorios const settingRepository = dataSource.getRepository(SystemSetting); const userRepository = dataSource.getRepository(User); const tenantRepository = dataSource.getRepository(Tenant); const refreshTokenRepository = dataSource.getRepository(RefreshToken); // Servicios const settingService = new SystemSettingService(settingRepository); const authService = new AuthService(userRepository, tenantRepository, refreshTokenRepository as any); const authMiddleware = new AuthMiddleware(authService, dataSource); // Helper para crear contexto const getContext = (req: Request): ServiceContext => { if (!req.tenantId) { throw new Error('Tenant ID is required'); } return { tenantId: req.tenantId, userId: req.user?.sub, }; }; /** * GET /settings */ router.get('/', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise => { try { if (!req.tenantId) { res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' }); return; } const page = parseInt(req.query.page as string) || 1; const limit = Math.min(parseInt(req.query.limit as string) || 50, 100); const filters: SettingFilters = {}; if (req.query.category) filters.category = req.query.category as any; if (req.query.isPublic !== undefined) filters.isPublic = req.query.isPublic === 'true'; if (req.query.search) filters.search = req.query.search as string; const result = await settingService.findWithFilters(getContext(req), filters, page, limit); res.status(200).json({ success: true, data: result.data, pagination: result.meta, }); } catch (error) { next(error); } }); /** * GET /settings/public */ router.get('/public', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise => { try { if (!req.tenantId) { res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' }); return; } const settings = await settingService.getPublicSettings(getContext(req)); res.status(200).json({ success: true, data: settings }); } catch (error) { next(error); } }); /** * GET /settings/stats */ router.get('/stats', authMiddleware.authenticate, authMiddleware.authorize('admin', 'director'), async (req: Request, res: Response, next: NextFunction): Promise => { try { if (!req.tenantId) { res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' }); return; } const stats = await settingService.getStats(getContext(req)); res.status(200).json({ success: true, data: stats }); } catch (error) { next(error); } }); /** * GET /settings/category/:category */ router.get('/category/:category', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise => { try { if (!req.tenantId) { res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' }); return; } const settings = await settingService.findByCategory(getContext(req), req.params.category as any); res.status(200).json({ success: true, data: settings }); } catch (error) { next(error); } }); /** * GET /settings/key/:key */ router.get('/key/:key', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise => { try { if (!req.tenantId) { res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' }); return; } const setting = await settingService.findByKey(getContext(req), req.params.key); if (!setting) { res.status(404).json({ error: 'Not Found', message: 'Setting not found' }); return; } res.status(200).json({ success: true, data: setting }); } catch (error) { next(error); } }); /** * GET /settings/key/:key/value */ router.get('/key/:key/value', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise => { try { if (!req.tenantId) { res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' }); return; } const value = await settingService.getValue(getContext(req), req.params.key); if (value === null) { res.status(404).json({ error: 'Not Found', message: 'Setting not found' }); return; } res.status(200).json({ success: true, data: { key: req.params.key, value } }); } catch (error) { next(error); } }); /** * GET /settings/:id */ router.get('/:id', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise => { try { if (!req.tenantId) { res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' }); return; } const setting = await settingService.findById(getContext(req), req.params.id); if (!setting) { res.status(404).json({ error: 'Not Found', message: 'Setting not found' }); return; } res.status(200).json({ success: true, data: setting }); } catch (error) { next(error); } }); /** * POST /settings */ router.post('/', authMiddleware.authenticate, authMiddleware.authorize('admin', 'director'), async (req: Request, res: Response, next: NextFunction): Promise => { try { if (!req.tenantId) { res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' }); return; } const dto: CreateSettingDto = req.body; if (!dto.key || !dto.name || dto.value === undefined) { res.status(400).json({ error: 'Bad Request', message: 'key, name, and value are required', }); return; } const setting = await settingService.createSetting(getContext(req), dto); res.status(201).json({ success: true, data: setting }); } catch (error) { if (error instanceof Error) { if (error.message.includes('already exists')) { res.status(409).json({ error: 'Conflict', message: error.message }); return; } if (error.message.includes('must be') || error.message.includes('pattern')) { res.status(400).json({ error: 'Bad Request', message: error.message }); return; } } next(error); } }); /** * PUT /settings/:id */ router.put('/:id', authMiddleware.authenticate, authMiddleware.authorize('admin', 'director'), async (req: Request, res: Response, next: NextFunction): Promise => { try { if (!req.tenantId) { res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' }); return; } const dto: UpdateSettingDto = req.body; const setting = await settingService.update(getContext(req), req.params.id, dto); if (!setting) { res.status(404).json({ error: 'Not Found', message: 'Setting not found' }); return; } res.status(200).json({ success: true, data: setting }); } catch (error) { next(error); } }); /** * PUT /settings/key/:key/value */ router.put('/key/:key/value', authMiddleware.authenticate, authMiddleware.authorize('admin', 'director'), async (req: Request, res: Response, next: NextFunction): Promise => { try { if (!req.tenantId) { res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' }); return; } const { value } = req.body; if (value === undefined) { res.status(400).json({ error: 'Bad Request', message: 'value is required' }); return; } const setting = await settingService.updateValue(getContext(req), req.params.key, value); if (!setting) { res.status(404).json({ error: 'Not Found', message: 'Setting not found' }); return; } res.status(200).json({ success: true, data: setting }); } catch (error) { if (error instanceof Error && (error.message.includes('must be') || error.message.includes('pattern'))) { res.status(400).json({ error: 'Bad Request', message: error.message }); return; } next(error); } }); /** * POST /settings/key/:key/reset */ router.post('/key/:key/reset', authMiddleware.authenticate, authMiddleware.authorize('admin', 'director'), async (req: Request, res: Response, next: NextFunction): Promise => { try { if (!req.tenantId) { res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' }); return; } const setting = await settingService.resetToDefault(getContext(req), req.params.key); if (!setting) { res.status(404).json({ error: 'Not Found', message: 'Setting not found or no default value' }); return; } res.status(200).json({ success: true, data: setting }); } catch (error) { next(error); } }); /** * PUT /settings/bulk */ router.put('/bulk', authMiddleware.authenticate, authMiddleware.authorize('admin', 'director'), async (req: Request, res: Response, next: NextFunction): Promise => { try { if (!req.tenantId) { res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' }); return; } const { settings } = req.body; if (!Array.isArray(settings)) { res.status(400).json({ error: 'Bad Request', message: 'settings array is required' }); return; } const result = await settingService.updateMultiple(getContext(req), settings); res.status(200).json({ success: true, data: result, message: `Updated ${result.updated} settings`, }); } catch (error) { next(error); } }); /** * DELETE /settings/:id */ router.delete('/:id', authMiddleware.authenticate, authMiddleware.authorize('admin'), async (req: Request, res: Response, next: NextFunction): Promise => { try { if (!req.tenantId) { res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' }); return; } // Check if system setting const setting = await settingService.findById(getContext(req), req.params.id); if (!setting) { res.status(404).json({ error: 'Not Found', message: 'Setting not found' }); return; } if (setting.isSystem) { res.status(403).json({ error: 'Forbidden', message: 'Cannot delete system settings' }); return; } const deleted = await settingService.hardDelete(getContext(req), req.params.id); if (!deleted) { res.status(404).json({ error: 'Not Found', message: 'Setting not found' }); return; } res.status(200).json({ success: true, message: 'Setting deleted' }); } catch (error) { next(error); } }); return router; } export default createSystemSettingController;