import { Hono } from 'hono' import type Stripe from 'stripe' import type * as App from '../../App.js' import * as Metadata from '../metadata.js' import { apiKeys } from './routes/api-keys.js' import { billing } from './routes/billing.js' import { faucet } from './routes/faucet.js' import { invitations } from './routes/invitations.js' import { inviteLinks } from './routes/invite-links.js' import { me } from './routes/me.js' import { members } from './routes/members.js' import { orgs } from './routes/orgs.js' import { projects } from './routes/projects.js' import { scopes } from './routes/scopes.js' import { usage } from './routes/usage.js' import { verifiedTokenRequests } from './routes/verified-token-requests.js' import { webhooks } from './routes/webhooks.js' /** * Minimal transactional email sender. Cloudflare deployments wrap the Email * Service binding (`{ from, send: (m) => env.EMAIL.send(m) }`); other hosts * pass any compatible sender. Dispatch is always best-effort. */ export type Email = { /** Console URL embedded in email links (e.g. sign-in URLs). Defaults to the hosted console; self-hosts point it at their own. */ consoleUrl?: string | undefined /** Sender address for management emails (e.g. `noreply@tempo.xyz`). */ from: string /** Sends one message. */ send: (message: Email.Message) => Promise } export declare namespace Email { /** One transactional message. */ type Message = { /** Sender address. */ from: string /** HTML body. */ html?: string | undefined /** Subject line. */ subject: string /** Plain-text body. */ text: string /** Recipient address. */ to: string } } /** Billing capability, one bag per billing source; Stripe is the sole source today. */ export type Billing = { /** * Stripe billing source. */ stripe: { /** Constructed Stripe client. */ client: Stripe /** Console URL for Checkout/portal return links when the request carries no `Origin`. Defaults to the hosted console. */ consoleUrl?: string | undefined /** Test-mode Stripe backing sandbox billing; omit to keep sandbox billing 501. */ sandbox?: | { /** Constructed test-mode Stripe client. */ client: Stripe /** Test-mode webhook endpoint signing secret (`whsec_…`). */ webhookSecret: string } | undefined /** Webhook endpoint signing secret (`whsec_…`). */ webhookSecret: string } } /** * Request environment for management routes: the shared app environment plus the * capabilities this group publishes on context (`billing`, `email`). Owned * here, not in core `App.Environment`, since these are management concerns. */ export type Environment = { Variables: App.Environment['Variables'] & { /** Stripe billing capability; undefined when billing is unconfigured. */ billing: Billing | undefined /** Transactional email sender for management emails; undefined when dispatch is disabled. */ email: Email | undefined } } /** * The management route group: the current user, orgs, projects, members, * invitations, and API-key management. Every route reads its dependencies from * request context set by {@link App.create}. * * The session-auth surface is owned by {@link App.create}; management routes * reference it alongside scoped API-key access. * * ```ts * App.create(options).route('/', management()) * ``` */ export function management(options: management.Options = {}) { const app = new Hono() // Publish this group's capabilities for handlers; undefined disables each. .use('*', async (c, next) => { c.set('billing', options.billing) c.set('email', options.email) await next() }) .route('/', apiKeys({ enabled: options.apiKeys !== false })) .route('/', billing()) .route('/', faucet()) .route('/', invitations()) .route('/', inviteLinks()) .route('/', me()) .route('/', members()) .route('/', orgs()) .route('/', projects()) .route('/', scopes()) .route('/', usage()) .route('/', verifiedTokenRequests()) .route('/', webhooks({ webhook: options.webhook === false ? undefined : options.webhook })) return Metadata.attach(app, openapi) } export declare namespace management { /** Options for the management route group. */ type Options = { /** Expose the API-key management routes in the OpenAPI document. @default true */ apiKeys?: boolean | undefined /** Stripe billing capability; omit to disable billing routes (501) and keep production sponsorship closed. */ billing?: Billing | undefined /** Transactional email sender for management emails (e.g. invitations); omit to disable dispatch — notified flows still work in-product. */ email?: Email | undefined /** Webhook capability shared with the data group and host poller; omit or pass false to disable management. */ webhook?: App.Webhook | false | undefined } } /** * The OpenAPI metadata this group owns: management operation tags and the * `session` security scheme. Tagged onto the {@link management} instance so * `App.create` collects it. */ const openapi = { securitySchemes: { session: { description: 'Cookie session established by sign-in (`POST /v1/auth/siwe`). Clients may also send the session token as `Authorization: Bearer `.', // prettier-ignore in: 'cookie', name: 'accounts_auth', type: 'apiKey', }, }, tags: [ { name: 'API Keys', description: 'Credentials for accessing the Tempo API.' }, { name: 'Billing', description: 'Organization billing backed by Stripe.' }, { name: 'Faucet', description: 'Test token funding for Tempo testnet accounts.' }, { name: 'Invitations', description: 'Pending organization invitations.' }, { name: 'Invite Links', description: 'Reusable organization invite links.' }, { name: 'Members', description: 'Organization team membership.' }, { name: 'Organizations', description: 'Teams that own application workspaces and members.' }, { name: 'Projects', description: 'Application workspaces within an organization.' }, { name: 'Usage', description: 'Request and sponsorship usage for organizations.' }, { name: 'Users', description: 'Users on the Tempo Platform.' }, { name: 'Verified Token Requests', description: 'Organization requests to add tokens to the curated verified list.' }, // prettier-ignore { name: 'Webhooks', description: 'Organization webhook subscriptions.' }, ], 'x-tagGroups': [ { name: 'Management API', tags: [ 'API Keys', 'Authentication', 'Billing', 'Invitations', 'Invite Links', 'Members', 'Organizations', 'Projects', 'Usage', 'Users', 'Verified Token Requests', 'Webhooks', ], }, ], } satisfies App.create.Metadata