chore: create ID per request for logging and error response

This commit is contained in:
WebStreamr 2025-05-17 21:06:51 +02:00
parent 827fd88110
commit f635d6c233
No known key found for this signature in database
19 changed files with 51 additions and 26 deletions

14
package-lock.json generated
View file

@ -20,6 +20,7 @@
"slugify": "^1.6.6",
"unpacker": "^1.0.1",
"user-agents": "^1.1.536",
"uuid": "^11.1.0",
"winston": "^3.17.0"
},
"devDependencies": {
@ -7096,6 +7097,19 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
"node_modules/uuid": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/esm/bin/uuid"
}
},
"node_modules/v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",

View file

@ -41,6 +41,7 @@
"slugify": "^1.6.6",
"unpacker": "^1.0.1",
"user-agents": "^1.1.536",
"uuid": "^11.1.0",
"winston": "^3.17.0"
},
"devDependencies": {

View file

@ -1,7 +1,7 @@
import { Request, Response, Router } from 'express';
import winston from 'winston';
import { Handler } from '../handler';
import { Config } from '../types';
import { Config, Context } from '../types';
import { StreamResolver } from '../utils';
export class StreamController {
@ -26,11 +26,16 @@ export class StreamController {
const type: string = req.params['type'] || '';
const id: string = req.params['id'] || '';
this.logger.info(`Search stream for type "${type}" and id "${id}"`);
const ctx: Context = {
id: res.getHeader('X-Request-ID') as string,
ip: req.ip as string,
};
this.logger.info(`Search stream for type "${type}" and id "${id}"`, ctx);
const selectedHandlers = this.handlers.filter(handler => handler.id in config);
const streams = await this.streamResolver.resolve({ ip: (req.ip as string) }, selectedHandlers, type, id);
const streams = await this.streamResolver.resolve(ctx, selectedHandlers, type, id);
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ streams }));

View file

