377 lines · 14.4 KB
import { first } from "../db";
import { accountDb } from "../scoped-db";
import { locate, locatedByPrefix, registerLocator } from "../scoped-db/locators";
import { scopedId } from "../scoped-db/ids";
import { accountsById } from "../people";
import { billingSettings, commitUsage, hardCap, periodUsage, releaseUsage, reserveUsage } from "../metering/quota";
import type { Reservation } from "../metering/quota";
import { recordUsage } from "../metering/record";
import { planAllowsEditorContainers } from "~/lib/plans";
import { containerLimits, containerStub } from "./container-config";
import { accountDisableState, assertAccountActive } from "../account-disable";

export const EDITOR_INSTANCE_TYPE = "standard-1";
export const EDITOR_METER = "compute.editor";
const HOUR_MS = 60 * 60 * 1000;
const HOUR_S = 60 * 60;
const SECOND_MS = 1000;
const MINUTE_S = 60;

export type ContainerUsage = {
  /** Seconds committed this month plus the elapsed time of sessions still running. */
  usedSeconds: number;
  includedSeconds: number;
  hardCapSeconds: number | null;
  /** False on plans with no editor allowance and a zero hard cap (free tier without a spending limit). */
  allowed: boolean;
};

export const CONTAINERS_PAID_ONLY = "Containers are available on paid plans";

/** The account's editor-container usage this month against its plan, for status bars and listings. */
export async function containerUsage(accountId: string): Promise<ContainerUsage> {
  const now = Date.now();
  const [settings, period, running] = await Promise.all([
    billingSettings(accountId),
    periodUsage(accountId, EDITOR_METER),
    accountDb(accountId).all<{ started_at: number }>(
      "SELECT started_at FROM editor_container_sessions WHERE account_id = ? AND stopped_at IS NULL",
      accountId,
    ),