Skip to content
quarry Artifact [5829c0967c]
Login

Artifact [5829c0967c]

Artifact 5829c0967c44ecec3936b42e0bd00f2bd0410c1b488f666f13587fa1e3cccb38:


import { EmailMessage } from "cloudflare:email";
import { initialState, step, needsTimeline, composeMessage } from "./logic.js";

const FORGE = "https://zeitforge.app";
const TIMEOUT_MS = 10_000;
const STATE_KEY = "state";

async function get(url) {
  try {
    return await fetch(url, { signal: AbortSignal.timeout(TIMEOUT_MS), redirect: "manual" });
  } catch (err) {
    return { error: String(err && err.message ? err.message : err) };
  }
}

async function checkHealth() {
  const res = await get(`${FORGE}/api/v1/version`);
  if (res.error) return { status: null, ready: false, error: res.error };
  let j = {};
  try {
    j = await res.json();
  } catch {}
  return { status: res.status, ready: j.ready === true, version: j.version, node: j.node };
}

async function checkTimeline() {
  const res = await get(`${FORGE}/r/quarry/timeline`);
  if (res.error) return 599;
  await res.body?.cancel();
  return res.status;
}

async function loadState(env) {
  const raw = await env.PROBE_STATE.get(STATE_KEY, "json");
  return raw || initialState();
}

async function send(env, action, state) {
  const { subject, body } = composeMessage(action, state, FORGE);
  const raw = [
    `From: ${env.ALERT_FROM}`,
    `To: ${env.ALERT_TO}`,
    `Subject: ${subject}`,
    `Date: ${new Date().toUTCString()}`,
    `Message-ID: <${crypto.randomUUID()}@${env.ALERT_FROM.split("@")[1]}>`,
    "MIME-Version: 1.0",
    "Content-Type: text/plain; charset=utf-8",
    "",
    body,
  ].join("\r\n");
  await env.ALERT_EMAIL.send(new EmailMessage(env.ALERT_FROM, env.ALERT_TO, raw));
}

async function probe(env) {
  const now = Date.now();
  const prev = await loadState(env);
  const health = await checkHealth();
  const timelineStatus = needsTimeline(health) ? await checkTimeline() : null;
  const { state, actions } = step(prev, { health, timelineStatus }, now);
  // Persist before sending so a mail failure retries neither as a duplicate nor forever.
  await env.PROBE_STATE.put(STATE_KEY, JSON.stringify(state));
  for (const a of actions) await send(env, a, state);
  return state;
}

export default {
  async scheduled(_event, env, ctx) {
    ctx.waitUntil(probe(env));
  },
  async fetch(_req, env) {
    return Response.json(await loadState(env));
  },
};