init test commit

This commit is contained in:
2026-11-10 20:36:01 -07:00
commit 8693b3bd8d
42 changed files with 311150 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
"""FastAPI routes for the Star Citizen inventory (scunpacked data)."""
import math
from fastapi import FastAPI, HTTPException, Query
from fastapi.middleware.cors import CORSMiddleware
from app import loader
app = FastAPI(
title="Star Citizen Inventory API",
description="Read-only item inventory built from scunpacked game data.",
version="1.0.0",
)
# Read-only public GET API; permissive CORS keeps local development easy.
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["GET"],
)
store = loader.load_store()
VALID_CATEGORIES = {"ships", "ship-items", "fps-items"}
def _filter_items(category, search, manufacturer, item_type, size):
result = store.items
if category is not None:
if category not in VALID_CATEGORIES:
raise HTTPException(
status_code=422,
detail=f"Invalid category '{category}'. Valid: {sorted(VALID_CATEGORIES)}",
)
result = [i for i in result if i["category"] == category]
if search is not None and search.strip():
needle = search.strip().lower()
result = [
i
for i in result
if needle in i["name"].lower() or needle in i["class_name"].lower()
]
if manufacturer is not None and manufacturer.strip():
code = manufacturer.strip().lower()
result = [i for i in result if i["manufacturer"]["code"].lower() == code]
if item_type is not None and item_type.strip():
wanted = item_type.strip().lower()
result = [i for i in result if (i["type"] or "").lower() == wanted]
if size is not None:
result = [i for i in result if i["size"] == size]
return result
@app.get("/healthz")
def healthz():
return {"status": "ok", "counts": store.counts}
@app.get("/api/v1/stats")
def stats():
return {"categories": store.counts, "manufacturers": len(store.manufacturers)}
@app.get("/api/v1/manufacturers")
def manufacturers():
return store.manufacturers
@app.get("/api/v1/items")
def list_items(
category: str | None = None,
search: str | None = None,
manufacturer: str | None = None,
item_type: str | None = Query(default=None, alias="type"),
size: int | None = None,
page: int = Query(default=1, ge=1),
page_size: int = Query(default=25, ge=1),
):
page_size = min(page_size, 100) # cap, don't reject
filtered = _filter_items(category, search, manufacturer, item_type, size)
total = len(filtered)
total_pages = max(1, math.ceil(total / page_size))
start = (page - 1) * page_size
return {
"total": total,
"page": page,
"page_size": page_size,
"total_pages": total_pages,
"items": filtered[start : start + page_size],
}
@app.get("/api/v1/items/{class_name}")
def item_detail(class_name: str):
summary = store.by_class_name.get(class_name)
if summary is None:
raise HTTPException(status_code=404, detail=f"Item '{class_name}' not found")
return {**summary, "details": store.details.get(class_name, {})}