66 lines
3.0 KiB
SQL
66 lines
3.0 KiB
SQL
-- ==========================================
|
|
-- Chat Switchboard — 013 Files
|
|
-- ==========================================
|
|
-- Unified files table replacing attachments.
|
|
-- Handles both upgrade (attachments exists) and fresh install.
|
|
-- ==========================================
|
|
|
|
CREATE TABLE IF NOT EXISTS files (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
channel_id UUID REFERENCES channels(id) ON DELETE CASCADE,
|
|
message_id UUID REFERENCES messages(id) ON DELETE SET NULL,
|
|
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
project_id UUID REFERENCES projects(id) ON DELETE SET NULL,
|
|
origin VARCHAR(20) NOT NULL DEFAULT 'user_upload'
|
|
CHECK (origin IN ('user_upload', 'tool_output', 'system')),
|
|
filename VARCHAR(255) NOT NULL,
|
|
content_type VARCHAR(127) NOT NULL DEFAULT 'application/octet-stream',
|
|
size_bytes BIGINT NOT NULL DEFAULT 0,
|
|
storage_key TEXT NOT NULL,
|
|
display_hint VARCHAR(20) NOT NULL DEFAULT 'download'
|
|
CHECK (display_hint IN ('inline', 'download', 'thumbnail')),
|
|
extracted_text TEXT,
|
|
metadata JSONB DEFAULT '{}'::jsonb,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- Ensure attachments table exists so the INSERT is always valid.
|
|
-- Upgrade: no-op (table already has data). Fresh install: empty table.
|
|
CREATE TABLE IF NOT EXISTS attachments (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
channel_id UUID, user_id UUID, message_id UUID, project_id UUID,
|
|
filename VARCHAR(255), content_type VARCHAR(127), size_bytes BIGINT,
|
|
storage_key TEXT, extracted_text TEXT, metadata JSONB, created_at TIMESTAMPTZ
|
|
);
|
|
|
|
INSERT INTO files (
|
|
id, channel_id, message_id, user_id, project_id,
|
|
origin, filename, content_type, size_bytes, storage_key,
|
|
display_hint, extracted_text, metadata, created_at, updated_at
|
|
)
|
|
SELECT
|
|
id, channel_id, message_id, user_id, project_id,
|
|
'user_upload',
|
|
filename, content_type, size_bytes, storage_key,
|
|
CASE
|
|
WHEN content_type LIKE 'image/%' THEN 'inline'
|
|
WHEN content_type LIKE 'video/%' THEN 'inline'
|
|
WHEN content_type LIKE 'audio/%' THEN 'inline'
|
|
WHEN content_type = 'application/pdf' THEN 'thumbnail'
|
|
WHEN content_type LIKE 'text/%' THEN 'inline'
|
|
ELSE 'download'
|
|
END,
|
|
extracted_text, metadata, created_at, created_at
|
|
FROM attachments
|
|
ON CONFLICT (id) DO NOTHING;
|
|
|
|
DROP TABLE IF EXISTS attachments;
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_files_channel ON files(channel_id);
|
|
CREATE INDEX IF NOT EXISTS idx_files_message ON files(message_id);
|
|
CREATE INDEX IF NOT EXISTS idx_files_user ON files(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_files_project ON files(project_id) WHERE project_id IS NOT NULL;
|
|
CREATE INDEX IF NOT EXISTS idx_files_origin ON files(origin);
|
|
CREATE INDEX IF NOT EXISTS idx_files_orphan ON files(created_at) WHERE message_id IS NULL AND origin = 'user_upload';
|