89 lines
2.3 KiB
TypeScript
89 lines
2.3 KiB
TypeScript
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<unknown>;
|
|
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<string, string | string[]>
|
|
);
|
|
|
|
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);
|
|
}
|