feat: crud and backups are working
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
/backups
|
||||||
+5
-1
@@ -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.
@@ -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
|
||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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
|
||||||
@@ -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"]
|
||||||
|
|
||||||
@@ -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")"
|
||||||
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"hash": "d81b31a4",
|
|
||||||
"configHash": "5834786f",
|
|
||||||
"lockfileHash": "e3b0c442",
|
|
||||||
"browserHash": "a25f5c91",
|
|
||||||
"optimized": {},
|
|
||||||
"chunks": {}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"type": "module"
|
|
||||||
}
|
|
||||||
+10
-1
@@ -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:
|
||||||
|
|||||||
@@ -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>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}, []);
|
}, []);
|
||||||
|
|||||||
Reference in New Issue
Block a user