refactor: replace qstash with Google Cloud Tasks
This commit is contained in:
130
src/libs/tasks/queueTask.ts
Normal file
130
src/libs/tasks/queueTask.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import type { HonoRequest } from "hono";
|
||||
import { DateTime, type DurationLike } from "luxon";
|
||||
|
||||
import type { Env } from "~/types/env";
|
||||
import type { WatchStatus } from "~/types/title/watchStatus";
|
||||
|
||||
import { getAdminSdkCredentials } from "../gcloud/getAdminSdkCredentials";
|
||||
import { getGoogleAuthToken } from "../gcloud/getGoogleAuthToken";
|
||||
import { getCurrentDomain } from "../getCurrentDomain";
|
||||
import { buildAnilistRetryTaskId, buildNewEpisodeTaskId } from "./id";
|
||||
import type { QueueName } from "./queueName";
|
||||
|
||||
type QueueBody = {
|
||||
anilist: {
|
||||
deviceId: string;
|
||||
watchStatus: WatchStatus | null;
|
||||
titleId: number;
|
||||
isRetrying: true;
|
||||
};
|
||||
"new-episode": { aniListId: number; episodeNumber: number };
|
||||
};
|
||||
|
||||
type ScheduleConfig =
|
||||
| { delay: DurationLike; epochTime: never }
|
||||
| { epochTime: number; delay: never };
|
||||
|
||||
interface QueueTaskOptionalArgs {
|
||||
taskId?: string;
|
||||
scheduleConfig?: ScheduleConfig;
|
||||
req?: HonoRequest;
|
||||
}
|
||||
|
||||
export async function queueTask(
|
||||
env: Env,
|
||||
queueName: QueueName,
|
||||
body: QueueBody[QueueName],
|
||||
{ taskId, scheduleConfig, req }: QueueTaskOptionalArgs = {},
|
||||
) {
|
||||
const domain = req
|
||||
? getCurrentDomain(req)
|
||||
: "https://aniplay-v2.rururu.workers.dev";
|
||||
if (!domain) {
|
||||
console.log("Skipping queue task due to local domain", queueName, body);
|
||||
return;
|
||||
}
|
||||
|
||||
const adminSdkCredentials = getAdminSdkCredentials(env);
|
||||
const { projectId } = adminSdkCredentials;
|
||||
|
||||
await fetch(
|
||||
`https://content-cloudtasks.googleapis.com/v2/projects/${projectId}/locations/northamerica-northeast1/queues/${queueName}/tasks?alt=json`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${await getGoogleAuthToken(adminSdkCredentials)}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
task: buildTask(
|
||||
projectId,
|
||||
queueName,
|
||||
taskId,
|
||||
scheduleConfig,
|
||||
domain,
|
||||
body,
|
||||
req.header(),
|
||||
),
|
||||
}),
|
||||
method: "POST",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function buildTask(
|
||||
projectId: string,
|
||||
queueName: QueueName,
|
||||
taskId: string | undefined,
|
||||
scheduleConfig: ScheduleConfig | undefined,
|
||||
domain: string,
|
||||
body: QueueBody[QueueName],
|
||||
headers: Record<string, string>,
|
||||
) {
|
||||
let scheduleTime: string | undefined;
|
||||
if (scheduleConfig) {
|
||||
const { delay, epochTime } = scheduleConfig;
|
||||
if (epochTime) {
|
||||
scheduleTime = DateTime.fromSeconds(epochTime).toUTC().toISO();
|
||||
} else if (delay) {
|
||||
scheduleTime = DateTime.now().plus(delay).toUTC().toISO();
|
||||
}
|
||||
}
|
||||
|
||||
switch (queueName) {
|
||||
case "new-episode":
|
||||
const { aniListId } = body as QueueBody["new-episode"];
|
||||
taskId ??= buildNewEpisodeTaskId(aniListId);
|
||||
|
||||
return {
|
||||
name: `projects/${projectId}/locations/northamerica-northeast1/queues/${queueName}/tasks/${taskId}`,
|
||||
scheduleTime,
|
||||
httpRequest: {
|
||||
url: `${domain}/internal/new-episode`,
|
||||
httpMethod: "POST",
|
||||
body: JSON.stringify(body),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Anilist-Token": headers["X-Anilist-Token"],
|
||||
},
|
||||
},
|
||||
};
|
||||
case "anilist":
|
||||
const { deviceId, titleId } = body as QueueBody["anilist"];
|
||||
taskId ??= buildAnilistRetryTaskId(deviceId, titleId);
|
||||
|
||||
return {
|
||||
name: `projects/${projectId}/locations/northamerica-northeast1/queues/${queueName}/tasks/${taskId}`,
|
||||
scheduleTime,
|
||||
httpRequest: {
|
||||
url: `${domain}/watch-status`,
|
||||
httpMethod: "POST",
|
||||
body: JSON.stringify(body),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Anilist-Token": headers["X-Anilist-Token"],
|
||||
},
|
||||
},
|
||||
};
|
||||
default:
|
||||
throw new Error(`Unknown queue name: ${queueName}`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user