Files
ipwatch/backend/app/routers/architecture.py
2026-02-07 19:33:05 +01:00

152 lines
4.4 KiB
Python

"""
Endpoints API pour l'éditeur d'architecture
"""
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from pydantic import BaseModel
from typing import List, Optional, Dict, Any
from uuid import uuid4
from datetime import datetime
import json
from pathlib import Path
from backend.app.core.database import get_arch_db
from backend.app.models.architecture import ArchitectureNode
router = APIRouter(prefix="/api/architecture", tags=["Architecture"])
DATA_DIR = Path(__file__).resolve().parents[3] / "data"
WORLD_FILE = DATA_DIR / "architecture.json"
class ArchitectureNodeCreate(BaseModel):
id: Optional[str] = None
type: str
x: int
y: int
width: int
height: int
rotation: int = 0
payload: Dict[str, Any]
class ArchitectureNodeResponse(BaseModel):
id: str
type: str
x: int
y: int
width: int
height: int
rotation: int
payload: Dict[str, Any]
created_at: datetime
class Config:
from_attributes = True
class ArchitectureWorldPayload(BaseModel):
# Format v1 (legacy)
items: Optional[List[Dict[str, Any]]] = None
splines: Optional[List[Dict[str, Any]]] = None
# Format v2 (Vue Flow)
nodes: Optional[List[Dict[str, Any]]] = None
edges: Optional[List[Dict[str, Any]]] = None
version: Optional[int] = None
@router.get("/nodes", response_model=List[ArchitectureNodeResponse])
async def list_nodes(db: Session = Depends(get_arch_db)):
"""Liste tous les noeuds d'architecture"""
nodes = db.query(ArchitectureNode).order_by(ArchitectureNode.created_at.asc()).all()
results = []
for node in nodes:
try:
payload = json.loads(node.payload or "{}")
except json.JSONDecodeError:
payload = {}
results.append(ArchitectureNodeResponse(
id=node.id,
type=node.type,
x=node.x,
y=node.y,
width=node.width,
height=node.height,
rotation=node.rotation,
payload=payload,
created_at=node.created_at
))
return results
@router.post("/nodes", response_model=ArchitectureNodeResponse)
async def create_node(payload: ArchitectureNodeCreate, db: Session = Depends(get_arch_db)):
"""Créer un noeud d'architecture"""
node_id = payload.id or str(uuid4())
node = ArchitectureNode(
id=node_id,
type=payload.type,
x=payload.x,
y=payload.y,
width=payload.width,
height=payload.height,
rotation=payload.rotation,
payload=json.dumps(payload.payload)
)
db.add(node)
db.commit()
db.refresh(node)
return ArchitectureNodeResponse(
id=node.id,
type=node.type,
x=node.x,
y=node.y,
width=node.width,
height=node.height,
rotation=node.rotation,
payload=payload.payload,
created_at=node.created_at
)
def ensure_world_file() -> None:
DATA_DIR.mkdir(parents=True, exist_ok=True)
if not WORLD_FILE.exists():
WORLD_FILE.write_text(json.dumps({"items": [], "splines": []}, indent=2), encoding="utf-8")
@router.get("/world")
async def get_world():
"""Charge le fichier architecture.json, le crée si absent."""
ensure_world_file()
try:
data = json.loads(WORLD_FILE.read_text(encoding="utf-8"))
except json.JSONDecodeError:
data = {"items": [], "splines": []}
return data
@router.post("/world")
async def save_world(payload: ArchitectureWorldPayload):
"""Sauvegarde les éléments du world dans architecture.json.
Accepte le format v1 (items/splines) ou v2 (nodes/edges)."""
ensure_world_file()
if payload.version and payload.version >= 2:
# Format v2 (Vue Flow)
data = {
"nodes": payload.nodes or [],
"edges": payload.edges or [],
"version": payload.version,
}
WORLD_FILE.write_text(json.dumps(data, indent=2), encoding="utf-8")
return {"status": "ok", "nodes": len(data["nodes"]), "edges": len(data["edges"])}
else:
# Format v1 (legacy)
items = payload.items or []
splines = payload.splines or []
WORLD_FILE.write_text(
json.dumps({"items": items, "splines": splines}, indent=2),
encoding="utf-8"
)
return {"status": "ok", "count": len(items), "splines": len(splines)}