-- 030_files_management.sql -- File Management (Origin Standard) -- === FILES === CREATE TABLE public.files ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE, -- File Info filename VARCHAR(255) NOT NULL, original_filename VARCHAR(255) NOT NULL, mime_type VARCHAR(100) NOT NULL, file_size BIGINT NOT NULL, -- Storage storage_path TEXT NOT NULL, storage_provider VARCHAR(50) NOT NULL DEFAULT 's3', bucket_name VARCHAR(255), -- URLs url TEXT NOT NULL, thumbnail_url TEXT, -- Metadata file_hash VARCHAR(64), metadata JSONB, -- Processing is_processed BOOLEAN NOT NULL DEFAULT false, processed_at TIMESTAMPTZ, processing_error TEXT, -- Security virus_scanned BOOLEAN NOT NULL DEFAULT false, virus_scan_result VARCHAR(50), virus_scanned_at TIMESTAMPTZ, -- Visibility is_public BOOLEAN NOT NULL DEFAULT false, -- Timestamps created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ, CONSTRAINT chk_files_size_positive CHECK (file_size > 0) ); CREATE INDEX idx_files_user_id ON public.files(user_id); CREATE INDEX idx_files_mime_type ON public.files(mime_type); CREATE INDEX idx_files_file_hash ON public.files(file_hash) WHERE file_hash IS NOT NULL; CREATE INDEX idx_files_created_at_desc ON public.files(created_at DESC); -- === FILE UPLOADS === CREATE TABLE public.file_uploads ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE, -- Upload Info filename VARCHAR(255) NOT NULL, file_size BIGINT NOT NULL, mime_type VARCHAR(100) NOT NULL, -- Progress bytes_uploaded BIGINT NOT NULL DEFAULT 0, chunks_uploaded INTEGER NOT NULL DEFAULT 0, total_chunks INTEGER, -- Status status VARCHAR(50) NOT NULL DEFAULT 'pending', -- Storage storage_key TEXT, upload_id TEXT, -- Metadata metadata JSONB, -- Timestamps created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), expires_at TIMESTAMPTZ NOT NULL, CONSTRAINT chk_file_uploads_bytes_uploaded CHECK (bytes_uploaded >= 0 AND bytes_uploaded <= file_size) ); CREATE INDEX idx_file_uploads_user_id ON public.file_uploads(user_id); CREATE INDEX idx_file_uploads_status ON public.file_uploads(status); CREATE INDEX idx_file_uploads_expires_at ON public.file_uploads(expires_at); -- === FILE METADATA === CREATE TABLE public.file_metadata ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), file_id UUID NOT NULL REFERENCES public.files(id) ON DELETE CASCADE, -- Audio title VARCHAR(255), artist VARCHAR(255), album VARCHAR(255), genre VARCHAR(100), year INTEGER, duration INTEGER, bitrate INTEGER, sample_rate INTEGER, channels INTEGER, codec VARCHAR(50), -- Image width INTEGER, height INTEGER, format VARCHAR(50), -- Video video_codec VARCHAR(50), audio_codec VARCHAR(50), framerate DECIMAL(10,2), -- Advanced bpm INTEGER, musical_key VARCHAR(10), time_signature VARCHAR(10), -- Raw raw_metadata JSONB, -- Timestamps extracted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT uq_file_metadata_file_id UNIQUE (file_id) ); CREATE INDEX idx_file_metadata_genre ON public.file_metadata(genre) WHERE genre IS NOT NULL; CREATE INDEX idx_file_metadata_duration ON public.file_metadata(duration) WHERE duration IS NOT NULL; -- === FILE CONVERSIONS === CREATE TABLE public.file_conversions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), source_file_id UUID NOT NULL REFERENCES public.files(id) ON DELETE CASCADE, converted_file_id UUID REFERENCES public.files(id) ON DELETE SET NULL, -- Conversion target_format VARCHAR(50) NOT NULL, target_quality VARCHAR(50), -- Status status VARCHAR(50) NOT NULL DEFAULT 'pending', progress INTEGER NOT NULL DEFAULT 0, -- Error error_message TEXT, -- Timestamps created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), completed_at TIMESTAMPTZ ); CREATE INDEX idx_file_conversions_source_file_id ON public.file_conversions(source_file_id); CREATE INDEX idx_file_conversions_status ON public.file_conversions(status);