import type { Request, Response } from 'express'; import type { Metadata } from 'twtxt-lib'; import { generateEtag, getQueryParameterArray, getValueOrFirstEntry, } from '../../lib/utils.js'; import { NodeCache } from '@cacheable/node-cache'; import { QueryParameters } from '../../types.js'; export interface MetadataHandler { cache: NodeCache; metadataParameter: QueryParameters['metadata']; req: Request; res: Response; } /** * * @param req * @param res * @param cache * @param metadataParameter */ export default function metadataHandler( req: Request, res: Response, metadata: Metadata, metadataParameter: QueryParameters['metadata'] ) { const metadataToMatch = getQueryParameterArray(req.query[metadataParameter]); const searchTermsToMatch = [ ...getQueryParameterArray(req.query.search), ...getQueryParameterArray(req.query.s), ]; const wantsJson = req.is('json') || getValueOrFirstEntry(getQueryParameterArray(req.query.format)) === 'json'; if (wantsJson) res.set('content-type', 'application/json'); else res.set('content-type', 'text/plain'); const matchedMetadata = Object.keys(metadata) .filter( (key) => (!metadataToMatch.length || (metadataToMatch.length === 1 && metadataToMatch[0] === '') || metadataToMatch.includes(key)) && (!searchTermsToMatch.length || searchTermsToMatch.some((term) => key.includes(term) || Array.isArray(metadata[key]) ? (metadata[key] as string[]).some((val) => val.includes(term)) : metadata[key].includes(term) )) ) .reduce( (acc, key) => { const value = metadata[key as keyof typeof metadata]; acc[key] = Array.isArray(value) ? value.filter( (value) => !searchTermsToMatch.length || searchTermsToMatch.some( (term) => key.includes(term) || value.includes(term) ) ) : value; return acc; }, {} as Record ); const result = wantsJson ? JSON.stringify(matchedMetadata) : Object.keys(matchedMetadata) .map((key) => { const value = matchedMetadata[key as keyof typeof matchedMetadata]; return Array.isArray(value) ? value.map((rowVal) => `${key}: ${rowVal}`).join('\n') : `${key}: ${value}`; }) .join('\n'); res.set('etag', generateEtag(result)).send(result); }