From e74ba52c478d44ee9e865257fa7ff2844ffcb9dd Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 13 Mar 2025 16:53:25 +0000 Subject: [PATCH] fix(listenbrainz): Improved slug and normal lz endpoint detection --- .../server/endpointListenbrainzRoutes.ts | 6 +- .../sources/EndpointListenbrainzSource.ts | 138 ++++++++---------- .../ingressNotifiers/LZEndpointNotifier.ts | 6 +- 3 files changed, 69 insertions(+), 81 deletions(-) diff --git a/src/backend/server/endpointListenbrainzRoutes.ts b/src/backend/server/endpointListenbrainzRoutes.ts index f82513a6..0f4b6052 100644 --- a/src/backend/server/endpointListenbrainzRoutes.ts +++ b/src/backend/server/endpointListenbrainzRoutes.ts @@ -2,7 +2,7 @@ import { ExpressWithAsync } from "@awaitjs/express"; import { childLogger, Logger } from "@foxxmd/logging"; import bodyParser from "body-parser"; -import { EndpointListenbrainzSource, playStateFromRequest } from "../sources/EndpointListenbrainzSource.js"; +import { EndpointListenbrainzSource, playStateFromRequest, parseDisplayIdentifiersFromRequest } from "../sources/EndpointListenbrainzSource.js"; import { LZEndpointNotifier } from "../sources/ingressNotifiers/LZEndpointNotifier.js"; import ScrobbleSources from "../sources/ScrobbleSources.js"; import { nonEmptyBody } from "./middleware.js"; @@ -17,7 +17,7 @@ export const setupLZEndpointRoutes = (app: ExpressWithAsync, parentLogger: Logge const nonEmptyCheck = nonEmptyBody(logger, 'LZ Endpoint'); const webhookIngress = new LZEndpointNotifier(logger); - app.useAsync(/\/api\/listenbrainz.*/, + app.useAsync(/(\/api\/listenbrainz.*)|(\/1\/submit-listens)/, async function (req, res, next) { // track request before parsing body to ensure we at least log that something is happening // (in the event body parsing does not work or request is not POST/PATCH) @@ -40,7 +40,7 @@ export const setupLZEndpointRoutes = (app: ExpressWithAsync, parentLogger: Logge const validSources = sources.filter(x => x.matchRequest(req)); if (validSources.length === 0) { - const [slug, token] = EndpointListenbrainzSource.parseDisplayIdentifiersFromRequest(req); + const [slug, token] = parseDisplayIdentifiersFromRequest(req); logger.warn(`No Listenbrainz endpoint config matched => Slug: ${slug} | Token: ${token}`); } diff --git a/src/backend/sources/EndpointListenbrainzSource.ts b/src/backend/sources/EndpointListenbrainzSource.ts index 9acf42d1..b2a20d78 100644 --- a/src/backend/sources/EndpointListenbrainzSource.ts +++ b/src/backend/sources/EndpointListenbrainzSource.ts @@ -15,8 +15,8 @@ import { ListenbrainzApiClient, ListenPayload, SubmitPayload } from "../common/v import { parseRegexSingleOrFail } from "../utils.js"; import MemorySource from "./MemorySource.js"; -const noSlugMatch = new RegExp(/\/api\/listenbrainz(?:\/?|\/1\/?|\/1\/submit-listens\/?)$/i); -const slugMatch = new RegExp(/\/api\/listenbrainz\/([^\/]+)(?:\/?|\/1\/?|\/1\/submit-listens\/?)$/i); +const noSlugMatch = new RegExp(/(?:\/api\/listenbrainz\/?)$|(?:\/1\/?|\/1\/submit-listens\/?)$/i); +const slugMatch = new RegExp(/\/api\/listenbrainz\/([^\/]+)$/i); export const authHeaderRegex = new RegExp(/Token (.+)$/i); @@ -42,70 +42,9 @@ export class EndpointListenbrainzSource extends MemorySource { }; } - static parseSlugFromString(path: string): string | false | undefined { - const noSlug = parseRegexSingleOrFail(noSlugMatch, path); - if (noSlug !== undefined) { - return undefined; - } - const slugResult = parseRegexSingleOrFail(slugMatch, path); - if (slugResult !== undefined) { - return slugResult.groups[0]; - } - return false; - } - - static parseSlugFromRequest(req: ExpressRequest): string | false | undefined { - return EndpointListenbrainzSource.parseSlugFromString(req.baseUrl); - } - - static parseTokenFromString(str: string): string | undefined { - const tokenMatch = parseRegexSingleOrFail(authHeaderRegex, str); - if(tokenMatch !== undefined) { - return tokenMatch.groups[0]; - } - return undefined; - } - - static parseTokenFromRequest(req: ExpressRequest): string | false | undefined { - const auth = req.header('Authorization'); - if(typeof auth === 'string' && auth !== '') { - const matchedToken = EndpointListenbrainzSource.parseTokenFromString(auth); - if(matchedToken === undefined) { - return false; - } - return matchedToken; - } - return undefined; - } - - static parseIdentifiersFromRequest(req: ExpressRequest): [string | false | undefined, false | string | undefined] { - const slug = EndpointListenbrainzSource.parseSlugFromRequest(req); - const token = EndpointListenbrainzSource.parseTokenFromRequest(req); - - return [slug, token]; - } - - static parseDisplayIdentifiersFromRequest(req: ExpressRequest): [string, string] { - const [slug, token] = EndpointListenbrainzSource.parseIdentifiersFromRequest(req); - let slugStr = '(no slug)'; - if (slug === false) { - slugStr = '(invalid slug)'; - } else if (slug !== undefined) { - slugStr = slug; - } - - let tokenStr = '(no token)'; - if (token === false) { - tokenStr = '(invalid token)'; - } else if (token !== undefined) { - tokenStr = `${token.substring(0,3)}****` - } - return [slugStr, tokenStr]; - } - matchRequest(req: ExpressRequest): boolean { let matchesToken = this.config.data.token === undefined; - const reqToken = EndpointListenbrainzSource.parseTokenFromRequest(req); + const reqToken = parseTokenFromRequest(req); if (reqToken === false) { return false; } @@ -118,7 +57,7 @@ export class EndpointListenbrainzSource extends MemorySource { } let matchesPath = false; - const slug = EndpointListenbrainzSource.parseSlugFromRequest(req); + const slug = parseSlugFromRequest(req); if (slug === false) { return false; } else { @@ -128,16 +67,6 @@ export class EndpointListenbrainzSource extends MemorySource { return matchesToken && matchesPath; } - static listenTypeAsPlayerStatus(event: string): ReportedPlayerStatus { - switch (event) { - case 'single': - case 'playing_now': - return REPORTED_PLAYER_STATUSES.playing; - default: - return REPORTED_PLAYER_STATUSES.unknown; - } - } - static formatPlayObj(obj: ListenPayload, options: FormatPlayObjectOptions & { nowPlaying?: boolean } = {}): PlayObject { @@ -188,4 +117,63 @@ export const listenTypeAsPlayerStatus = (event: string): ReportedPlayerStatus => default: return REPORTED_PLAYER_STATUSES.unknown; } +} + +export const parseTokenFromString = (str: string): string | undefined => { + const tokenMatch = parseRegexSingleOrFail(authHeaderRegex, str); + if(tokenMatch !== undefined) { + return tokenMatch.groups[0]; + } + return undefined; +} + +export const parseTokenFromRequest = (req: ExpressRequest): string | false | undefined => { + const auth = req.header('Authorization'); + if(typeof auth === 'string' && auth !== '') { + const matchedToken = parseTokenFromString(auth); + if(matchedToken === undefined) { + return false; + } + return matchedToken; + } + return undefined; +} + +export const parseSlugFromString = (path: string): string | false | undefined => { + const noSlug = parseRegexSingleOrFail(noSlugMatch, path); + if (noSlug !== undefined) { + return undefined; + } + const slugResult = parseRegexSingleOrFail(slugMatch, path); + if (slugResult !== undefined) { + return slugResult.groups[0]; + } + return false; +} + +export const parseSlugFromRequest = (req: ExpressRequest): string | false | undefined => parseSlugFromString(req.baseUrl); + +export const parseIdentifiersFromRequest = (req: ExpressRequest): [string | false | undefined, false | string | undefined] => { + const slug = parseSlugFromRequest(req); + const token = parseTokenFromRequest(req); + + return [slug, token]; +} + +export const parseDisplayIdentifiersFromRequest = (req: ExpressRequest): [string, string] => { + const [slug, token] = parseIdentifiersFromRequest(req); + let slugStr = '(no slug)'; + if (slug === false) { + slugStr = '(invalid slug)'; + } else if (slug !== undefined) { + slugStr = slug; + } + + let tokenStr = '(no token)'; + if (token === false) { + tokenStr = '(invalid token)'; + } else if (token !== undefined) { + tokenStr = `${token.substring(0,3)}****` + } + return [slugStr, tokenStr]; } \ No newline at end of file diff --git a/src/backend/sources/ingressNotifiers/LZEndpointNotifier.ts b/src/backend/sources/ingressNotifiers/LZEndpointNotifier.ts index b99bfec3..0bd8e95c 100644 --- a/src/backend/sources/ingressNotifiers/LZEndpointNotifier.ts +++ b/src/backend/sources/ingressNotifiers/LZEndpointNotifier.ts @@ -1,6 +1,6 @@ import { Logger } from "@foxxmd/logging"; import { Request } from "express"; -import { EndpointListenbrainzSource } from "../EndpointListenbrainzSource.js"; +import { EndpointListenbrainzSource, parseIdentifiersFromRequest } from "../EndpointListenbrainzSource.js"; import { IngressNotifier } from "./IngressNotifier.js"; export class LZEndpointNotifier extends IngressNotifier { @@ -14,9 +14,9 @@ export class LZEndpointNotifier extends IngressNotifier { if(!isRaw) { - const [slug, token] = EndpointListenbrainzSource.parseIdentifiersFromRequest(req); + const [slug, token] = parseIdentifiersFromRequest(req); if(slug === false) { - return [false, `Request URL was not a valid: ${req.baseUrl}`]; + return [false, `Request URL was not valid: ${req.baseUrl}`]; } const slugStr = slug ?? '(no slug)'; -- 2.51.2