From 278c1043f545d1431c9f2f716a708b06f66c58eb Mon Sep 17 00:00:00 2001 From: Wanjohi Date: Sat, 27 Sep 2025 10:25:13 +0300 Subject: [PATCH] feat: Move API to CF workers (WIP) --- cloud/infra/api.ts | 12 ++++++ cloud/infra/urls.ts | 10 +++++ cloud/packages/core/src/steam/index.ts | 1 - cloud/packages/functions/src/api/index.ts | 1 + .../packages/functions/src/api/utils/auth.ts | 40 ++++++++++++------- cloud/packages/functions/src/auth/index.ts | 4 +- cloud/packages/functions/sst-env.d.ts | 7 ++++ sst-env.d.ts | 7 ++++ 8 files changed, 66 insertions(+), 16 deletions(-) create mode 100644 cloud/infra/api.ts create mode 100644 cloud/infra/urls.ts diff --git a/cloud/infra/api.ts b/cloud/infra/api.ts new file mode 100644 index 00000000..4a4f75f7 --- /dev/null +++ b/cloud/infra/api.ts @@ -0,0 +1,12 @@ +import { urls } from "./urls"; +import { auth } from "./auth"; +import { domain } from "./stage"; +import { secret } from "./secrets"; +import { database } from "./database"; + +export const api = new sst.cloudflare.Worker("Api", { + url: true, + domain: `api.${domain}`, + handler: "cloud/packages/functions/src/api/index.ts", + link: [database, secret.POLAR_API_KEY, urls, auth], +}); diff --git a/cloud/infra/urls.ts b/cloud/infra/urls.ts new file mode 100644 index 00000000..8526c424 --- /dev/null +++ b/cloud/infra/urls.ts @@ -0,0 +1,10 @@ +import { domain } from "./stage"; + +export const urls = new sst.Linkable("Urls", { + properties: { + api: "https://api." + domain, + auth: "https://auth." + domain, + site: $dev ? "http://localhost:4321" : "https://" + domain, + openapi: "https://api." + domain + "/doc", + }, +}); diff --git a/cloud/packages/core/src/steam/index.ts b/cloud/packages/core/src/steam/index.ts index 5d82f2b5..2edf58e9 100644 --- a/cloud/packages/core/src/steam/index.ts +++ b/cloud/packages/core/src/steam/index.ts @@ -2,7 +2,6 @@ import { z } from "zod"; import { fn } from "../utils"; import { Resource } from "sst"; import { Actor } from "../actor"; -import { bus } from "sst/aws/bus"; import { Common } from "../common"; import { Database } from "../drizzle"; import { Examples } from "../examples"; diff --git a/cloud/packages/functions/src/api/index.ts b/cloud/packages/functions/src/api/index.ts index 8d9a1d19..e5270f93 100644 --- a/cloud/packages/functions/src/api/index.ts +++ b/cloud/packages/functions/src/api/index.ts @@ -9,6 +9,7 @@ import { AccountApi } from "./account"; import { openAPISpecs } from "hono-openapi"; import { patchLogger } from "../utils/patch-logger"; import { HTTPException } from "hono/http-exception"; +import type { Service } from "@cloudflare/workers-types"; import { ErrorCodes, VisibleError } from "@nestri/core/error"; patchLogger(); diff --git a/cloud/packages/functions/src/api/utils/auth.ts b/cloud/packages/functions/src/api/utils/auth.ts index ff388817..ef8f4aa1 100644 --- a/cloud/packages/functions/src/api/utils/auth.ts +++ b/cloud/packages/functions/src/api/utils/auth.ts @@ -2,14 +2,18 @@ import { Resource } from "sst"; import { subjects } from "../../subjects"; import { Actor } from "@nestri/core/actor"; import { type MiddlewareHandler } from "hono"; +import { memo } from "@nestri/core/utils/memo"; import { Steam } from "@nestri/core/steam/index"; import { createClient } from "@openauthjs/openauth/client"; import { ErrorCodes, VisibleError } from "@nestri/core/error"; -const client = createClient({ - clientID: "api", - issuer: Resource.Auth.url, -}); +const client = memo(() => + createClient({ + clientID: "api", + fetch: (input, init) => Resource.Auth.fetch(input, init), + issuer: Resource.Urls.auth, + }), +); export const notPublic: MiddlewareHandler = async (c, next) => { const actor = Actor.use(); @@ -34,7 +38,8 @@ export const auth: MiddlewareHandler = async (c, next) => { ); } const bearerToken = match[1]; - let result = await client.verify(subjects, bearerToken!); + //@ts-expect-error + let result = await client().verify(subjects, bearerToken!); if (result.err) { throw new VisibleError( "authentication", @@ -46,22 +51,27 @@ export const auth: MiddlewareHandler = async (c, next) => { if (result.subject.type === "user") { const steamID = c.req.header("x-nestri-steam"); if (!steamID) { - return Actor.provide(result.subject.type, result.subject.properties, next); + return Actor.provide( + result.subject.type, + // @ts-expect-error + result.subject.properties, + next, + ); } - const userID = result.subject.properties.userID + const userID = result.subject.properties.userID; return Actor.provide( "steam", { - steamID + steamID, }, async () => { - const steamAcc = await Steam.confirmOwnerShip(userID) + const steamAcc = await Steam.confirmOwnerShip(userID); if (!steamAcc) { throw new VisibleError( "authentication", ErrorCodes.Authentication.UNAUTHORIZED, - `You don't have permission to access this resource.` - ) + `You don't have permission to access this resource.`, + ); } return Actor.provide( "member", @@ -69,9 +79,11 @@ export const auth: MiddlewareHandler = async (c, next) => { steamID, userID, }, - next) - }); + next, + ); + }, + ); } return Actor.provide("public", {}, next); -}; \ No newline at end of file +}; diff --git a/cloud/packages/functions/src/auth/index.ts b/cloud/packages/functions/src/auth/index.ts index 1f902881..d3754250 100644 --- a/cloud/packages/functions/src/auth/index.ts +++ b/cloud/packages/functions/src/auth/index.ts @@ -95,6 +95,8 @@ export default { throw new Error("Something went seriously wrong"); }, - }).use(logger()); + }) + .use(logger()) + .fetch(request, env, ctx); }, }; diff --git a/cloud/packages/functions/sst-env.d.ts b/cloud/packages/functions/sst-env.d.ts index 5f7aa835..a544f67e 100644 --- a/cloud/packages/functions/sst-env.d.ts +++ b/cloud/packages/functions/sst-env.d.ts @@ -25,6 +25,13 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "Urls": { + "api": string + "auth": string + "openapi": string + "site": string + "type": "sst.sst.Linkable" + } } } // cloudflare diff --git a/sst-env.d.ts b/sst-env.d.ts index 5f7aa835..a544f67e 100644 --- a/sst-env.d.ts +++ b/sst-env.d.ts @@ -25,6 +25,13 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "Urls": { + "api": string + "auth": string + "openapi": string + "site": string + "type": "sst.sst.Linkable" + } } } // cloudflare