intra_max_bot/app/db.py
2026-03-19 16:47:49 +03:00

117 lines
3.9 KiB
Python

from __future__ import annotations
import sqlite3
from contextlib import contextmanager
from pathlib import Path
from typing import Iterator
from app.config import Settings
from app.models import utc_now_iso
def ensure_db(settings: Settings) -> None:
db_path = Path(settings.db_path)
db_path.parent.mkdir(parents=True, exist_ok=True)
with sqlite3.connect(db_path) as conn:
conn.row_factory = sqlite3.Row
conn.executescript(
"""
PRAGMA foreign_keys = ON;
CREATE TABLE IF NOT EXISTS applications (
id INTEGER PRIMARY KEY AUTOINCREMENT,
customer_id INTEGER,
customer_name TEXT NOT NULL,
chat_id INTEGER,
category TEXT NOT NULL,
description TEXT NOT NULL,
attachments_json TEXT NOT NULL DEFAULT '[]',
status TEXT NOT NULL DEFAULT 'new',
external_sync_status TEXT NOT NULL DEFAULT 'not_sent',
external_id TEXT,
external_error TEXT,
source TEXT NOT NULL DEFAULT 'bot',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_applications_status
ON applications(status);
CREATE INDEX IF NOT EXISTS idx_applications_created_at
ON applications(created_at);
CREATE TABLE IF NOT EXISTS application_events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
application_id INTEGER NOT NULL,
event_type TEXT NOT NULL,
actor TEXT NOT NULL,
details_json TEXT NOT NULL DEFAULT '{}',
created_at TEXT NOT NULL,
FOREIGN KEY(application_id) REFERENCES applications(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_events_application_id
ON application_events(application_id);
CREATE TABLE IF NOT EXISTS bot_chats (
chat_id INTEGER PRIMARY KEY,
chat_type TEXT NOT NULL,
title TEXT,
username TEXT,
last_seen_at TEXT NOT NULL,
is_active INTEGER NOT NULL DEFAULT 1
);
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
role TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
"""
)
conn.commit()
class Database:
def __init__(self, db_path: str) -> None:
self._db_path = db_path
def connect(self) -> sqlite3.Connection:
conn = sqlite3.connect(self._db_path)
conn.row_factory = sqlite3.Row
conn.execute("PRAGMA foreign_keys = ON;")
return conn
@contextmanager
def transaction(self) -> Iterator[sqlite3.Connection]:
conn = self.connect()
try:
yield conn
conn.commit()
except Exception:
conn.rollback()
raise
finally:
conn.close()
def ensure_admin_user_seed(db: Database, username: str, password_hash: str, role: str = "admin") -> None:
now = utc_now_iso()
with db.connect() as conn:
row = conn.execute("SELECT id FROM users WHERE username = ?", (username,)).fetchone()
if row is None:
conn.execute(
"""
INSERT INTO users (username, password_hash, role, created_at, updated_at)
VALUES (?, ?, ?, ?, ?)
""",
(username, password_hash, role, now, now),
)
else:
conn.execute(
"UPDATE users SET password_hash = ?, role = ?, updated_at = ? WHERE username = ?",
(password_hash, role, now, username),
)
conn.commit()