import type {Logger} from "@foxxmd/logging"; import type {ExpressHandler} from "../common/infrastructure/Atomic.ts"; import type ScrobbleClients from "../scrobblers/ScrobbleClients.ts"; import type ScrobbleSources from "../sources/ScrobbleSources.ts"; import type {Request, Response, NextFunction} from "express"; import type AbstractSource from "../sources/AbstractSource.ts"; import type AbstractScrobbleClient from "../scrobblers/AbstractScrobbleClient.ts"; export const makeSourceCheckMiddle = (sources: any) => (required: boolean): ExpressHandler => (req: any, res: any, next: any) => { const { query: { name, type } = {} } = req; if (required && name === undefined) { return res.status(404).send('Source name must be defined'); } else if (name !== undefined) { const source = sources.getByNameAndType(name, type); if (source === undefined) { return res.status(404).send(`No source with the name [${name}] and type [${type}`); } req.sourceName = name; req.scrobbleSource = source; } next(); } export const makeClientCheckMiddle = (clients: any) => (required: boolean): ExpressHandler => (req: any, res: any, next: any) => { const { query: { name } = {} } = req; if (required && name === undefined) { return res.status(404).send('Client name must be defined'); } else if (name !== undefined) { const client = clients.getByName(name); if (client === undefined) { return res.status(404).send(`No client with the name: ${name}`); } req.scrobbleClient = client; } next(); } export const nonEmptyBody = (logger: Logger, origin: string = 'Origin'): ExpressHandler => async (req, res, next) => { const bodyEmpty = req.body === undefined || req.body === null || (typeof req.body === 'object' && Object.keys(req.body).length === 0); if (bodyEmpty) { const length = req.header('content-length') !== undefined ? Number.parseInt(req.header('content-length')) : undefined; // can't think of a way a user would send an empty body for a payload but if they meant to do it don't spam them with errors... if (length === 0) { return; } if (length === undefined) { logger.warn(`${origin} is not sending a well-formatted request. It does not have valid headers (application/json - text/*) OR it is missing content-length header: Content-Type => '${req.header('content-type')}' | Length => ${length}`); } else { logger.warn(`${origin} is not sending a request with valid headers. Content-Type must be either application/json or a text/* wildcard (like text/plain) -- given: Content-Type => '${req.header('content-type')}'`); } res.status(400).send('Invalid Content-Type. Must be either application/json or a text wildcard (like text/plain)'); return; } next(); } export interface ComponentAwareRequest extends Request { component: AbstractSource | AbstractScrobbleClient } export const makeComponentMiddle = (sources: ScrobbleSources, clients: ScrobbleClients): ExpressHandler => async (req: Request, res: Response, next: NextFunction) => { const { params: { componentVal } } = req; const componentId = Number.parseInt(componentVal as string); if (isNaN(componentId)) { return res.status(400).json({ error: 'Component id must be a number' }); } let component: AbstractSource | AbstractScrobbleClient; component = sources.sources.find(x => x.componentId === componentId); if (component === undefined) { component = clients.clients.find(x => x.componentId === componentId); } if(component === undefined) { return res.status(404).json({error: `No Component with the Id ${componentId} exists`}); } (req as ComponentAwareRequest).component = component; next(); } export interface SourceAwareRequest extends Request { component: AbstractSource } export interface ClientAwareRequest extends Request { component: AbstractScrobbleClient } export const makeSourceNextMiddle = (sources: ScrobbleSources): ExpressHandler => async (req: Request, res: Response, next: NextFunction) => { const { params: { componentVal } } = req; const componentId = Number.parseInt(componentVal as string); if (isNaN(componentId)) { return res.status(400).json({ error: 'Source Id must be a number' }); } let component: AbstractSource; component = sources.sources.find(x => x.componentId === componentId); if(component === undefined) { return res.status(404).json({error: `No Source with the Id ${componentId} exists`}); } (req as SourceAwareRequest).component = component; next(); } export const makeClientNextMiddle = (clients: ScrobbleClients): ExpressHandler => async (req: Request, res: Response, next: NextFunction) => { const { params: { componentVal } } = req; const componentId = Number.parseInt(componentVal as string); if (isNaN(componentId)) { return res.status(400).json({ error: 'Source Id must be a number' }); } let component: AbstractScrobbleClient; component = clients.clients.find(x => x.componentId === componentId); if(component === undefined) { return res.status(404).json({error: `No Client with the Id ${componentId} exists`}); } (req as ClientAwareRequest).component = component; next(); }