126 lines
4.7 KiB
Python
126 lines
4.7 KiB
Python
from fastapi import FastAPI, HTTPException, Depends
|
|
from sqlalchemy import create_engine, Column, Integer, String, Boolean, text
|
|
from sqlalchemy.orm import declarative_base, Session, sessionmaker
|
|
from pydantic import BaseModel
|
|
from typing import Optional
|
|
import os
|
|
import time
|
|
import logging
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
log = logging.getLogger(__name__)
|
|
|
|
# ── Database connection ───────────────────────────────────────────────────────
|
|
DB_HOST = os.getenv("DATABASE_HOST", "localhost")
|
|
DB_PORT = os.getenv("DATABASE_PORT", "5432")
|
|
DB_USER = os.getenv("POSTGRES_USER", "fastapiuser")
|
|
DB_PASS = os.getenv("POSTGRES_PASSWORD", "student123")
|
|
DB_NAME = os.getenv("POSTGRES_DB", "fastapidb")
|
|
|
|
DATABASE_URL = f"postgresql://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
|
|
|
|
# Retry logic — baza mozda nije odmah dostupna kad Pod starta
|
|
engine = None
|
|
for attempt in range(10):
|
|
try:
|
|
engine = create_engine(DATABASE_URL)
|
|
with engine.connect() as conn:
|
|
conn.execute(text("SELECT 1"))
|
|
log.info(f"Baza dostupna (pokusaj {attempt + 1})")
|
|
break
|
|
except Exception as e:
|
|
log.warning(f"Baza nije dostupna, cekam 3s... (pokusaj {attempt + 1}): {e}")
|
|
time.sleep(3)
|
|
|
|
if engine is None:
|
|
raise RuntimeError("Ne mogu se spojiti na bazu nakon 10 pokusaja")
|
|
|
|
SessionLocal = sessionmaker(bind=engine)
|
|
Base = declarative_base()
|
|
|
|
# ── Model ─────────────────────────────────────────────────────────────────────
|
|
class Todo(Base):
|
|
__tablename__ = "todos"
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
title = Column(String(200), nullable=False)
|
|
done = Column(Boolean, default=False)
|
|
|
|
Base.metadata.create_all(bind=engine)
|
|
|
|
# ── Schemas ───────────────────────────────────────────────────────────────────
|
|
class TodoCreate(BaseModel):
|
|
title: str
|
|
done: Optional[bool] = False
|
|
|
|
class TodoUpdate(BaseModel):
|
|
title: Optional[str] = None
|
|
done: Optional[bool] = None
|
|
|
|
class TodoOut(BaseModel):
|
|
id: int
|
|
title: str
|
|
done: bool
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
# ── App ───────────────────────────────────────────────────────────────────────
|
|
app = FastAPI(title="FastAPI Todo API", version="1.0.0")
|
|
|
|
def get_db():
|
|
db = SessionLocal()
|
|
try:
|
|
yield db
|
|
finally:
|
|
db.close()
|
|
|
|
# ── Routes ────────────────────────────────────────────────────────────────────
|
|
@app.get("/health")
|
|
def health():
|
|
try:
|
|
with engine.connect() as conn:
|
|
conn.execute(text("SELECT 1"))
|
|
return {"status": "ok", "database": "connected"}
|
|
except Exception as e:
|
|
raise HTTPException(status_code=503, detail=f"Baza nedostupna: {e}")
|
|
|
|
@app.get("/todos", response_model=list[TodoOut])
|
|
def get_todos(db: Session = Depends(get_db)):
|
|
return db.query(Todo).all()
|
|
|
|
@app.get("/todos/{todo_id}", response_model=TodoOut)
|
|
def get_todo(todo_id: int, db: Session = Depends(get_db)):
|
|
todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
|
if not todo:
|
|
raise HTTPException(status_code=404, detail="Todo nije pronaden")
|
|
return todo
|
|
|
|
@app.post("/todos", response_model=TodoOut, status_code=201)
|
|
def create_todo(payload: TodoCreate, db: Session = Depends(get_db)):
|
|
todo = Todo(title=payload.title, done=payload.done)
|
|
db.add(todo)
|
|
db.commit()
|
|
db.refresh(todo)
|
|
return todo
|
|
|
|
@app.put("/todos/{todo_id}", response_model=TodoOut)
|
|
def update_todo(todo_id: int, payload: TodoUpdate, db: Session = Depends(get_db)):
|
|
todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
|
if not todo:
|
|
raise HTTPException(status_code=404, detail="Todo nije pronaden")
|
|
if payload.title is not None:
|
|
todo.title = payload.title
|
|
if payload.done is not None:
|
|
todo.done = payload.done
|
|
db.commit()
|
|
db.refresh(todo)
|
|
return todo
|
|
|
|
@app.delete("/todos/{todo_id}")
|
|
def delete_todo(todo_id: int, db: Session = Depends(get_db)):
|
|
todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
|
if not todo:
|
|
raise HTTPException(status_code=404, detail="Todo nije pronaden")
|
|
db.delete(todo)
|
|
db.commit()
|
|
return {"message": f"Todo {todo_id} obrisan"}
|