diff --git a/api/src/controllers/profiles/history.ts b/api/src/controllers/profiles/history.ts index 71af81ad..9a780ff6 100644 --- a/api/src/controllers/profiles/history.ts +++ b/api/src/controllers/profiles/history.ts @@ -450,6 +450,12 @@ export const historyH = new Elysia({ tags: ["profiles"] }) "/profiles/me/history", async ({ body, jwt: { sub }, status }) => { const profilePk = await getOrCreateProfile(sub); + if (!profilePk) { + return status(401, { + status: 401, + message: "Guest don't have history", + }); + } const ret = await updateProgress(profilePk, body); return status(ret.status, ret); @@ -465,6 +471,7 @@ export const historyH = new Elysia({ tags: ["profiles"] }) description: "The number of history entry inserted", }), }), + 401: { ...KError, description: "Non logged users don't have history" }, 404: { ...KError, description: "No entry found with the given id or slug.", diff --git a/api/src/controllers/profiles/profile.ts b/api/src/controllers/profiles/profile.ts index 7719b512..7b18fe42 100644 --- a/api/src/controllers/profiles/profile.ts +++ b/api/src/controllers/profiles/profile.ts @@ -3,6 +3,9 @@ import { db } from "~/db"; import { profiles } from "~/db/schema"; export async function getOrCreateProfile(userId: string) { + // id of the guest user + if (userId === "00000000-0000-0000-0000-000000000000") return null; + let [profile] = await db .select({ pk: profiles.pk }) .from(profiles) diff --git a/api/src/controllers/profiles/watchlist.ts b/api/src/controllers/profiles/watchlist.ts index 57f01959..b8db0f4d 100644 --- a/api/src/controllers/profiles/watchlist.ts +++ b/api/src/controllers/profiles/watchlist.ts @@ -34,21 +34,17 @@ import { } from "~/models/watchlist"; import { getOrCreateProfile } from "./profile"; -console.log(); - async function setWatchStatus({ show, status, - userId, + userPk, }: { show: | { pk: number; kind: "movie" } | { pk: number; kind: "serie"; entriesCount: number }; status: SeedSerieWatchStatus; - userId: string; + userPk: number; }) { - const profilePk = await getOrCreateProfile(userId); - const firstEntryQ = db .select({ pk: entries.pk }) .from(entries) @@ -61,7 +57,7 @@ async function setWatchStatus({ .values({ ...status, startedAt: coalesce(sql`${status.startedAt ?? null}`, sql`now()`), - profilePk: profilePk, + profilePk: userPk, seenCount: status.status === "completed" ? show.kind === "movie" @@ -269,6 +265,14 @@ export const watchlistH = new Elysia({ tags: ["profiles"] }) .post( "/series/:id/watchstatus", async ({ params: { id }, body, jwt: { sub }, status }) => { + const profilePk = await getOrCreateProfile(sub); + if (!profilePk) { + return status(401, { + status: 401, + message: "Guest can't set watchstatus", + }); + } + const [show] = await db .select({ pk: shows.pk, entriesCount: shows.entriesCount }) .from(shows) @@ -287,7 +291,7 @@ export const watchlistH = new Elysia({ tags: ["profiles"] }) } return await setWatchStatus({ show: { pk: show.pk, kind: "serie", entriesCount: show.entriesCount }, - userId: sub, + userPk: profilePk, status: body, }); }, @@ -302,6 +306,7 @@ export const watchlistH = new Elysia({ tags: ["profiles"] }) body: SeedSerieWatchStatus, response: { 200: t.Intersect([SerieWatchStatus, DbMetadata]), + 401: { ...KError, description: "Guest can't set their watchstatus" }, 404: KError, }, permissions: ["core.read"], @@ -310,6 +315,14 @@ export const watchlistH = new Elysia({ tags: ["profiles"] }) .post( "/movies/:id/watchstatus", async ({ params: { id }, body, jwt: { sub }, status }) => { + const profilePk = await getOrCreateProfile(sub); + if (!profilePk) { + return status(401, { + status: 401, + message: "Guest can't set watchstatus", + }); + } + const [show] = await db .select({ pk: shows.pk }) .from(shows) @@ -329,7 +342,7 @@ export const watchlistH = new Elysia({ tags: ["profiles"] }) return await setWatchStatus({ show: { pk: show.pk, kind: "movie" }, - userId: sub, + userPk: profilePk, status: { ...body, startedAt: body.completedAt, @@ -347,6 +360,7 @@ export const watchlistH = new Elysia({ tags: ["profiles"] }) body: SeedMovieWatchStatus, response: { 200: t.Intersect([MovieWatchStatus, DbMetadata]), + 401: { ...KError, description: "Guest can't set their watchstatus" }, 404: KError, }, permissions: ["core.read"], @@ -356,6 +370,12 @@ export const watchlistH = new Elysia({ tags: ["profiles"] }) "/series/:id/watchstatus", async ({ params: { id }, jwt: { sub }, status }) => { const profilePk = await getOrCreateProfile(sub); + if (!profilePk) { + return status(401, { + status: 401, + message: "Guest can't set watchstatus", + }); + } const rows = await db.execute(sql` delete from ${watchlist} using ${shows} @@ -396,6 +416,12 @@ export const watchlistH = new Elysia({ tags: ["profiles"] }) "/movies/:id/watchstatus", async ({ params: { id }, jwt: { sub }, status }) => { const profilePk = await getOrCreateProfile(sub); + if (!profilePk) { + return status(401, { + status: 401, + message: "Guest can't set watchstatus", + }); + } const rows = await db.execute(sql` delete from ${watchlist} using ${shows} @@ -428,6 +454,7 @@ export const watchlistH = new Elysia({ tags: ["profiles"] }) }), response: { 200: t.Intersect([MovieWatchStatus, DbMetadata]), + 401: { ...KError, description: "Guest can't set their watchstatus" }, 404: KError, }, permissions: ["core.read"], diff --git a/api/src/websockets.ts b/api/src/websockets.ts index dbc76a0c..6cc2bc7a 100644 --- a/api/src/websockets.ts +++ b/api/src/websockets.ts @@ -26,6 +26,14 @@ const actionMap = { permissions: ["core.read"], async message(ws, body) { const profilePk = await getOrCreateProfile(ws.data.jwt.sub); + if (!profilePk) { + ws.send({ + action: "watch", + status: 401, + message: "Guest can't set watchstatus", + }); + return; + } const ret = await updateProgress(profilePk, [ { ...body, playedDate: null }, diff --git a/auth/jwt.go b/auth/jwt.go index ab72be8c..60cbef19 100644 --- a/auth/jwt.go +++ b/auth/jwt.go @@ -44,16 +44,16 @@ func (h *Handler) CreateJwt(c echo.Context) error { var token string if auth == "" { - cookie, _ := c.Request().Cookie("X-Bearer") - if cookie != nil { - token = cookie.Value + protocol, ok := c.Request().Header["Sec-Websocket-Protocol"] + if ok && + len(protocol) == 2 && + protocol[0] == "kyoo" && + strings.HasPrefix(protocol[1], "Bearer ") { + token = protocol[1][len("Bearer "):] } else { - protocol, ok := c.Request().Header["Sec-Websocket-Protocol"] - if ok && - len(protocol) == 2 && - protocol[0] == "kyoo" && - strings.HasPrefix(protocol[1], "Bearer") { - token = protocol[1][len("Bearer "):] + cookie, _ := c.Request().Cookie("X-Bearer") + if cookie != nil { + token = cookie.Value } } } else if strings.HasPrefix(auth, "Bearer ") { diff --git a/chart/templates/traefikproxy/configmap.yaml b/chart/templates/traefikproxy/configmap.yaml index a75e2eed..bf146ea7 100644 --- a/chart/templates/traefikproxy/configmap.yaml +++ b/chart/templates/traefikproxy/configmap.yaml @@ -66,6 +66,7 @@ data: - "Authorization" - "X-Api-Key" - "Cookie" + - "Sec-WebSocket-Protocol" authResponseHeaders: - Authorization services: diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 471fe4ba..53115170 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -23,7 +23,7 @@ x-transcoder: &transcoder-base - "traefik.http.routers.transcoder.rule=PathPrefix(`/video`)" - "traefik.http.routers.transcoder.middlewares=phantom-token" - "traefik.http.middlewares.phantom-token.forwardauth.address=http://auth:4568/auth/jwt" - - "traefik.http.middlewares.phantom-token.forwardauth.authRequestHeaders=Authorization,Cookie,X-Api-Key" + - "traefik.http.middlewares.phantom-token.forwardauth.authRequestHeaders=Authorization,Cookie,X-Api-Key,Sec-WebSocket-Protocol" - "traefik.http.middlewares.phantom-token.forwardauth.authResponseHeaders=Authorization" develop: watch: @@ -95,7 +95,7 @@ services: - "traefik.http.routers.api.rule=PathPrefix(`/api/`)" - "traefik.http.routers.api.middlewares=phantom-token" - "traefik.http.middlewares.phantom-token.forwardauth.address=http://auth:4568/auth/jwt" - - "traefik.http.middlewares.phantom-token.forwardauth.authRequestHeaders=Authorization,Cookie,X-Api-Key" + - "traefik.http.middlewares.phantom-token.forwardauth.authRequestHeaders=Authorization,Cookie,X-Api-Key,Sec-WebSocket-Protocol" - "traefik.http.middlewares.phantom-token.forwardauth.authResponseHeaders=Authorization" develop: watch: @@ -131,7 +131,7 @@ services: - "traefik.http.routers.scanner.rule=PathPrefix(`/scanner/`)" - "traefik.http.routers.scanner.middlewares=phantom-token" - "traefik.http.middlewares.phantom-token.forwardauth.address=http://auth:4568/auth/jwt" - - "traefik.http.middlewares.phantom-token.forwardauth.authRequestHeaders=Authorization,Cookie,X-Api-Key" + - "traefik.http.middlewares.phantom-token.forwardauth.authRequestHeaders=Authorization,Cookie,X-Api-Key,Sec-WebSocket-Protocol" - "traefik.http.middlewares.phantom-token.forwardauth.authResponseHeaders=Authorization" command: fastapi dev scanner --host 0.0.0.0 --port 4389 develop: diff --git a/docker-compose.yml b/docker-compose.yml index ade086ff..8297667d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,7 +19,7 @@ x-transcoder: &transcoder-base - "traefik.http.routers.transcoder.rule=PathPrefix(`/video`)" - "traefik.http.routers.transcoder.middlewares=phantom-token" - "traefik.http.middlewares.phantom-token.forwardauth.address=http://auth:4568/auth/jwt" - - "traefik.http.middlewares.phantom-token.forwardauth.authRequestHeaders=Authorization,Cookie,X-Api-Key" + - "traefik.http.middlewares.phantom-token.forwardauth.authRequestHeaders=Authorization,Cookie,X-Api-Key,Sec-WebSocket-Protocol" - "traefik.http.middlewares.phantom-token.forwardauth.authResponseHeaders=Authorization" services: @@ -65,7 +65,7 @@ services: - "traefik.http.routers.api.rule=PathPrefix(`/api/`)" - "traefik.http.routers.api.middlewares=phantom-token" - "traefik.http.middlewares.phantom-token.forwardauth.address=http://auth:4568/auth/jwt" - - "traefik.http.middlewares.phantom-token.forwardauth.authRequestHeaders=Authorization,Cookie,X-Api-Key" + - "traefik.http.middlewares.phantom-token.forwardauth.authRequestHeaders=Authorization,Cookie,X-Api-Key,Sec-WebSocket-Protocol" - "traefik.http.middlewares.phantom-token.forwardauth.authResponseHeaders=Authorization" scanner: @@ -88,7 +88,7 @@ services: - "traefik.http.routers.scanner.rule=PathPrefix(`/scanner/`)" - "traefik.http.routers.scanner.middlewares=phantom-token" - "traefik.http.middlewares.phantom-token.forwardauth.address=http://auth:4568/auth/jwt" - - "traefik.http.middlewares.phantom-token.forwardauth.authRequestHeaders=Authorization,Cookie,X-Api-Key" + - "traefik.http.middlewares.phantom-token.forwardauth.authRequestHeaders=Authorization,Cookie,X-Api-Key,Sec-WebSocket-Protocol" - "traefik.http.middlewares.phantom-token.forwardauth.authResponseHeaders=Authorization" transcoder: diff --git a/front/src/query/websockets.ts b/front/src/query/websockets.ts index fcbd7d77..213109c2 100644 --- a/front/src/query/websockets.ts +++ b/front/src/query/websockets.ts @@ -1,4 +1,5 @@ import { useEffect } from "react"; +import { Platform } from "react-native"; import useWebSocket from "react-use-websocket"; import { useToken } from "~/providers/account-context"; @@ -9,13 +10,17 @@ export const useWebsockets = ({ }) => { const { apiUrl, authToken } = useToken(); const ret = useWebSocket(`${apiUrl}/api/ws`, { - protocols: authToken ? ["kyoo", `Bearer ${authToken}`] : undefined, + // on web use cookies, firefox doesn't like protocols idk why + protocols: + Platform.OS !== "web" && authToken + ? ["kyoo", `Bearer ${authToken}`] + : ["kyoo"], filter: (msg) => filterActions.includes(msg.data.action), share: true, retryOnError: true, heartbeat: { - message: `{ "action": "ping" }`, - returnMessage: `{ "response": "pong" }`, + message: `{"action": "ping"}`, + returnMessage: `{"action":"ping","response":"pong"}`, interval: 25_000, }, });