import { type Context, Hono } from 'hono' import * as z from 'zod/mini' import * as Analytics from '../../analytics/Analytics.js' import * as ApiKeys from '../../ApiKeys.js' import * as core_Billing from '../../apps/management/Billing.js' import * as BillingSettings from '../../db/tables/billingSettings.js' import * as Db from '../../db/Db.js' import * as EnabledBillingSources from '../../db/tables/enabledBillingSources.js' import * as Memberships from '../../db/tables/memberships.js' import * as Organizations from '../../db/tables/organizations.js' import * as Projects from '../../db/tables/projects.js' import * as SponsoredTransactions from '../../db/tables/sponsoredTransactions.js' import * as StripeCustomers from '../../db/tables/stripeCustomers.js' import * as WebhookDeliveries from '../../db/tables/webhookDeliveries.js' import * as WebhookSubscriptions from '../../db/tables/webhookSubscriptions.js' import * as Cursor from '../../internal/Cursor.js' import * as OpenApi from '../../internal/OpenApi.js' import * as Response from '../../internal/Response.js' import * as Schema from '../../internal/Schema.js' import * as Viem from '../../internal/Viem.js' import type * as App from '../App.js' /** Zod schemas owned by the admin organizations handler. */ export namespace schema { const timestamp = z .string() .check(z.describe('An ISO 8601 timestamp.'), z.meta({ examples: ['2026-01-01T00:00:00.000Z'] })) const enabledBillingSources = z .array(z.enum(EnabledBillingSources.sources)) .check( z.describe('Billing-source types available for new setup.'), z.meta({ examples: [['stripe', 'tempo']] }), ) const organizationId = z .string() .check( z.minLength(1), z.describe('Stable organization id.'), z.meta({ examples: ['org_1a2b3c4d5e6f7g8h9j0k1m2n'] }), ) const Organization = Schema.describe( z.object({ createdAt: timestamp, createdBy: z .nullable(z.string()) .check( z.describe('Identity that created the organization, or null.'), z.meta({ examples: ['admin@tempo.xyz'] }), ), id: z .string() .check( z.describe('Stable organization id.'), z.meta({ examples: ['org_1a2b3c4d5e6f7g8h9j0k1m2n'] }), ), name: z .string() .check(z.describe('Human-readable organization name.'), z.meta({ examples: ['Acme'] })), updatedAt: timestamp, userId: z .nullable(z.string()) .check( z.describe('Owning user id, or null for a super-admin-created organization.'), z.meta({ examples: ['usr_1a2b3c4d5e6f7g8h9j0k1m2n'] }), ), }), 'An organization visible to an authenticated administrator.', ) const SponsorshipSubsidy = Schema.describe( z.object({ durationDays: z .nullable(z.literal(90)) .check(z.describe('Promotion duration in days, or null when disabled.'), z.meta({ examples: [90] })), // prettier-ignore projectSpendLimit: z .nullable(z.string()) .check(z.describe('Optional per-project promotion cap in USD.'), z.meta({ examples: ['1000'] })), // prettier-ignore }), 'Current per-project sponsorship subsidy policy.', ) /** Schemas for the listOrganizations operation. */ export namespace listOrganizations { /** Query parameters for organization lookup. */ export const Query = z .strictObject({ cursor: Schema.Cursor, limit: Schema.Limit, query: z .optional( z .pipe( z.string(), z.transform((value) => value.trim()), ) .check(z.minLength(3), z.maxLength(200)), ) .check( z.describe('Exact organization or user id, or a name or member-email prefix.'), z.meta({ examples: ['acme@example.com'] }), ), }) .check(z.describe('Query parameters for listing organizations.')) /** Organization summary shown in lookup results. */ export const OrganizationSummary = Schema.describe( z.extend(Organization, { memberCount: z .number() .check( z.int(), z.nonnegative(), z.describe('Number of organization members.'), z.meta({ examples: [3] }), ), projectCount: z .number() .check( z.int(), z.nonnegative(), z.describe('Number of organization projects.'), z.meta({ examples: [2] }), ), }), 'An organization lookup result.', ) /** Cursor-paginated organization lookup results. */ export const Response = Schema.describe( z.object({ data: z .array(OrganizationSummary) .check(z.describe('Organizations on this page, newest first.')), nextCursor: Schema.NextCursor, }), 'A page of organization lookup results.', ) } /** Schemas for the getOrganization operation. */ export namespace getOrganization { /** Path parameters identifying an organization. */ export const Params = z .object({ orgId: organizationId, }) .check(z.describe('Path parameters for one organization.')) const BillingSettingsRecord = Schema.describe( z.object({ currency: z .enum(BillingSettings.currencies) .check(z.describe('Limit currency.'), z.meta({ examples: ['usd'] })), period: z .enum(BillingSettings.periods) .check(z.describe('Spend-limit period.'), z.meta({ examples: ['month'] })), spendLimit: z .nullable(z.string()) .check( z.describe('Configured period spend limit, or null.'), z.meta({ examples: ['250'] }), ), txFeeLimit: z .nullable(z.string()) .check( z.describe('Configured per-transaction fee limit, or null.'), z.meta({ examples: ['0.50'] }), ), updatedAt: timestamp, }), 'Stored billing limits for one environment.', ) const BillingSource = Schema.describe( z.object({ status: z .enum(StripeCustomers.statuses) .check( z.describe('Billing status derived from Stripe.'), z.meta({ examples: ['active'] }), ), stripeCustomerId: z .string() .check(z.describe('Backing Stripe customer id.'), z.meta({ examples: ['cus_abc123'] })), updatedAt: timestamp, }), 'Stored Stripe billing source for one environment.', ) const Billing = Schema.describe( z.object({ settings: z.nullable(BillingSettingsRecord).check( z.describe('Configured billing limits, or null when absent.'), z.meta({ examples: [ { currency: 'usd', period: 'month', spendLimit: '250', txFeeLimit: '0.50', updatedAt: '2026-01-01T00:00:00.000Z', }, ], }), ), source: z.nullable(BillingSource).check( z.describe('Connected billing source, or null when absent.'), z.meta({ examples: [ { status: 'active', stripeCustomerId: 'cus_abc123', updatedAt: '2026-01-01T00:00:00.000Z', }, ], }), ), }), 'Billing state for one environment.', ) /** An organization member with resolved identity. */ export const Member = Schema.describe( z.object({ address: z .nullable(z.string()) .check( z.describe('Member wallet address, or null.'), z.meta({ examples: [`0x${'ab'.repeat(20)}`] }), ), createdAt: timestamp, email: z .nullable(z.string()) .check( z.describe('Verified member email, or null.'), z.meta({ examples: ['owner@acme.example'] }), ), role: z .enum(Memberships.roles) .check(z.describe('Member role.'), z.meta({ examples: ['owner'] })), updatedAt: timestamp, userId: z .string() .check( z.describe('Member user id.'), z.meta({ examples: ['usr_1a2b3c4d5e6f7g8h9j0k1m2n'] }), ), }), 'An organization member and their resolved identity.', ) /** A project owned by an organization. */ export const Project = Schema.describe( z.object({ createdAt: timestamp, id: z .string() .check( z.describe('Stable project id.'), z.meta({ examples: ['prj_1a2b3c4d5e6f7g8h9j0k1m2n'] }), ), name: z .string() .check(z.describe('Human-readable project name.'), z.meta({ examples: ['Checkout'] })), updatedAt: timestamp, }), 'A project owned by the organization.', ) /** Detailed organization lookup response. */ export const Response = Schema.describe( z.extend(listOrganizations.OrganizationSummary, { billing: z .object({ enabledSources: enabledBillingSources, production: Billing, sandbox: Billing, }) .check( z.describe('Organization-wide availability and environment billing state.'), z.meta({ examples: [ { enabledSources: ['stripe', 'tempo'], production: { settings: null, source: { status: 'active', stripeCustomerId: 'cus_abc123', updatedAt: '2026-01-01T00:00:00.000Z', }, }, sandbox: { settings: null, source: null }, }, ], }), ), sponsorshipSubsidy: SponsorshipSubsidy, }), 'Detailed organization information for administrators.', ) } /** Schemas for the setBillingSource operation. */ export namespace setBillingSource { /** Path parameters identifying an organization billing source. */ export const Params = z .object({ orgId: organizationId, source: z .enum(EnabledBillingSources.sources) .check( z.describe('Billing-source type to enable or disable.'), z.meta({ examples: ['stripe'] }), ), }) .check(z.describe('Path parameters for one organization billing source.')) /** Current enabled billing sources. */ export const Response = Schema.describe( z.object({ data: enabledBillingSources }), 'Current enabled billing sources for the organization.', ) } /** Schemas for the listOrganizationMembers operation. */ export namespace listOrganizationMembers { /** Query parameters for paginating organization members. */ export const Query = z .strictObject({ cursor: Schema.Cursor, limit: Schema.Limit }) .check(z.describe('Query parameters for listing organization members.')) /** Cursor-paginated organization members. */ export const Response = Schema.describe( z.object({ data: z .array(getOrganization.Member) .check(z.describe('Organization members, oldest first.')), nextCursor: Schema.NextCursor, }), 'A page of organization members.', ) } /** Schemas for the listOrganizationProjects operation. */ export namespace listOrganizationProjects { /** Query parameters for paginating organization projects. */ export const Query = z .strictObject({ cursor: Schema.Cursor, limit: Schema.Limit }) .check(z.describe('Query parameters for listing organization projects.')) /** Cursor-paginated organization projects. */ export const Response = Schema.describe( z.object({ data: z .array(getOrganization.Project) .check(z.describe('Organization projects, newest first.')), nextCursor: Schema.NextCursor, }), 'A page of organization projects.', ) } /** Schemas for the listOrganizationApiKeys operation. */ export namespace listOrganizationApiKeys { /** Active organization API keys. */ export const Response = Schema.describe( z.object({ data: z.array(ApiKeys.schema.Record), }), 'Active API keys owned by an organization.', ) } /** Schemas for the getOrganizationUsage operation. */ export namespace getOrganizationUsage { /** Recent-usage window. */ export const Query = z .object({ period: z ._default(z.enum(['24h', '7d', '30d']), '7d') .check(z.describe('Recent usage window.'), z.meta({ examples: ['7d'] })), }) .check(z.describe('Query parameters for organization usage.')) const Breakdown = z.object({ errors: z .number() .check(z.describe('Requests returning an error.'), z.meta({ examples: [4] })), requests: z.number().check(z.describe('Total requests.'), z.meta({ examples: [120] })), }) /** Recent API usage and error breakdowns. */ export const Response = Schema.describe( z.object({ byError: z.array( z.object({ code: z .string() .check(z.describe('Stable API error code.'), z.meta({ examples: ['rate_limited'] })), requests: z .number() .check(z.describe('Requests returning this error.'), z.meta({ examples: [4] })), }), ), byRoute: z.array( z.extend(Breakdown, { averageDurationMs: z .number() .check( z.describe('Average request duration in milliseconds.'), z.meta({ examples: [18.4] }), ), route: z .string() .check(z.describe('Matched API route.'), z.meta({ examples: ['/v1/transactions'] })), }), ), byStatus: z.array( z.object({ requests: z .number() .check(z.describe('Requests returning this status.'), z.meta({ examples: [4] })), status: z .number() .check(z.int(), z.describe('HTTP response status.'), z.meta({ examples: [429] })), }), ), from: timestamp, period: z .enum(['24h', '7d', '30d']) .check(z.describe('Usage window.'), z.meta({ examples: ['7d'] })), to: timestamp, totals: z.extend(Breakdown, { averageDurationMs: z .number() .check( z.describe('Average request duration in milliseconds.'), z.meta({ examples: [18.4] }), ), }), }), 'Recent API usage for an organization.', ) } /** Schemas for organization sponsorship operations. */ export namespace organizationSponsorship { const Environment = z.object({ committedSpend: z .string() .check( z.describe('Committed spend in decimal currency units.'), z.meta({ examples: ['1.20'] }), ), currency: z .enum(BillingSettings.currencies) .check(z.describe('Spend currency.'), z.meta({ examples: ['usd'] })), from: timestamp, period: z .enum(BillingSettings.periods) .check(z.describe('Spend-limit period.'), z.meta({ examples: ['month'] })), }) /** Current-period sponsorship spend by key environment. */ export const GetResponse = Schema.describe( z.object({ production: Environment, sandbox: Environment, to: timestamp, }), 'Current-period sponsorship spend for an organization.', ) /** Replacement subsidy policy. */ export const SetBody = z .union([ z.object({ enabled: z.literal(true), projectSpendLimit: z.optional( z .string() .check( z.regex(/^(?:0|[1-9]\d*)(?:\.\d{1,6})?$/), z.describe('Optional per-project promotion cap in USD.'), z.meta({ examples: ['1000'] }), ), ), }), z.object({ enabled: z.literal(false) }), ]) .check(z.describe('Replacement per-project sponsorship subsidy policy.')) /** Current subsidy policy. */ export const SetResponse = SponsorshipSubsidy } /** Schemas for the getOrganizationWebhooks operation. */ export namespace getOrganizationWebhooks { const Failure = z.object({ attempt: z .number() .check(z.int(), z.describe('Delivery attempt number.'), z.meta({ examples: [3] })), createdAt: timestamp, error: z .nullable(z.string()) .check( z.describe('Failure detail, or null.'), z.meta({ examples: ['Connection timed out'] }), ), id: z.string().check(z.describe('Delivery id.'), z.meta({ examples: ['whd_123'] })), responseStatus: z .nullable(z.number()) .check(z.describe('HTTP response status, or null.'), z.meta({ examples: [500] })), subscriptionId: z .string() .check(z.describe('Owning subscription id.'), z.meta({ examples: ['wh_123'] })), }) const Subscription = z.object({ chainId: z .number() .check(z.int(), z.describe('Subscribed chain id.'), z.meta({ examples: [42431] })), createdAt: timestamp, destination: z .string() .check( z.describe('Redacted delivery destination.'), z.meta({ examples: ['https://hooks.example.com/…'] }), ), eventType: z .string() .check(z.describe('Subscribed event type.'), z.meta({ examples: ['transfer'] })), failureCount: z .number() .check( z.int(), z.nonnegative(), z.describe('Consecutive delivery failures, capped at the auto-disable threshold.'), z.meta({ examples: [2] }), ), id: z.string().check(z.describe('Subscription id.'), z.meta({ examples: ['wh_123'] })), lastDeliveryAt: z .nullable(timestamp) .check( z.describe('Last successful delivery (refreshed at most once per minute), or null.'), ), status: z .enum(['active', 'disabled', 'paused']) .check(z.describe('Subscription status.'), z.meta({ examples: ['active'] })), }) /** Webhook subscriptions and recent failures. */ export const Response = Schema.describe( z.object({ failureCount: z .number() .check( z.int(), z.nonnegative(), z.describe('Failed deliveries in the response window.'), z.meta({ examples: [2] }), ), failures: z.array(Failure), failuresTruncated: z .boolean() .check( z.describe('Whether additional failures are omitted.'), z.meta({ examples: [false] }), ), from: timestamp, subscriptions: z.array(Subscription), to: timestamp, }), 'Webhook health for an organization.', ) } } /** Organization lookup and support routes for administrators. */ export function organizations() { return new Hono() .get( '/', OpenApi.validate('query', schema.listOrganizations.Query, { code: 'query_invalid', message: 'Invalid query parameters', }), OpenApi.describeRoute({ operationId: 'listAdminOrganizations', responses: OpenApi.responses({ success: { description: 'Organizations matching the lookup query.', schema: schema.listOrganizations.Response, }, }), summary: 'List organizations', tags: ['Organizations'], }), async (c) => { const { cursor, limit, query } = c.req.valid('query') const decoded = cursor ? Cursor.decode(cursor, ['timestamp', 'id']) : undefined const db = Db.get(c.get('db')) try { const records = await Organizations.search(db, { ...(decoded ? { cursor: { createdAt: String(decoded[0]), id: String(decoded[1]) } } : {}), limit, ...(query ? { query } : {}), }) const page = Cursor.paginate({ key: (record) => [record.createdAt, record.id], limit, rows: records, }) return c.json( Response.validated(schema.listOrganizations.Response, { data: page.rows, nextCursor: page.nextCursor, }), 200, ) } catch (cause) { return Response.upstream(c, cause) } }, ) .get( '/:orgId', OpenApi.validate('param', schema.getOrganization.Params, { code: 'param_invalid', message: 'Invalid path parameters', }), OpenApi.describeRoute({ operationId: 'getAdminOrganization', responses: OpenApi.responses({ errors: { 404: 'Organization not found.' }, success: { description: 'Detailed organization information.', schema: schema.getOrganization.Response, }, }), summary: 'Get organization', tags: ['Organizations'], }), async (c) => { const { orgId } = c.req.valid('param') const db = Db.get(c.get('db')) try { const organization = await Organizations.getSummary(db, orgId) if (!organization) return organizationNotFound(c) const [ enabledSources, productionSettings, productionSource, sandboxSettings, sandboxSource, ] = await Promise.all([ EnabledBillingSources.listByOrg(db, orgId), BillingSettings.get(db, orgId, 'production'), StripeCustomers.get(db, orgId, 'production'), BillingSettings.get(db, orgId, 'sandbox'), StripeCustomers.get(db, orgId, 'sandbox'), ]) return c.json( Response.validated(schema.getOrganization.Response, { ...organization, billing: { enabledSources: enabledSources.map((record) => record.source), production: serializeBilling(productionSettings, productionSource), sandbox: serializeBilling(sandboxSettings, sandboxSource), }, sponsorshipSubsidy: { durationDays: organization.sponsorshipSubsidyDurationDays, projectSpendLimit: organization.sponsorshipSubsidyProjectSpendLimit, }, }), 200, ) } catch (cause) { return Response.upstream(c, cause) } }, ) .put( '/:orgId/sponsorship', OpenApi.validate('param', schema.getOrganization.Params, { code: 'param_invalid', message: 'Invalid path parameters', }), OpenApi.validate('json', schema.organizationSponsorship.SetBody, { code: 'body_invalid', message: 'Invalid request body', }), OpenApi.describeRoute({ operationId: 'setAdminOrganizationSponsorshipSubsidy', responses: OpenApi.responses({ errors: { 404: 'Organization not found.' }, success: { description: 'Current sponsorship subsidy policy.', schema: schema.organizationSponsorship.SetResponse, }, }), summary: 'Set sponsorship subsidy', tags: ['Organizations'], }), async (c) => { const { orgId } = c.req.valid('param') const policy = c.req.valid('json') try { const organization = await Organizations.setSponsorshipSubsidy( Db.get(c.get('db')), orgId, policy.enabled ? { durationDays: 90, projectSpendLimit: policy.projectSpendLimit ?? null } : { durationDays: null, projectSpendLimit: null }, ) if (!organization) return organizationNotFound(c) return c.json( Response.validated(schema.organizationSponsorship.SetResponse, { durationDays: organization.sponsorshipSubsidyDurationDays, projectSpendLimit: organization.sponsorshipSubsidyProjectSpendLimit, }), 200, ) } catch (cause) { return Response.upstream(c, cause) } }, ) .put( '/:orgId/billing-sources/:source', OpenApi.validate('param', schema.setBillingSource.Params, { code: 'param_invalid', message: 'Invalid path parameters', }), OpenApi.describeRoute({ operationId: 'enableAdminOrganizationBillingSource', responses: OpenApi.responses({ errors: { 404: 'Organization not found.' }, success: { description: 'Current enabled billing sources.', schema: schema.setBillingSource.Response, }, }), summary: 'Enable billing source', tags: ['Organizations'], }), async (c) => { const { orgId, source } = c.req.valid('param') const db = Db.get(c.get('db')) try { if (!(await Organizations.get(db, orgId))) return organizationNotFound(c) await EnabledBillingSources.add(db, { createdBy: c.get('identity')!.email, orgId, source, }) return c.json( Response.validated(schema.setBillingSource.Response, { data: (await EnabledBillingSources.listByOrg(db, orgId)).map( (record) => record.source, ), }), 200, ) } catch (cause) { return Response.upstream(c, cause) } }, ) .delete( '/:orgId/billing-sources/:source', OpenApi.validate('param', schema.setBillingSource.Params, { code: 'param_invalid', message: 'Invalid path parameters', }), OpenApi.describeRoute({ operationId: 'disableAdminOrganizationBillingSource', responses: OpenApi.responses({ errors: { 404: 'Organization not found.' }, success: { description: 'Current enabled billing sources.', schema: schema.setBillingSource.Response, }, }), summary: 'Disable billing source', tags: ['Organizations'], }), async (c) => { const { orgId, source } = c.req.valid('param') const db = Db.get(c.get('db')) try { if (!(await Organizations.get(db, orgId))) return organizationNotFound(c) await EnabledBillingSources.remove(db, { orgId, source }) return c.json( Response.validated(schema.setBillingSource.Response, { data: (await EnabledBillingSources.listByOrg(db, orgId)).map( (record) => record.source, ), }), 200, ) } catch (cause) { return Response.upstream(c, cause) } }, ) .get( '/:orgId/members', OpenApi.validate('param', schema.getOrganization.Params, { code: 'param_invalid', message: 'Invalid path parameters', }), OpenApi.validate('query', schema.listOrganizationMembers.Query, { code: 'query_invalid', message: 'Invalid query parameters', }), OpenApi.describeRoute({ operationId: 'listAdminOrganizationMembers', responses: OpenApi.responses({ errors: { 404: 'Organization not found.' }, success: { description: 'Organization members.', schema: schema.listOrganizationMembers.Response, }, }), summary: 'List organization members', tags: ['Organizations'], }), async (c) => { const { orgId } = c.req.valid('param') const { cursor, limit } = c.req.valid('query') const decoded = cursor ? Cursor.decode(cursor, ['timestamp', 'id']) : undefined const db = Db.get(c.get('db')) try { if (!(await Organizations.get(db, orgId))) return organizationNotFound(c) const records = await Memberships.listByOrgDetailedPage(db, orgId, { ...(decoded ? { cursor: { createdAt: String(decoded[0]), userId: String(decoded[1]) } } : {}), limit, }) const page = Cursor.paginate({ key: (record) => [record.createdAt, record.userId], limit, rows: records, }) return c.json( Response.validated(schema.listOrganizationMembers.Response, { data: page.rows.map(({ orgId: _orgId, ...member }) => member), nextCursor: page.nextCursor, }), 200, ) } catch (cause) { return Response.upstream(c, cause) } }, ) .get( '/:orgId/projects', OpenApi.validate('param', schema.getOrganization.Params, { code: 'param_invalid', message: 'Invalid path parameters', }), OpenApi.validate('query', schema.listOrganizationProjects.Query, { code: 'query_invalid', message: 'Invalid query parameters', }), OpenApi.describeRoute({ operationId: 'listAdminOrganizationProjects', responses: OpenApi.responses({ errors: { 404: 'Organization not found.' }, success: { description: 'Organization projects.', schema: schema.listOrganizationProjects.Response, }, }), summary: 'List organization projects', tags: ['Organizations'], }), async (c) => { const { orgId } = c.req.valid('param') const { cursor, limit } = c.req.valid('query') const decoded = cursor ? Cursor.decode(cursor, ['timestamp', 'id']) : undefined const db = Db.get(c.get('db')) try { if (!(await Organizations.get(db, orgId))) return organizationNotFound(c) const records = await Projects.listByOrgPage(db, orgId, { ...(decoded ? { cursor: { createdAt: String(decoded[0]), id: String(decoded[1]) } } : {}), limit, }) const page = Cursor.paginate({ key: (record) => [record.createdAt, record.id], limit, rows: records, }) return c.json( Response.validated(schema.listOrganizationProjects.Response, { data: page.rows.map(({ orgId: _orgId, ...project }) => project), nextCursor: page.nextCursor, }), 200, ) } catch (cause) { return Response.upstream(c, cause) } }, ) .get( '/:orgId/api-keys', OpenApi.validate('param', schema.getOrganization.Params, { code: 'param_invalid', message: 'Invalid path parameters', }), OpenApi.describeRoute({ operationId: 'listAdminOrganizationApiKeys', responses: OpenApi.responses({ errors: { 404: 'Organization not found.' }, success: { description: 'Organization API keys.', schema: schema.listOrganizationApiKeys.Response, }, }), summary: 'List organization keys', tags: ['Organizations'], }), async (c) => { const { orgId } = c.req.valid('param') try { if (!(await Organizations.get(Db.get(c.get('db')), orgId))) return organizationNotFound(c) const records = await ApiKeys.listByOrg(c.get('kv').store, orgId) return c.json( Response.validated(schema.listOrganizationApiKeys.Response, { data: records, }), 200, ) } catch (cause) { return Response.upstream(c, cause) } }, ) .get( '/:orgId/usage', OpenApi.validate('param', schema.getOrganization.Params, { code: 'param_invalid', message: 'Invalid path parameters', }), OpenApi.validate('query', schema.getOrganizationUsage.Query, { code: 'query_invalid', message: 'Invalid query parameters', }), OpenApi.describeRoute({ operationId: 'getAdminOrganizationUsage', responses: OpenApi.responses({ errors: { 404: 'Organization not found.' }, success: { description: 'Recent organization API usage.', schema: schema.getOrganizationUsage.Response, }, }), summary: 'Get organization usage', tags: ['Organizations'], }), async (c) => { const { orgId } = c.req.valid('param') const { period } = c.req.valid('query') try { if (!(await Organizations.get(Db.get(c.get('db')), orgId))) return organizationNotFound(c) const to = new Date().toISOString() const from = new Date(Date.now() - usagePeriods[period]).toISOString() const options = { attributions: [{}], from, interval: period === '24h' ? 'hour' : 'day', orgId, to, } as const const [usage, byError] = await Promise.all([ Analytics.readProjectUsage(c.get('analytics'), options), Analytics.readErrorBreakdown(c.get('analytics'), options), ]) return c.json( Response.validated(schema.getOrganizationUsage.Response, { byError, byRoute: usage.byRoute, byStatus: usage.byStatus, from, period, to, totals: usage.totals, }), 200, ) } catch (cause) { return Response.upstream(c, cause) } }, ) .get( '/:orgId/sponsorship', OpenApi.validate('param', schema.getOrganization.Params, { code: 'param_invalid', message: 'Invalid path parameters', }), OpenApi.describeRoute({ operationId: 'getAdminOrganizationSponsorship', responses: OpenApi.responses({ errors: { 404: 'Organization not found.' }, success: { description: 'Recent organization sponsorship activity.', schema: schema.organizationSponsorship.GetResponse, }, }), summary: 'Get sponsorship activity', tags: ['Organizations'], }), async (c) => { const { orgId } = c.req.valid('param') const db = Db.get(c.get('db')) try { if (!(await Organizations.get(db, orgId))) return organizationNotFound(c) const now = new Date() const to = now.toISOString() const [production, sandbox] = await Promise.all( (['production', 'sandbox'] as const).map(async (environment) => { const settings = await BillingSettings.get(db, orgId, environment) const currency = settings?.currency ?? 'usd' const period = settings?.period ?? 'month' const from = core_Billing.periodStart(period, now) const committedSpend = await SponsoredTransactions.spend(db, { chainIds: [ environment === 'production' ? Viem.chainId.mainnet : Viem.chainId.testnet, ], environment, orgId, since: from, }) return { committedSpend: core_Billing.fromBaseUnits(committedSpend), currency, from, period, } }), ) return c.json( Response.validated(schema.organizationSponsorship.GetResponse, { production, sandbox, to, }), 200, ) } catch (cause) { return Response.upstream(c, cause) } }, ) .get( '/:orgId/webhooks', OpenApi.validate('param', schema.getOrganization.Params, { code: 'param_invalid', message: 'Invalid path parameters', }), OpenApi.describeRoute({ operationId: 'getAdminOrganizationWebhooks', responses: OpenApi.responses({ errors: { 404: 'Organization not found.' }, success: { description: 'Organization webhook health.', schema: schema.getOrganizationWebhooks.Response, }, }), summary: 'Get webhook health', tags: ['Organizations'], }), async (c) => { const { orgId } = c.req.valid('param') const db = Db.get(c.get('db')) try { if (!(await Organizations.get(db, orgId))) return organizationNotFound(c) const to = new Date().toISOString() const from = new Date(Date.now() - usagePeriods['7d']).toISOString() const [subscriptions, failures, failureCount] = await Promise.all([ WebhookSubscriptions.list(db, { orgId, type: 'api_key' }, { limit: 25, now: to }), WebhookDeliveries.listFailuresByOrg(db, orgId, { from, limit: 25, now: to }), WebhookDeliveries.countFailuresByOrg(db, orgId, { from, now: to }), ]) return c.json( Response.validated(schema.getOrganizationWebhooks.Response, { failureCount, failures, failuresTruncated: failureCount > failures.length, from, subscriptions: subscriptions.map((subscription) => ({ chainId: subscription.chainId, createdAt: subscription.createdAt, destination: redactDestination(subscription.destination.url), eventType: subscription.eventType, failureCount: subscription.failureCount, id: subscription.id, lastDeliveryAt: subscription.lastDeliveryAt ?? null, status: subscription.status, })), to, }), 200, ) } catch (cause) { return Response.upstream(c, cause) } }, ) } const usagePeriods = { '24h': 24 * 60 * 60 * 1_000, '30d': 30 * 24 * 60 * 60 * 1_000, '7d': 7 * 24 * 60 * 60 * 1_000, } as const function redactDestination(value: string) { return `${new URL(value).origin}/…` } function organizationNotFound(c: Context) { return Response.error(c, { code: 'not_found', message: 'Organization not found.', status: 404, }) } function serializeBilling( settings: BillingSettings.Record | undefined, source: StripeCustomers.Record | undefined, ) { return { settings: settings ? { currency: settings.currency, period: settings.period, spendLimit: settings.spendLimit, txFeeLimit: settings.txFeeLimit, updatedAt: settings.updatedAt, } : null, source: source ? { status: source.status, stripeCustomerId: source.stripeCustomerId, updatedAt: source.updatedAt, } : null, } }