Compare commits
4 Commits
main
...
3fab9cacbc
| Author | SHA1 | Date | |
|---|---|---|---|
| 3fab9cacbc | |||
| f9ca949d8e | |||
| 37b4f0bf2b | |||
| d66903400d |
@@ -66,7 +66,6 @@ pnpm run deploy
|
|||||||
|
|
||||||
- `src/controllers`: API route handlers (titles, episodes, search, etc.)
|
- `src/controllers`: API route handlers (titles, episodes, search, etc.)
|
||||||
- `src/libs`: Shared utilities and logic (AniList integration, background tasks)
|
- `src/libs`: Shared utilities and logic (AniList integration, background tasks)
|
||||||
- `src/middleware`: Middleware handlers (authentication, authorization, etc.)
|
|
||||||
- `src/models`: Database schema and models
|
- `src/models`: Database schema and models
|
||||||
- `src/scripts`: Utility scripts for maintenance and setup
|
- `src/scripts`: Utility scripts for maintenance and setup
|
||||||
- `src/types`: TypeScript type definitions
|
- `src/types`: TypeScript type definitions
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ type AiringSchedule = {
|
|||||||
id: number;
|
id: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function getUpcomingTitlesFromAnilist() {
|
export async function getUpcomingTitlesFromAnilist(req: HonoRequest) {
|
||||||
const durableObjectId = env.ANILIST_DO.idFromName("GLOBAL");
|
const durableObjectId = env.ANILIST_DO.idFromName("GLOBAL");
|
||||||
const stub = env.ANILIST_DO.get(durableObjectId);
|
const stub = env.ANILIST_DO.get(durableObjectId);
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import { getUpcomingTitlesFromAnilist } from "./anilist";
|
|||||||
|
|
||||||
const app = new Hono();
|
const app = new Hono();
|
||||||
|
|
||||||
export async function checkUpcomingTitles() {
|
app.post("/", async (c) => {
|
||||||
const titles = await getUpcomingTitlesFromAnilist();
|
const titles = await getUpcomingTitlesFromAnilist(c.req);
|
||||||
|
|
||||||
await Promise.allSettled(
|
await Promise.allSettled(
|
||||||
titles.map(async (title) => {
|
titles.map(async (title) => {
|
||||||
@@ -44,10 +44,6 @@ export async function checkUpcomingTitles() {
|
|||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
app.post("/", async (c) => {
|
|
||||||
await checkUpcomingTitles();
|
|
||||||
|
|
||||||
return c.json(SuccessResponse, 200);
|
return c.json(SuccessResponse, 200);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ describe('requests the "/title" route', () => {
|
|||||||
headers: new Headers({ "x-anilist-token": "asd" }),
|
headers: new Headers({ "x-anilist-token": "asd" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect(response.json()).resolves.toMatchSnapshot();
|
expect(await response.json()).toMatchSnapshot();
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ describe('requests the "/title" route', () => {
|
|||||||
|
|
||||||
const response = await app.request("/title?id=10");
|
const response = await app.request("/title?id=10");
|
||||||
|
|
||||||
await expect(response.json()).resolves.toMatchSnapshot();
|
expect(await response.json()).toMatchSnapshot();
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ describe('requests the "/title" route', () => {
|
|||||||
|
|
||||||
const response = await app.request("/title?id=-1");
|
const response = await app.request("/title?id=-1");
|
||||||
|
|
||||||
await expect(response.json()).resolves.toEqual({ success: false });
|
expect(await response.json()).toEqual({ success: false });
|
||||||
expect(response.status).toBe(404);
|
expect(response.status).toBe(404);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
19
src/index.ts
19
src/index.ts
@@ -12,8 +12,6 @@ import {
|
|||||||
} from "~/libs/tasks/queueTask";
|
} from "~/libs/tasks/queueTask";
|
||||||
import { maybeUpdateLastConnectedAt } from "~/middleware/maybeUpdateLastConnectedAt";
|
import { maybeUpdateLastConnectedAt } from "~/middleware/maybeUpdateLastConnectedAt";
|
||||||
|
|
||||||
import { checkUpcomingTitles } from "./controllers/internal/upcoming-titles";
|
|
||||||
|
|
||||||
export const app = new OpenAPIHono<{ Bindings: Env }>();
|
export const app = new OpenAPIHono<{ Bindings: Env }>();
|
||||||
|
|
||||||
app.use(maybeUpdateLastConnectedAt);
|
app.use(maybeUpdateLastConnectedAt);
|
||||||
@@ -123,20 +121,9 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
async scheduled(event, env, ctx) {
|
async scheduled(event, env, ctx) {
|
||||||
switch (event.cron) {
|
const { processDelayedTasks } =
|
||||||
case "0 */12 * * *":
|
await import("~/libs/tasks/processDelayedTasks");
|
||||||
const { processDelayedTasks } =
|
await processDelayedTasks(env);
|
||||||
await import("~/libs/tasks/processDelayedTasks");
|
|
||||||
await processDelayedTasks(env);
|
|
||||||
break;
|
|
||||||
case "0 18 * * *":
|
|
||||||
const { checkUpcomingTitles } =
|
|
||||||
await import("~/controllers/internal/upcoming-titles");
|
|
||||||
await checkUpcomingTitles();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(`Unhandled cron: ${event.cron}`);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
} satisfies ExportedHandler<Env>;
|
} satisfies ExportedHandler<Env>;
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export class AnilistDurableObject extends DurableObject {
|
|||||||
|
|
||||||
async getTitle(
|
async getTitle(
|
||||||
id: number,
|
id: number,
|
||||||
userId?: number,
|
userId?: string,
|
||||||
token?: string,
|
token?: string,
|
||||||
): Promise<Title | null> {
|
): Promise<Title | null> {
|
||||||
const promises: Promise<any>[] = [
|
const promises: Promise<any>[] = [
|
||||||
@@ -135,7 +135,8 @@ export class AnilistDurableObject extends DurableObject {
|
|||||||
nextSeason,
|
nextSeason,
|
||||||
nextYear,
|
nextYear,
|
||||||
limit,
|
limit,
|
||||||
});
|
page,
|
||||||
|
}).then((data) => data?.Page);
|
||||||
},
|
},
|
||||||
DateTime.now().plus({ days: 1 }),
|
DateTime.now().plus({ days: 1 }),
|
||||||
);
|
);
|
||||||
@@ -154,8 +155,7 @@ export class AnilistDurableObject extends DurableObject {
|
|||||||
nextSeason,
|
nextSeason,
|
||||||
nextYear,
|
nextYear,
|
||||||
limit,
|
limit,
|
||||||
page,
|
});
|
||||||
}).then((data) => data?.Page);
|
|
||||||
},
|
},
|
||||||
DateTime.now().plus({ days: 1 }),
|
DateTime.now().plus({ days: 1 }),
|
||||||
);
|
);
|
||||||
@@ -276,11 +276,13 @@ export class AnilistDurableObject extends DurableObject {
|
|||||||
|
|
||||||
const result = await fetcher();
|
const result = await fetcher();
|
||||||
await this.state.storage.put(key, result);
|
await this.state.storage.put(key, result);
|
||||||
|
console.debug(`Retrieved alarms from cache:`, Object.entries(alarms));
|
||||||
|
|
||||||
const calculatedTtl = typeof ttl === "function" ? ttl(result) : ttl;
|
const calculatedTtl = typeof ttl === "function" ? ttl(result) : ttl;
|
||||||
if (calculatedTtl) {
|
if (calculatedTtl) {
|
||||||
const alarmTime = calculatedTtl.toMillis();
|
const alarmTime = calculatedTtl.toMillis();
|
||||||
await this.state.storage.setAlarm(alarmTime);
|
await this.state.storage.setAlarm(alarmTime);
|
||||||
|
console.debug(`Deleting storage key ${storageKey} & alarm ${key}`);
|
||||||
await this.state.storage.put(`alarm:${key}`, alarmTime);
|
await this.state.storage.put(`alarm:${key}`, alarmTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,13 +292,11 @@ export class AnilistDurableObject extends DurableObject {
|
|||||||
async alarm() {
|
async alarm() {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const alarms = await this.state.storage.list({ prefix: "alarm:" });
|
const alarms = await this.state.storage.list({ prefix: "alarm:" });
|
||||||
console.debug(`Retrieved alarms from cache:`, Object.entries(alarms));
|
|
||||||
for (const [key, ttl] of Object.entries(alarms)) {
|
for (const [key, ttl] of Object.entries(alarms)) {
|
||||||
if (now >= ttl) {
|
if (now >= ttl) {
|
||||||
// The key in alarms is `alarm:${storageKey}`
|
// The key in alarms is `alarm:${storageKey}`
|
||||||
// We want to delete the storageKey
|
// We want to delete the storageKey
|
||||||
const storageKey = key.replace("alarm:", "");
|
const storageKey = key.replace("alarm:", "");
|
||||||
console.debug(`Deleting storage key ${storageKey} & alarm ${key}`);
|
|
||||||
await this.state.storage.delete(storageKey);
|
await this.state.storage.delete(storageKey);
|
||||||
await this.state.storage.delete(key);
|
await this.state.storage.delete(key);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,8 +259,8 @@ export const NextSeasonPopularQuery = graphql(
|
|||||||
$nextSeason: MediaSeason
|
$nextSeason: MediaSeason
|
||||||
$nextYear: Int
|
$nextYear: Int
|
||||||
$limit: Int!
|
$limit: Int!
|
||||||
$page: Int!
|
|
||||||
) {
|
) {
|
||||||
|
$page: Int!
|
||||||
Page(page: $page, perPage: $limit) {
|
Page(page: $page, perPage: $limit) {
|
||||||
media(
|
media(
|
||||||
season: $nextSeason
|
season: $nextSeason
|
||||||
|
|||||||
@@ -3,13 +3,11 @@ import mapKeys from "lodash.mapkeys";
|
|||||||
|
|
||||||
import { Case, changeStringCase } from "../changeStringCase";
|
import { Case, changeStringCase } from "../changeStringCase";
|
||||||
|
|
||||||
export function getAdminSdkCredentials(
|
export function getAdminSdkCredentials(env: Cloudflare.Env = cloudflareEnv) {
|
||||||
env: Cloudflare.Env = cloudflareEnv,
|
|
||||||
): AdminSdkCredentials {
|
|
||||||
return mapKeys(
|
return mapKeys(
|
||||||
JSON.parse(env.ADMIN_SDK_JSON) as AdminSdkCredentials,
|
JSON.parse(env.ADMIN_SDK_JSON) as AdminSdkCredentials,
|
||||||
(_, key) => changeStringCase(key, Case.snake_case, Case.camelCase),
|
(_, key) => changeStringCase(key, Case.snake_case, Case.camelCase),
|
||||||
) satisfies AdminSdkCredentials;
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdminSdkCredentials {
|
export interface AdminSdkCredentials {
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ id = "c8db249d8ee7462b91f9c374321776e4"
|
|||||||
preview_id = "ff38240eb2aa4b1388c705f4974f5aec"
|
preview_id = "ff38240eb2aa4b1388c705f4974f5aec"
|
||||||
|
|
||||||
[triggers]
|
[triggers]
|
||||||
crons = ["0 */12 * * *", "0 18 * * *"]
|
crons = ["0 */12 * * *"]
|
||||||
|
|
||||||
[[d1_databases]]
|
[[d1_databases]]
|
||||||
binding = "DB"
|
binding = "DB"
|
||||||
|
|||||||
Reference in New Issue
Block a user