Files

118 lines
3.4 KiB
Python

"""
ArchStore Backend — Main Application
A lightweight package store API for Arch Linux.
"""
import sys
import os
from pathlib import Path
# Add backend directory to path for imports
sys.path.insert(0, os.path.dirname(__file__))
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException, status
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
from database.db import db
from api.routes import packages, updates, categories
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application lifecycle: startup and shutdown."""
# Startup
await db.connect()
print("✓ Database connected")
print("✓ ArchStore backend ready")
yield
# Shutdown
await db.close()
print("✗ Database disconnected")
app = FastAPI(
title="ArchStore API",
description="A lightweight package store API for Arch Linux combining pacman and AUR.",
version="1.0.0",
lifespan=lifespan,
)
# CORS — allow frontend dev server
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:5173",
"http://127.0.0.1:5173",
"http://localhost:3000",
],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Mount route modules
app.include_router(packages.router)
app.include_router(updates.router)
app.include_router(categories.router)
@app.get("/api/health")
async def health():
"""Detailed health check."""
return {
"status": "healthy",
"database": "connected",
"version": "1.0.0",
}
@app.post("/api/cache/clear")
async def clear_cache():
"""Clear all cached data."""
await db.clear_cache()
return {"status": "ok", "message": "Cache cleared"}
# Frontend Static Files Serving for Production / Desktop Launch
STATIC_DIR = "/usr/share/archstore/frontend"
if not os.path.exists(STATIC_DIR):
STATIC_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "frontend", "dist")
if os.path.exists(STATIC_DIR):
assets_dir = os.path.join(STATIC_DIR, "assets")
if os.path.exists(assets_dir):
app.mount("/assets", StaticFiles(directory=assets_dir), name="assets")
static_root = Path(STATIC_DIR).resolve()
def resolve_static_path(requested_path: str) -> Path:
candidate = (static_root / requested_path).resolve()
if static_root == candidate or static_root in candidate.parents:
return candidate
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Not found")
@app.get("/{fallback_path:path}")
async def serve_frontend(fallback_path: str):
# Serve favicon or other root files directly if they exist
if fallback_path:
safe_path = resolve_static_path(fallback_path)
if safe_path.exists() and safe_path.is_file():
return FileResponse(str(safe_path))
# Otherwise fallback to index.html for React SPA routing
index_path = static_root / "index.html"
if index_path.exists():
return FileResponse(str(index_path))
else:
@app.get("/")
async def root():
"""Health check fallback when static files are missing."""
return {
"name": "ArchStore API",
"version": "1.0.0",
"status": "running",
"info": "Frontend static files not found. Run dev server on port 5173."
}