Files
Wiring-Designer/backend/app/main.py
T
Kyle f32203b630 Initial commit — wiring diagram maker with full feature set
- FastAPI + SQLite backend with routers for diagrams, devices, wires,
  bundles, connectors, export, and Octopart/Nexar search
- Konva.js canvas: ortho/direct/curved routing, wire crossings, harness
  mode, snap-to-grid, multi-select, drag, resize, undo
- Wire features: color/stripe, twisted pair helix, shielded glow,
  gauge size label, multi-core bundle grouping, length/unit tracking
- Device library: connector, terminal block, cable, splice, label, group,
  relay, fuse, switch, component with custom connector support
- Exports: BOM CSV, assembly TXT, JSON, formboard layout, PNG image
- Auto-migration for schema changes via ALTER TABLE at startup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 12:51:21 -04:00

91 lines
3.7 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, octopart, 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()
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(octopart.router, prefix="/api/octopart")
app.include_router(export.router, prefix="/api/export")
app.include_router(connectors.router, prefix="/api/connectors")
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)