feat: init
This commit is contained in:
@@ -0,0 +1,320 @@
|
||||
-- 009: IM Features — Discord/Slack-class messaging support
|
||||
--
|
||||
-- New tables:
|
||||
-- user_presence, user_activity,
|
||||
-- channel_category, channel_permission_overwrite, im_integration,
|
||||
-- message_attachment, message_embed, message_draft, message_pin,
|
||||
-- message_edit_history, saved_message, thread_read_state,
|
||||
-- custom_emoji
|
||||
|
||||
-- ============================================================
|
||||
-- 1. User Presence
|
||||
-- ============================================================
|
||||
|
||||
-- models/users/user_presence.rs → user_presence
|
||||
CREATE TABLE IF NOT EXISTS user_presence (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
status TEXT NOT NULL,
|
||||
custom_status_text TEXT NULL,
|
||||
custom_status_emoji TEXT NULL,
|
||||
device_type TEXT NULL,
|
||||
ip_address TEXT NULL,
|
||||
last_active_at TIMESTAMPTZ NOT NULL,
|
||||
last_seen_at TIMESTAMPTZ NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_user_presence_user_id UNIQUE (user_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_presence_status ON user_presence (status);
|
||||
|
||||
-- ============================================================
|
||||
-- 2. User Activity (Rich Presence)
|
||||
-- ============================================================
|
||||
|
||||
-- models/users/user_activity.rs → user_activity
|
||||
CREATE TABLE IF NOT EXISTS user_activity (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
activity_type TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
details TEXT NULL,
|
||||
state TEXT NULL,
|
||||
application_id TEXT NULL,
|
||||
assets JSONB NULL,
|
||||
party_id TEXT NULL,
|
||||
party_current_size INTEGER NULL,
|
||||
party_max_size INTEGER NULL,
|
||||
large_image_url TEXT NULL,
|
||||
small_image_url TEXT NULL,
|
||||
start_at TIMESTAMPTZ NULL,
|
||||
end_at TIMESTAMPTZ NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_activity_user_id ON user_activity (user_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 3. Channel Categories
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/channel_categories.rs → channel_category
|
||||
CREATE TABLE IF NOT EXISTS channel_category (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
collapsed BOOLEAN NOT NULL,
|
||||
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_category_workspace_id ON channel_category (workspace_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 4. ALTER channel — add category_id (after channel_category exists)
|
||||
-- ============================================================
|
||||
|
||||
ALTER TABLE channel ADD COLUMN IF NOT EXISTS category_id UUID NULL REFERENCES channel_category(id) ON DELETE SET NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_category_id ON channel (category_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 5. Channel Permission Overwrites
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/channel_permission_overwrites.rs → channel_permission_overwrite
|
||||
CREATE TABLE IF NOT EXISTS channel_permission_overwrite (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
target_type TEXT NOT NULL,
|
||||
target_id UUID NOT NULL,
|
||||
allow TEXT[] NOT NULL,
|
||||
deny TEXT[] NOT NULL,
|
||||
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_channel_perm_overwrite UNIQUE (channel_id, target_type, target_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_perm_overwrite_channel_id ON channel_permission_overwrite (channel_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_perm_overwrite_target ON channel_permission_overwrite (target_type, target_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 6. IM Integrations (External Bridge)
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/im_integrations.rs → im_integration
|
||||
CREATE TABLE IF NOT EXISTS im_integration (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
||||
provider TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
external_workspace_id TEXT NULL,
|
||||
internal_channel_id UUID NULL REFERENCES channel(id) ON DELETE SET NULL,
|
||||
external_channel_id TEXT NULL,
|
||||
bot_token_ciphertext TEXT NULL,
|
||||
webhook_url TEXT NULL,
|
||||
sync_direction TEXT NOT NULL,
|
||||
user_mapping JSONB NULL,
|
||||
enabled BOOLEAN NOT NULL,
|
||||
installed_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
last_sync_at TIMESTAMPTZ NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_im_integration_workspace_id ON im_integration (workspace_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_im_integration_internal_channel_id ON im_integration (internal_channel_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_im_integration_provider ON im_integration (provider);
|
||||
|
||||
-- ============================================================
|
||||
-- 7. Message Attachments
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/message_attachments.rs → message_attachment
|
||||
CREATE TABLE IF NOT EXISTS message_attachment (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
message_id UUID NOT NULL REFERENCES message(id) ON DELETE CASCADE,
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
filename TEXT NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
proxy_url TEXT NULL,
|
||||
size_bytes BIGINT NOT NULL,
|
||||
mime_type TEXT NOT NULL,
|
||||
width INTEGER NULL,
|
||||
height INTEGER NULL,
|
||||
duration_ms BIGINT NULL,
|
||||
thumbnail_url TEXT NULL,
|
||||
blurhash TEXT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_attachment_message_id ON message_attachment (message_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_attachment_channel_id ON message_attachment (channel_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 8. Message Embeds (Rich Text / Link Previews)
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/message_embeds.rs → message_embed
|
||||
CREATE TABLE IF NOT EXISTS message_embed (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
message_id UUID NOT NULL REFERENCES message(id) ON DELETE CASCADE,
|
||||
embed_type TEXT NOT NULL,
|
||||
title TEXT NULL,
|
||||
description TEXT NULL,
|
||||
url TEXT NULL,
|
||||
author_name TEXT NULL,
|
||||
author_url TEXT NULL,
|
||||
author_icon_url TEXT NULL,
|
||||
thumbnail_url TEXT NULL,
|
||||
thumbnail_width INTEGER NULL,
|
||||
thumbnail_height INTEGER NULL,
|
||||
image_url TEXT NULL,
|
||||
image_width INTEGER NULL,
|
||||
image_height INTEGER NULL,
|
||||
video_url TEXT NULL,
|
||||
video_width INTEGER NULL,
|
||||
video_height INTEGER NULL,
|
||||
color INTEGER NULL,
|
||||
fields JSONB NULL,
|
||||
footer_text TEXT NULL,
|
||||
footer_icon_url TEXT NULL,
|
||||
provider_name TEXT NULL,
|
||||
provider_url TEXT NULL,
|
||||
"timestamp" TIMESTAMPTZ NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_embed_message_id ON message_embed (message_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 9. Message Drafts
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/message_drafts.rs → message_draft
|
||||
CREATE TABLE IF NOT EXISTS message_draft (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
thread_id UUID NULL,
|
||||
reply_to_message_id UUID NULL,
|
||||
content TEXT NOT NULL,
|
||||
attachments JSONB NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_message_draft_user_channel UNIQUE (user_id, channel_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_draft_user_id ON message_draft (user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_draft_channel_id ON message_draft (channel_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 10. Custom Emojis
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/custom_emojis.rs → custom_emoji
|
||||
CREATE TABLE IF NOT EXISTS custom_emoji (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
animated BOOLEAN NOT NULL,
|
||||
managed BOOLEAN NOT NULL,
|
||||
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_custom_emoji_workspace_name UNIQUE (workspace_id, name)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_custom_emoji_workspace_id ON custom_emoji (workspace_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 11. Message Pins
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/message_pins.rs → message_pin
|
||||
CREATE TABLE IF NOT EXISTS message_pin (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
message_id UUID NOT NULL REFERENCES message(id) ON DELETE CASCADE,
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
pinned_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
pinned_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_message_pin_message_id UNIQUE (message_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_pin_channel_id ON message_pin (channel_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_pin_message_id ON message_pin (message_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 12. Message Edit History
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/message_edit_history.rs → message_edit_history
|
||||
CREATE TABLE IF NOT EXISTS message_edit_history (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
message_id UUID NOT NULL REFERENCES message(id) ON DELETE CASCADE,
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
previous_body TEXT NOT NULL,
|
||||
edited_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
edited_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_edit_history_message_id ON message_edit_history (message_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_edit_history_channel_id ON message_edit_history (channel_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 13. Saved Messages
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/saved_messages.rs → saved_message
|
||||
CREATE TABLE IF NOT EXISTS saved_message (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
message_id UUID NOT NULL REFERENCES message(id) ON DELETE CASCADE,
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
note TEXT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_saved_message_user_message UNIQUE (user_id, message_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_saved_message_user_id ON saved_message (user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_saved_message_message_id ON saved_message (message_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 14. Thread Read States
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/thread_read_states.rs → thread_read_state
|
||||
CREATE TABLE IF NOT EXISTS thread_read_state (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
thread_id UUID NOT NULL,
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
last_read_message_id UUID NULL,
|
||||
last_read_at TIMESTAMPTZ NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_thread_read_state_user_thread UNIQUE (user_id, thread_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_thread_read_state_user_id ON thread_read_state (user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_thread_read_state_thread_id ON thread_read_state (thread_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_thread_read_state_channel_id ON thread_read_state (channel_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 15. Triggers — auto-refresh updated_at
|
||||
-- ============================================================
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_user_presence_updated_at ON user_presence;
|
||||
CREATE TRIGGER trg_user_presence_updated_at BEFORE UPDATE ON user_presence FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_user_activity_updated_at ON user_activity;
|
||||
CREATE TRIGGER trg_user_activity_updated_at BEFORE UPDATE ON user_activity FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_channel_category_updated_at ON channel_category;
|
||||
CREATE TRIGGER trg_channel_category_updated_at BEFORE UPDATE ON channel_category FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_channel_permission_overwrite_updated_at ON channel_permission_overwrite;
|
||||
CREATE TRIGGER trg_channel_permission_overwrite_updated_at BEFORE UPDATE ON channel_permission_overwrite FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_im_integration_updated_at ON im_integration;
|
||||
CREATE TRIGGER trg_im_integration_updated_at BEFORE UPDATE ON im_integration FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_message_draft_updated_at ON message_draft;
|
||||
CREATE TRIGGER trg_message_draft_updated_at BEFORE UPDATE ON message_draft FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_custom_emoji_updated_at ON custom_emoji;
|
||||
CREATE TRIGGER trg_custom_emoji_updated_at BEFORE UPDATE ON custom_emoji FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_thread_read_state_updated_at ON thread_read_state;
|
||||
CREATE TRIGGER trg_thread_read_state_updated_at BEFORE UPDATE ON thread_read_state FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
Reference in New Issue
Block a user