mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
Client UI: - Design system: Colors, Typography, Controls resource dictionaries - Message actions: reply compose bar, hover edit/delete/reply buttons - Rich content: code blocks, attachments, system messages, content parser - Server strip: 72px sidebar with server icons, home button, add server - Status picker: popup for changing online/idle/dnd/invisible status - ConnectPage: server health check dots with auto-refresh - User popup: profile card with banner, avatar, roles, member since - Emoji picker: 6 categories, search, grid of Unicode emojis - Settings overlay: full-screen with sidebar navigation - Friends/DM view: sidebar + friends list with tabs (online/all/pending) - Toast notifications: auto-dismiss after 3s with fade animation Models & services: - Attachment model added to Message, ApiMessage, ChatMessagePayload - EditMessageAsync, DeleteMessageAsync, SendStatusChangeAsync APIs - MessageContentParser (code blocks, inline code, bold, italic) - EmojiData, ToastService, HealthStatusToBrushConverter Server (from prior session): - Voice room management, SFU, speaker detection - ACME/TLS support, config improvements - Protocol and schema updates Tests: 331 passing (61 converter + 24 voice service + 34 voice VM + 41 parser + 9 edit/delete + existing)
10 KiB
10 KiB
Database Schema (SQLite)
Single file: data/chatserver.db. WAL mode enabled.
Migrations run automatically on startup.
Users
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE COLLATE NOCASE,
password TEXT NOT NULL, -- bcrypt hash
avatar TEXT, -- filename in uploads/ or NULL
role_id INTEGER NOT NULL DEFAULT 4 REFERENCES roles(id),
totp_secret TEXT, -- encrypted TOTP secret or NULL if 2FA disabled
status TEXT NOT NULL DEFAULT 'offline', -- online, idle, dnd, offline
created_at TEXT NOT NULL DEFAULT (datetime('now')),
last_seen TEXT,
banned INTEGER NOT NULL DEFAULT 0,
ban_reason TEXT,
ban_expires TEXT -- NULL = permanent, datetime = temp ban
);
Sessions
CREATE TABLE sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token TEXT NOT NULL UNIQUE, -- 256-bit random, hex encoded
device TEXT, -- user-agent or client identifier
ip_address TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
last_used TEXT NOT NULL DEFAULT (datetime('now')),
expires_at TEXT NOT NULL -- 30 days from creation
);
CREATE INDEX idx_sessions_token ON sessions(token);
CREATE INDEX idx_sessions_user ON sessions(user_id);
Roles
CREATE TABLE roles (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
color TEXT, -- hex color e.g. #E74C3C, NULL for default
permissions INTEGER NOT NULL DEFAULT 0, -- bitfield
position INTEGER NOT NULL DEFAULT 0, -- hierarchy: higher = more power
is_default INTEGER NOT NULL DEFAULT 0 -- 1 = assigned to new users
);
-- Default roles (inserted on first run)
-- Owner: permissions = 0x7FFFFFFF (all bits set)
-- Admin: permissions = 0x3FFFFFFF
-- Moderator: permissions = 0x000FFFFF
-- Member: permissions = 0x00000663
Permission Bitfield
Bit 0: SEND_MESSAGES (0x1)
Bit 1: READ_MESSAGES (0x2)
Bit 5: ATTACH_FILES (0x20)
Bit 6: ADD_REACTIONS (0x40)
Bit 8: USE_SOUNDBOARD (0x100)
Bit 9: CONNECT_VOICE (0x200)
Bit 10: SPEAK_VOICE (0x400)
Bit 11: USE_VIDEO (0x800)
Bit 12: SHARE_SCREEN (0x1000)
Bit 16: MANAGE_MESSAGES (0x10000) -- delete others' messages, pin
Bit 17: MANAGE_CHANNELS (0x20000)
Bit 18: KICK_MEMBERS (0x40000)
Bit 19: BAN_MEMBERS (0x80000)
Bit 20: MUTE_MEMBERS (0x100000) -- server mute/deafen
Bit 24: MANAGE_ROLES (0x1000000)
Bit 25: MANAGE_SERVER (0x2000000)
Bit 26: MANAGE_INVITES (0x4000000)
Bit 27: VIEW_AUDIT_LOG (0x8000000)
Bit 30: ADMINISTRATOR (0x40000000) -- bypasses all checks
Channels
CREATE TABLE channels (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
type TEXT NOT NULL DEFAULT 'text', -- text, voice, announcement
category TEXT, -- category name for grouping
topic TEXT, -- channel description
position INTEGER NOT NULL DEFAULT 0,
slow_mode INTEGER NOT NULL DEFAULT 0, -- seconds, 0=off
archived INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
voice_max_users INTEGER NOT NULL DEFAULT 0, -- 0 = unlimited
voice_quality TEXT, -- low|medium|high; NULL=default
mixing_threshold INTEGER, -- NULL = server default
voice_max_video INTEGER NOT NULL DEFAULT 10 -- max video streams
);
Channel Permission Overrides
CREATE TABLE channel_overrides (
id INTEGER PRIMARY KEY AUTOINCREMENT,
channel_id INTEGER NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
role_id INTEGER NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
allow INTEGER NOT NULL DEFAULT 0, -- permission bits to grant
deny INTEGER NOT NULL DEFAULT 0, -- permission bits to revoke
UNIQUE(channel_id, role_id)
);
Messages
CREATE TABLE messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
channel_id INTEGER NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
user_id INTEGER NOT NULL REFERENCES users(id),
content TEXT NOT NULL,
reply_to INTEGER REFERENCES messages(id) ON DELETE SET NULL,
edited_at TEXT,
deleted INTEGER NOT NULL DEFAULT 0, -- 1 = soft deleted
pinned INTEGER NOT NULL DEFAULT 0,
timestamp TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX idx_messages_channel ON messages(channel_id, id DESC);
CREATE INDEX idx_messages_user ON messages(user_id);
Message Full-Text Search
CREATE VIRTUAL TABLE messages_fts USING fts5(
content,
content='messages',
content_rowid='id'
);
-- Triggers to keep FTS in sync
CREATE TRIGGER messages_ai AFTER INSERT ON messages BEGIN
INSERT INTO messages_fts(rowid, content) VALUES (new.id, new.content);
END;
CREATE TRIGGER messages_ad AFTER DELETE ON messages BEGIN
INSERT INTO messages_fts(messages_fts, rowid, content)
VALUES('delete', old.id, old.content);
END;
CREATE TRIGGER messages_au AFTER UPDATE ON messages BEGIN
INSERT INTO messages_fts(messages_fts, rowid, content)
VALUES('delete', old.id, old.content);
INSERT INTO messages_fts(rowid, content) VALUES (new.id, new.content);
END;
Attachments
CREATE TABLE attachments (
id TEXT PRIMARY KEY, -- UUID
message_id INTEGER REFERENCES messages(id) ON DELETE CASCADE,
filename TEXT NOT NULL, -- original filename
stored_as TEXT NOT NULL, -- UUID filename on disk
mime_type TEXT NOT NULL,
size INTEGER NOT NULL, -- bytes
uploaded_at TEXT NOT NULL DEFAULT (datetime('now'))
);
Reactions
CREATE TABLE reactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
message_id INTEGER NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
emoji TEXT NOT NULL,
UNIQUE(message_id, user_id, emoji)
);
Invites
CREATE TABLE invites (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT NOT NULL UNIQUE, -- random token
created_by INTEGER NOT NULL REFERENCES users(id),
redeemed_by INTEGER REFERENCES users(id),
max_uses INTEGER, -- NULL = unlimited
use_count INTEGER NOT NULL DEFAULT 0,
expires_at TEXT, -- NULL = never
created_at TEXT NOT NULL DEFAULT (datetime('now')),
revoked INTEGER NOT NULL DEFAULT 0
);
CREATE INDEX idx_invites_code ON invites(code);
Read State (unread tracking)
CREATE TABLE read_states (
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
channel_id INTEGER NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
last_message_id INTEGER NOT NULL DEFAULT 0,
mention_count INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (user_id, channel_id)
);
Audit Log
CREATE TABLE audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER REFERENCES users(id),
action TEXT NOT NULL, -- e.g. user_ban, channel_create
target_type TEXT, -- user, channel, message, role, invite
target_id INTEGER,
details TEXT, -- JSON with extra context
timestamp TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX idx_audit_timestamp ON audit_log(timestamp DESC);
Login Attempts (rate limiting)
CREATE TABLE login_attempts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ip_address TEXT NOT NULL,
username TEXT,
success INTEGER NOT NULL DEFAULT 0,
timestamp TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX idx_login_ip ON login_attempts(ip_address, timestamp);
Server Settings (key-value)
CREATE TABLE settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
-- Default settings inserted on first run:
-- server_name, server_icon, motd, max_upload_bytes, voice_quality,
-- require_2fa, registration_open (always 0), backup_schedule, backup_retention
Custom Emoji
CREATE TABLE emoji (
id INTEGER PRIMARY KEY AUTOINCREMENT,
shortcode TEXT NOT NULL UNIQUE, -- e.g. :pepe:
filename TEXT NOT NULL, -- stored in uploads/emoji/
uploaded_by INTEGER NOT NULL REFERENCES users(id),
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
Soundboard
CREATE TABLE sounds (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
filename TEXT NOT NULL, -- stored in uploads/sounds/
duration_ms INTEGER NOT NULL,
uploaded_by INTEGER NOT NULL REFERENCES users(id),
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
Voice States
CREATE TABLE voice_states (
user_id INTEGER PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
channel_id INTEGER NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
muted INTEGER NOT NULL DEFAULT 0,
deafened INTEGER NOT NULL DEFAULT 0,
speaking INTEGER NOT NULL DEFAULT 0,
camera INTEGER NOT NULL DEFAULT 0,
screenshare INTEGER NOT NULL DEFAULT 0,
joined_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX idx_voice_states_channel ON voice_states(channel_id);
On startup: DELETE FROM voice_states; clears stale state from previous run.
Notes
- All datetimes stored as ISO 8601 UTC strings.
- Enable WAL mode on connection:
PRAGMA journal_mode=WAL; - Enable foreign keys:
PRAGMA foreign_keys=ON; - Use
modernc.org/sqlite(pure Go, no CGO needed). - Migrations: schema version in
settings, apply incremental SQL on startup.