""" Минимальное API для демонстрации Compose: - подключение к Postgres по DATABASE_URL - таблица notes при старте - загрузка файла в каталог UPLOAD_DIR (том) """ import os import uuid from pathlib import Path from flask import Flask, jsonify, request import psycopg2 from psycopg2.extras import RealDictCursor app = Flask(__name__) UPLOAD_DIR = Path(os.environ.get("UPLOAD_DIR", "/data/uploads")) DATABASE_URL = os.environ.get( "DATABASE_URL", "postgresql://app:app@localhost:5432/appdb", ) def get_conn(): return psycopg2.connect(DATABASE_URL, cursor_factory=RealDictCursor) def init_db(): UPLOAD_DIR.mkdir(parents=True, exist_ok=True) with get_conn() as conn: with conn.cursor() as cur: cur.execute( """ CREATE TABLE IF NOT EXISTS notes ( id SERIAL PRIMARY KEY, body TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); """ ) conn.commit() @app.route("/health") def health(): try: with get_conn() as conn: with conn.cursor() as cur: cur.execute("SELECT 1 AS ok;") row = cur.fetchone() return jsonify({"status": "ok", "db": bool(row)}), 200 except Exception as exc: # noqa: BLE001 — демо return jsonify({"status": "error", "detail": str(exc)}), 503 def _note_row(r): d = dict(r) if d.get("created_at") is not None: d["created_at"] = d["created_at"].isoformat() return d @app.route("/notes", methods=["GET"]) def list_notes(): with get_conn() as conn: with conn.cursor() as cur: cur.execute( "SELECT id, body, created_at FROM notes ORDER BY id DESC LIMIT 50;" ) rows = cur.fetchall() return jsonify([_note_row(r) for r in rows]) @app.route("/notes", methods=["POST"]) def create_note(): data = request.get_json(silent=True) or {} body = (data.get("body") or "").strip() if not body: return jsonify({"error": "body required"}), 400 with get_conn() as conn: with conn.cursor() as cur: cur.execute( "INSERT INTO notes (body) VALUES (%s) RETURNING id, body, created_at;", (body,), ) row = cur.fetchone() conn.commit() return jsonify(_note_row(row)), 201 @app.route("/upload", methods=["POST"]) def upload(): if "file" not in request.files: return jsonify({"error": "file field required"}), 400 f = request.files["file"] if not f.filename: return jsonify({"error": "empty filename"}), 400 safe_name = f"{uuid.uuid4().hex}_{f.filename}" dest = UPLOAD_DIR / safe_name f.save(dest) return jsonify({"saved_as": safe_name, "path_in_container": str(dest)}), 201 @app.route("/uploads", methods=["GET"]) def list_uploads(): files = sorted(p.name for p in UPLOAD_DIR.iterdir() if p.is_file()) return jsonify({"files": files}) with app.app_context(): init_db() if __name__ == "__main__": app.run(host="0.0.0.0", port=5000)