import sqlite3 import hashlib import secrets from datetime import datetime, timedelta from typing import Optional, List, Dict from contextlib import contextmanager from config import settings @contextmanager def get_db(): conn = sqlite3.connect(settings.database_url.replace("sqlite:///", "")) conn.row_factory = sqlite3.Row try: yield conn conn.commit() except Exception: conn.rollback() raise finally: conn.close() def init_db(): with get_db() as conn: conn.execute(""" CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, email TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, api_key TEXT UNIQUE NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, is_active BOOLEAN DEFAULT 1 ) """) conn.execute(""" CREATE TABLE IF NOT EXISTS download_sessions ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, session_id TEXT UNIQUE NOT NULL, status TEXT DEFAULT 'pending', ollama_version TEXT, model_name TEXT, progress INTEGER DEFAULT 0, current_step TEXT, total_size_mb INTEGER DEFAULT 0, downloaded_mb INTEGER DEFAULT 0, speed_kbps INTEGER DEFAULT 0, eta_seconds INTEGER DEFAULT 0, error_message TEXT, started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, completed_at TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users (id) ) """) conn.execute(""" CREATE TABLE IF NOT EXISTS api_keys ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, key_name TEXT NOT NULL, api_key TEXT UNIQUE NOT NULL, is_active BOOLEAN DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, last_used_at TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users (id) ) """) def hash_password(password: str) -> str: return hashlib.sha256(password.encode()).hexdigest() def verify_password(password: str, password_hash: str) -> bool: return hash_password(password) == password_hash def generate_api_key() -> str: return f"ollama_{secrets.token_urlsafe(32)}" def create_user(username: str, email: str, password: str) -> Optional[int]: with get_db() as conn: try: cursor = conn.execute( "INSERT INTO users (username, email, password_hash, api_key) VALUES (?, ?, ?, ?)", (username, email, hash_password(password), generate_api_key()) ) return cursor.lastrowid except sqlite3.IntegrityError: return None def get_user_by_username(username: str) -> Optional[Dict]: with get_db() as conn: row = conn.execute("SELECT * FROM users WHERE username = ?", (username,)).fetchone() return dict(row) if row else None def get_user_by_api_key(api_key: str) -> Optional[Dict]: with get_db() as conn: row = conn.execute("SELECT * FROM users WHERE api_key = ? AND is_active = 1", (api_key,)).fetchone() if row: return dict(row) row = conn.execute( "SELECT u.* FROM users u JOIN api_keys a ON u.id = a.user_id WHERE a.api_key = ? AND a.is_active = 1", (api_key,) ).fetchone() return dict(row) if row else None def create_download_session(user_id: int, session_id: str, model_name: str) -> int: with get_db() as conn: cursor = conn.execute( "INSERT INTO download_sessions (user_id, session_id, model_name, status) VALUES (?, ?, ?, 'pending')", (user_id, session_id, model_name) ) return cursor.lastrowid def update_download_progress(session_id: str, **kwargs): with get_db() as conn: fields = ", ".join([f"{k} = ?" for k in kwargs.keys()]) values = list(kwargs.values()) + [session_id] conn.execute(f"UPDATE download_sessions SET {fields} WHERE session_id = ?", values) def get_download_session(session_id: str) -> Optional[Dict]: with get_db() as conn: row = conn.execute("SELECT * FROM download_sessions WHERE session_id = ?", (session_id,)).fetchone() return dict(row) if row else None def get_user_sessions(user_id: int) -> List[Dict]: with get_db() as conn: rows = conn.execute( "SELECT * FROM download_sessions WHERE user_id = ? ORDER BY started_at DESC", (user_id,) ).fetchall() return [dict(row) for row in rows] def create_api_key(user_id: int, key_name: str) -> str: api_key = generate_api_key() with get_db() as conn: conn.execute( "INSERT INTO api_keys (user_id, key_name, api_key) VALUES (?, ?, ?)", (user_id, key_name, api_key) ) return api_key def get_user_api_keys(user_id: int) -> List[Dict]: with get_db() as conn: rows = conn.execute("SELECT * FROM api_keys WHERE user_id = ?", (user_id,)).fetchall() return [dict(row) for row in rows] def revoke_api_key(key_id: int, user_id: int): with get_db() as conn: conn.execute("UPDATE api_keys SET is_active = 0 WHERE id = ? AND user_id = ?", (key_id, user_id))