diff --git a/bunfig.toml b/bunfig.toml index a53b5e8..b9357dc 100644 --- a/bunfig.toml +++ b/bunfig.toml @@ -3,4 +3,5 @@ preload = [ "./testSetup.ts", "./src/mocks/consumet.ts", "./src/mocks/getGoogleAuthToken.ts", + "./src/mocks/qstash.ts", ] diff --git a/src/controllers/watch-status/index.ts b/src/controllers/watch-status/index.ts index a2d93e6..2be9053 100644 --- a/src/controllers/watch-status/index.ts +++ b/src/controllers/watch-status/index.ts @@ -1,6 +1,8 @@ import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi"; import { env } from "hono/adapter"; +import { verifyQstashHeader } from "~/libs/qstash/verifyQstashHeader"; +import { readEnvVariable } from "~/libs/readEnvVariable"; import { setWatchStatus } from "~/models/watchStatus"; import type { Env } from "~/types/env"; import { @@ -70,7 +72,18 @@ app.openapi(route, async (c) => { } = await c.req.json(); const aniListToken = c.req.header("X-AniList-Token"); - if (!isRetrying) { + if (isRetrying) { + if ( + !(await verifyQstashHeader( + env(c, "workerd"), + c.req.path, + c.req.header("Upstash-Signature"), + await c.req.text(), + )) + ) { + return c.json(ErrorResponse, { status: 401 }); + } + } else { try { const { wasAdded, wasDeleted } = await setWatchStatus( env(c, "workerd"), @@ -95,6 +108,19 @@ app.openapi(route, async (c) => { console.error( new Error("Failed to update watch status on Anilist", { cause: error }), ); + await import("@upstash/qstash") + .then( + ({ Client }) => + new Client({ token: readEnvVariable(c.env, "QSTASH_TOKEN") }), + ) + .then((client) => + client.publishJSON({ + url: c.req.url, + body: { deviceId, watchStatus, titleId, isRetrying: true }, + retries: 0, + delay: 60, + }), + ); } return c.json(SuccessResponse, { status: 200 }); diff --git a/src/mocks/qstash.ts b/src/mocks/qstash.ts new file mode 100644 index 0000000..aed561f --- /dev/null +++ b/src/mocks/qstash.ts @@ -0,0 +1,18 @@ +import { SignatureError } from "@upstash/qstash"; + +import { mock } from "bun:test"; + +class MockQstashClient { + batchJSON = mock(); + publishJSON = mock(); +} + +class MockQstashReceiver { + verify = mock(); +} + +mock.module("@upstash/qstash", () => ({ + Client: MockQstashClient, + Receiver: MockQstashReceiver, + SignatureError, +})); diff --git a/src/models/watchStatus.ts b/src/models/watchStatus.ts index 9995e5c..d6b60ee 100644 --- a/src/models/watchStatus.ts +++ b/src/models/watchStatus.ts @@ -19,7 +19,7 @@ export function setWatchStatus( watchStatus: (typeof WatchStatusValues)[number] | null, ) { let dbAction; - const isSavingTitle = watchStatus === "CURRENT"; + const isSavingTitle = watchStatus === "CURRENT" || watchStatus === "PLANNING"; if (isSavingTitle) { dbAction = saveTitle(env, deviceId, titleId); } else {