merge: bring notifications-realtime + modules into master (preserves files attachments)
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
-- Per-user notifications + persisted @mentions.
|
||||
-- Created by card chat messages (card_messages).
|
||||
--
|
||||
-- Kinds:
|
||||
-- mention — user mentioned via @username in body
|
||||
-- assigned_chat — user is the card's assignee and someone else commented
|
||||
-- reply — user previously commented on this card (or is requester)
|
||||
-- A row is created per (recipient_user, message). The kind chosen is the
|
||||
-- highest priority among those that apply: mention > assigned_chat > reply.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS notifications (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL,
|
||||
card_id TEXT NOT NULL,
|
||||
message_id TEXT NOT NULL,
|
||||
kind TEXT NOT NULL,
|
||||
actor_id TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
read_at TEXT,
|
||||
FOREIGN KEY (card_id) REFERENCES cards(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (message_id) REFERENCES card_messages(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_notifications_user_unread
|
||||
ON notifications(user_id, read_at, created_at DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_notifications_user_created
|
||||
ON notifications(user_id, created_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS card_mentions (
|
||||
id TEXT PRIMARY KEY,
|
||||
card_id TEXT NOT NULL,
|
||||
message_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
FOREIGN KEY (card_id) REFERENCES cards(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (message_id) REFERENCES card_messages(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_card_mentions_user ON card_mentions(user_id, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_card_mentions_card ON card_mentions(card_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_card_mentions_message ON card_mentions(message_id);
|
||||
@@ -0,0 +1,45 @@
|
||||
-- Outbound modules (integrations): kanban events → external systems.
|
||||
--
|
||||
-- A module is a configured subscription. The dispatcher (modules.go)
|
||||
-- subscribes to the EventHub and, for each event whose type matches the
|
||||
-- module's event_filter, calls the kind-specific handler with the
|
||||
-- decrypted config.
|
||||
--
|
||||
-- Tokens / secrets are encrypted with AES-GCM at rest. The key is derived
|
||||
-- from the KANBAN_MODULE_KEY environment variable (sha256 of the value).
|
||||
|
||||
CREATE TABLE IF NOT EXISTS modules (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
kind TEXT NOT NULL, -- 'jira' | 'webhook' | …
|
||||
enabled INTEGER NOT NULL DEFAULT 1,
|
||||
event_filter TEXT NOT NULL, -- comma-separated event types
|
||||
config_cipher BLOB NOT NULL, -- AES-GCM ciphertext of JSON
|
||||
config_nonce BLOB NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS module_logs (
|
||||
id TEXT PRIMARY KEY,
|
||||
module_id TEXT NOT NULL,
|
||||
event_type TEXT NOT NULL,
|
||||
card_id TEXT,
|
||||
status INTEGER, -- HTTP status or 0 if pre-flight
|
||||
duration_ms INTEGER,
|
||||
error TEXT,
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_module_logs_module_created
|
||||
ON module_logs(module_id, created_at DESC);
|
||||
|
||||
-- jira_key: 1:1 link between a kanban card and its Jira issue. Empty
|
||||
-- string when the card has not yet been synced to Jira.
|
||||
ALTER TABLE cards ADD COLUMN jira_key TEXT NOT NULL DEFAULT '';
|
||||
|
||||
-- is_admin: gates /api/modules access and the Modulos menu item.
|
||||
-- Bootstrap: egutierrez (the initial admin) is marked admin so the
|
||||
-- feature is reachable on first deploy. Other users start as non-admin.
|
||||
ALTER TABLE users ADD COLUMN is_admin INTEGER NOT NULL DEFAULT 0;
|
||||
UPDATE users SET is_admin = 1 WHERE username = 'egutierrez';
|
||||
@@ -0,0 +1,26 @@
|
||||
-- Per-user MCP access tokens. Users mint tokens from the settings UI and
|
||||
-- paste them into their local Claude (`claude mcp add --transport http ...`).
|
||||
-- The plaintext token is shown ONCE at creation time; we only store the hash.
|
||||
--
|
||||
-- token_hash is a SHA-256 hex digest of the plaintext token. Lookup on
|
||||
-- incoming requests: hash the bearer, look up the row, accept if not revoked.
|
||||
--
|
||||
-- revoked_at is NULL for active tokens. Tokens are never deleted (audit
|
||||
-- trail); revocation is a soft delete.
|
||||
CREATE TABLE IF NOT EXISTS mcp_tokens (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
token_hash TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL,
|
||||
last_used_at TEXT,
|
||||
revoked_at TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_mcp_tokens_user_active
|
||||
ON mcp_tokens(user_id)
|
||||
WHERE revoked_at IS NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_mcp_tokens_hash_active
|
||||
ON mcp_tokens(token_hash)
|
||||
WHERE revoked_at IS NULL;
|
||||
Reference in New Issue
Block a user