from fastapi import FastAPI, APIRouter from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse import uvicorn import os from dotenv import load_dotenv import yaml from pathlib import Path from pydantic import BaseModel from minio import Minio app = FastAPI() router = APIRouter() app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) load_dotenv() MINIO_ENDPOINT = os.getenv("MINIO_ENDPOINT") MINIO_ACCESS_KEY = os.getenv("MINIO_ACCESS_KEY") MINIO_SECRET_KEY = os.getenv("MINIO_SECRET_KEY") MINIO_BUCKET = os.getenv("MINIO_BUCKET") minio_client = Minio( MINIO_ENDPOINT, access_key=MINIO_ACCESS_KEY, secret_key=MINIO_SECRET_KEY, secure=True ) BUCKET = MINIO_BUCKET or "navix-icons" APPS_FILE = Path(__file__).parent / "apps.yaml" @router.get("/") def root(): return {"message": "Welcome to the FastAPI application!"} @router.get("/apps") def get_apps(): if not APPS_FILE.exists(): return {"error": "apps.yaml not found"} with open(APPS_FILE, "r") as f: return yaml.safe_load(f) class AppData(BaseModel): name: str icon: str description: str url: str class AppEntry(BaseModel): section: str app: AppData original_name: str | None = None @router.post("/add_app") def add_app(entry: AppEntry): if not APPS_FILE.exists(): current = {"sections": []} else: with open(APPS_FILE, "r") as f: current = yaml.safe_load(f) or {"sections": []} for section in current["sections"]: if section["name"] == entry.section: section["apps"].append(entry.app.dict()) break else: current["sections"].append({ "name": entry.section, "apps": [entry.app.dict()] }) with open(APPS_FILE, "w") as f: yaml.safe_dump(current, f) return {"status": "added"} @router.post("/edit_app") def edit_app(entry: AppEntry): if not APPS_FILE.exists(): return {"error": "apps.yaml not found"} with open(APPS_FILE, "r") as f: current = yaml.safe_load(f) or {"sections": []} updated = False for section in current["sections"]: if section["name"] == entry.section: for i, app in enumerate(section["apps"]): if app["name"] == (entry.original_name or entry.app.name): section["apps"][i] = entry.app.dict() updated = True break break if not updated: return JSONResponse(status_code=404, content={"error": "App not found to edit"}) with open(APPS_FILE, "w") as f: yaml.safe_dump(current, f) return {"status": "updated"} @router.post("/delete_app") def delete_app(entry: AppEntry): if not APPS_FILE.exists(): return {"error": "apps.yaml not found"} with open(APPS_FILE, "r") as f: current = yaml.safe_load(f) or {"sections": []} deleted = False for section in current["sections"]: if section["name"] == entry.section: original_len = len(section["apps"]) section["apps"] = [a for a in section["apps"] if a["name"] != entry.app.name] if len(section["apps"]) != original_len: deleted = True break if not deleted: return JSONResponse(status_code=404, content={"error": "App not found to delete"}) with open(APPS_FILE, "w") as f: yaml.safe_dump(current, f) return {"status": "deleted"} @router.get("/icon/{filename}") def get_public_icon_url(filename: str): url = f"https://{MINIO_ENDPOINT}/{BUCKET}/{filename}" return JSONResponse(content={"url": url}) app.include_router(router, prefix="/api") # ✅ This is the missing part: if __name__ == "__main__": uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)