import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi"; import { env } from "hono/adapter"; import mapKeys from "lodash.mapkeys"; import { Case, changeStringCase } from "~/libs/changeStringCase"; import type { AdminSdkCredentials } from "~/libs/fcm/getGoogleAuthToken"; import { verifyFcmToken } from "~/libs/fcm/verifyFcmToken"; import { readEnvVariable } from "~/libs/readEnvVariable"; import { saveToken } from "~/models/token"; import type { Env } from "~/types/env"; import { ErrorResponse, ErrorResponseSchema, SuccessResponse, SuccessResponseSchema, } from "~/types/schema"; const app = new OpenAPIHono(); const SaveTokenRequest = z.object({ token: z.string(), deviceId: z.string(), }); const SaveTokenResponse = SuccessResponseSchema(); const route = createRoute({ tags: ["aniplay", "notifications"], operationId: "saveToken", summary: "Saves FCM token", method: "post", path: "/", request: { body: { content: { "application/json": { schema: SaveTokenRequest, }, }, }, }, responses: { 200: { content: { "application/json": { schema: SaveTokenResponse, }, }, description: "Saved token successfully", }, 412: { content: { "application/json": { schema: ErrorResponseSchema, }, }, description: "Token already exists", }, 500: { content: { "application/json": { schema: ErrorResponseSchema, }, }, description: "Unknown error occurred", }, }, }); app.openapi(route, async (c) => { const { token, deviceId } = await c.req.json(); try { const isValidToken = await verifyFcmToken( token, mapKeys( readEnvVariable(c.env, "ADMIN_SDK_JSON"), (_, key) => changeStringCase(key, Case.snake_case, Case.camelCase), ) as unknown as AdminSdkCredentials, ); if (!isValidToken) { return c.json(ErrorResponse, 401); } await saveToken(env(c, "workerd"), deviceId, token); } catch (error) { console.error(new Error("Failed to save token", { cause: error })); return c.json(ErrorResponse, 500); } return c.json(SuccessResponse); }); export default app;