diff --git a/package.json b/package.json index 3536d7d..f822165 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "backend", - "version": "1.0.2", + "version": "1.0.3", "private": true, "homepage": "https://github.com/movie-web/backend", "engines": { diff --git a/src/config/index.ts b/src/config/index.ts index 06d2868..485e56e 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -8,7 +8,7 @@ const fragments = { dockerdev: dockerFragment, }; -export const version = '1.0.0'; +export const version = '1.0.3'; export const conf = createConfigLoader() .addFromEnvironment('MWB_') diff --git a/src/config/orm.ts b/src/config/orm.ts new file mode 100644 index 0000000..5116bad --- /dev/null +++ b/src/config/orm.ts @@ -0,0 +1,20 @@ +import { createConfigLoader } from 'neat-config'; +import { z } from 'zod'; + +export const ormConfigSchema = z.object({ + postgres: z.object({ + // connection URL for postgres database + connection: z.string(), + }), +}); + +export const ormConf = createConfigLoader() + .addFromEnvironment('MWB_') + .addFromCLI('mwb-') + .addFromFile('.env', { + prefix: 'MWB_', + }) + .addFromFile('config.json') + .addZodSchema(ormConfigSchema) + .freeze() + .load(); diff --git a/src/db/migrations/.snapshot-movie_web.json b/src/db/migrations/.snapshot-movie_web.json index 45a00cb..7cbd87f 100644 --- a/src/db/migrations/.snapshot-movie_web.json +++ b/src/db/migrations/.snapshot-movie_web.json @@ -184,6 +184,24 @@ "nullable": true, "mappedType": "string" }, + "season_number": { + "name": "season_number", + "type": "int", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "integer" + }, + "episode_number": { + "name": "episode_number", + "type": "int", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "integer" + }, "meta": { "name": "meta", "type": "jsonb", diff --git a/src/db/migrations/Migration20231105150807.ts b/src/db/migrations/Migration20231105150807.ts new file mode 100644 index 0000000..68d4c7c --- /dev/null +++ b/src/db/migrations/Migration20231105150807.ts @@ -0,0 +1,14 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20231105150807 extends Migration { + async up(): Promise { + this.addSql( + 'alter table "progress_items" add column "season_number" int null, add column "episode_number" int null;', + ); + } + + async down(): Promise { + this.addSql('alter table "progress_items" drop column "season_number";'); + this.addSql('alter table "progress_items" drop column "episode_number";'); + } +} diff --git a/src/db/models/ProgressItem.ts b/src/db/models/ProgressItem.ts index 06f12ae..aad2070 100644 --- a/src/db/models/ProgressItem.ts +++ b/src/db/models/ProgressItem.ts @@ -6,7 +6,7 @@ export const progressMetaSchema = z.object({ title: z.string(), year: z.number(), poster: z.string().optional(), - type: z.string(), + type: z.union([z.literal('show'), z.literal('movie')]), }); export type ProgressMeta = z.infer; @@ -29,6 +29,12 @@ export class ProgressItem { @Property({ name: 'episode_id', nullable: true }) episodeId?: string; + @Property({ name: 'season_number', nullable: true }) + seasonNumber?: number; + + @Property({ name: 'episode_number', nullable: true }) + episodeNumber?: number; + @Property({ name: 'meta', type: types.json, @@ -48,8 +54,14 @@ export class ProgressItem { export interface ProgressItemDTO { tmdbId: string; - seasonId?: string; - episodeId?: string; + season: { + id?: string; + number?: number; + }; + episode: { + id?: string; + number?: number; + }; meta: { title: string; year: number; @@ -66,8 +78,14 @@ export function formatProgressItem( ): ProgressItemDTO { return { tmdbId: progressItem.tmdbId, - seasonId: progressItem.seasonId, - episodeId: progressItem.episodeId, + episode: { + id: progressItem.episodeId, + number: progressItem.episodeNumber, + }, + season: { + id: progressItem.seasonId, + number: progressItem.seasonNumber, + }, meta: { title: progressItem.meta.title, year: progressItem.meta.year, diff --git a/src/mikro-orm.config.ts b/src/mikro-orm.config.ts index 7ee210d..e27bb97 100644 --- a/src/mikro-orm.config.ts +++ b/src/mikro-orm.config.ts @@ -1,4 +1,4 @@ +import { ormConf } from '@/config/orm'; import { makeOrmConfig } from '@/modules/mikro/orm'; -import { conf } from '@/config'; -export default makeOrmConfig(conf.postgres.connection); +export default makeOrmConfig(ormConf.postgres.connection); diff --git a/src/routes/auth/login.ts b/src/routes/auth/login.ts index 1902863..a2118be 100644 --- a/src/routes/auth/login.ts +++ b/src/routes/auth/login.ts @@ -1,6 +1,6 @@ import { ChallengeCode } from '@/db/models/ChallengeCode'; import { formatSession } from '@/db/models/Session'; -import { User } from '@/db/models/User'; +import { User, formatUser } from '@/db/models/User'; import { assertChallengeCode } from '@/services/challenge'; import { StatusError } from '@/services/error'; import { handle } from '@/services/handler'; @@ -85,6 +85,7 @@ export const loginAuthRouter = makeRouter((app) => { await em.persistAndFlush([session, user]); return { + user: formatUser(user), session: formatSession(session), token: makeSessionToken(session), }; diff --git a/src/routes/meta.ts b/src/routes/meta.ts index 5eea650..d45313b 100644 --- a/src/routes/meta.ts +++ b/src/routes/meta.ts @@ -1,4 +1,4 @@ -import { conf } from '@/config'; +import { conf, version } from '@/config'; import { handle } from '@/services/handler'; import { makeRouter } from '@/services/router'; @@ -26,6 +26,7 @@ export const metaRouter = makeRouter((app) => { return { name: conf.meta.name, description: conf.meta.description, + version: version, hasCaptcha: conf.captcha.enabled, captchaClientKey: conf.captcha.clientKey, }; diff --git a/src/routes/users/progress.ts b/src/routes/users/progress.ts index 3ad6e1b..37f5797 100644 --- a/src/routes/users/progress.ts +++ b/src/routes/users/progress.ts @@ -19,10 +19,12 @@ export const userProgressRouter = makeRouter((app) => { }), body: z.object({ meta: progressMetaSchema, - seasonId: z.string().optional(), - episodeId: z.string().optional(), duration: z.number(), watched: z.number(), + seasonId: z.string().optional(), + episodeId: z.string().optional(), + seasonNumber: z.number().optional(), + episodeNumber: z.number().optional(), }), }, }, @@ -38,12 +40,15 @@ export const userProgressRouter = makeRouter((app) => { episodeId: body.episodeId, seasonId: body.seasonId, }); + if (!progressItem) { progressItem = new ProgressItem(); progressItem.tmdbId = params.tmdbid; progressItem.userId = params.uid; progressItem.episodeId = body.episodeId; progressItem.seasonId = body.seasonId; + progressItem.episodeNumber = body.episodeNumber; + progressItem.seasonNumber = body.seasonNumber; } em.assign(progressItem, { diff --git a/src/services/logger.ts b/src/services/logger.ts index 83ab54f..7237dae 100644 --- a/src/services/logger.ts +++ b/src/services/logger.ts @@ -64,6 +64,8 @@ export function scopedLogger(service: string, meta: object = {}) { return logger; } +const ignoredUrls = ['/healthcheck', '/metrics']; + export function makeFastifyLogger(logger: winston.Logger) { logger.format = winston.format.combine( winston.format((info) => { @@ -78,6 +80,9 @@ export function makeFastifyLogger(logger: winston.Logger) { let url = request.url; try { const pathParts = (request.url as string).split('?', 2); + + if (ignoredUrls.includes(pathParts[0])) return false; + if (pathParts[1]) { const searchParams = new URLSearchParams(pathParts[1]); pathParts[1] = searchParams.toString();