284 lines
7.6 KiB
Python
284 lines
7.6 KiB
Python
"""
|
|
Soleprint - Overview and routing hub.
|
|
|
|
Development workflow and documentation system
|
|
👣 Mapping development footprints
|
|
|
|
Systems:
|
|
💉 Artery (artery) - Todo lo vital
|
|
🗺️ Atlas (atlas) - Documentación accionable
|
|
🎛️ Station (station) - Monitores, Entornos y Herramientas
|
|
|
|
Routes:
|
|
/ → index
|
|
/health → health check
|
|
/api/data/artery → artery data
|
|
/api/data/atlas → atlas data
|
|
/api/data/station → station data
|
|
/artery/* → proxy to artery service
|
|
/atlas/* → proxy to atlas service
|
|
/station/* → proxy to station service
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
from dataloader import get_artery_data, get_atlas_data, get_station_data
|
|
from fastapi import FastAPI, Request
|
|
from fastapi.responses import FileResponse, RedirectResponse
|
|
from fastapi.templating import Jinja2Templates
|
|
from pydantic import BaseModel
|
|
|
|
app = FastAPI(title="Soleprint", version="0.1.0")
|
|
|
|
templates = Jinja2Templates(directory=Path(__file__).parent)
|
|
|
|
|
|
# === Generation Models ===
|
|
|
|
|
|
class FrameworkConfig(BaseModel):
|
|
name: str = "soleprint"
|
|
icon: Optional[str] = None
|
|
|
|
|
|
class SystemsConfig(BaseModel):
|
|
artery: str = "artery"
|
|
atlas: str = "atlas"
|
|
station: str = "station"
|
|
|
|
|
|
class ManagedConfig(BaseModel):
|
|
name: str
|
|
repos: dict[str, str]
|
|
|
|
|
|
class GenerationRequest(BaseModel):
|
|
room_name: str
|
|
framework: FrameworkConfig = FrameworkConfig()
|
|
systems: SystemsConfig = SystemsConfig()
|
|
managed: Optional[ManagedConfig] = None
|
|
|
|
|
|
# Load config if available
|
|
CONFIG_PATH = Path(__file__).parent / "cfg" / "config.json"
|
|
CONFIG = {}
|
|
if CONFIG_PATH.exists():
|
|
CONFIG = json.loads(CONFIG_PATH.read_text())
|
|
|
|
# Get hub port from config
|
|
HUB_PORT = CONFIG.get("framework", {}).get("hub_port", 12000)
|
|
|
|
|
|
@app.get("/health")
|
|
def health():
|
|
return {
|
|
"status": "ok",
|
|
"service": "soleprint",
|
|
"subsystems": {
|
|
"artery": ARTERY_URL,
|
|
"atlas": ATLAS_URL,
|
|
"station": STATION_URL,
|
|
},
|
|
}
|
|
|
|
|
|
# === Data API ===
|
|
|
|
|
|
@app.get("/api/data/artery")
|
|
def api_artery_data():
|
|
"""Data for artery service."""
|
|
return get_artery_data()
|
|
|
|
|
|
@app.get("/api/data/atlas")
|
|
def api_atlas_data():
|
|
"""Data for atlas service."""
|
|
return get_atlas_data()
|
|
|
|
|
|
@app.get("/api/data/station")
|
|
def api_station_data():
|
|
"""Data for station service."""
|
|
return get_station_data()
|
|
|
|
|
|
# === Generation API ===
|
|
|
|
|
|
@app.get("/generate")
|
|
def generation_ui():
|
|
"""Serve the generation UI."""
|
|
return FileResponse(Path(__file__).parent / "generate.html")
|
|
|
|
|
|
@app.post("/api/generate")
|
|
def generate_config(req: GenerationRequest):
|
|
"""Generate a config.json for a new room."""
|
|
config = {
|
|
"framework": {
|
|
"name": req.framework.name,
|
|
"slug": req.framework.name.lower().replace(" ", "-"),
|
|
"version": "0.1.0",
|
|
"description": "Development workflow and documentation system",
|
|
"tagline": "Mapping development footprints",
|
|
"icon": req.framework.icon or "",
|
|
"hub_port": HUB_PORT,
|
|
},
|
|
"systems": [
|
|
{
|
|
"key": "data_flow",
|
|
"name": req.systems.artery,
|
|
"slug": req.systems.artery.lower(),
|
|
"title": req.systems.artery.title(),
|
|
"tagline": "Todo lo vital",
|
|
"icon": "",
|
|
},
|
|
{
|
|
"key": "documentation",
|
|
"name": req.systems.atlas,
|
|
"slug": req.systems.atlas.lower(),
|
|
"title": req.systems.atlas.title(),
|
|
"tagline": "Documentacion accionable",
|
|
"icon": "",
|
|
},
|
|
{
|
|
"key": "execution",
|
|
"name": req.systems.station,
|
|
"slug": req.systems.station.lower(),
|
|
"title": req.systems.station.title(),
|
|
"tagline": "Monitores, Entornos y Herramientas",
|
|
"icon": "",
|
|
},
|
|
],
|
|
"components": {
|
|
"shared": {
|
|
"config": {
|
|
"name": "room",
|
|
"title": "Room",
|
|
"description": "Runtime environment configuration",
|
|
"plural": "rooms",
|
|
},
|
|
"data": {
|
|
"name": "depot",
|
|
"title": "Depot",
|
|
"description": "Data storage / provisions",
|
|
"plural": "depots",
|
|
},
|
|
},
|
|
"data_flow": {
|
|
"connector": {
|
|
"name": "vein",
|
|
"title": "Vein",
|
|
"description": "Stateless API connector",
|
|
"plural": "veins",
|
|
},
|
|
"mock": {
|
|
"name": "shunt",
|
|
"title": "Shunt",
|
|
"description": "Fake connector for testing",
|
|
"plural": "shunts",
|
|
},
|
|
},
|
|
"documentation": {
|
|
"library": {
|
|
"name": "book",
|
|
"title": "Book",
|
|
"description": "Documentation library",
|
|
},
|
|
},
|
|
"execution": {
|
|
"utility": {
|
|
"name": "tool",
|
|
"title": "Tool",
|
|
"description": "Execution utility",
|
|
"plural": "tools",
|
|
},
|
|
"watcher": {
|
|
"name": "monitor",
|
|
"title": "Monitor",
|
|
"description": "Service monitor",
|
|
"plural": "monitors",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if req.managed:
|
|
config["managed"] = {
|
|
"name": req.managed.name,
|
|
"repos": req.managed.repos,
|
|
}
|
|
|
|
return {"config": config, "room_name": req.room_name}
|
|
|
|
|
|
@app.post("/api/generate/preview")
|
|
def generate_preview(req: GenerationRequest):
|
|
"""Preview the generated folder structure."""
|
|
room = req.room_name or "room"
|
|
fw = req.framework.name or "soleprint"
|
|
sys = req.systems
|
|
|
|
lines = [f'<span class="folder">gen/{room}/</span>']
|
|
|
|
if req.managed and req.managed.name:
|
|
lines.append(f' <span class="folder">{req.managed.name}/</span>')
|
|
lines.append(' <span class="folder">link/</span>')
|
|
|
|
lines.append(f' <span class="folder">{fw}/</span>')
|
|
|
|
return {"tree": "\n".join(lines)}
|
|
|
|
|
|
@app.get("/")
|
|
def index(request: Request):
|
|
return templates.TemplateResponse(
|
|
"index.html",
|
|
{
|
|
"request": request,
|
|
"artery": ARTERY_EXTERNAL_URL,
|
|
"atlas": ATLAS_EXTERNAL_URL,
|
|
"station": STATION_EXTERNAL_URL,
|
|
},
|
|
)
|
|
|
|
|
|
# === Cross-system redirects ===
|
|
# These allow soleprint to act as a hub, redirecting to subsystem routes
|
|
|
|
|
|
@app.get("/artery")
|
|
@app.get("/artery/{path:path}")
|
|
def artery_redirect(path: str = ""):
|
|
"""Redirect to artery service."""
|
|
return RedirectResponse(url=f"{ARTERY_EXTERNAL_URL}/{path}")
|
|
|
|
|
|
@app.get("/atlas")
|
|
@app.get("/atlas/{path:path}")
|
|
def atlas_redirect(path: str = ""):
|
|
"""Redirect to atlas service."""
|
|
return RedirectResponse(url=f"{ATLAS_EXTERNAL_URL}/{path}")
|
|
|
|
|
|
@app.get("/station")
|
|
@app.get("/station/{path:path}")
|
|
def station_redirect(path: str = ""):
|
|
"""Redirect to station service."""
|
|
return RedirectResponse(url=f"{STATION_EXTERNAL_URL}/{path}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
|
|
uvicorn.run(
|
|
"main:app",
|
|
host="0.0.0.0",
|
|
port=int(os.getenv("PORT", "12000")),
|
|
reload=os.getenv("DEV", "").lower() in ("1", "true"),
|
|
)
|