Migrate to PostgreSQL + add FastAPI webapp for Coolify deploy

Backend migration:
- Replace pyodbc/SQL Server with psycopg2/PostgreSQL throughout
- Rewrite Database class with portable SQL: SERIAL, ON CONFLICT, NOW()
- Lowercase table names (rip_help_files, rip_help_sections) - Postgres convention
- libpq connection string format in HELP_DB_CONN

Webapp (webapp/):
- FastAPI app: GET /, GET /images/<f>, GET /home-image, GET /api/sections,
  POST /api/keywords/<code>, GET /healthz
- Jinja2 template extracted from generate_html.py with HTTP image URLs
- Direct keyword save to DB (no JSON download detour)
- Same prefix scoping as CLI tools (?prefix=RIP)

Deployment:
- Dockerfile (python:3.12-slim + uvicorn)
- docker-compose.yml for local dev
- requirements-webapp.txt (minimal, no Windows-only deps)
- .dockerignore excludes pipeline scripts and BAT files
- README updated with webapp section and Coolify deploy guide

Also: switch AI model to claude-haiku-4-5 (~3x cheaper, same quality for this task)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-20 17:00:44 +03:00
parent 711053b8bd
commit 9613420d1d
13 changed files with 1034 additions and 167 deletions

View File

@@ -11,16 +11,13 @@ from datetime import datetime
from typing import Optional
try:
import pyodbc
import psycopg2
except ImportError:
sys.exit("Инсталирай pyodbc: pip install pyodbc")
sys.exit("Инсталирай psycopg2: pip install psycopg2-binary")
CONN_STR = os.getenv(
"HELP_DB_CONN",
"DRIVER={ODBC Driver 18 for SQL Server};"
"TrustServerCertificate=yes;"
"SERVER=94.26.63.238,13151;DATABASE=blondina;"
"UID=blondina_login;PWD=blondina_parola_123"
"host=192.168.88.18 port=5432 dbname=rip_help_system user=sa password=Parola~12345!!!"
)
OUT_HTML = Path(__file__).parent / "help_viewer.html"
@@ -80,26 +77,26 @@ def _rich_html_with_images(html: str, output_dir: Path, embed: bool = False) ->
def fetch_sections(prefix: Optional[str] = None):
conn = pyodbc.connect(CONN_STR, autocommit=True)
conn = psycopg2.connect(CONN_STR)
cur = conn.cursor()
if prefix:
cur.execute("""
SELECT s.prefix, s.code, s.title, s.keywords, s.char_count,
s.source_file, s.output_path, s.updated_at,
s.images, s.html_text, f.section_count
FROM RIP_help_sections s
LEFT JOIN RIP_help_files f
FROM rip_help_sections s
LEFT JOIN rip_help_files f
ON f.file_path = s.source_file AND f.prefix = s.prefix
WHERE s.prefix = ?
WHERE s.prefix = %s
ORDER BY s.code
""", prefix)
""", (prefix,))
else:
cur.execute("""
SELECT s.prefix, s.code, s.title, s.keywords, s.char_count,
s.source_file, s.output_path, s.updated_at,
s.images, s.html_text, f.section_count
FROM RIP_help_sections s
LEFT JOIN RIP_help_files f
FROM rip_help_sections s
LEFT JOIN rip_help_files f
ON f.file_path = s.source_file AND f.prefix = s.prefix
ORDER BY s.prefix, s.code
""")