38 lines · 1.7 KB
import { domainSites, sitesOwnedBy } from "./store";
import { clearDomainLapse, lapsedDomainAccounts, startUnpaidGraceClocks } from "../billing/domain-addon";
import { releaseSiteDomain } from "./site";

/** Matches the 72 h stale-claim window that lets another site take an unverified domain over. */
const UNVERIFIED_CLAIM_MS = 72 * 60 * 60 * 1000;
const BATCH = 20;
const ACCOUNT_DOMAINS_PAGE = 100;
/** Never fully verified: no verification timestamp, and not currently active either. */
const NEVER_VERIFIED = "(domain_verified_at IS NULL AND (domain_status IS NULL OR domain_status NOT LIKE 'active%'))";

/** Cron: unverified custom domain claims older than the claim window lose their Cloudflare hostname. */
async function expireUnverifiedClaims(now: number): Promise<void> {
  const cutoff = now - UNVERIFIED_CLAIM_MS;
  const sites = (await domainSites(`${NEVER_VERIFIED} AND COALESCE(domain_claimed_at, updated_at) < ?`, cutoff)).slice(
    0,
    BATCH,
  );
  for (const site of sites) await releaseSiteDomain(site);
}

/** Cron: once a lapsed add-on's grace period ends, every custom domain of the account is unmapped and deleted. */
async function releaseLapsedAddons(now: number): Promise<void> {
  for (const accountId of await lapsedDomainAccounts(now)) {
    const sites = (await sitesOwnedBy(accountId))
      .filter((site) => site.custom_domain !== null)
      .slice(0, ACCOUNT_DOMAINS_PAGE);
    for (const site of sites) await releaseSiteDomain(site);
    if (sites.length < ACCOUNT_DOMAINS_PAGE) await clearDomainLapse(accountId);
  }
}

export async function sweepCustomDomains(now = Date.now()): Promise<void> {
  await expireUnverifiedClaims(now);
  await startUnpaidGraceClocks(now);
  await releaseLapsedAddons(now);
}