fix: UniFi, Synology, Network widgets + Grafana HTTPS

- Rewrite UniFi API: port 443, /api/auth/login, /proxy/network/ prefix,
  native https module for self-signed cert, cookie-based session
- Update credentials to Vault-Admin (view-only local account)
- Rewrite NetworkWidget to show UniFi devices with clients/uptime
- Fix Synology API: correct field mappings (size.total/size.used),
  add CPU/memory utilization endpoint
- Fix network API: use prometheus:9090 container DNS
- Add NODE_TLS_REJECT_UNAUTHORIZED=0 for UniFi self-signed cert
- Expand AI container group (rag, litellm, qdrant)
- Add force-dynamic to API routes
This commit is contained in:
2026-02-13 13:54:48 -05:00
parent e00c6efcda
commit 1102f27f45
8 changed files with 635 additions and 255 deletions

View File

@@ -1,48 +1,61 @@
"use client";
import { BarChart3 } from "lucide-react";
interface GrafanaWidgetProps {
title: string;
dashboardId: string;
panelId: number;
}
export default function GrafanaWidget({
title,
dashboardId,
panelId,
}: GrafanaWidgetProps) {
const grafanaHost =
process.env.NEXT_PUBLIC_GRAFANA_HOST || "http://100.104.196.38:3000";
const iframeUrl = `${grafanaHost}/d-solo/${dashboardId}?orgId=1&panelId=${panelId}&theme=dark`;
return (
<div className="bg-gray-800/40 backdrop-blur-sm rounded-lg border border-gray-700 overflow-hidden">
<div className="px-4 py-3 border-b border-gray-700 bg-gray-800/60">
<h3 className="text-sm font-semibold text-white flex items-center gap-2">
<BarChart3 className="w-4 h-4 text-orange-500" />
{title}
</h3>
</div>
<div className="relative h-48">
<iframe
src={iframeUrl}
className="w-full h-full"
frameBorder="0"
title={title}
/>
<div className="absolute top-2 right-2">
<a
href={`${grafanaHost}/d/${dashboardId}`}
target="_blank"
rel="noopener noreferrer"
className="text-xs text-gray-400 hover:text-white transition-colors"
>
Open in Grafana
</a>
</div>
</div>
</div>
);
}
"use client";
import { useState } from "react";
import { BarChart3, ExternalLink, AlertCircle } from "lucide-react";
interface GrafanaWidgetProps {
title: string;
dashboardUid: string;
panelId: number;
}
export default function GrafanaWidget({
title,
dashboardUid,
panelId,
}: GrafanaWidgetProps) {
const [loadError, setLoadError] = useState(false);
const grafanaHost = process.env.NEXT_PUBLIC_GRAFANA_HOST || "https://grafana.guapo613.beer";
const iframeUrl = `${grafanaHost}/d-solo/${dashboardUid}?orgId=1&panelId=${panelId}&theme=dark&refresh=30s`;
const dashUrl = `${grafanaHost}/d/${dashboardUid}`;
return (
<div className="bg-gray-800/40 backdrop-blur-sm rounded-lg border border-gray-700 overflow-hidden">
<div className="px-4 py-3 border-b border-gray-700 bg-gray-800/60 flex items-center justify-between">
<h3 className="text-sm font-semibold text-white flex items-center gap-2">
<BarChart3 className="w-4 h-4 text-orange-500" />
{title}
</h3>
<a
href={dashUrl}
target="_blank"
rel="noopener noreferrer"
className="text-xs text-gray-400 hover:text-white transition-colors flex items-center gap-1"
>
<ExternalLink className="w-3 h-3" />
Open
</a>
</div>
<div className="relative h-48">
{loadError ? (
<div className="flex flex-col items-center justify-center h-full text-gray-500">
<AlertCircle className="w-8 h-8 mb-2" />
<p className="text-xs">Panel unavailable</p>
<a href={dashUrl} target="_blank" rel="noopener noreferrer"
className="text-xs text-blue-400 hover:text-blue-300 mt-1">
View in Grafana
</a>
</div>
) : (
<iframe
src={iframeUrl}
className="w-full h-full border-0"
title={title}
loading="lazy"
onError={() => setLoadError(true)}
/>
)}
</div>
</div>
);
}