62 lines · 2.0 KB
import { env } from "cloudflare:workers";
import type { RepoRow } from "~/lib/types";

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<ArtifactsRepo> {
  return env.ARTIFACTS.get(artifactsName);
}

/** Mints (or reuses a cached) Artifacts token for git protocol access. */
export async function repoTokenSecret(repo: Pick<RepoRow, "artifacts_name">, scope: TokenScope): Promise<string> {
  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 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]();