mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-27 18:57:09 +00:00
feat(nodes): add capability-based node compatibility negotiation (#350)
* feat(nodes): add capability-based node compatibility negotiation Each Sencho instance now exposes /api/meta with its version and supported capabilities. When the user switches nodes, the frontend fetches this metadata and disables features the remote node doesn't support via a CapabilityGate overlay. Version is shown in the node switcher dropdown and connection test results. - Backend: CapabilityRegistry with static capability list and fetchRemoteMeta helper - Backend: /api/meta (public) and /api/nodes/:id/meta (auth) endpoints - Frontend: NodeContext enhanced with per-node meta caching (5min TTL) - Frontend: CapabilityGate component with typed Capability union - Frontend: 13 features wrapped with capability gates - Docs: node-compatibility.mdx + OpenAPI spec updates * fix(nodes): revert to require() for package.json version reading The static import fails in the Docker multi-stage build because the root package.json is not copied into the backend-builder stage. The require() call resolves at runtime when the file is available.
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import { type ReactNode } from 'react';
|
||||
import { Unplug } from 'lucide-react';
|
||||
import { useNodes } from '@/context/NodeContext';
|
||||
import type { Capability } from '@/lib/capabilities';
|
||||
|
||||
interface CapabilityGateProps {
|
||||
capability: Capability;
|
||||
featureName?: string;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function CapabilityGate({ capability, featureName = 'This feature', children }: CapabilityGateProps) {
|
||||
const { hasCapability, activeNode, activeNodeMeta } = useNodes();
|
||||
|
||||
if (hasCapability(capability)) return <>{children}</>;
|
||||
|
||||
const nodeName = activeNode?.name ?? 'this node';
|
||||
const versionHint = activeNodeMeta?.version
|
||||
? `${nodeName} is running v${activeNodeMeta.version}`
|
||||
: `${nodeName} does not support this capability`;
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<div className="opacity-40 pointer-events-none select-none blur-[2px]">
|
||||
{children}
|
||||
</div>
|
||||
<div className="absolute inset-0 flex items-start justify-center pt-8">
|
||||
<div className="flex items-center gap-2 px-3 py-1.5 rounded-full bg-muted/80 border border-border text-muted-foreground text-xs">
|
||||
<Unplug className="w-3 h-3" strokeWidth={1.5} />
|
||||
{featureName} is not available — {versionHint}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user