← back to Stayclaim

db/migrations/019_user_contributions.sql

77 lines

-- Slice 1 of PLAN-geo-archival.md (2026-05-01)
-- Adds moderation + contributor tracking to place_event,
-- plus a new user_image table for user-submitted photos.
--
-- Auth model: stayclaim has NO users table. Identity is delegated to
-- Site Factory (claimmyaddress.com) via session token. We store
-- contributor_email (mirroring address_submission's pattern) rather
-- than a cross-DB user_id FK we couldn't enforce.

BEGIN;

-- 1. Augment place_event for user-submitted memories / facts
ALTER TABLE place_event ADD COLUMN IF NOT EXISTS contributor_email   text;
ALTER TABLE place_event ADD COLUMN IF NOT EXISTS moderation_status   text NOT NULL DEFAULT 'APPROVED'
  CHECK (moderation_status IN ('PENDING','APPROVED','REJECTED'));
ALTER TABLE place_event ADD COLUMN IF NOT EXISTS moderation_reason   text;
ALTER TABLE place_event ADD COLUMN IF NOT EXISTS moderated_at        timestamptz;
ALTER TABLE place_event ADD COLUMN IF NOT EXISTS moderator_email     text;

-- Existing rows: backfilled to APPROVED (they were always public_visible).
-- New user-submitted rows MUST insert with moderation_status='PENDING' and
-- public_visible=false until approved; the API enforces this, not the schema.

CREATE INDEX IF NOT EXISTS idx_place_event_pending
  ON place_event (moderation_status)
  WHERE moderation_status = 'PENDING';

-- 2. New user_image table — user-submitted photos.
-- Distinct from pd_film_image (which is rights='public-domain' only).
-- All rows here are tier C (community-submitted) by definition.
CREATE TABLE IF NOT EXISTS user_image (
  id                  uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  listing_id          uuid NOT NULL REFERENCES listing(id) ON DELETE CASCADE,
  contributor_email   text NOT NULL,
  caption             text,
  image_url           text NOT NULL,                         -- CF R2 URL once uploaded
  thumb_url           text,
  width               int,
  height              int,
  source_tier         char(1) NOT NULL DEFAULT 'C'
    CHECK (source_tier = 'C'),                                -- always C; row-level lock
  source_label        text NOT NULL,                          -- e.g. "Submitted by Steve A."
  rights              text NOT NULL DEFAULT 'user-submitted', -- not PD
  rights_url          text,
  moderation_status   text NOT NULL DEFAULT 'PENDING'
    CHECK (moderation_status IN ('PENDING','APPROVED','REJECTED')),
  moderation_reason   text,
  moderated_at        timestamptz,
  moderator_email     text,
  uploaded_at         timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_user_image_listing_approved
  ON user_image (listing_id)
  WHERE moderation_status = 'APPROVED';
CREATE INDEX IF NOT EXISTS idx_user_image_pending
  ON user_image (moderation_status)
  WHERE moderation_status = 'PENDING';
CREATE INDEX IF NOT EXISTS idx_user_image_contributor
  ON user_image (contributor_email);

COMMIT;

-- ROLLBACK plan (if needed):
-- BEGIN;
-- DROP INDEX IF EXISTS idx_user_image_contributor;
-- DROP INDEX IF EXISTS idx_user_image_pending;
-- DROP INDEX IF EXISTS idx_user_image_listing_approved;
-- DROP TABLE IF EXISTS user_image;
-- DROP INDEX IF EXISTS idx_place_event_pending;
-- ALTER TABLE place_event DROP COLUMN IF EXISTS moderator_email;
-- ALTER TABLE place_event DROP COLUMN IF EXISTS moderated_at;
-- ALTER TABLE place_event DROP COLUMN IF EXISTS moderation_reason;
-- ALTER TABLE place_event DROP COLUMN IF EXISTS moderation_status;
-- ALTER TABLE place_event DROP COLUMN IF EXISTS contributor_email;
-- COMMIT;