182 lines
6.7 KiB
Python
182 lines
6.7 KiB
Python
from __future__ import annotations
|
|
|
|
import sqlite3
|
|
from contextlib import contextmanager
|
|
from pathlib import Path
|
|
from typing import Any, Iterator
|
|
|
|
|
|
def utc_now() -> str:
|
|
from datetime import datetime, timezone
|
|
|
|
return datetime.now(timezone.utc).replace(microsecond=0).isoformat()
|
|
|
|
|
|
def dict_factory(cursor: sqlite3.Cursor, row: sqlite3.Row) -> dict[str, Any]:
|
|
return {col[0]: row[idx] for idx, col in enumerate(cursor.description)}
|
|
|
|
|
|
class Database:
|
|
def __init__(self, path: str) -> None:
|
|
self.path = Path(path)
|
|
self.path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
def connect(self) -> sqlite3.Connection:
|
|
conn = sqlite3.connect(self.path)
|
|
conn.row_factory = dict_factory
|
|
conn.execute("PRAGMA foreign_keys = ON")
|
|
return conn
|
|
|
|
@contextmanager
|
|
def session(self) -> Iterator[sqlite3.Connection]:
|
|
conn = self.connect()
|
|
try:
|
|
yield conn
|
|
conn.commit()
|
|
finally:
|
|
conn.close()
|
|
|
|
def fetch_one(self, sql: str, params: tuple[Any, ...] = ()) -> dict[str, Any] | None:
|
|
with self.session() as conn:
|
|
return conn.execute(sql, params).fetchone()
|
|
|
|
def fetch_all(self, sql: str, params: tuple[Any, ...] = ()) -> list[dict[str, Any]]:
|
|
with self.session() as conn:
|
|
return list(conn.execute(sql, params).fetchall())
|
|
|
|
def execute(self, sql: str, params: tuple[Any, ...] = ()) -> None:
|
|
with self.session() as conn:
|
|
conn.execute(sql, params)
|
|
|
|
def init_schema(self) -> None:
|
|
schema = """
|
|
CREATE TABLE IF NOT EXISTS accounts (
|
|
id TEXT PRIMARY KEY,
|
|
username TEXT NOT NULL UNIQUE,
|
|
password_hash TEXT NOT NULL,
|
|
password_salt TEXT NOT NULL,
|
|
display_name TEXT NOT NULL,
|
|
role TEXT NOT NULL,
|
|
approval_status TEXT NOT NULL,
|
|
approved_by TEXT,
|
|
approved_at TEXT,
|
|
preferred_analysis_model_id TEXT,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS auth_tokens (
|
|
token TEXT PRIMARY KEY,
|
|
account_id TEXT NOT NULL,
|
|
created_at TEXT NOT NULL,
|
|
FOREIGN KEY(account_id) REFERENCES accounts(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS model_profiles (
|
|
id TEXT PRIMARY KEY,
|
|
owner_account_id TEXT,
|
|
name TEXT NOT NULL,
|
|
provider TEXT NOT NULL,
|
|
base_url TEXT NOT NULL,
|
|
api_key TEXT NOT NULL DEFAULT '',
|
|
model_name TEXT NOT NULL,
|
|
is_system INTEGER NOT NULL DEFAULT 0,
|
|
is_default INTEGER NOT NULL DEFAULT 0,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
FOREIGN KEY(owner_account_id) REFERENCES accounts(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS knowledge_bases (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT NOT NULL,
|
|
name TEXT NOT NULL,
|
|
description TEXT NOT NULL DEFAULT '',
|
|
fastgpt_dataset_id TEXT,
|
|
sync_status TEXT NOT NULL DEFAULT 'pending',
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
FOREIGN KEY(user_id) REFERENCES accounts(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS knowledge_documents (
|
|
id TEXT PRIMARY KEY,
|
|
knowledge_base_id TEXT NOT NULL,
|
|
title TEXT NOT NULL,
|
|
source_type TEXT NOT NULL,
|
|
source_url TEXT NOT NULL DEFAULT '',
|
|
transcript_text TEXT NOT NULL DEFAULT '',
|
|
style_summary TEXT NOT NULL DEFAULT '',
|
|
combined_text TEXT NOT NULL DEFAULT '',
|
|
fastgpt_collection_id TEXT NOT NULL DEFAULT '',
|
|
analysis_model_profile_id TEXT NOT NULL DEFAULT '',
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
FOREIGN KEY(knowledge_base_id) REFERENCES knowledge_bases(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS assistants (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT NOT NULL,
|
|
name TEXT NOT NULL,
|
|
description TEXT NOT NULL DEFAULT '',
|
|
system_prompt TEXT NOT NULL DEFAULT '',
|
|
generation_goal TEXT NOT NULL DEFAULT '',
|
|
fastgpt_app_key TEXT NOT NULL DEFAULT '',
|
|
model_profile_id TEXT NOT NULL DEFAULT '',
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
FOREIGN KEY(user_id) REFERENCES accounts(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS assistant_knowledge_bases (
|
|
assistant_id TEXT NOT NULL,
|
|
knowledge_base_id TEXT NOT NULL,
|
|
PRIMARY KEY (assistant_id, knowledge_base_id),
|
|
FOREIGN KEY(assistant_id) REFERENCES assistants(id) ON DELETE CASCADE,
|
|
FOREIGN KEY(knowledge_base_id) REFERENCES knowledge_bases(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS jobs (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT NOT NULL,
|
|
assistant_id TEXT,
|
|
knowledge_base_id TEXT NOT NULL,
|
|
source_type TEXT NOT NULL,
|
|
source_url TEXT,
|
|
title TEXT NOT NULL,
|
|
language TEXT NOT NULL DEFAULT 'auto',
|
|
status TEXT NOT NULL,
|
|
transcript_text TEXT NOT NULL DEFAULT '',
|
|
style_summary TEXT NOT NULL DEFAULT '',
|
|
fastgpt_collection_id TEXT NOT NULL DEFAULT '',
|
|
upload_status TEXT NOT NULL DEFAULT 'pending',
|
|
error TEXT NOT NULL DEFAULT '',
|
|
artifacts_json TEXT NOT NULL DEFAULT '{}',
|
|
analysis_model_profile_id TEXT NOT NULL DEFAULT '',
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
FOREIGN KEY(user_id) REFERENCES accounts(id) ON DELETE CASCADE,
|
|
FOREIGN KEY(assistant_id) REFERENCES assistants(id) ON DELETE SET NULL,
|
|
FOREIGN KEY(knowledge_base_id) REFERENCES knowledge_bases(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS app_updates (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
platform TEXT NOT NULL,
|
|
channel TEXT NOT NULL,
|
|
version_code INTEGER NOT NULL,
|
|
version_name TEXT NOT NULL,
|
|
min_supported_code INTEGER NOT NULL,
|
|
apk_url TEXT NOT NULL,
|
|
apk_sha256 TEXT NOT NULL DEFAULT '',
|
|
notes TEXT NOT NULL DEFAULT '',
|
|
force_update INTEGER NOT NULL DEFAULT 0,
|
|
is_active INTEGER NOT NULL DEFAULT 1,
|
|
published_at INTEGER NOT NULL,
|
|
created_by TEXT NOT NULL
|
|
);
|
|
"""
|
|
with self.session() as conn:
|
|
conn.executescript(schema)
|