Files
docker/examples/multi-service/api/app.py
T
2026-05-04 11:07:31 +03:00

116 lines
3.2 KiB
Python

"""
Минимальное 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)