CREATE EXTENSION IF NOT EXISTS "pgcrypto"; CREATE TYPE "DevicePlatform" AS ENUM ('MACOS', 'IPHONE'); CREATE TYPE "TrackStatus" AS ENUM ('ACTIVE', 'DELETED'); CREATE TYPE "UploadSessionStatus" AS ENUM ('PENDING', 'READY_TO_UPLOAD', 'COMPLETED', 'FAILED'); CREATE TYPE "EntityType" AS ENUM ('TRACK', 'AUDIO_ASSET', 'ARTWORK_ASSET'); CREATE TYPE "EventAction" AS ENUM ('CREATED', 'UPDATED', 'DELETED'); CREATE TABLE "devices" ( "id" UUID NOT NULL DEFAULT gen_random_uuid(), "platform" "DevicePlatform" NOT NULL, "device_name" TEXT NOT NULL, "app_version" TEXT NOT NULL, "install_token_hash" TEXT NOT NULL, "last_seen_at" TIMESTAMP(3) NOT NULL, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "devices_pkey" PRIMARY KEY ("id") ); CREATE TABLE "tracks" ( "id" UUID NOT NULL DEFAULT gen_random_uuid(), "primary_audio_asset_id" UUID, "artwork_asset_id" UUID, "title" TEXT NOT NULL, "artist" TEXT NOT NULL, "album" TEXT, "album_artist" TEXT, "genre" TEXT, "disc_number" INTEGER, "track_number" INTEGER, "year" INTEGER, "duration_ms" INTEGER, "status" "TrackStatus" NOT NULL DEFAULT 'ACTIVE', "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "deleted_at" TIMESTAMP(3), CONSTRAINT "tracks_pkey" PRIMARY KEY ("id") ); CREATE TABLE "audio_assets" ( "id" UUID NOT NULL DEFAULT gen_random_uuid(), "track_id" UUID, "sha256" TEXT NOT NULL, "storage_key" TEXT NOT NULL, "original_filename" TEXT NOT NULL, "mime_type" TEXT NOT NULL, "file_extension" TEXT NOT NULL, "file_size_bytes" BIGINT NOT NULL, "bit_rate_kbps" INTEGER, "sample_rate_hz" INTEGER, "channels" INTEGER, "duration_ms" INTEGER, "source_device_id" UUID, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "audio_assets_pkey" PRIMARY KEY ("id") ); CREATE TABLE "artwork_assets" ( "id" UUID NOT NULL DEFAULT gen_random_uuid(), "sha256" TEXT NOT NULL, "storage_key" TEXT NOT NULL, "mime_type" TEXT NOT NULL, "width" INTEGER, "height" INTEGER, "file_size_bytes" BIGINT NOT NULL, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "artwork_assets_pkey" PRIMARY KEY ("id") ); CREATE TABLE "upload_sessions" ( "id" UUID NOT NULL DEFAULT gen_random_uuid(), "device_id" UUID NOT NULL, "expected_sha256" TEXT NOT NULL, "expected_size_bytes" BIGINT NOT NULL, "received_bytes" BIGINT NOT NULL DEFAULT 0, "temp_storage_path" TEXT NOT NULL, "status" "UploadSessionStatus" NOT NULL DEFAULT 'PENDING', "expires_at" TIMESTAMP(3) NOT NULL, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "upload_sessions_pkey" PRIMARY KEY ("id") ); CREATE TABLE "library_events" ( "id" BIGSERIAL NOT NULL, "entity_type" "EntityType" NOT NULL, "entity_id" UUID NOT NULL, "action" "EventAction" NOT NULL, "payload_version" INTEGER NOT NULL DEFAULT 1, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "library_events_pkey" PRIMARY KEY ("id") ); CREATE TABLE "device_sync_cursors" ( "device_id" UUID NOT NULL, "last_event_id" BIGINT NOT NULL DEFAULT 0, "last_full_sync_at" TIMESTAMP(3), "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "device_sync_cursors_pkey" PRIMARY KEY ("device_id") ); CREATE UNIQUE INDEX "tracks_primary_audio_asset_id_key" ON "tracks"("primary_audio_asset_id"); CREATE UNIQUE INDEX "tracks_artwork_asset_id_key" ON "tracks"("artwork_asset_id"); CREATE UNIQUE INDEX "audio_assets_sha256_key" ON "audio_assets"("sha256"); CREATE UNIQUE INDEX "audio_assets_storage_key" ON "audio_assets"("storage_key"); CREATE UNIQUE INDEX "artwork_assets_sha256_key" ON "artwork_assets"("sha256"); CREATE UNIQUE INDEX "artwork_assets_storage_key" ON "artwork_assets"("storage_key"); ALTER TABLE "audio_assets" ADD CONSTRAINT "audio_assets_track_id_fkey" FOREIGN KEY ("track_id") REFERENCES "tracks"("id") ON DELETE SET NULL ON UPDATE CASCADE; ALTER TABLE "audio_assets" ADD CONSTRAINT "audio_assets_source_device_id_fkey" FOREIGN KEY ("source_device_id") REFERENCES "devices"("id") ON DELETE SET NULL ON UPDATE CASCADE; ALTER TABLE "tracks" ADD CONSTRAINT "tracks_primary_audio_asset_id_fkey" FOREIGN KEY ("primary_audio_asset_id") REFERENCES "audio_assets"("id") ON DELETE SET NULL ON UPDATE CASCADE; ALTER TABLE "tracks" ADD CONSTRAINT "tracks_artwork_asset_id_fkey" FOREIGN KEY ("artwork_asset_id") REFERENCES "artwork_assets"("id") ON DELETE SET NULL ON UPDATE CASCADE; ALTER TABLE "upload_sessions" ADD CONSTRAINT "upload_sessions_device_id_fkey" FOREIGN KEY ("device_id") REFERENCES "devices"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "device_sync_cursors" ADD CONSTRAINT "device_sync_cursors_device_id_fkey" FOREIGN KEY ("device_id") REFERENCES "devices"("id") ON DELETE CASCADE ON UPDATE CASCADE;