import { Response, Router } from 'express'; import { deletePluginEventDefinition, deletePluginRequirement, getPluginRequirementsSnapshot, PluginSupportError, upsertPluginEventDefinition, upsertPluginRequirement } from '../services/plugin-support.service'; import { broadcastToServer } from '../websocket/broadcast'; const router = Router(); function sendPluginSupportError(error: unknown, res: Response): void { if (error instanceof PluginSupportError) { res.status(error.status).json({ error: error.message, errorCode: error.code }); return; } console.error('Unhandled plugin support error:', error); res.status(500).json({ error: 'Internal server error', errorCode: 'INTERNAL_ERROR' }); } function readActorUserId(value: unknown): string { return typeof value === 'string' ? value.trim() : ''; } async function broadcastRequirementsSnapshot(serverId: string): Promise { const snapshot = await getPluginRequirementsSnapshot(serverId); broadcastToServer(serverId, { type: 'plugin_requirements_changed', serverId, snapshot }); } router.get('/:serverId/plugins', async (req, res) => { try { res.json(await getPluginRequirementsSnapshot(req.params.serverId)); } catch (error) { sendPluginSupportError(error, res); } }); router.put('/:serverId/plugins/:pluginId/requirement', async (req, res) => { const { serverId, pluginId } = req.params; try { const requirement = await upsertPluginRequirement({ actorUserId: readActorUserId(req.body.actorUserId), installUrl: req.body.installUrl, manifest: req.body.manifest, pluginId, reason: req.body.reason, serverId, sourceUrl: req.body.sourceUrl, status: req.body.status, versionRange: req.body.versionRange }); await broadcastRequirementsSnapshot(serverId); res.json({ requirement }); } catch (error) { sendPluginSupportError(error, res); } }); router.delete('/:serverId/plugins/:pluginId/requirement', async (req, res) => { const { serverId, pluginId } = req.params; try { await deletePluginRequirement({ actorUserId: readActorUserId(req.body.actorUserId), pluginId, serverId }); await broadcastRequirementsSnapshot(serverId); res.json({ ok: true }); } catch (error) { sendPluginSupportError(error, res); } }); router.put('/:serverId/plugins/:pluginId/events/:eventName', async (req, res) => { const { serverId, pluginId, eventName } = req.params; try { const eventDefinition = await upsertPluginEventDefinition({ actorUserId: readActorUserId(req.body.actorUserId), direction: req.body.direction, eventName, maxPayloadBytes: req.body.maxPayloadBytes, pluginId, rateLimitJson: req.body.rateLimitJson, schemaJson: req.body.schemaJson, scope: req.body.scope, serverId }); await broadcastRequirementsSnapshot(serverId); res.json({ eventDefinition }); } catch (error) { sendPluginSupportError(error, res); } }); router.delete('/:serverId/plugins/:pluginId/events/:eventName', async (req, res) => { const { serverId, pluginId, eventName } = req.params; try { await deletePluginEventDefinition({ actorUserId: readActorUserId(req.body.actorUserId), eventName, pluginId, serverId }); await broadcastRequirementsSnapshot(serverId); res.json({ ok: true }); } catch (error) { sendPluginSupportError(error, res); } }); router.get('/:serverId/plugins/:pluginId/data', (_req, res) => { res.status(410).json({ error: 'Plugin data persistence is disabled on the signal server', errorCode: 'PLUGIN_DATA_DISABLED' }); }); router.put('/:serverId/plugins/:pluginId/data/:key', (_req, res) => { res.status(410).json({ error: 'Plugin data persistence is disabled on the signal server', errorCode: 'PLUGIN_DATA_DISABLED' }); }); router.delete('/:serverId/plugins/:pluginId/data/:key', (_req, res) => { res.status(410).json({ error: 'Plugin data persistence is disabled on the signal server', errorCode: 'PLUGIN_DATA_DISABLED' }); }); export default router;