feat: crud and backups are working

This commit is contained in:
2025-05-30 18:49:40 -04:00
parent 3ee136d52d
commit b8ce9ded76
19 changed files with 191 additions and 21 deletions
+1
View File
@@ -0,0 +1 @@
/backups
+5 -1
View File
@@ -1,3 +1,4 @@
FROM ghcr.io/astral-sh/uv:python3.13-alpine FROM ghcr.io/astral-sh/uv:python3.13-alpine
WORKDIR /app WORKDIR /app
@@ -6,6 +7,9 @@ COPY pyproject.toml /app/
RUN uv sync RUN uv sync
COPY ./ /app COPY . /app
# Run the FastAPI app
CMD ["uv","run","uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] CMD ["uv","run","uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+26
View File
@@ -0,0 +1,26 @@
from sqlalchemy.orm import Session
from app import models, schemas
def get_item(db: Session, item_id: int):
return db.query(models.Item).filter(models.Item.id == item_id).first()
def get_items(db: Session, skip: int = 0, limit: int = 10):
return db.query(models.Item).offset(skip).limit(limit).all()
def create_item(db: Session, item: schemas.ItemCreate):
db_item = models.Item(**item.model_dump())
db.add(db_item)
db.commit()
db.refresh(db_item)
return db_item
def delete_item(db: Session, item_id: int):
item = db.query(models.Item).filter(models.Item.id == item_id).first()
if item:
db.delete(item)
db.commit()
return item
+11
View File
@@ -0,0 +1,11 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
import os
DATABASE_URL = os.getenv(
"DATABASE_URL", "postgresql://user:password@db:5432/mydatabase"
)
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
+44 -4
View File
@@ -1,8 +1,48 @@
from fastapi import FastAPI from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from . import schemas, crud
from .database import SessionLocal, engine, Base
Base.metadata.create_all(bind=engine)
app = FastAPI() app = FastAPI()
@app.get("/") # Dependency
async def read_root(): def get_db():
return {"hello": "world"} db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/check-health")
def health_check():
return {"Health": "Super Healthy!"}
@app.post("/items/", response_model=schemas.Item)
def create_item(item: schemas.ItemCreate, db: Session = Depends(get_db)):
return crud.create_item(db, item)
@app.get("/items/", response_model=list[schemas.Item])
def read_items(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)):
return crud.get_items(db, skip, limit)
@app.get("/items/{item_id}", response_model=schemas.Item)
def read_item(item_id: int, db: Session = Depends(get_db)):
db_item = crud.get_item(db, item_id)
if db_item is None:
raise HTTPException(status_code=404, detail="Item not found")
return db_item
@app.delete("/items/{item_id}", response_model=schemas.Item)
def delete_item(item_id: int, db: Session = Depends(get_db)):
item = crud.delete_item(db, item_id)
if item is None:
raise HTTPException(status_code=404, detail="Item not found")
return item
+11
View File
@@ -0,0 +1,11 @@
from sqlalchemy import JSON, Column, Integer, String
from .database import Base
class Item(Base):
__tablename__ = "items"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)
description = Column(String, nullable=True)
body = Column(JSON, nullable=False)
+17
View File
@@ -0,0 +1,17 @@
from pydantic import BaseModel
class ItemBase(BaseModel):
name: str
description: str | None = None
class ItemCreate(ItemBase):
pass
class Item(ItemBase):
id: int
class Config:
from_attributes = True
+9
View File
@@ -0,0 +1,9 @@
FROM alpine:latest
RUN apk add --no-cache docker-cli bash findutils postgresql-client
COPY backup.sh /backup.sh
RUN chmod +x /backup.sh
CMD ["/backup.sh"]
+46
View File
@@ -0,0 +1,46 @@
#!/bin/sh
echo "[INFO] Backup entry success"
# === Configuration ===
PG_CONTAINER_NAME=$(docker ps --filter "name=_db" --format "{{.Names}}" | head -n 1)
PG_USER="user"
PG_PASSWORD="password"
PG_DB="mydatabase"
BACKUP_DIR="/backups"
LOG_DIR="/backups/logs"
RETENTION_DAYS=7
TIMESTAMP=$(date +"%Y%m%d-%H%M%S")
FILENAME="$BACKUP_DIR/backup-$TIMESTAMP.sql"
LOGFILE="$LOG_DIR/backup-$TIMESTAMP.log"
# === Setup Directories ===
mkdir -p "$BACKUP_DIR"
mkdir -p "$LOG_DIR"
# === Begin Logging ===
exec > "$LOGFILE" 2>&1
echo "[INFO] Backup script started at $TIMESTAMP"
echo "[INFO] Backing up database '$PG_DB' from container '$PG_CONTAINER_NAME'..."
# === Perform Backup ===
PGPASSWORD="$PG_PASSWORD" pg_dump -h db -U "$PG_USER" -d "$PG_DB" > "$FILENAME"
if [ $? -eq 0 ]; then
echo "[INFO] Backup successful: $FILENAME"
else
echo "[ERROR] Backup failed!" >&2
exit 1
fi
# === Rotate Old Backups ===
echo "[INFO] Removing backups older than $RETENTION_DAYS days..."
find "$BACKUP_DIR" -type f -name "backup-*.sql" -mtime +$RETENTION_DAYS -exec rm -f {} \;
# === Rotate Old Logs ===
echo "[INFO] Removing logs older than $RETENTION_DAYS days..."
find "$LOG_DIR" -type f -name "backup-*.log" -mtime +$RETENTION_DAYS -exec rm -f {} \;
echo "[INFO] Backup and cleanup completed at $(date +"%Y%m%d-%H%M%S")"
-8
View File
@@ -1,8 +0,0 @@
{
"hash": "d81b31a4",
"configHash": "5834786f",
"lockfileHash": "e3b0c442",
"browserHash": "a25f5c91",
"optimized": {},
"chunks": {}
}
-3
View File
@@ -1,3 +0,0 @@
{
"type": "module"
}
+10 -1
View File
@@ -11,13 +11,22 @@ services:
- postgres_data:/var/lib/postgresql/data - postgres_data:/var/lib/postgresql/data
networks: networks:
- app-network - app-network
backup:
build: ./backup
volumes:
- ./backups:/backups
- /var/run/docker.sock:/var/run/docker.sock
depends_on:
- db
networks:
- app-network
backend: backend:
build: ./backend build: ./backend
volumes: volumes:
- ./backend/app:/app/app - ./backend/app:/app/app
environment: environment:
DATABASE_URL: postgres://user:password@db:5432/mydatabase DATABASE_URL: postgresql://user:password@db:5432/mydatabase
depends_on: depends_on:
- db - db
ports: ports:
+10 -3
View File
@@ -1,5 +1,12 @@
export function BlogList() { import { useState, useEffect } from "react";
fetch("public/blogs/*.md").then((ret) => console.log(ret.text()));
return <div></div>; export function BlogList() {
const [content, setContent] = useState("");
useEffect(() => {
fetch(`localhost:8000/get-blogs`)
.then((res) => res.text())
.then(setContent);
}, []);
return <div>{content}</div>;
} }
+1 -1
View File
@@ -7,7 +7,7 @@ export function BlogViewer() {
const { slug } = useParams(); const { slug } = useParams();
useEffect(() => { useEffect(() => {
fetch(`/blogs/${slug ?? "home"}.md`) fetch(`localhost:8000/get-blogs/${slug}`)
.then((res) => res.text()) .then((res) => res.text())
.then(setContent); .then(setContent);
}, []); }, []);