From 7921cf81df1885192719feb2cc32e404cde75444 Mon Sep 17 00:00:00 2001 From: William Oldham Date: Sat, 4 Nov 2023 13:28:39 +0000 Subject: [PATCH] Add provider metrics Co-authored-by: mrjvs --- src/db/models/ProviderMetrics.ts | 48 +++++++++++++++++++++++++++++ src/modules/fastify/routes.ts | 2 ++ src/routes/metrics.ts | 52 ++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 src/db/models/ProviderMetrics.ts create mode 100644 src/routes/metrics.ts diff --git a/src/db/models/ProviderMetrics.ts b/src/db/models/ProviderMetrics.ts new file mode 100644 index 0000000..4f7e3b8 --- /dev/null +++ b/src/db/models/ProviderMetrics.ts @@ -0,0 +1,48 @@ +import { Entity, PrimaryKey, Property } from '@mikro-orm/core'; +import { randomUUID } from 'crypto'; + +export const status = { + failed: 'failed', + notfound: 'notfound', + success: 'success', +} as const; +type Status = keyof typeof status; + +@Entity({ tableName: 'provider_metrics' }) +export class ProviderMetric { + @PrimaryKey({ name: 'id', type: 'uuid' }) + id: string = randomUUID(); + + @Property({ name: 'tmdb_id' }) + tmdbId!: string; + + @Property({ name: 'type' }) + type!: string; + + @Property({ name: 'title' }) + title!: string; + + @Property({ name: 'season_id', nullable: true }) + seasonId?: string; + + @Property({ name: 'episode_id', nullable: true }) + episodeId?: string; + + @Property({ name: 'created_at', type: 'date' }) + createdAt = new Date(); + + @Property({ name: 'status' }) + status!: Status; + + @Property({ name: 'provider_id' }) + providerId!: string; + + @Property({ name: 'embed_id', nullable: true }) + embedId?: string; + + @Property({ name: 'error_message', nullable: true }) + errorMessage?: string; + + @Property({ name: 'full_error', nullable: true }) + fullError?: string; +} diff --git a/src/modules/fastify/routes.ts b/src/modules/fastify/routes.ts index 832881f..6d9849c 100644 --- a/src/modules/fastify/routes.ts +++ b/src/modules/fastify/routes.ts @@ -1,6 +1,7 @@ import { loginAuthRouter } from '@/routes/auth/login'; import { manageAuthRouter } from '@/routes/auth/manage'; import { metaRouter } from '@/routes/meta'; +import { metricsRouter } from '@/routes/metrics'; import { sessionsRouter } from '@/routes/sessions'; import { userBookmarkRouter } from '@/routes/users/bookmark'; import { userDeleteRouter } from '@/routes/users/delete'; @@ -23,4 +24,5 @@ export async function setupRoutes(app: FastifyInstance) { await app.register(userBookmarkRouter.register); await app.register(userSettingsRouter.register); await app.register(userGetRouter.register); + await app.register(metricsRouter.register); } diff --git a/src/routes/metrics.ts b/src/routes/metrics.ts new file mode 100644 index 0000000..f3d9f34 --- /dev/null +++ b/src/routes/metrics.ts @@ -0,0 +1,52 @@ +import { handle } from '@/services/handler'; +import { makeRouter } from '@/services/router'; +import { z } from 'zod'; +import { ProviderMetric, status } from '@/db/models/ProviderMetrics'; + +const metricsProviderSchema = z.object({ + tmdbId: z.string(), + type: z.string(), + title: z.string(), + seasonId: z.string().optional(), + episodeId: z.string().optional(), + status: z.nativeEnum(status), + providerId: z.string(), + embedId: z.string().optional(), + errorMessage: z.string().optional(), + fullError: z.string().optional(), +}); + +const metricsProviderInputSchema = z.object({ + items: z.array(metricsProviderSchema).max(10).min(1), +}); + +export const metricsRouter = makeRouter((app) => { + app.post( + '/metrics/providers', + { + schema: { + body: metricsProviderInputSchema, + }, + }, + handle(async ({ em, body }) => { + const entities = body.items.map((v) => { + const metric = new ProviderMetric(); + em.assign(metric, { + providerId: v.providerId, + embedId: v.embedId, + fullError: v.fullError, + errorMessage: v.errorMessage, + episodeId: v.episodeId, + seasonId: v.seasonId, + status: v.status, + title: v.title, + tmdbId: v.tmdbId, + type: v.type, + }); + return metric; + }); + await em.persistAndFlush(entities); + return true; + }), + ); +});