119 lines
3.5 KiB
Python
119 lines
3.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""SQLite schema for Loogle MCP Hub."""
|
|
|
|
import os
|
|
import sqlite3
|
|
import threading
|
|
|
|
DB_PATH = os.environ.get("MCP_DB", "/data/loogle_mcp.db")
|
|
_local = threading.local()
|
|
|
|
SCHEMA = """
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
username TEXT UNIQUE NOT NULL,
|
|
password_hash TEXT NOT NULL,
|
|
is_admin INTEGER NOT NULL DEFAULT 0,
|
|
must_change_password INTEGER NOT NULL DEFAULT 0,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS oauth_clients (
|
|
client_id TEXT PRIMARY KEY,
|
|
client_name TEXT NOT NULL,
|
|
redirect_uris TEXT NOT NULL,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS oauth_codes (
|
|
code TEXT PRIMARY KEY,
|
|
client_id TEXT NOT NULL,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
redirect_uri TEXT NOT NULL,
|
|
scope TEXT NOT NULL,
|
|
code_challenge TEXT,
|
|
code_challenge_method TEXT,
|
|
expires_at TEXT NOT NULL,
|
|
used INTEGER NOT NULL DEFAULT 0
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS refresh_tokens (
|
|
token TEXT PRIMARY KEY,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
client_id TEXT NOT NULL,
|
|
scope TEXT NOT NULL,
|
|
expires_at TEXT NOT NULL,
|
|
revoked INTEGER NOT NULL DEFAULT 0
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS revoked_jtis (
|
|
jti TEXT PRIMARY KEY,
|
|
revoked_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
expires_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS audit_log (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
username TEXT NOT NULL,
|
|
tool_name TEXT NOT NULL,
|
|
resource_id TEXT,
|
|
detail TEXT,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now','localtime'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS indexed_documents (
|
|
doc_id INTEGER PRIMARY KEY,
|
|
title TEXT,
|
|
owner TEXT,
|
|
visibility TEXT NOT NULL DEFAULT 'family',
|
|
indexed_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
chunk_count INTEGER NOT NULL DEFAULT 0
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS indexed_gitea_files (
|
|
repo TEXT NOT NULL,
|
|
path TEXT NOT NULL,
|
|
sha TEXT,
|
|
owner TEXT NOT NULL,
|
|
visibility TEXT NOT NULL DEFAULT 'family',
|
|
chunk_count INTEGER NOT NULL DEFAULT 0,
|
|
indexed_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
PRIMARY KEY (repo, path)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_gitea_indexed_repo ON indexed_gitea_files(repo);
|
|
CREATE INDEX IF NOT EXISTS idx_gitea_indexed_at ON indexed_gitea_files(indexed_at DESC);
|
|
|
|
CREATE TABLE IF NOT EXISTS indexed_apps_records (
|
|
source TEXT NOT NULL,
|
|
record_id TEXT NOT NULL,
|
|
title TEXT,
|
|
owner TEXT NOT NULL DEFAULT 'family',
|
|
chunk_count INTEGER NOT NULL DEFAULT 0,
|
|
indexed_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
PRIMARY KEY (source, record_id)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_apps_indexed_source ON indexed_apps_records(source);
|
|
CREATE INDEX IF NOT EXISTS idx_apps_indexed_at ON indexed_apps_records(indexed_at DESC);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_log(created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_oauth_codes_expires ON oauth_codes(expires_at);
|
|
"""
|
|
|
|
|
|
def get_conn() -> sqlite3.Connection:
|
|
conn = getattr(_local, "conn", None)
|
|
if conn is None:
|
|
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
|
|
conn = sqlite3.connect(DB_PATH, timeout=30)
|
|
conn.row_factory = sqlite3.Row
|
|
conn.execute("PRAGMA journal_mode=WAL")
|
|
conn.execute("PRAGMA foreign_keys=ON")
|
|
_local.conn = conn
|
|
return conn
|
|
|
|
|
|
def init_db() -> None:
|
|
get_conn().executescript(SCHEMA)
|