diff --git a/.env.example b/.env.example index 7ca66f24..a4c83d29 100644 --- a/.env.example +++ b/.env.example @@ -96,7 +96,7 @@ RABBITMQ_DEFAULT_PASS=aohohunuhouhuhhoahothonseuhaoensuthoaentsuhha # v5 stuff, does absolutely nothing on master (aka: you can delete this) -EXTRA_CLAIMS='{"permissions": [], "verified": false}' -FIRST_USER_CLAIMS='{"permissions": ["user.read", "users.write", "users.delete"], "verified": true}' -GUEST_CLAIMS='{"permissions": []}' +EXTRA_CLAIMS='{"permissions": ["core.read"], "verified": false}' +FIRST_USER_CLAIMS='{"permissions": ["user.read", "users.write", "users.delete", "core.read"], "verified": true}' +GUEST_CLAIMS='{"permissions": ["core.read"]}' PROTECTED_CLAIMS="permissions,verified" diff --git a/api/src/auth.ts b/api/src/auth.ts index ff6bd3e5..a2991372 100644 --- a/api/src/auth.ts +++ b/api/src/auth.ts @@ -26,35 +26,36 @@ export const auth = new Elysia({ name: "auth" }) authorization: t.TemplateLiteral("Bearer ${string}"), }), }) + .resolve(async ({ headers: { authorization }, error }) => { + const bearer = authorization?.slice(7); + if (!bearer) { + return error(500, { + status: 500, + message: "No jwt, auth server configuration error.", + }); + } + + // @ts-expect-error ts can't understand that there's two overload idk why + const { payload } = await jwtVerify(bearer, jwtSecret ?? jwks, { + issuer: process.env.JWT_ISSUER, + }); + const jwt = validator.Decode(payload); + + return { jwt }; + }) .macro({ permissions(perms: string[]) { return { - resolve: async ({ headers: { authorization }, error }) => { - const bearer = authorization?.slice(7); - if (!bearer) { - return error(500, { - status: 500, - message: "No jwt, auth server configuration error.", - }); - } - - // @ts-expect-error ts can't understand that there's two overload idk why - const { payload } = await jwtVerify(bearer, jwtSecret ?? jwks, { - issuer: process.env.JWT_ISSUER, - }); - const jwt = validator.Decode(payload); - + beforeHandle: ({ jwt, error }) => { for (const perm of perms) { - if (!jwt.permissions.includes(perm)) { + if (!jwt!.permissions.includes(perm)) { return error(403, { status: 403, message: `Missing permission: '${perm}'.`, - details: { current: jwt.permissions, required: perms }, + details: { current: jwt!.permissions, required: perms }, }); } } - - return { jwt }; }, }; }, diff --git a/api/src/controllers/shows/logic.ts b/api/src/controllers/shows/logic.ts index f4f86bf9..15b46ab2 100644 --- a/api/src/controllers/shows/logic.ts +++ b/api/src/controllers/shows/logic.ts @@ -6,6 +6,7 @@ import { entryTranslations, entryVideoJoin, history, + profiles, showStudioJoin, showTranslations, shows, @@ -36,7 +37,7 @@ import { sortToSql, } from "~/models/utils"; import type { EmbeddedVideo } from "~/models/video"; -import { entryVideosQ, getEntryProgressQ } from "../entries"; +import { entryVideosQ, getEntryProgressQ, mapProgress } from "../entries"; export const showFilters: FilterDef = { genres: { @@ -151,7 +152,7 @@ const showRelations = { firstEntry: ({ languages, userId, - }: { languages: string[]; userId: number }) => { + }: { languages: string[]; userId: string }) => { const transQ = db .selectDistinctOn([entryTranslations.pk]) .from(entryTranslations) @@ -171,7 +172,7 @@ const showRelations = { ...transCol, number: entries.episodeNumber, videos: entryVideosQ.videos, - progress: getColumns(progressQ), + progress: mapProgress(progressQ), }).as("firstEntry"), }) .from(entries) @@ -189,7 +190,7 @@ const showRelations = { watchStatusQ, }: { languages: string[]; - userId: number; + userId: string; watchStatusQ: PgSelect; }) => { const transQ = db @@ -211,7 +212,7 @@ const showRelations = { ...transCol, number: entries.episodeNumber, videos: entryVideosQ.videos, - progress: getColumns(progressQ), + progress: mapProgress(progressQ), }).as("nextEntry"), }) .from(entries) @@ -244,7 +245,7 @@ export async function getShows({ fallbackLanguage?: boolean; preferOriginal?: boolean; relations?: (keyof typeof showRelations)[]; - userId: number; + userId: string; }) { const transQ = db .selectDistinctOn([showTranslations.pk]) @@ -263,10 +264,11 @@ export async function getShows({ const watchStatusQ = db .select({ ...getColumns(watchlist), - percent: watchlist.seenCount, + percent: sql`${watchlist.seenCount}`.as("percent"), }) .from(watchlist) - .where(eq(watchlist.profilePk, userId)) + .leftJoin(profiles, eq(watchlist.profilePk, profiles.pk)) + .where(eq(profiles.id, userId)) .as("watchstatus"); return await db diff --git a/api/src/models/movie.ts b/api/src/models/movie.ts index 4110f000..9de88479 100644 --- a/api/src/models/movie.ts +++ b/api/src/models/movie.ts @@ -56,7 +56,7 @@ export const Movie = t.Intersect([ t.Object({ original: Original, isAvailable: t.Boolean(), - watchStatus: t.Omit(WatchStatus, ["seenCount"]), + watchStatus: t.Nullable(t.Omit(WatchStatus, ["seenCount"])), }), ]); export type Movie = Prettify; diff --git a/api/src/models/serie.ts b/api/src/models/serie.ts index 22e894ce..78ed77e8 100644 --- a/api/src/models/serie.ts +++ b/api/src/models/serie.ts @@ -71,7 +71,7 @@ export const Serie = t.Intersect([ availableCount: t.Integer({ description: "The number of episodes that can be played right away", }), - watchStatus: t.Omit(WatchStatus, ["percent"]), + watchStatus: t.Nullable(t.Omit(WatchStatus, ["percent"])), }), ]); export type Serie = Prettify;