/** * Global Profile Plugin - Fields Routes * * Handles custom fields management API endpoints */ const sdk = require('../../../includes/plugins/sdk'); const { dbErrorResponse } = require('../db-errors'); /** * Handle fields-related routes */ async function handleFieldsRoutes(req, res, path, query, method) { // GET /api/fields - Get all registered fields if (path === 'api/fields' && method === 'GET') { try { await sdk.db.ready(); const fields = await sdk.db.find('@profile/fields', {}); return sdk.router.json(res, { fields: fields || [] }); } catch (err) { sdk.log.error('global.profile', `Error getting fields: ${err.message}`); const { status, body } = dbErrorResponse(err); return sdk.router.json(res, { fields: [], ...body }, status); } } // POST /api/fields - Register a new custom field if (path === 'api/fields' && method === 'POST') { // Require authentication for write operations const peerId = await sdk.auth.requireLocalPeer(req, res); if (!peerId) return true; // 401 already sent let body = ''; req.on('data', chunk => { body += chunk.toString(); }); req.on('end', async () => { try { if (!body) { return sdk.router.error(res, 'Request body is required', 400); } let fieldData; try { fieldData = JSON.parse(body); } catch (parseErr) { return sdk.router.error(res, 'Invalid JSON in request body', 400); } const pluginDomain = process.env.PLUGIN_DOMAIN || 'unknown'; if (!fieldData.fieldKey || !fieldData.label || !fieldData.type) { return sdk.router.error(res, 'Missing required fields: fieldKey, label, type', 400); } // Validate fieldKey format if (typeof fieldData.fieldKey !== 'string' || fieldData.fieldKey.trim() === '') { return sdk.router.error(res, 'fieldKey must be a non-empty string', 400); } // Validate type const validTypes = ['string', 'number', 'boolean', 'array', 'object']; if (!validTypes.includes(fieldData.type)) { return sdk.router.error(res, `type must be one of: ${validTypes.join(', ')}`, 400); } await sdk.db.ready(); // Check if field already exists const existing = await sdk.db.get('@profile/fields', { fieldKey: fieldData.fieldKey }); if (existing && existing.registeredBy !== pluginDomain) { return sdk.router.error(res, 'Field already registered by another plugin', 409); } const fieldDef = { fieldKey: fieldData.fieldKey, registeredBy: pluginDomain, label: fieldData.label, type: fieldData.type, description: fieldData.description || '', defaultValue: fieldData.defaultValue ? JSON.stringify(fieldData.defaultValue) : '' }; await sdk.db.insert('@profile/fields', fieldDef); await sdk.db.flush(); return sdk.router.json(res, { success: true, field: fieldDef }); } catch (err) { sdk.log.error('global.profile', `Error registering field: ${err.message}`); if (err.message && err.message.includes('Unexpected token')) { return sdk.router.error(res, 'Invalid JSON in request body', 400); } return sdk.router.error(res, err.message, 500); } }); return true; } return false; // Not handled by this module } module.exports = { handleFieldsRoutes };