This commit is contained in:
seb
2026-08-02 19:04:56 +02:00
parent bfb1d8762a
commit aed0857f23
21 changed files with 1953 additions and 0 deletions

239
src/api/routes/brand.js Normal file
View File

@@ -0,0 +1,239 @@
import * as db from '../../db/brand.js';
function requireSelectedCompany(req) {
const companyId = req.user.selectedCompanyId;
if (companyId == null) {
return { error: 'No company selected' };
}
return { companyId };
}
function brandErrorStatus(error) {
if (error.message === 'Not authorized') return 403;
if (error.message === 'Company not found') return 404;
if (error.message === 'Brand not found') return 404;
if (error.message === 'Invalid brand') return 400;
if (error.message === 'Brand in use') return 409;
return 500;
}
/**
* Registers brand routes for the authenticated user's selected company.
* @param {import('express').Application} app
* @param {string} [basePath='']
*/
export function register(app, basePath = '') {
/**
* @openapi
* /brand:
* get:
* tags: [Brand]
* summary: List brands
* description: Lists brands for the authenticated user's selected company, sorted by name.
* security:
* - cookieAuth: []
* x-markdown: brand/list.md
* responses:
* 200:
* description: Brand list
* content:
* application/json:
* schema:
* type: object
* properties:
* brands:
* type: array
* items:
* $ref: '#/components/schemas/Brand'
* 400:
* description: No company selected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 403:
* description: Not authorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Company not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* put:
* tags: [Brand]
* summary: Upsert brand
* description: |
* Insert or update brand by `name` for the selected company. `gsprpinfo` optional.
* security:
* - cookieAuth: []
* x-markdown: brand/upsert.md
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [name]
* properties:
* name:
* type: string
* gsprpinfo:
* type: string
* example:
* name: Acme
* gsprpinfo: 'Acme GmbH'
* responses:
* 200:
* description: Upserted brand
* content:
* application/json:
* schema:
* type: object
* properties:
* brand:
* $ref: '#/components/schemas/Brand'
* 400:
* description: Invalid brand 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: Not authorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Company not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.get(`${basePath}/brand`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
try {
const brands = await db.list(req.user.id, selected.companyId);
res.status(200).json({ brands });
} catch (error) {
const status = brandErrorStatus(error);
if (status === 500) console.error('List brands failed:', error);
res.status(status).json({ error: error.message });
}
});
app.put(`${basePath}/brand`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
const { name, gsprpinfo } = req.body ?? {};
try {
const brand = await db.upsert(req.user.id, selected.companyId, {
name,
gsprpinfo,
});
res.status(200).json({ brand });
} catch (error) {
const status = brandErrorStatus(error);
if (status === 500) console.error('Upsert brand failed:', error);
res.status(status).json({ error: error.message });
}
});
/**
* @openapi
* /brand/{id}:
* delete:
* tags: [Brand]
* summary: Delete brand
* description: |
* Deletes the brand for the selected company. Fails with `Brand in use`
* if any article still references it.
* security:
* - cookieAuth: []
* x-markdown: brand/delete.md
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* responses:
* 204:
* description: Deleted
* 400:
* description: No company selected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 403:
* description: Not authorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Brand not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 409:
* description: Brand in use
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.delete(`${basePath}/brand/:id`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
const id = Number(req.params.id);
if (!Number.isInteger(id) || id <= 0) {
return res.status(400).json({ error: 'id is required' });
}
try {
await db.remove(req.user.id, selected.companyId, id);
res.sendStatus(204);
} catch (error) {
const status = brandErrorStatus(error);
if (status === 500) console.error('Delete brand failed:', error);
res.status(status).json({ error: error.message });
}
});
}