48 lines
1.4 KiB
TypeScript
48 lines
1.4 KiB
TypeScript
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
|
// External usage delivery runs as an action, never from a Convex mutation. The stable SDK
|
|
// idempotency key makes scheduler retries safe if the ingest accepted a request but its response
|
|
// was lost.
|
|
|
|
"use node"
|
|
|
|
import { UsageClient } from "@conusai-sdk/usage"
|
|
import { makeFunctionReference } from "convex/server"
|
|
import { v } from "convex/values"
|
|
import { internalAction } from "./_generated/server"
|
|
|
|
const requiredEnv = (name: string): string => {
|
|
const value = process.env[name]?.trim()
|
|
if (!value) throw new Error(`Convex deployment is missing ${name}`)
|
|
return value
|
|
}
|
|
|
|
export const deliver = internalAction({
|
|
args: {
|
|
attempt: v.number(),
|
|
event: v.any(),
|
|
orgSlug: v.string(),
|
|
},
|
|
handler: async (ctx, { attempt, event, orgSlug }) => {
|
|
const usage = new UsageClient({
|
|
baseUrl: requiredEnv("CONUSAI_API_URL"),
|
|
apiPrefix: "",
|
|
apiKey: requiredEnv("CONUSAI_API_TOKEN"),
|
|
orgSlug,
|
|
maxRetries: 0,
|
|
flushIntervalMs: 0,
|
|
})
|
|
usage.track(event)
|
|
try {
|
|
await usage.flush()
|
|
} catch (error) {
|
|
if (attempt >= 7) throw error
|
|
const delayMs = Math.min(300_000, 1_000 * 2 ** attempt)
|
|
await ctx.scheduler.runAfter(delayMs, makeFunctionReference<"action">("usage:deliver"), {
|
|
attempt: attempt + 1,
|
|
event,
|
|
orgSlug,
|
|
})
|
|
}
|
|
},
|
|
})
|