@ -10,7 +10,7 @@ const fetcher = new Fetcher();
const extractorRegistry = new ExtractorRegistry(logger, fetcher);
describe('ExtractorRegistry', () => {
const ctx: Context = { ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
test('returns undefined when no extractor can be found', async () => {
const urlResult = await extractorRegistry.handle(ctx, new URL('https://some-url.test'), 'en');

View file

@ -33,12 +33,12 @@ export class ExtractorRegistry {
return undefined;
}
this.logger.info(`Extract stream URL using ${extractor.id} extractor from ${url}`);
this.logger.info(`Extract stream URL using ${extractor.id} extractor from ${url}`, ctx);
try {
urlResult = await extractor.extract(ctx, url, countryCode);
} catch (error) {
this.logger.warn(`${extractor.id} error: ` + error);
this.logger.warn(`${extractor.id} error: ` + error, ctx);
return undefined;
}

View file

@ -9,7 +9,7 @@ const logger = winston.createLogger({ transports: [new winston.transports.Consol
// @ts-expect-error No constructor args needed
const fetcher = new Fetcher();
const handler = new CineHDPlus(fetcher, new ExtractorRegistry(logger, fetcher));
const ctx: Context = { ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
describe('CineHDPlus', () => {
test('does not handle non imdb series', async () => {

View file

@ -9,7 +9,7 @@ const logger = winston.createLogger({ transports: [new winston.transports.Consol
// @ts-expect-error No constructor args needed
const fetcher = new Fetcher();
const handler = new Eurostreaming(fetcher, new ExtractorRegistry(logger, fetcher));
const ctx: Context = { ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
describe('Eurostreaming', () => {
test('does not handle non imdb series', async () => {

View file

@ -9,7 +9,7 @@ const logger = winston.createLogger({ transports: [new winston.transports.Consol
// @ts-expect-error No constructor args needed
const fetcher = new Fetcher();
const handler = new Frembed(fetcher, new ExtractorRegistry(logger, fetcher));
const ctx: Context = { ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
describe('Frembed', () => {
test('does not handle non imdb series', async () => {

View file

@ -9,7 +9,7 @@ const logger = winston.createLogger({ transports: [new winston.transports.Consol
// @ts-expect-error No constructor args needed
const fetcher = new Fetcher();
const handler = new FrenchCloud(fetcher, new ExtractorRegistry(logger, fetcher));
const ctx: Context = { ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
describe('FrenchCloud', () => {
test('does not handle non imdb movies', async () => {

View file

@ -9,7 +9,7 @@ const logger = winston.createLogger({ transports: [new winston.transports.Consol
// @ts-expect-error No constructor args needed
const fetcher = new Fetcher();
const handler = new KinoKiste(fetcher, new ExtractorRegistry(logger, fetcher));
const ctx: Context = { ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
describe('KinoKiste', () => {
test('does not handle non imdb series', async () => {

View file

@ -9,7 +9,7 @@ const logger = winston.createLogger({ transports: [new winston.transports.Consol
// @ts-expect-error No constructor args needed
const fetcher = new Fetcher();
const handler = new MeineCloud(fetcher, new ExtractorRegistry(logger, fetcher));
const ctx: Context = { ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
describe('MeineCloud', () => {
test('does not handle non imdb movies', async () => {

View file

@ -9,7 +9,7 @@ const logger = winston.createLogger({ transports: [new winston.transports.Consol
// @ts-expect-error No constructor args needed
const fetcher = new Fetcher();
const handler = new MostraGuarda(fetcher, new ExtractorRegistry(logger, fetcher));
const ctx: Context = { ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
describe('MostraGuarda', () => {
test('does not handle non imdb movies', async () => {

View file

@ -9,7 +9,7 @@ const logger = winston.createLogger({ transports: [new winston.transports.Consol
// @ts-expect-error No constructor args needed
const fetcher = new Fetcher();
const handler = new VerHdLink(fetcher, new ExtractorRegistry(logger, fetcher));
const ctx: Context = { ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
describe('VerHdLink', () => {
test('does not handle non imdb movies', async () => {

View file

@ -1,4 +1,5 @@
import express, { NextFunction, Request, Response } from 'express';
import { v4 as uuidv4 } from 'uuid';
import axios, { AxiosError } from 'axios';
import axiosRetry, { isNetworkOrIdempotentRequestError } from 'axios-retry';
import { setupCache } from 'axios-cache-interceptor';
@ -24,8 +25,7 @@ const logger = winston.createLogger({
format: winston.format.combine(
winston.format.cli(),
winston.format.timestamp(),
winston.format.printf(({ level, message, timestamp }) => `${timestamp} ${level}: ${message}`),
),
winston.format.printf(({ level, message, timestamp, id }) => `${timestamp} ${level} ${id}: ${message}`)),
}),
],
});
@ -60,6 +60,8 @@ const addon = express();
addon.set('trust proxy', true);
addon.use((_req: Request, res: Response, next: NextFunction) => {
res.setHeader('X-Request-ID', uuidv4());
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', '*');

View file

@ -1,6 +1,9 @@
import { Manifest, ManifestConfig } from 'stremio-addon-sdk';
export interface Context { ip: string }
export interface Context {
id: string;
ip: string;
}
export type ManifestWithConfig = Manifest & { config: ManifestConfig[] };

View file

@ -9,7 +9,7 @@ const axiosMock = new AxiosMockAdapter(axios);
const fetcher = new Fetcher(axios, winston.createLogger({ transports: [new winston.transports.Console({ level: 'nope' })] }));
describe('fetch', () => {
const ctx: Context = { ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
test('text passes successful response through setting headers', async () => {
axiosMock.onGet().reply(200, 'some text');

View file

@ -16,7 +16,7 @@ export class Fetcher {
}
readonly text = async (ctx: Context, url: URL, config?: AxiosRequestConfig): Promise<string> => {
this.logger.info(`Fetch ${url}`);
this.logger.info(`Fetch ${url}`, ctx);
const response = await this.axios.get(url.href, this.getConfig(ctx, url, config));
@ -24,7 +24,7 @@ export class Fetcher {
};
readonly textPost = async (ctx: Context, url: URL, data: unknown, config?: AxiosRequestConfig): Promise<string> => {
this.logger.info(`Fetch post ${url}`);
this.logger.info(`Fetch post ${url}`, ctx);
const response = await this.axios.post(url.href, data, this.getConfig(ctx, url, config));

View file

@ -8,7 +8,7 @@ jest.mock('../utils/Fetcher');
const logger = winston.createLogger({ transports: [new winston.transports.Console({ level: 'nope' })] });
const streamResolver = new StreamResolver(logger);
const ctx: Context = { ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
// @ts-expect-error No constructor args needed
const fetcher = new Fetcher();
@ -34,7 +34,7 @@ describe('resolve', () => {
expect(streams).toStrictEqual([{
name: 'WebStreamr',
title: '❌ Error with handler "meinecloud". Please check the logs or create an issue if this persists.',
title: '❌ Error with handler "meinecloud". Please create an issue if this persists. Request-id: id',
ytId: 'E4WlUXrJgy4',
}]);
});

View file

@ -31,17 +31,17 @@ export class StreamResolver {
try {
const handlerUrlResults = await handler.handle(ctx, type, id);
this.logger.info(`${handler.id} returned ${handlerUrlResults.length} urls`);
this.logger.info(`${handler.id} returned ${handlerUrlResults.length} urls`, ctx);
urlResults.push(...(handlerUrlResults.filter(handlerUrlResult => handlerUrlResult !== undefined)));
} catch (err) {
streams.push({
name: 'WebStreamr',
title: `❌ Error with handler "${handler.id}". Please check the logs or create an issue if this persists.`,
title: `❌ Error with handler "${handler.id}". Please create an issue if this persists. Request-id: ${ctx.id}`,
ytId: 'E4WlUXrJgy4',
});
this.logger.error(`${handler.id} error: ${err}`);
this.logger.error(`${handler.id} error: ${err}`, ctx);
}
});
await Promise.all(handlerPromises);
@ -60,7 +60,7 @@ export class StreamResolver {
return a.label.localeCompare(b.label);
});
this.logger.info(`Return ${urlResults.length} streams`);
this.logger.info(`Return ${urlResults.length} streams`, ctx);
streams.push(
...urlResults.map((urlResult) => {