/** * API token management procedures * Allows users to create and manage API tokens for CLI/programmatic access */ import { ORPCError } from "@orpc/server"; import { generateSecureBase58Token, hashToken, TOKEN_PREFIX, } from "../../utils/crypto.js"; import { authMiddleware, os } from "../base.js"; /** Token expiration: 365 days */ const TOKEN_EXPIRATION_DAYS = 365; /** * List all API tokens for the current user * Returns token metadata (not the actual token values) */ export const listApiTokens = os.me.apiTokens.list .use(authMiddleware) .handler(async ({ context }) => { const tokens = await context.db .selectFrom("api_tokens") .select(["id", "name", "last_used_at", "created_at", "expires_at"]) .where("user_id", "=", context.user.id) .orderBy("created_at", "desc") .execute(); return tokens.map((token) => ({ id: Number(token.id), name: token.name, lastUsedAt: token.last_used_at?.toISOString() ?? null, createdAt: token.created_at.toISOString(), expiresAt: token.expires_at.toISOString(), })); }); /** * Create a new API token * Requires superuser status and trusted session */ export const createApiToken = os.me.apiTokens.create .use(authMiddleware) .handler(async ({ input, context }) => { // Require superuser status if (!context.user.isSuperuser) { throw new ORPCError("FORBIDDEN", { message: "Only superusers can create API tokens.", }); } // Require trusted session for creating API tokens if (!context.session.trustedMode) { throw new ORPCError("FORBIDDEN", { message: "Creating API tokens requires a trusted session. Please re-authenticate.", }); } const { name } = input; // Generate a new API token const token = generateSecureBase58Token(TOKEN_PREFIX); const tokenHash = await hashToken(token); // Calculate expiration const expiresAt = new Date( Date.now() + TOKEN_EXPIRATION_DAYS * 24 * 60 * 60 * 1000, ); // Insert into api_tokens table await context.db .insertInto("api_tokens") .values({ user_id: context.user.id, token_hash: tokenHash, name, expires_at: expiresAt, }) .execute(); return { token, expiresAt: expiresAt.toISOString(), }; }); /** * Delete an API token */ export const deleteApiToken = os.me.apiTokens.delete .use(authMiddleware) .handler(async ({ input, context }) => { const result = await context.db .deleteFrom("api_tokens") .where("id", "=", input.tokenId.toString()) .where("user_id", "=", context.user.id) .executeTakeFirst(); if (result.numDeletedRows === 0n) { throw new ORPCError("NOT_FOUND", { message: "API token not found", }); } return { success: true }; });