182 lines · 8.5 KB
import { z } from "zod";
import { peekAuthToken, storeAuthToken } from "../auth-tokens";
import { randomToken } from "../crypto";
import { badRequest, forbidden, HttpError, unauthorized } from "../errors";
import { publicUrl } from "../events/emit";
import { authenticatedRemote, mintRepoToken } from "../git/artifacts";
import { limitWrite } from "../limits";
import { hasPermission, loadRepoById, repoPermission } from "../repos";
import { loadUserById, toViewer } from "../auth";
import { containerLimits, containersEnabled, containerStub } from "./container-config";
import { CONTAINERS_PAID_ONLY, containerUsage, EDITOR_METER } from "./container-meter";
import { remainingQuota } from "../metering/quota";
import { json } from "./http";
import { editorOriginFor } from "./origin";
import { editorRoomStub } from "./room-client";
import { canWrite, EDITOR_TOKEN_PREFIX, resolveEditorToken } from "./session";
import type { EditorSession } from "./session";
import { EDITOR_WS_PROTOCOL } from "~/lib/editor-protocol";

const PREVIEW_PREFIX = "gitfor_pvw";
const PREVIEW_TTL_SECONDS = 60 * 60;
const UNLIMITED_ATTEMPTS = Number.MAX_SAFE_INTEGER;
const HTTP_PAYMENT_REQUIRED = 402;
const MAX_PORT = 65535;
const SECONDS_PER_MINUTE = 60;
const SECONDS_PER_HOUR = 3600;
/** Starting for less than this isn't worth a boot; the user is told the month's quota is used up. */
const MIN_BUDGET_SECONDS = 60;

function requireContainerAccess(session: EditorSession): void {
  if (!containersEnabled()) throw forbidden("Containers aren't enabled on this forge");
  if (!canWrite(session)) throw forbidden("Containers need write access to a branch");
}

/** Plan gate for anything that boots or talks to a container; status stays readable so the UI can explain. */
async function requirePlanAllows(session: EditorSession): Promise<void> {
  if (!(await containerUsage(session.viewer.id)).allowed)
    throw new HttpError(HTTP_PAYMENT_REQUIRED, CONTAINERS_PAID_ONLY);
}