import { env } from "cloudflare:workers"; import type { RepoRow } from "~/lib/types"; import { artifactsCall } from "./artifacts-call"; const TOKEN_TTL_SECONDS = 3600; const TOKEN_CACHE_SECONDS = 3000; export type TokenScope = "read" | "write"; export function tokenSecret(plaintext: string): string { return plaintext.split("?expires=")[0]; } export function basicAuthHeader(secret: string): string { return `Basic ${btoa(`x:${secret}`)}`; } export function authenticatedRemote(remote: string, secret: string): string { const url = new URL(remote); url.username = "x"; url.password = secret; return url.toString(); } export async function openArtifactsRepo(artifactsName: string): Promise { return artifactsCall("open repository", artifactsName, () => env.ARTIFACTS.get(artifactsName), { idempotent: true }); } /** Mints (or reuses a cached) Artifacts token for git protocol access. */ export async function repoTokenSecret(repo: Pick, scope: TokenScope): Promise { const cacheKey = `artifacts-token:${repo.artifacts_name}:${scope}`; const cached = await env.KV.get(cacheKey); if (cached) return cached; const handle = await openArtifactsRepo(repo.artifacts_name); try { const token = await artifactsCall("mint an access token", repo.artifacts_name, () => handle.createToken(scope, TOKEN_TTL_SECONDS), ); const secret = tokenSecret(token.plaintext); await env.KV.put(cacheKey, secret, { expirationTtl: TOKEN_CACHE_SECONDS }); return secret; } finally { handle[Symbol.dispose](); } } /** Mints a dedicated short-lived token, e.g. for CI jobs that outlive the cache window. */ export async function mintRepoToken(artifactsName: string, scope: TokenScope, ttlSeconds: number): Promise { const handle = await openArtifactsRepo(artifactsName); try { const token = await artifactsCall("mint an access token", artifactsName, () => handle.createToken(scope, ttlSeconds), ); return tokenSecret(token.plaintext); } finally { handle[Symbol.dispose](); } } export function isArtifactsError(error: unknown): error is ArtifactsError { return error instanceof Error && error.name === "ArtifactsError" && "code" in error; } export function artifactsNameFor(repoId: string): string { return `r-${repoId}`; }