Skip to the content

Capsid menu

workers/media-events.ts

103 lines and 3 functions in DrDustinEdwards/dustinedwards-info, at commit 1590ba4 on 2026-10-24. This file on GitHub.

Functions

The functions of media-events.ts
FunctionLinesComplexityCoveredCRAPMutation
objectKeyOf13 to 197No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
handleMediaEvents24 to 424No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
indexOne45 to 11614No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code

Imports

It imports app/db/index.ts, app/lib/error-message.mjs, app/lib/media/backup.server.ts, app/lib/media/classify.mjs, app/lib/media/core.server.ts. Imported by workers/app.ts.

Source

import { deleteMediaRecord, upsertDerivedMedia } from "~/db";
import { bucketFor, classify, isRaster, roleOf, storageOf } from "~/lib/media/classify.mjs";
import { mirrorObject } from "~/lib/media/backup.server";
import { measureDimensions, placeholderFor } from "~/lib/media/core.server";
import { errorMessage } from "~/lib/error-message.mjs";

/**
 * The authoritative media row writer, fed by R2 event notifications rather than a dual write. Must be
 * idempotent: Queues delivers at least once and out of order, so the object is re-read, never trusted
 * from the event, and R2 wins any disagreement.
 */

function objectKeyOf(body: unknown): string | null {
  if (typeof body !== "object" || body === null) return null;
  const object = (body as { object?: unknown }).object;
  if (typeof object !== "object" || object === null) return null;
  const key = (object as { key?: unknown }).key;
  return typeof key === "string" && key.length > 0 ? key : null;
}

// A missing object deletes the row, so reading the wrong bucket for a key deletes it too. Use the
// shared `bucketFor`; never write a second copy.

export async function handleMediaEvents(batch: MessageBatch<unknown>, env: Env) {
  for (const message of batch.messages) {
    const key = objectKeyOf(message.body);
    if (!key) {
      console.error(JSON.stringify({ alert: "media-event-no-object-key", body: message.body }));
      message.ack();
      continue;
    }

    try {
      await indexOne(env, key);
      message.ack();
    } catch (error) {
      // Never ack a transient failure, or the row silently never appears.
      console.error(JSON.stringify({ alert: "media-event-failed", key, detail: errorMessage(error) }));
      message.retry();
    }
  }
}

/** Ignores the event's action on purpose: asking R2 what is true now makes reordering safe. */
async function indexOne(env: Env, key: string) {
  const object = await bucketFor(env, key).get(key);

  if (!object) {
    // Logged, because a genuine delete and a key looked up in the wrong bucket both land here.
    console.log(
      JSON.stringify({
        event: "media-event-no-object",
        key,
        bucket: storageOf(key) === "r2-derived" ? "OG" : "MEDIA",
        action: "deleting any row",
      }),
    );
    await deleteMediaRecord(env, key);
    return;
  }

  const { kind, mime } = classify(key);

  let width: number | null = null;
  let height: number | null = null;
  let placeholder: string | null = null;
  // An unreadable image still gets a row, or the index would disagree with the bucket; the stored
  // sizes are left alone and the failure is thrown after the write, so the message retries and a
  // permanent failure lands in the dead-letter queue.
  let measureFailure: unknown = null;
  if (isRaster(key)) {
    try {
      const dimensions = await measureDimensions(env, object.body);
      width = dimensions?.width ?? null;
      height = dimensions?.height ?? null;
    } catch (error) {
      measureFailure = error;
    }

    // Derived here, or the upsert erases the bulk pass's placeholder. A second GET, because `.info()`
    // consumed the first body stream.
    const reread = await bucketFor(env, key).get(key);
    if (reread) placeholder = await placeholderFor(env, reread.body);
  }

  await upsertDerivedMedia(env, {
    key,
    storage: storageOf(key),
    kind,
    role: roleOf(key),
    mime,
    bytes: object.size,
    width,
    height,
    // From the object: a content-addressed key cannot yield a filename. Null means "do not touch".
    originalName: object.customMetadata?.originalName ?? null,
    // Null means "do not touch", not "erase".
    placeholder,
    uploadedAt:
      object.uploaded instanceof Date ? object.uploaded.toISOString() : String(object.uploaded ?? ""),
  });

  // Nothing here deletes a backup twin: the backup is the copy that survives a delete. OG cards are
  // regenerable, so not mirrored. A failed mirror is swallowed; the health poll sees the drift.
  if (storageOf(key) === "r2") {
    try {
      await mirrorObject(env, key);
    } catch (error) {
      // The row stands; media-backup-drift will report it and backup_media repairs it.
      console.log(JSON.stringify({ event: "media-mirror-failed", key, detail: errorMessage(error) }));
    }
  }

  // Last, so the row and the mirror both happened before the retry is asked for.
  if (measureFailure) throw measureFailure;
}