116 lines
3.2 KiB
Python
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)
|