48 lines · 1.8 KB
import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";
import { requireViewer, viewerMiddleware } from "./middleware";
import { all } from "~/server/db";
import { notFoundError } from "~/server/errors";

const HOUR_MS = 60 * 60 * 1000;
const MAX_FAILURES = 200;

export type ArtifactsOperationStats = { operation: string; calls: number; failures: number };
export type ArtifactsFailure = {
  id: string;
  operation: string;
  target: string;
  reference: string | null;
  message: string;
  attempts: number;
  created_at: number;
};

export const getArtifactsHealth = createServerFn({ method: "GET" })
  .middleware([viewerMiddleware])
  .validator(z.object({ hours: z.number().int().min(1).max(720), operation: z.string().max(100).optional() }))
  .handler(async ({ data, context }) => {
    const viewer = requireViewer(context.viewer);
    if (!viewer.isAdmin) throw notFoundError("Not found");
    const since = Date.now() - data.hours * HOUR_MS;
    const sinceHour = Math.floor(since / HOUR_MS) * HOUR_MS;
    const operations = await all<ArtifactsOperationStats>(
      `SELECT operation, SUM(calls) AS calls, SUM(failures) AS failures FROM artifacts_call_stats
       WHERE hour >= ? GROUP BY operation ORDER BY failures DESC, calls DESC`,
      sinceHour,
    );
    const failures = data.operation
      ? await all<ArtifactsFailure>(
          `SELECT * FROM artifacts_failures WHERE operation = ? AND created_at >= ? ORDER BY created_at DESC LIMIT ?`,
          data.operation,
          since,
          MAX_FAILURES,
        )