~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain

photography-focused photo sharing platform

grain.social
1 collaborator
0
2
83 lines · 2.7 KB Raw History
// Deletes a gallery and all associated records (items, photos, EXIF, favorites, comments).
//   POST /xrpc/social.grain.unspecced.deleteGallery { rkey }

import { defineProcedure } from "$hatk";

export default defineProcedure("social.grain.unspecced.deleteGallery", async (ctx) => {
  const { db, ok, viewer, deleteRecord } = ctx;
  const { rkey } = ctx.input;

  if (!viewer) throw new Error("Authentication required");

  const galleryUri = `at://${viewer.did}/social.grain.gallery/${rkey}`;

  // Verify the gallery exists and belongs to the viewer
  const [gallery] = (await db.query(
    `SELECT uri FROM "social.grain.gallery" WHERE uri = $1 AND did = $2`,
    [galleryUri, viewer.did],
  )) as { uri: string }[];
  if (!gallery) throw new Error("Gallery not found");

  // Find gallery items (join records linking gallery → photo)
  const items = (await db.query(
    `SELECT uri, item FROM "social.grain.gallery.item" WHERE gallery = $1`,
    [galleryUri],
  )) as { uri: string; item: string }[];

  const photoUris = items.map((i) => i.item);

  // Delete EXIF records for these photos
  if (photoUris.length > 0) {
    const exifRows = (await db.query(
      `SELECT uri FROM "social.grain.photo.exif" WHERE photo IN (${photoUris.map((_, i) => `$${i + 1}`).join(",")})`,
      photoUris,
    )) as { uri: string }[];
    for (const row of exifRows) {
      const exifRkey = row.uri.split("/").pop()!;
      await deleteRecord("social.grain.photo.exif", exifRkey);
    }
  }

  // Delete gallery items
  for (const item of items) {
    const itemRkey = item.uri.split("/").pop()!;
    await deleteRecord("social.grain.gallery.item", itemRkey);
  }

  // Delete photos
  for (const photoUri of photoUris) {
    const photoRkey = photoUri.split("/").pop()!;
    await deleteRecord("social.grain.photo", photoRkey);
  }

  // Delete favorites on this gallery
  const favs = (await db.query(`SELECT uri FROM "social.grain.favorite" WHERE subject = $1`, [
    galleryUri,
  ])) as { uri: string }[];
  for (const fav of favs) {
    const favRkey = fav.uri.split("/").pop()!;
    try {
      await deleteRecord("social.grain.favorite", favRkey);
    } catch {
      // Favorite may belong to another user, skip
    }
  }

  // Delete comments on this gallery
  const comments = (await db.query(`SELECT uri FROM "social.grain.comment" WHERE subject = $1`, [
    galleryUri,
  ])) as { uri: string }[];
  for (const comment of comments) {
    const commentRkey = comment.uri.split("/").pop()!;
    try {
      await deleteRecord("social.grain.comment", commentRkey);
    } catch {
      // Comment may belong to another user, skip
    }
  }

  // Delete the gallery itself
  await deleteRecord("social.grain.gallery", rkey);

  return ok({});
});