"""
Album - Documentation system.
"""
import os
import httpx
from pathlib import Path
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
app = FastAPI(title="Album", version="0.1.0")
BASE_DIR = Path(__file__).parent.resolve()
BOOK_DIR = BASE_DIR / "book"
STATIC_DIR = BASE_DIR / "static"
# Create static directory if it doesn't exist
STATIC_DIR.mkdir(exist_ok=True)
templates = Jinja2Templates(directory=str(BASE_DIR))
# Serve static files
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
# Pawprint URL for data fetching
PAWPRINT_URL = os.getenv("PAWPRINT_URL", "http://localhost:12000")
def get_data():
"""Fetch data from pawprint hub."""
try:
resp = httpx.get(f"{PAWPRINT_URL}/api/data/album", timeout=5.0)
if resp.status_code == 200:
return resp.json()
except Exception as e:
print(f"Failed to fetch data from pawprint: {e}")
return {"templates": [], "larders": [], "books": []}
@app.get("/health")
def health():
return {"status": "ok", "service": "album"}
@app.get("/")
def index(request: Request):
data = get_data()
return templates.TemplateResponse("index.html", {
"request": request,
"pawprint_url": os.getenv("PAWPRINT_EXTERNAL_URL", PAWPRINT_URL),
**data,
})
@app.get("/api/data")
def api_data():
"""API endpoint for frontend data (proxied from pawprint)."""
return get_data()
# --- Book: Feature Flow (HTML presentations) ---
@app.get("/book/feature-flow/", response_class=HTMLResponse)
@app.get("/book/feature-flow", response_class=HTMLResponse)
def feature_flow_index():
"""Redirect to English presentation by default"""
return """
Feature Flow
Feature Flow - Standardization Pipeline
"""
@app.get("/book/feature-flow/en", response_class=HTMLResponse)
def feature_flow_en():
html_file = BOOK_DIR / "feature-flow" / "index-en.html"
return HTMLResponse(html_file.read_text())
@app.get("/book/feature-flow/es", response_class=HTMLResponse)
def feature_flow_es():
html_file = BOOK_DIR / "feature-flow" / "index-es.html"
return HTMLResponse(html_file.read_text())
# --- Book: Feature Form Samples (templated book) ---
@app.get("/book/feature-form-samples/", response_class=HTMLResponse)
@app.get("/book/feature-form-samples", response_class=HTMLResponse)
def feature_form_samples_index(request: Request):
"""Templated book landing page"""
data = get_data()
book = next((b for b in data.get("books", []) if b["slug"] == "feature-form-samples"), None)
if not book:
return HTMLResponse("Book not found
", status_code=404)
return templates.TemplateResponse("book-template.html", {
"request": request,
"book": book,
})
@app.get("/book/feature-form-samples/template/", response_class=HTMLResponse)
@app.get("/book/feature-form-samples/template", response_class=HTMLResponse)
def feature_form_samples_template():
"""View the template - styled like actual feature forms"""
html = """
Feature Form Template · Album
Feature Form Template
Template
"""
return HTMLResponse(html)
@app.get("/book/feature-form-samples/larder/", response_class=HTMLResponse)
@app.get("/book/feature-form-samples/larder", response_class=HTMLResponse)
def feature_form_samples_larder():
"""Browse the larder (actual data)"""
html_file = BOOK_DIR / "feature-form-samples" / "index.html"
if html_file.exists():
return HTMLResponse(html_file.read_text())
return HTMLResponse("Larder index not found
", status_code=404)
@app.get("/book/feature-form-samples/larder/{user_type}/{filename}", response_class=HTMLResponse)
def feature_form_samples_detail(request: Request, user_type: str, filename: str):
"""View a specific feature form"""
# Look in the larder subfolder (feature-form)
larder_dir = BOOK_DIR / "feature-form-samples" / "feature-form"
file_path = larder_dir / user_type / filename
if not file_path.exists():
return HTMLResponse("Not found
", status_code=404)
content = file_path.read_text()
detail_file = BOOK_DIR / "feature-form-samples" / "detail.html"
return templates.TemplateResponse(str(detail_file.relative_to(BASE_DIR)), {
"request": request,
"user_type": user_type,
"filename": filename,
"content": content,
})
# --- Book: Gherkin Samples ---
@app.get("/book/gherkin-samples/", response_class=HTMLResponse)
@app.get("/book/gherkin-samples", response_class=HTMLResponse)
@app.get("/book/gherkin/", response_class=HTMLResponse) # Alias
@app.get("/book/gherkin", response_class=HTMLResponse) # Alias
def gherkin_samples_index():
"""Browse gherkin samples"""
html_file = BOOK_DIR / "gherkin-samples" / "index.html"
return HTMLResponse(html_file.read_text())
@app.get("/book/gherkin-samples/{lang}/{user_type}/{filename}", response_class=HTMLResponse)
@app.get("/book/gherkin/{lang}/{user_type}/{filename}", response_class=HTMLResponse) # Alias
def gherkin_samples_detail(request: Request, lang: str, user_type: str, filename: str):
"""View a specific gherkin file"""
file_path = BOOK_DIR / "gherkin-samples" / lang / user_type / filename
if not file_path.exists() or not file_path.suffix == ".feature":
return HTMLResponse("Not found
", status_code=404)
content = file_path.read_text()
detail_file = BOOK_DIR / "gherkin-samples" / "detail.html"
return templates.TemplateResponse(str(detail_file.relative_to(BASE_DIR)), {
"request": request,
"lang": lang,
"user_type": user_type,
"filename": filename,
"content": content,
})
# --- Book: Architecture Model (static site) ---
app.mount("/book/arch-model", StaticFiles(directory=str(BOOK_DIR / "arch-model"), html=True), name="arch-model")
# --- Book: Drive Index ---
@app.get("/book/drive-index/", response_class=HTMLResponse)
@app.get("/book/drive-index", response_class=HTMLResponse)
def drive_index():
"""Browse drive index"""
html_file = BOOK_DIR / "drive-index" / "index.html"
return HTMLResponse(html_file.read_text())
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"main:app",
host="0.0.0.0",
port=int(os.getenv("PORT", "12002")),
reload=os.getenv("DEV", "").lower() in ("1", "true"),
)