import * as db from '../../db/category.js'; function parsePositiveInt(value, field) { const id = Number(value); if (!Number.isInteger(id) || id <= 0) { return { error: `${field} is required` }; } return { id }; } function categoryErrorStatus(error) { if (error.message === 'Not authorized') return 403; if (error.message === 'Company not found') return 404; if (error.message === 'Parent not found') return 404; if (error.message === 'Cannot delete root category') return 400; return 500; } /** * Registers category routes (requires auth middleware). * @param {import('express').Application} app - Express app * @param {string} [basePath=''] - Optional URL prefix for all routes in this module */ export function register(app, basePath = '') { /** * @route GET /category/tree * @description Returns the category tree for the authenticated user's selected company. * @returns {Object} 200 - `{ languageId, tree, articles }` */ app.get(`${basePath}/category/tree`, async (req, res) => { const companyId = req.user.selectedCompanyId; if (companyId == null) { return res.status(400).json({ error: 'No company selected' }); } try { const result = await db.getTreeForCompany(req.user.id, companyId); res.status(200).json(result); } catch (error) { const status = categoryErrorStatus(error); if (status === 500) console.error('Get category tree failed:', error); res.status(status).json({ error: error.message }); } }); /** * @route POST /category * @description Creates a category under an authorized parent. * @body {number} parentId * @body {string} name * @returns {Object} 201 - `{ category: { id, parentId, sort, name } }` */ app.post(`${basePath}/category`, async (req, res) => { const parent = parsePositiveInt(req.body?.parentId, 'parentId'); if (parent.error) { return res.status(400).json({ error: parent.error }); } const name = typeof req.body?.name === 'string' ? req.body.name.trim() : ''; if (!name) { return res.status(400).json({ error: 'name is required' }); } try { const category = await db.createUnderParent({ googleAuthId: req.user.id, parentId: parent.id, name, }); res.status(201).json({ category }); } catch (error) { const status = categoryErrorStatus(error); if (status === 500) console.error('Create category failed:', error); res.status(status).json({ error: error.message }); } }); /** * @route GET /category/:id/articles * @description Returns articles in a category the user is authorized for. * @returns {Object} 200 - `{ articles }` */ app.get(`${basePath}/category/:id/articles`, async (req, res) => { const parsed = parsePositiveInt(req.params.id, 'id'); if (parsed.error) { return res.status(400).json({ error: parsed.error }); } try { const articles = await db.getCategoryArticles(req.user.id, parsed.id); res.status(200).json({ articles }); } catch (error) { const status = categoryErrorStatus(error); if (status === 500) console.error('Get category articles failed:', error); res.status(status).json({ error: error.message }); } }); /** * @route DELETE /category/:id * @description Deletes a category the user is authorized for (not the company root). * @returns {Object} 204 - No content */ app.delete(`${basePath}/category/:id`, async (req, res) => { const parsed = parsePositiveInt(req.params.id, 'id'); if (parsed.error) { return res.status(400).json({ error: parsed.error }); } try { await db.deleteCategory({ googleAuthId: req.user.id, id: parsed.id, }); res.sendStatus(204); } catch (error) { const status = categoryErrorStatus(error); if (status === 500) console.error('Delete category failed:', error); res.status(status).json({ error: error.message }); } }); }