erp-construccion-backend-v2/src/modules/admin/controllers/system-setting.controller.ts
rckrdmrd 7c1480a819 Migración desde erp-construccion/backend - Estándar multi-repo v2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 08:11:14 -06:00

371 lines
12 KiB
TypeScript

/**
* 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<void> => {
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<void> => {
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<void> => {
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<void> => {
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<void> => {
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<void> => {
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<void> => {
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<void> => {
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<void> => {
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<void> => {
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<void> => {
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<void> => {
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<void> => {
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;