chore: create ID per request for logging and error response
This commit is contained in:
parent
827fd88110
commit
f635d6c233
19 changed files with 51 additions and 26 deletions
14
package-lock.json
generated
14
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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 }));
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -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', '*');
|
||||
|
||||
|
|
|
|||
|
|
@ -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[] };
|
||||
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
}]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue