feat(core): implement secure MVP core with dictionary search, auth, and CRUD

This commit is contained in:
2026-09-22 13:27:11 +00:00
parent 4dbc53890a
commit 813d567b0e
14 changed files with 2960 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
import sqlite3
import contextlib
from typing import Generator
from app import config
def get_db_connection() -> sqlite3.Connection:
conn = sqlite3.connect(
config.DB_PATH,
timeout=15.0
)
conn.row_factory = sqlite3.Row
conn.execute("PRAGMA journal_mode=WAL;")
conn.execute("PRAGMA synchronous=NORMAL;")
conn.execute("PRAGMA foreign_keys=ON;")
return conn
@contextlib.contextmanager
def get_db() -> Generator[sqlite3.Connection, None, None]:
conn = get_db_connection()
try:
yield conn
conn.commit()
except Exception:
conn.rollback()
raise
finally:
conn.close()
def init_db():
config.DB_PATH.parent.mkdir(parents=True, exist_ok=True)
with get_db() as conn:
conn.executescript("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL COLLATE NOCASE,
password_hash TEXT NOT NULL,
salt TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS sessions (
token TEXT PRIMARY KEY,
user_id INTEGER NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP NOT NULL,
user_agent TEXT,
ip TEXT,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS vocabulary_cards (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
term TEXT NOT NULL,
translation TEXT NOT NULL,
context_sentence TEXT,
phonetic TEXT,
audio_url TEXT,
part_of_speech TEXT,
definition_en TEXT,
tags TEXT,
notes TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_vocab_user ON vocabulary_cards(user_id);
CREATE INDEX IF NOT EXISTS idx_vocab_term ON vocabulary_cards(term);
CREATE INDEX IF NOT EXISTS idx_vocab_created ON vocabulary_cards(created_at DESC);
CREATE TABLE IF NOT EXISTS dictionary_cache (
query TEXT PRIMARY KEY COLLATE NOCASE,
response_json TEXT NOT NULL,
cached_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
""")