import { Request, Response } from 'express'; import { z } from 'zod'; import settingsService from '../services/settings.service'; import pool from '../config/database'; import logger from '../utils/logger'; const updateSchema = z.object({ value: z.any(), }); const externalLinkSchema = z.array(z.object({ id: z.string().min(1), name: z.string().min(1).max(200), links: z.array(z.object({ name: z.string().min(1).max(200), url: z.string().url().max(500), })), })); class SettingsController { async getAll(_req: Request, res: Response): Promise { try { const settings = await settingsService.getAll(); res.json({ success: true, data: settings }); } catch (error) { logger.error('Failed to get settings', { error }); res.status(500).json({ success: false, message: 'Failed to get settings' }); } } async get(req: Request, res: Response): Promise { try { const setting = await settingsService.get(req.params.key as string); if (!setting) { res.status(404).json({ success: false, message: 'Setting not found' }); return; } res.json({ success: true, data: setting }); } catch (error) { logger.error('Failed to get setting', { error }); res.status(500).json({ success: false, message: 'Failed to get setting' }); } } async update(req: Request, res: Response): Promise { try { const { value } = updateSchema.parse(req.body); // Validate external_links specifically if ((req.params.key as string) === 'external_links') { externalLinkSchema.parse(value); } const setting = await settingsService.set(req.params.key as string, value, req.user!.id); res.json({ success: true, data: setting }); } catch (error) { if (error instanceof z.ZodError) { res.status(400).json({ success: false, message: 'Invalid input', errors: error.issues }); return; } logger.error('Failed to update setting', { error }); res.status(500).json({ success: false, message: 'Failed to update setting' }); } } async getUserPreferences(req: Request, res: Response): Promise { try { const userId = (req as any).user.id; const result = await pool.query('SELECT preferences FROM users WHERE id = $1', [userId]); const prefs = result.rows[0]?.preferences ?? {}; res.json({ success: true, data: prefs }); } catch (error) { logger.error('Failed to get user preferences', { error }); res.status(500).json({ success: false, message: 'Failed to get user preferences' }); } } async updateUserPreferences(req: Request, res: Response): Promise { try { const userId = (req as any).user.id; const preferences = req.body; // Basic validation — reject excessively large or non-object payloads if (typeof preferences !== 'object' || preferences === null || Array.isArray(preferences)) { res.status(400).json({ success: false, message: 'Preferences must be a JSON object' }); return; } if (JSON.stringify(preferences).length > 10_000) { res.status(400).json({ success: false, message: 'Preferences payload too large' }); return; } await pool.query( 'UPDATE users SET preferences = $1 WHERE id = $2', [JSON.stringify(preferences), userId] ); res.json({ success: true }); } catch (error) { logger.error('Failed to update user preferences', { error }); res.status(500).json({ success: false, message: 'Failed to update user preferences' }); } } } export default new SettingsController();