chore: move plugins/ to separate repo, gitignore the directory

plugins/ is now tracked at bvandeusen/fabledscryer-plugins.
Removed tracked traefik files from this repo's index (files remain on disk).
Added plugins/ to .gitignore so the directory is ignored going forward.

PLUGIN_DIR in config.yaml still points to plugins/ for local dev and Docker.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-22 18:33:22 -04:00
parent 230b542015
commit fa318073f1
14 changed files with 4 additions and 637 deletions
-93
View File
@@ -1,93 +0,0 @@
# plugins/traefik/routes.py
from __future__ import annotations
from quart import Blueprint, current_app, render_template
from sqlalchemy import select
from fablednetmon.auth.middleware import require_role
from fablednetmon.models.users import UserRole
from .models import TraefikMetric
traefik_bp = Blueprint("traefik", __name__, template_folder="templates")
def _sparkline(values: list[float], width: int = 80, height: int = 20) -> str:
"""Generate an inline SVG sparkline from a list of float values."""
if len(values) < 2:
return f'<svg width="{width}" height="{height}"></svg>'
mn, mx = min(values), max(values)
if mx == mn:
mx = mn + 1.0
step = width / (len(values) - 1)
pts = []
for i, v in enumerate(values):
x = i * step
y = height - (v - mn) / (mx - mn) * (height - 2) - 1
pts.append(f"{x:.1f},{y:.1f}")
poly = " ".join(pts)
return (
f'<svg width="{width}" height="{height}" viewBox="0 0 {width} {height}" '
f'style="vertical-align:middle;">'
f'<polyline points="{poly}" fill="none" stroke="#6060c0" stroke-width="1.5"/>'
f'</svg>'
)
@traefik_bp.get("/")
@require_role(UserRole.viewer)
async def index():
# Last 20 data points per router for sparklines
history_limit = 20
async with current_app.db_sessionmaker() as db:
# Get distinct router names with a recent row
result = await db.execute(
select(TraefikMetric.router_name)
.distinct()
.order_by(TraefikMetric.router_name)
)
routers = [row[0] for row in result.all()]
router_data = []
for router in routers:
result = await db.execute(
select(TraefikMetric)
.where(TraefikMetric.router_name == router)
.order_by(TraefikMetric.scraped_at.desc())
.limit(history_limit)
)
rows = list(reversed(result.scalars().all()))
if not rows:
continue
latest = rows[-1]
router_data.append({
"name": router,
"latest": latest,
"sparkline_req": _sparkline([r.request_rate for r in rows]),
"sparkline_p95": _sparkline([r.latency_p95_ms for r in rows]),
"sparkline_5xx": _sparkline([r.error_rate_5xx_pct for r in rows]),
})
return await render_template("traefik/index.html", router_data=router_data)
@traefik_bp.get("/widget")
@require_role(UserRole.viewer)
async def widget():
"""HTMX dashboard widget fragment."""
async with current_app.db_sessionmaker() as db:
result = await db.execute(
select(TraefikMetric.router_name).distinct().order_by(TraefikMetric.router_name)
)
routers = [row[0] for row in result.all()]
router_data = []
for router in routers:
result = await db.execute(
select(TraefikMetric)
.where(TraefikMetric.router_name == router)
.order_by(TraefikMetric.scraped_at.desc())
.limit(1)
)
latest = result.scalar_one_or_none()
if latest:
router_data.append({"name": router, "latest": latest})
return await render_template("traefik/widget.html", router_data=router_data)