import type {Express, Request} from 'express'; import { childLogger, type Logger } from "@foxxmd/logging"; import bodyParser from "body-parser"; import cors from 'cors'; import path from "path"; import { WebhookNotifier } from "../sources/ingressNotifiers/WebhookNotifier.ts"; import type ScrobbleSources from "../sources/ScrobbleSources.ts"; import { WebScrobblerSource } from "../sources/WebScrobblerSource.ts"; import { nonEmptyBody } from "./middleware.ts"; import type { createTypedRouter, TypedMiddleware } from "@minisylar/express-typed-router"; import { webScrobblePayloadSchema, type WebScrobblerPayload } from '../common/vendor/webscrobbler/interfaces.ts'; const corsOpts: cors.CorsOptions = { methods: ['POST'] } export const setupWebscrobblerRoutes = (app: Express, router: ReturnType, parentLogger: Logger, scrobbleSources: ScrobbleSources) => { const logger = childLogger(parentLogger, ['Ingress', 'WebScrobbler']); const webScrobblerJsonParser = bodyParser.json({ type: ['text/*', 'application/json'], // verify: function(req, res, buf, encoding) { // // get rawBody // // @ts-ignore // req.rawBody = buf.toString(); // } }); const webhookIngress = new WebhookNotifier(logger); const rawIngress: TypedMiddleware = (req, res, next) => { webhookIngress.trackIngress(req, true); next(); }; app.options('/api/webscrobbler*path', async (req, res, next) => { webhookIngress.trackIngress(req, true); next(); }, cors(corsOpts)); router.post('/api/webscrobbler{*splat}', { middleware: [rawIngress,cors(corsOpts) as TypedMiddleware,webScrobblerJsonParser,nonEmptyBody(logger, 'WebScrobbler Extension')], bodySchema: webScrobblePayloadSchema, tags: ['WebScrobbler Ingress'], pathExample: '/api/webscrobbler', summary: 'Accepts a Webscrobbler native scrobble', description: 'Use the default path `/api/webscrobbler` for standard configs or append a suffix if using a slug EX `/api/webscrobbler/mySlug`' }, async (req, res) => { webhookIngress.trackIngress(req as Request, false); res.sendStatus(200); const parts = path.parse(req.path); const slug = parts.name === 'webscrobbler' ? undefined : parts.name; // let cleanPath = req.path; // if (cleanPath.charAt(cleanPath.length - 1) === '/') { // cleanPath = cleanPath.slice(0, -1); // } // const splitPath = cleanPath.split('/'); // const slug = splitPath[splitPath.length - 1]; const playerState = WebScrobblerSource.playStateFromRequest(req.body as unknown as WebScrobblerPayload); const sources = scrobbleSources.getByType('webscrobbler') as WebScrobblerSource[]; if (sources.length === 0) { logger.warn('Received WebScrobbler payload but no WebScrobbler sources are configured'); } let slugMatched = false; for (const source of sources) { if (source.matchSlug(slug)) { await source.handle(playerState); slugMatched = true; } } if (!slugMatched) { if (slug === undefined) { logger.warn(`Request URL did not have a slug and no WebScrobbler source was configured without a slug.`); } else { logger.warn(`No WebScrobbler souce had the given slug '${slug}'`); } } }); }