Files
Wiring-Designer/backend/app/main.py
T
Kyle 01e7b5630b Add git version control panel and LAN hosting support
- Git modal (⎇ Git button): commit all diagrams as JSON to diagrams/ folder,
  push to remote, view history, restore any diagram from any past commit
- Diagrams exported to diagrams/{id}_{name}.json on each commit so diffs are
  human-readable and individual diagrams can be restored independently
- backend/run.py: changed host to 0.0.0.0 for LAN access

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 19:48:29 -04:00

96 lines
4.0 KiB
Python

import os
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
from sqlalchemy import text, inspect as sa_inspect
from .database import Base, engine, SessionLocal
from .routers import bundles, connectors, diagrams, devices, export, git_mgr, octopart, views, wires
Base.metadata.create_all(bind=engine)
# Lightweight column migrations (SQLite ALTER TABLE IF NOT EXISTS unsupported)
def _migrate():
import sys
# ── Column additions ────────────────────────────────────────────────────────
try:
with engine.connect() as conn:
def _cols(table):
return {c["name"] for c in sa_inspect(engine).get_columns(table)}
if "waypoints" not in _cols("wires"):
conn.execute(text("ALTER TABLE wires ADD COLUMN waypoints TEXT DEFAULT '[]'"))
conn.commit()
if "sheet_id" not in _cols("devices"):
conn.execute(text("ALTER TABLE devices ADD COLUMN sheet_id INTEGER REFERENCES sheets(id)"))
conn.commit()
if "sheet_id" not in _cols("wires"):
conn.execute(text("ALTER TABLE wires ADD COLUMN sheet_id INTEGER REFERENCES sheets(id)"))
conn.commit()
if "twisted_pair" not in _cols("wires"):
conn.execute(text("ALTER TABLE wires ADD COLUMN twisted_pair INTEGER DEFAULT 0"))
conn.commit()
if "twist_pitch" not in _cols("wires"):
conn.execute(text("ALTER TABLE wires ADD COLUMN twist_pitch REAL DEFAULT 16.0"))
conn.commit()
if "shielded" not in _cols("wires"):
conn.execute(text("ALTER TABLE wires ADD COLUMN shielded INTEGER DEFAULT 0"))
conn.commit()
if "bundle_id" not in _cols("wires"):
conn.execute(text("ALTER TABLE wires ADD COLUMN bundle_id INTEGER REFERENCES wire_bundles(id)"))
conn.commit()
if "show_size_label" not in _cols("wires"):
conn.execute(text("ALTER TABLE wires ADD COLUMN show_size_label INTEGER DEFAULT 0"))
conn.commit()
# diagram_views / view_device_positions / view_wire_waypoints are created
# via Base.metadata.create_all — no ALTER TABLE needed for new tables
except Exception as e:
print(f"[migrate] column error: {e}", file=sys.stderr)
_migrate()
app = FastAPI(title="Wiring Diagram Maker", version="1.0.0")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(bundles.router, prefix="/api/bundles")
app.include_router(diagrams.router, prefix="/api/diagrams")
app.include_router(devices.router, prefix="/api/devices")
app.include_router(wires.router, prefix="/api/wires")
app.include_router(views.router, prefix="/api/views")
app.include_router(octopart.router, prefix="/api/octopart")
app.include_router(export.router, prefix="/api/export")
app.include_router(connectors.router, prefix="/api/connectors")
app.include_router(git_mgr.router, prefix="/api/git")
FRONTEND_DIR = os.path.normpath(
os.path.join(os.path.dirname(__file__), "..", "..", "frontend")
)
if os.path.isdir(FRONTEND_DIR):
for _sub in ("css", "js"):
_d = os.path.join(FRONTEND_DIR, _sub)
if os.path.isdir(_d):
app.mount(f"/{_sub}", StaticFiles(directory=_d), name=_sub)
_index = os.path.join(FRONTEND_DIR, "index.html")
@app.get("/")
async def _root():
return FileResponse(_index)
@app.exception_handler(404)
async def _spa_fallback(request: Request, exc):
if request.url.path.startswith("/api"):
return JSONResponse(status_code=404, content={"detail": "Not Found"})
return FileResponse(_index)