From 071312fc0c309e6f08e2fd18dfcb3ce96d2400b4 Mon Sep 17 00:00:00 2001 From: anti Date: Tue, 21 Apr 2026 10:24:01 -0400 Subject: [PATCH] feat(web/api): expose archetype catalog endpoint /api/v1/topologies/archetypes returns the archetype registry (slug, display name, description, preferred services/distros, nmap_os fingerprint) so the frontend wizard can render a live catalog instead of hardcoding a copy. --- decnet/web/router/topology/api_catalog.py | 31 +++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/decnet/web/router/topology/api_catalog.py b/decnet/web/router/topology/api_catalog.py index 728f26a8..858640e8 100644 --- a/decnet/web/router/topology/api_catalog.py +++ b/decnet/web/router/topology/api_catalog.py @@ -7,6 +7,7 @@ from __future__ import annotations from fastapi import APIRouter, Depends, HTTPException, Query +from decnet.archetypes import all_archetypes from decnet.fleet import all_service_names from decnet.telemetry import traced as _traced from decnet.topology.allocator import ( @@ -16,6 +17,8 @@ from decnet.topology.allocator import ( reserved_subnets, ) from decnet.web.db.models import ( + ArchetypeCatalogResponse, + ArchetypeEntry, NextIPResponse, NextSubnetResponse, ServiceCatalogResponse, @@ -42,6 +45,34 @@ async def api_list_services( return ServiceCatalogResponse(services=all_service_names()) +@router.get( + "/archetypes", + tags=["MazeNET Topologies"], + response_model=ArchetypeCatalogResponse, + responses={ + 401: {"description": "Missing or invalid credentials"}, + 403: {"description": "Insufficient permissions"}, + }, +) +@_traced("api.topology.catalog.archetypes") +async def api_list_archetypes( + _viewer: dict = Depends(require_viewer), +) -> ArchetypeCatalogResponse: + return ArchetypeCatalogResponse( + archetypes=[ + ArchetypeEntry( + slug=a.slug, + display_name=a.display_name, + description=a.description, + services=list(a.services), + preferred_distros=list(a.preferred_distros), + nmap_os=a.nmap_os, + ) + for a in all_archetypes().values() + ], + ) + + @router.get( "/next-subnet", tags=["MazeNET Topologies"],