import { createServerFn } from "@tanstack/react-start"; import { currentSessionHandle } from "~/server/auth"; import { z } from "zod"; import { requireViewer, viewerMiddleware } from "./middleware"; import { limitBrowse, limitWrite } from "~/server/limits"; import { requireRepoAccess } from "~/server/repos"; import { listRefs, resolveRevision, splitRevisionPath } from "~/server/git/refs"; import { assertVisibleRevision } from "~/server/git/reachable"; import { notFoundError } from "~/server/errors"; import { editorSessionInfo, mintEditorToken, resolveEditorToken } from "~/server/editor/session"; import { editorOriginFor } from "~/server/editor/origin"; import { getRequestUrl } from "@tanstack/react-start/server"; import { repoRef } from "~/lib/validation"; /** `/edit/` with no file path opens the workspace editor; `/edit//` keeps the single-file editor. */ export const resolveEditTarget = createServerFn({ method: "GET" }) .middleware([viewerMiddleware]) .validator(repoRef.extend({ splat: z.string().default("") })) .handler(async ({ data, context }) => { await limitBrowse(context.viewer); const { repo } = await requireRepoAccess(data.owner, data.repo, context.viewer, "read"); const refs = await listRefs(repo); const splat = data.splat.replace(/\/+$/, "") || repo.default_branch; const resolved = splitRevisionPath(refs, splat); if (!resolved) throw notFoundError("Branch or path not found"); if (resolved.path) return { kind: "file" as const }; return { kind: "workspace" as const, ref: resolved.revision.name, refKind: resolved.revision.kind, editorOrigin: editorOriginFor(new URL(getRequestUrl())), }; }); /** Mints the short-lived, ref-scoped token the sandboxed editor frame uses instead of the session cookie. */ export const mintEditorSession = createServerFn({ method: "POST" }) .middleware([viewerMiddleware]) .validator(repoRef.extend({ ref: z.string().min(1).max(250) })) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); await limitWrite(viewer); const { repo, permission } = await requireRepoAccess(data.owner, data.repo, viewer, "read"); const revision = resolveRevision(await listRefs(repo), data.ref); if (!revision) throw notFoundError("Branch or ref not found"); await assertVisibleRevision(repo, revision); const minted = await mintEditorToken(viewer, repo, revision.name, { ceiling: permission, canOpenPull: true, patId: null, sessionHandle: await currentSessionHandle(), }); return editorSessionInfo(await resolveEditorToken(minted.token)); });