mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-28 07:41:27 +00:00
feat(nodes): mesh awareness + node detail page
- agent reports tailscale/netbird/zerotier/wireguard interface IPs at register (authoritative — absent interfaces clear stored state) - backend stores under node_agents.metadata.mesh, merged on re-register - node detail page shows mesh addresses under the primary IP Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
1361868946
commit
7cb5e10eb6
7 files changed
+84
-5
No files matched your search
@@ -156,6 +156,11 @@ export function NodeDetailPage() {
|
||||
{agent.version ? ` · v${agent.version}` : ''}
|
||||
{agent.lastHeartbeat ? ` · heartbeat ${formatRelative(agent.lastHeartbeat)}` : ''}
|
||||
</p>
|
||||
{meshAddresses(agent).length > 0 && (
|
||||
<p className="v-mono mt-1 text-[11px] text-[var(--text-tertiary)]">
|
||||
mesh: {meshAddresses(agent).map(([iface, ip]) => `${iface}=${ip}`).join(' · ')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className={`v-mono text-[11px] px-2 py-1 rounded-md border ${online
|
||||
@@ -312,6 +317,13 @@ export function NodeDetailPage() {
|
||||
);
|
||||
}
|
||||
|
||||
function meshAddresses(agent: { metadata: Record<string, unknown> }): [string, string][] {
|
||||
const mesh = agent.metadata?.mesh;
|
||||
if (!mesh || typeof mesh !== 'object' || Array.isArray(mesh)) return [];
|
||||
return Object.entries(mesh as Record<string, unknown>)
|
||||
.filter((entry): entry is [string, string] => typeof entry[1] === 'string' && entry[1] !== '');
|
||||
}
|
||||
|
||||
function Stat({ label, value, sub }: { label: string; value: string; sub?: string }) {
|
||||
return (
|
||||
<div className="panel p-4">
|
||||
|
||||
@@ -12026,6 +12026,10 @@ export interface components {
|
||||
capabilities: components["schemas"]["AgentCapabilities"];
|
||||
/** @description May also be sent via the X-Agent-Token header */
|
||||
auth_token?: string;
|
||||
/** @description Overlay-network interface IPs (tailscale/netbird/zerotier/wireguard). Stored under agent metadata.mesh; authoritative — send an empty object when no mesh interfaces exist. */
|
||||
mesh?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
};
|
||||
AgentHeartbeat: {
|
||||
agent_id?: string;
|
||||
|
||||
@@ -342,6 +342,7 @@ export type NodeAgentEntity = {
|
||||
capabilities: AgentCapabilities;
|
||||
resources: NodeResources;
|
||||
autoPrune: boolean;
|
||||
metadata: Record<string, unknown>;
|
||||
lastHeartbeat?: string;
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
@@ -1078,6 +1079,7 @@ function normalizeAgent(agent: RawNodeAgent): NodeAgentEntity | null {
|
||||
},
|
||||
lastHeartbeat: agent.last_heartbeat,
|
||||
autoPrune: agent.auto_prune ?? false,
|
||||
metadata: (agent.metadata ?? {}) as Record<string, unknown>,
|
||||
createdAt: agent.created_at,
|
||||
updatedAt: agent.updated_at,
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user