This is an automated email from the ASF dual-hosted git repository.

wu-sheng pushed a commit to branch feat/service-internal-topology
in repository https://gitbox.apache.org/repos/asf/skywalking-horizon-ui.git

commit a7258e13f35745d94158ed03c519da615379c73d
Author: Wu Sheng <[email protected]>
AuthorDate: Tue Jun 9 15:45:52 2026 +0800

    feat(menu): config-driven excluded layers (horizon.yaml layers.excluded)
    
    Replaces the removed hard-coded hide list with an operator-configurable
    `layers.excluded` block. Each entry is { key, reason? }; the menu drops
    those layers from the sidebar (both live + OAP-down paths), matched
    case-insensitively. Defaults to FaaS (deprecated) and Virtual Gateway (not
    planned to set up); documented in horizon.example.yaml. Clear the list to
    surface every reported layer.
---
 apps/bff/src/config/schema.ts   | 26 ++++++++++++++++++++++++++
 apps/bff/src/http/query/menu.ts | 14 ++++++++++----
 horizon.example.yaml            | 14 ++++++++++++++
 3 files changed, 50 insertions(+), 4 deletions(-)

diff --git a/apps/bff/src/config/schema.ts b/apps/bff/src/config/schema.ts
index 0aea48f..ed17d7c 100644
--- a/apps/bff/src/config/schema.ts
+++ b/apps/bff/src/config/schema.ts
@@ -319,9 +319,35 @@ const querySchema = z
   .strict()
   .default({ landingServiceCap: 100 });
 
+// Layers hidden from the sidebar / menu even when OAP reports them in
+// `listLayers`. Config-driven (replaces a former hard-coded hide list): an
+// operator can clear `excluded` to surface every reported layer, or add keys
+// for internal-only layers they don't want on the menu. The `reason` is
+// documentation for whoever reads this file — it isn't shown in the UI (an
+// excluded layer simply doesn't appear).
+const excludedLayerSchema = z
+  .object({
+    /** OAP layer key (UPPER_SNAKE), matched case-insensitively. */
+    key: z.string().min(1),
+    /** Why it's hidden — operator-facing note, not surfaced in the UI. */
+    reason: z.string().optional(),
+  })
+  .strict();
+const DEFAULT_EXCLUDED_LAYERS = [
+  { key: 'FAAS', reason: 'Deprecated.' },
+  { key: 'VIRTUAL_GATEWAY', reason: 'Not planned to set up.' },
+];
+const layersSchema = z
+  .object({
+    excluded: z.array(excludedLayerSchema).default(DEFAULT_EXCLUDED_LAYERS),
+  })
+  .strict()
+  .default({ excluded: DEFAULT_EXCLUDED_LAYERS });
+
 export const configSchema = z
   .object({
     server: serverSchema.default({}),
+    layers: layersSchema,
     oap: oapSchema.default({}),
     auth: authSchema,
     rbac: rbacSchema,
diff --git a/apps/bff/src/http/query/menu.ts b/apps/bff/src/http/query/menu.ts
index ebd7fb6..c4965fd 100644
--- a/apps/bff/src/http/query/menu.ts
+++ b/apps/bff/src/http/query/menu.ts
@@ -338,6 +338,12 @@ export function registerMenuRoute(app: FastifyInstance, 
deps: MenuRouteDeps): vo
     const queryUrl = cfg.oap.queryUrl;
     const opts = buildOapOpts(cfg, deps.fetch);
     const locale = localeFromRequest(req);
+    // Operator-configured hidden layers (horizon.yaml `layers.excluded`).
+    // Defaults to FaaS (deprecated) + Virtual Gateway (not planned). Matched
+    // case-insensitively against the canonical layer key.
+    const excludedLayers = new Set(
+      (cfg.layers?.excluded ?? []).map((e) => e.key.toUpperCase()),
+    );
     try {
       const raw = await graphqlPost<MenuRaw>(opts, MENU_QUERY);
 
@@ -392,10 +398,10 @@ export function registerMenuRoute(app: FastifyInstance, 
deps: MenuRouteDeps): vo
 
       // Every layer OAP surfaces in `listLayers` is shown — including
       // ones with no Horizon template (they render with default caps, a
-      // bare Service page). Disabled-in-admin layers are the only drop:
-      // they're soft-deleted (matches how disabled overviews vanish).
+      // bare Service page). Dropped only when admin-disabled (soft-deleted,
+      // like disabled overviews) or config-excluded (`layers.excluded`).
       const layers = ordered
-        .filter((key) => !disabled.has(key))
+        .filter((key) => !disabled.has(key) && 
!excludedLayers.has(key.toUpperCase()))
         .map((key) =>
           deriveLayer(
             key,
@@ -428,7 +434,7 @@ export function registerMenuRoute(app: FastifyInstance, 
deps: MenuRouteDeps): vo
       const emptyRows = new Map<string, TemplateRow>();
       for (const rawKey of Object.keys(LAYER_DEFAULTS)) {
         const key = canonical(rawKey);
-        if (seen.has(key)) continue;
+        if (seen.has(key) || excludedLayers.has(key.toUpperCase())) continue;
         seen.add(key);
         layers.push(deriveLayer(key, false, null, -1, null, locale, emptyRows, 
null));
       }
diff --git a/horizon.example.yaml b/horizon.example.yaml
index 07780ed..05984c8 100644
--- a/horizon.example.yaml
+++ b/horizon.example.yaml
@@ -24,6 +24,20 @@ server:
   host: 127.0.0.1
   port: 8081
 
+# Layers hidden from the sidebar even when OAP reports them in listLayers.
+# Config-driven — there is no hard-coded hide list. Clear `excluded` to
+# surface every reported layer, or add keys for internal-only layers you
+# don't want on the menu. `reason` is a note for whoever reads this file;
+# it is NOT shown in the UI (an excluded layer simply doesn't appear).
+# Keys are OAP layer keys (UPPER_SNAKE), matched case-insensitively.
+# The defaults below are applied when this block is omitted entirely.
+layers:
+  excluded:
+    - key: FAAS
+      reason: Deprecated.
+    - key: VIRTUAL_GATEWAY
+      reason: Not planned to set up.
+
 oap:
   # OAP query host (port 12800 by default; GraphQL + /status/*). One URL
   # because query traffic is load-balanceable — any OAP node can answer.

Reply via email to