diff --git a/api/src/auth.ts b/api/src/auth.ts index 812f754f..71a925be 100644 --- a/api/src/auth.ts +++ b/api/src/auth.ts @@ -1,7 +1,9 @@ import { TypeCompiler } from "@sinclair/typebox/compiler"; +import { Value } from "@sinclair/typebox/value"; import Elysia, { t } from "elysia"; import { createRemoteJWKSet, jwtVerify } from "jose"; import { KError } from "./models/error"; +import type { Prettify } from "./utils"; const jwtSecret = process.env.JWT_SECRET ? new TextEncoder().encode(process.env.JWT_SECRET) @@ -13,12 +15,22 @@ const jwks = createRemoteJWKSet( ), ); +const Settings = t.Object( + { + preferOriginal: t.Boolean({ default: true }), + }, + { additionalProperties: true }, +); +type Settings = typeof Settings.static; + const Jwt = t.Object({ sub: t.String({ description: "User id" }), sid: t.String({ description: "Session id" }), username: t.String(), permissions: t.Array(t.String()), + settings: t.Optional(t.Partial(Settings, { default: {} })), }); +type Jwt = typeof Jwt.static; const validator = TypeCompiler.Compile(Jwt); export const auth = new Elysia({ name: "auth" }) @@ -44,7 +56,10 @@ export const auth = new Elysia({ name: "auth" }) const { payload } = await jwtVerify(bearer, jwtSecret ?? jwks, { issuer: process.env.JWT_ISSUER, }); - const jwt = validator.Decode(payload); + const raw = validator.Decode(payload); + const jwt = Value.Default(Jwt, raw) as Prettify< + Jwt & { settings: Settings } + >; return { jwt }; } catch (err) { diff --git a/api/src/controllers/profiles/watchlist.ts b/api/src/controllers/profiles/watchlist.ts index de642087..ee3ea827 100644 --- a/api/src/controllers/profiles/watchlist.ts +++ b/api/src/controllers/profiles/watchlist.ts @@ -100,7 +100,7 @@ export const watchlistH = new Elysia({ tags: ["profiles"] }) query: { limit, after, query, sort, filter, preferOriginal }, headers: { "accept-language": languages }, request: { url }, - jwt: { sub }, + jwt: { sub, settings }, }) => { const langs = processLanguages(languages); const items = await getShows({ @@ -114,7 +114,7 @@ export const watchlistH = new Elysia({ tags: ["profiles"] }) filter, ), languages: langs, - preferOriginal, + preferOriginal: preferOriginal ?? settings.preferOriginal, userId: sub, }); return createPage(items, { url, sort, limit }); @@ -138,6 +138,7 @@ export const watchlistH = new Elysia({ tags: ["profiles"] }) async ({ params: { id }, query: { limit, after, query, sort, filter, preferOriginal }, + jwt: { settings }, headers: { "accept-language": languages, authorization }, request: { url }, error, @@ -157,7 +158,7 @@ export const watchlistH = new Elysia({ tags: ["profiles"] }) filter, ), languages: langs, - preferOriginal, + preferOriginal: preferOriginal ?? settings.preferOriginal, userId: uInfo.id, }); return createPage(items, { url, sort, limit }); diff --git a/api/src/controllers/shows/collections.ts b/api/src/controllers/shows/collections.ts index 3d5bb02b..75bc5dcb 100644 --- a/api/src/controllers/shows/collections.ts +++ b/api/src/controllers/shows/collections.ts @@ -40,7 +40,7 @@ export const collections = new Elysia({ params: { id }, headers: { "accept-language": languages }, query: { preferOriginal, with: relations }, - jwt: { sub }, + jwt: { sub, settings }, error, set, }) => { @@ -53,7 +53,7 @@ export const collections = new Elysia({ ), languages: langs, fallbackLanguage: langs.includes("*"), - preferOriginal, + preferOriginal: preferOriginal ?? settings.preferOriginal, relations, userId: sub, }); @@ -144,7 +144,7 @@ export const collections = new Elysia({ async ({ query: { limit, after, query, sort, filter, preferOriginal }, headers: { "accept-language": languages }, - jwt: { sub }, + jwt: { sub, settings }, request: { url }, }) => { const langs = processLanguages(languages); @@ -155,7 +155,7 @@ export const collections = new Elysia({ sort, filter: and(eq(shows.kind, "collection"), filter), languages: langs, - preferOriginal, + preferOriginal: preferOriginal ?? settings.preferOriginal, userId: sub, }); return createPage(items, { url, sort, limit }); @@ -228,7 +228,7 @@ export const collections = new Elysia({ params: { id }, query: { limit, after, query, sort, filter, preferOriginal }, headers: { "accept-language": languages }, - jwt: { sub }, + jwt: { sub, settings }, request: { url }, error, }) => { @@ -262,7 +262,7 @@ export const collections = new Elysia({ filter, ), languages: langs, - preferOriginal, + preferOriginal: preferOriginal ?? settings.preferOriginal, userId: sub, }); return createPage(items, { url, sort, limit }); @@ -285,7 +285,7 @@ export const collections = new Elysia({ params: { id }, query: { limit, after, query, sort, filter, preferOriginal }, headers: { "accept-language": languages }, - jwt: { sub }, + jwt: { sub, settings }, request: { url }, error, }) => { @@ -319,7 +319,7 @@ export const collections = new Elysia({ filter, ), languages: langs, - preferOriginal, + preferOriginal: preferOriginal ?? settings.preferOriginal, userId: sub, }); return createPage(items, { url, sort, limit }); @@ -342,7 +342,7 @@ export const collections = new Elysia({ params: { id }, query: { limit, after, query, sort, filter, preferOriginal }, headers: { "accept-language": languages }, - jwt: { sub }, + jwt: { sub, settings }, request: { url }, error, }) => { @@ -372,7 +372,7 @@ export const collections = new Elysia({ sort, filter: and(eq(shows.collectionPk, collection.pk), filter), languages: langs, - preferOriginal, + preferOriginal: preferOriginal ?? settings.preferOriginal, userId: sub, }); return createPage(items, { url, sort, limit }); diff --git a/api/src/controllers/shows/movies.ts b/api/src/controllers/shows/movies.ts index 18b969dd..3fadc317 100644 --- a/api/src/controllers/shows/movies.ts +++ b/api/src/controllers/shows/movies.ts @@ -30,7 +30,7 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] }) params: { id }, headers: { "accept-language": languages }, query: { preferOriginal, with: relations }, - jwt: { sub }, + jwt: { sub, settings }, error, set, }) => { @@ -43,7 +43,7 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] }) ), languages: langs, fallbackLanguage: langs.includes("*"), - preferOriginal, + preferOriginal: preferOriginal ?? settings.preferOriginal, relations, userId: sub, }); @@ -135,7 +135,7 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] }) query: { limit, after, query, sort, filter, preferOriginal }, headers: { "accept-language": languages }, request: { url }, - jwt: { sub }, + jwt: { sub, settings }, }) => { const langs = processLanguages(languages); const items = await getShows({ @@ -145,7 +145,7 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] }) sort, filter: and(eq(shows.kind, "movie"), filter), languages: langs, - preferOriginal, + preferOriginal: preferOriginal ?? settings.preferOriginal, userId: sub, }); return createPage(items, { url, sort, limit }); diff --git a/api/src/controllers/shows/series.ts b/api/src/controllers/shows/series.ts index 6e914647..e3d1dbb6 100644 --- a/api/src/controllers/shows/series.ts +++ b/api/src/controllers/shows/series.ts @@ -30,7 +30,7 @@ export const series = new Elysia({ prefix: "/series", tags: ["series"] }) params: { id }, headers: { "accept-language": languages }, query: { preferOriginal, with: relations }, - jwt: { sub }, + jwt: { sub, settings }, error, set, }) => { @@ -43,7 +43,7 @@ export const series = new Elysia({ prefix: "/series", tags: ["series"] }) ), languages: langs, fallbackLanguage: langs.includes("*"), - preferOriginal, + preferOriginal: preferOriginal ?? settings.preferOriginal, relations, userId: sub, }); @@ -138,7 +138,7 @@ export const series = new Elysia({ prefix: "/series", tags: ["series"] }) query: { limit, after, query, sort, filter, preferOriginal }, headers: { "accept-language": languages }, request: { url }, - jwt: { sub }, + jwt: { sub, settings }, }) => { const langs = processLanguages(languages); const items = await getShows({ @@ -148,7 +148,7 @@ export const series = new Elysia({ prefix: "/series", tags: ["series"] }) sort, filter: and(eq(shows.kind, "serie"), filter), languages: langs, - preferOriginal, + preferOriginal: preferOriginal ?? settings.preferOriginal, userId: sub, }); return createPage(items, { url, sort, limit }); diff --git a/api/src/controllers/shows/shows.ts b/api/src/controllers/shows/shows.ts index 397c4f43..85002465 100644 --- a/api/src/controllers/shows/shows.ts +++ b/api/src/controllers/shows/shows.ts @@ -65,7 +65,7 @@ export const showsH = new Elysia({ prefix: "/shows", tags: ["shows"] }) }, headers: { "accept-language": languages }, request: { url }, - jwt: { sub }, + jwt: { sub, settings }, }) => { const langs = processLanguages(languages); const items = await getShows({ @@ -78,7 +78,7 @@ export const showsH = new Elysia({ prefix: "/shows", tags: ["shows"] }) filter, ), languages: langs, - preferOriginal, + preferOriginal: preferOriginal ?? settings.preferOriginal, userId: sub, }); return createPage(items, { url, sort, limit }); diff --git a/api/src/controllers/staff.ts b/api/src/controllers/staff.ts index 7ce783f3..3ee9321a 100644 --- a/api/src/controllers/staff.ts +++ b/api/src/controllers/staff.ts @@ -191,7 +191,7 @@ export const staffH = new Elysia({ tags: ["staff"] }) query: { limit, after, query, sort, filter, preferOriginal }, headers: { "accept-language": languages }, request: { url }, - jwt: { sub }, + jwt: { sub, settings }, error, }) => { const [member] = await db @@ -243,7 +243,7 @@ export const staffH = new Elysia({ tags: ["staff"] }) kind: sql`${shows.kind}`, isAvailable: sql`${shows.availableCount} != 0`, - ...(preferOriginal && { + ...((preferOriginal ?? settings.preferOriginal) && { poster: sql`coalesce(nullif(${shows.original}->'poster', 'null'::jsonb), ${transQ.poster})`, thumbnail: sql`coalesce(nullif(${shows.original}->'thumbnail', 'null'::jsonb), ${transQ.thumbnail})`, banner: sql`coalesce(nullif(${shows.original}->'banner', 'null'::jsonb), ${transQ.banner})`, diff --git a/api/src/controllers/studios.ts b/api/src/controllers/studios.ts index d9682b78..373df884 100644 --- a/api/src/controllers/studios.ts +++ b/api/src/controllers/studios.ts @@ -303,7 +303,7 @@ export const studiosH = new Elysia({ prefix: "/studios", tags: ["studios"] }) params: { id }, query: { limit, after, query, sort, filter, preferOriginal }, headers: { "accept-language": languages }, - jwt: { sub }, + jwt: { sub, settings }, request: { url }, error, }) => { @@ -341,7 +341,7 @@ export const studiosH = new Elysia({ prefix: "/studios", tags: ["studios"] }) filter, ), languages: langs, - preferOriginal, + preferOriginal: preferOriginal ?? settings.preferOriginal, userId: sub, }); return createPage(items, { url, sort, limit }); @@ -364,7 +364,7 @@ export const studiosH = new Elysia({ prefix: "/studios", tags: ["studios"] }) params: { id }, query: { limit, after, query, sort, filter, preferOriginal }, headers: { "accept-language": languages }, - jwt: { sub }, + jwt: { sub, settings }, request: { url }, error, }) => { @@ -403,7 +403,7 @@ export const studiosH = new Elysia({ prefix: "/studios", tags: ["studios"] }) filter, ), languages: langs, - preferOriginal, + preferOriginal: preferOriginal ?? settings.preferOriginal, userId: sub, }); return createPage(items, { url, sort, limit }); @@ -426,7 +426,7 @@ export const studiosH = new Elysia({ prefix: "/studios", tags: ["studios"] }) params: { id }, query: { limit, after, query, sort, filter, preferOriginal }, headers: { "accept-language": languages }, - jwt: { sub }, + jwt: { sub, settings }, request: { url }, error, }) => { @@ -465,7 +465,7 @@ export const studiosH = new Elysia({ prefix: "/studios", tags: ["studios"] }) filter, ), languages: langs, - preferOriginal, + preferOriginal: preferOriginal ?? settings.preferOriginal, userId: sub, }); return createPage(items, { url, sort, limit }); diff --git a/api/tests/series/history.test.ts b/api/tests/series/history.test.ts index 9bc14696..1382b176 100644 --- a/api/tests/series/history.test.ts +++ b/api/tests/series/history.test.ts @@ -126,7 +126,7 @@ describe("Set & get history", () => { }); }); - // extras, unknowns + // TODO: extras, unknowns it("Update watchlist", async () => { const [resp, body] = await getWatchlist("me", {});