// Copyright (c) 2020 Jan Kaßel // Copyright (c) 2022 Gerben // // SPDX-License-Identifier: MIT import { v4 as uuid } from 'uuid'; // import validateAnnotation from 'validate-web-annotation'; import db from '../db.js'; import { Container, sendAnnotation, extractAnnotationIdFromUrl } from '../ldp.js'; import type { Request, Response } from 'express'; import { asArray } from 'web-annotation-utils'; export async function createAnnotation(req: Request, res: Response) { const collectionKey = `${req.params.user}/${req.params.collection}`; try { await db.get(collectionKey); } catch (err: any) { if (err.notFound) { res.status(404).send('Not found'); } else { console.error(req.method, req.path, err); res.status(500).send('Internal server error'); } return; } if (!req.body) { res.status(400).send('Bad request: Missing request body'); return; } const annotation = req.body; // TODO Improve validator; it appears to disagree about the date format. // if (!validateAnnotation(annotation, { optionalId: true })) { // res.status(400).send('Bad request: Invalid body schema'); // return; // } const id = uuid(); const annotationKey = `${collectionKey}/${id}`; if (annotation.id) { annotation.via = [...asArray(annotation.via), annotation.id]; } annotation.id = id; try { await db.get(annotationKey); res.status(409).send('Conflict'); } catch (err: any) { if (!err.notFound) { console.error(req.method, req.path, err); res.status(500).send('Internal server error'); return; } annotation.creator ??= { type: 'Person', nickname: req.params.user, }; await db.put(annotationKey, annotation); const collectionInfo = await db.get(collectionKey); const containerInfo = new Container(req, collectionKey, collectionInfo); res .status(201) .header('Location', `/${annotationKey}`) .header('Content-Location', `/${annotationKey}`); sendAnnotation(req, res, annotation, containerInfo); } } export async function getAnnotation(req: Request, res: Response) { const collectionKey = `${req.params.user}/${req.params.collection}`; try { await db.get(collectionKey); } catch (err: any) { if (err.notFound) { res.status(404).send('Not found'); } else { console.error(req.method, req.path, err); res.status(500).send('Internal server error'); } return; } const annotationKey = `${collectionKey}/${req.params.annotation}`; try { const annotation = await db.get(annotationKey); const collectionInfo = await db.get(collectionKey); const containerInfo = new Container(req, collectionKey, collectionInfo); sendAnnotation(req, res, annotation, containerInfo); } catch (err: any) { if (err.notFound) { res.status(404).send('Not found'); } else { console.error(req.method, req.path, err); res.status(500).send('Internal server error'); } return; } } export async function updateAnnotation(req: Request, res: Response) { const collectionKey = `${req.params.user}/${req.params.collection}`; try { await db.get(collectionKey); } catch (err: any) { if (err.notFound) { res.status(404).send('Not found'); } else { console.error(req.method, req.path, err); res.status(500).send('Internal server error'); } return; } const annotation = req.body; if ( !annotation || // TODO Improve validator; it appears to disagree about the date format. // || !validateAnnotation(annotation) !annotation.id ) { res.status(400).send('Bad request: Invalid body schema'); return; } const collectionInfo = await db.get(collectionKey); const containerInfo = new Container(req, collectionKey, collectionInfo); const normalizedId = extractAnnotationIdFromUrl( annotation.id, containerInfo.url, ); if (!normalizedId) { res .status(400) .send( `Bad Request: Annotation ID did not match the expected container IRI: ${containerInfo.url}`, ); return; } const contractedAnnotation = { ...annotation, id: normalizedId, }; const annotationKey = `${collectionKey}/${contractedAnnotation.id}`; try { await db.get(annotationKey); await db.put(annotationKey, contractedAnnotation); const collectionInfo = await db.get(collectionKey); const containerInfo = new Container(req, collectionKey, collectionInfo); sendAnnotation(req, res, contractedAnnotation, containerInfo); } catch (err: any) { if (err.notFound) { res.status(404).send('Not found'); } else { console.error(req.method, req.path, err); res.status(500).send('Internal server error'); } return; } } export async function deleteAnnotation(req: Request, res: Response) { const collectionKey = `${req.params.user}/${req.params.collection}`; try { await db.get(collectionKey); } catch (err: any) { if (err.notFound) { res.status(404).send('Not found'); } else { console.error(req.method, req.path, err); res.status(500).send('Internal server error'); } return; } const annotationKey = `${collectionKey}/${req.params.annotation}`; try { await db.get(annotationKey); await db.del(annotationKey); res.status(204).send(); } catch (err: any) { if (err.notFound) { res.status(404).send('Not found'); } else { console.error(req.method, req.path, err); res.status(500).send('Internal server error'); } return; } }