import * as db from '../../db/propertyname.js'; import { emit } from '../sse.js'; function requireSelectedCompany(req) { const companyId = req.user?.selectedCompanyId; if (companyId == null) { return { error: 'No company selected' }; } return { companyId }; } function propertyErrorStatus(error) { if (error.message === 'No company selected') return 400; if (error.message === 'Not authorized') return 403; if (error.message === 'Company not found') return 404; if (error.message === 'Property name not found') return 404; if (error.message === 'Name required') return 400; if (error.message === 'Invalid property name order') return 400; if (error.message === 'Invalid id') return 400; if (error.message === 'Property name already exists') return 409; if (error.message === 'Property name in use' || error.message?.startsWith('Cannot set property exclusive') || error.message?.includes('FOREIGN KEY constraint')) return 409; return 500; } /** * Registers propertyname routes. * @param {import('express').Application} app * @param {string} [basePath=''] */ export function register(app, basePath = '') { /** * @openapi * /propertyname: * get: * tags: [Article] * summary: List property names * description: Lists all property names defined for the selected company, ordered by sort. * security: * - cookieAuth: [] * responses: * 200: * description: List of property names * content: * application/json: * schema: * type: object * properties: * propertynames: * type: array * items: * $ref: '#/components/schemas/Propertyname' * 400: * description: No company selected * content: * application/json: * schema: * $ref: '#/components/schemas/Error' * post: * tags: [Article] * summary: Create property name * description: Creates a new property name for the selected company. * security: * - cookieAuth: [] * x-signals: [propertyname] * requestBody: * required: true * content: * application/json: * schema: * type: object * required: [name] * properties: * name: * type: string * sort: * type: integer * exclusive: * type: boolean * responses: * 201: * description: Created property name * content: * application/json: * schema: * type: object * properties: * propertyname: * $ref: '#/components/schemas/Propertyname' * 400: * description: Bad request * content: * application/json: * schema: * $ref: '#/components/schemas/Error' */ app.get(`${basePath}/propertyname`, async (req, res) => { const selected = requireSelectedCompany(req); if (selected.error) { return res.status(400).json({ error: selected.error }); } try { const propertynames = await db.list(req.user.id); res.json({ propertynames }); } catch (error) { res.status(propertyErrorStatus(error)).json({ error: error.message }); } }); app.post(`${basePath}/propertyname`, async (req, res) => { const selected = requireSelectedCompany(req); if (selected.error) { return res.status(400).json({ error: selected.error }); } try { const propertyname = await db.create(req.user.id, req.body ?? {}); emit('propertyname', { companyId: selected.companyId }); res.status(201).json({ propertyname }); } catch (error) { res.status(propertyErrorStatus(error)).json({ error: error.message }); } }); /** * @openapi * /propertyname/order: * put: * tags: [Article] * summary: Reorder property names * description: Updates the display sort order of property names for the selected company. * security: * - cookieAuth: [] * x-signals: [propertyname] * requestBody: * required: true * content: * application/json: * schema: * type: object * required: [propertynameIds] * properties: * propertynameIds: * type: array * items: * type: integer * responses: * 200: * description: Reordered successfully * content: * application/json: * schema: * $ref: '#/components/schemas/SuccessResponse' * 400: * description: Bad request */ app.put(`${basePath}/propertyname/order`, async (req, res) => { const selected = requireSelectedCompany(req); if (selected.error) { return res.status(400).json({ error: selected.error }); } try { await db.order(req.user.id, req.body ?? {}); emit('propertyname', { companyId: selected.companyId }); res.json({ ok: true }); } catch (error) { res.status(propertyErrorStatus(error)).json({ error: error.message }); } }); /** * @openapi * /propertyname/{id}: * patch: * tags: [Article] * summary: Update property name * description: Updates name and/or sort for a property name. * security: * - cookieAuth: [] * x-signals: [propertyname] * parameters: * - in: path * name: id * required: true * schema: * type: integer * requestBody: * required: true * content: * application/json: * schema: * type: object * properties: * name: * type: string * sort: * type: integer * exclusive: * type: boolean * responses: * 200: * description: Updated property name * content: * application/json: * schema: * type: object * properties: * propertyname: * $ref: '#/components/schemas/Propertyname' * delete: * tags: [Article] * summary: Delete property name * description: Deletes a property name and cascades to article properties. * security: * - cookieAuth: [] * x-signals: [propertyname] * parameters: * - in: path * name: id * required: true * schema: * type: integer * responses: * 200: * description: Deleted successfully * content: * application/json: * schema: * $ref: '#/components/schemas/SuccessResponse' * 400: * description: Bad request or no company selected * content: * application/json: * schema: * $ref: '#/components/schemas/Error' * 401: * description: Unauthorized * content: * application/json: * schema: * $ref: '#/components/schemas/Error' * 403: * description: Forbidden * content: * application/json: * schema: * $ref: '#/components/schemas/Error' * 404: * description: Property name not found * content: * application/json: * schema: * $ref: '#/components/schemas/Error' * 409: * description: Property name in use * content: * application/json: * schema: * $ref: '#/components/schemas/Error' * 500: * description: Server error * content: * application/json: * schema: * $ref: '#/components/schemas/Error' */ app.patch(`${basePath}/propertyname/:id`, async (req, res) => { const selected = requireSelectedCompany(req); if (selected.error) { return res.status(400).json({ error: selected.error }); } try { const id = Number(req.params.id); const propertyname = await db.update(req.user.id, id, req.body ?? {}); emit('propertyname', { companyId: selected.companyId }); res.json({ propertyname }); } catch (error) { const status = propertyErrorStatus(error); if (status === 500) console.error('Update property name failed:', error); res.status(status).json({ error: error.message }); } }); app.delete(`${basePath}/propertyname/:id`, async (req, res) => { const selected = requireSelectedCompany(req); if (selected.error) { return res.status(400).json({ error: selected.error }); } try { const id = Number(req.params.id); await db.remove(req.user.id, id); emit('propertyname', { companyId: selected.companyId }); res.json({ ok: true }); } catch (error) { const status = propertyErrorStatus(error); const message = (error.message === 'Property name in use' || error.message?.includes('FOREIGN KEY constraint')) ? 'Property name in use' : error.message; if (status === 500) console.error('Delete property name failed:', error); res.status(status).json({ error: message }); } }); }