diff --git a/infra/console.ts b/infra/console.ts index ab6502a8f8..c247dfcdb2 100644 --- a/infra/console.ts +++ b/infra/console.ts @@ -293,3 +293,12 @@ new sst.cloudflare.x.SolidStart("Console", { }, }, }) + +//////////////// +// HELPERS +//////////////// + +export const stat = new sst.cloudflare.Worker("Stat", { + handler: "packages/console/function/src/stat.ts", + link: [database], +}) diff --git a/packages/console/core/sst-env.d.ts b/packages/console/core/sst-env.d.ts index 9680a53aab..088db5be2c 100644 --- a/packages/console/core/sst-env.d.ts +++ b/packages/console/core/sst-env.d.ts @@ -298,6 +298,7 @@ declare module "sst" { "EnterpriseStorage": cloudflare.R2Bucket "GatewayKv": cloudflare.KVNamespace "LogProcessor": cloudflare.Service + "Stat": cloudflare.Service "ZenData": cloudflare.R2Bucket "ZenDataNew": cloudflare.R2Bucket } diff --git a/packages/console/function/src/stat.ts b/packages/console/function/src/stat.ts new file mode 100644 index 0000000000..078aabb519 --- /dev/null +++ b/packages/console/function/src/stat.ts @@ -0,0 +1,39 @@ +import { WorkerEntrypoint } from "cloudflare:workers" +import { and, Database, inArray } from "@opencode-ai/console-core/drizzle/index.js" +import { ModelTpsRateLimitTable } from "@opencode-ai/console-core/schema/ip.sql.js" + +type Result = Record + +export default class Stat extends WorkerEntrypoint { + async fetch() { + return new Response("Not Found", { status: 404 }) + } + + async getStats(ids: string[]): Promise { + if (ids.length === 0) return {} + + const toInterval = (date: Date) => + parseInt( + date + .toISOString() + .replace(/[^0-9]/g, "") + .substring(0, 12), + ) + const now = Date.now() + const intervals = Array.from({ length: 5 }, (_, i) => toInterval(new Date(now - i * 60 * 1000))) + + const rows = await Database.use((tx) => + tx + .select() + .from(ModelTpsRateLimitTable) + .where(and(inArray(ModelTpsRateLimitTable.id, ids), inArray(ModelTpsRateLimitTable.interval, intervals))), + ) + + const result: Result = Object.fromEntries(ids.map((id) => [id, { qualify: 0, unqualify: 0 }])) + for (const row of rows) { + result[row.id].qualify += row.qualify + result[row.id].unqualify += row.unqualify + } + return result + } +} diff --git a/packages/console/function/sst-env.d.ts b/packages/console/function/sst-env.d.ts index 9680a53aab..088db5be2c 100644 --- a/packages/console/function/sst-env.d.ts +++ b/packages/console/function/sst-env.d.ts @@ -298,6 +298,7 @@ declare module "sst" { "EnterpriseStorage": cloudflare.R2Bucket "GatewayKv": cloudflare.KVNamespace "LogProcessor": cloudflare.Service + "Stat": cloudflare.Service "ZenData": cloudflare.R2Bucket "ZenDataNew": cloudflare.R2Bucket } diff --git a/packages/console/resource/sst-env.d.ts b/packages/console/resource/sst-env.d.ts index 9680a53aab..088db5be2c 100644 --- a/packages/console/resource/sst-env.d.ts +++ b/packages/console/resource/sst-env.d.ts @@ -298,6 +298,7 @@ declare module "sst" { "EnterpriseStorage": cloudflare.R2Bucket "GatewayKv": cloudflare.KVNamespace "LogProcessor": cloudflare.Service + "Stat": cloudflare.Service "ZenData": cloudflare.R2Bucket "ZenDataNew": cloudflare.R2Bucket } diff --git a/packages/enterprise/sst-env.d.ts b/packages/enterprise/sst-env.d.ts index 9680a53aab..088db5be2c 100644 --- a/packages/enterprise/sst-env.d.ts +++ b/packages/enterprise/sst-env.d.ts @@ -298,6 +298,7 @@ declare module "sst" { "EnterpriseStorage": cloudflare.R2Bucket "GatewayKv": cloudflare.KVNamespace "LogProcessor": cloudflare.Service + "Stat": cloudflare.Service "ZenData": cloudflare.R2Bucket "ZenDataNew": cloudflare.R2Bucket } diff --git a/packages/function/sst-env.d.ts b/packages/function/sst-env.d.ts index 9680a53aab..088db5be2c 100644 --- a/packages/function/sst-env.d.ts +++ b/packages/function/sst-env.d.ts @@ -298,6 +298,7 @@ declare module "sst" { "EnterpriseStorage": cloudflare.R2Bucket "GatewayKv": cloudflare.KVNamespace "LogProcessor": cloudflare.Service + "Stat": cloudflare.Service "ZenData": cloudflare.R2Bucket "ZenDataNew": cloudflare.R2Bucket } diff --git a/sst-env.d.ts b/sst-env.d.ts index e75c54d056..a55dd4bb49 100644 --- a/sst-env.d.ts +++ b/sst-env.d.ts @@ -153,6 +153,9 @@ declare module "sst" { "type": "sst.sst.Linkable" "value": string } + "Stat": { + "type": "sst.cloudflare.Worker" + } "Teams": { "type": "sst.cloudflare.SolidStart" "url": string diff --git a/sst.config.ts b/sst.config.ts index 696a6fa768..2feb3ac248 100644 --- a/sst.config.ts +++ b/sst.config.ts @@ -19,10 +19,14 @@ export default $config({ }, async run() { await import("./infra/app.js") - await import("./infra/console.js") + const { stat } = await import("./infra/console.js") await import("./infra/enterprise.js") if ($app.stage === "production" || $app.stage === "vimtor") { await import("./infra/monitoring.js") } + + return { + STAT_WORKER_NAME: stat.nodes.worker.scriptName, + } }, })