diff --git a/modules/config/nix.nix b/modules/config/nix.nix index a3f4e66..9222f9a 100644 --- a/modules/config/nix.nix +++ b/modules/config/nix.nix @@ -16,12 +16,10 @@ substituters = [ "https://cache.nixos.org" "https://nix-community.cachix.org" - "https://nix-config.cachix.org" ]; trusted-public-keys = [ "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" - "nix-config.cachix.org-1:Vd6raEuldeIZpttVQfrUbLvXJHzzzkS0pezXCVVjDG4=" ]; cores = 0; max-jobs = "auto"; diff --git a/pkgs/html-to-svg/default.nix b/pkgs/html-to-svg/default.nix index 201954d..4ca5030 100644 --- a/pkgs/html-to-svg/default.nix +++ b/pkgs/html-to-svg/default.nix @@ -2,7 +2,7 @@ buildNpmPackage, lib, }: -buildNpmPackage rec { +buildNpmPackage { pname = "html-to-svg"; version = "1.0.0"; diff --git a/topology/options/services.nix b/topology/options/services.nix index 24918b3..4851d3f 100644 --- a/topology/options/services.nix +++ b/topology/options/services.nix @@ -35,7 +35,7 @@ in hidden = mkOption { description = "Whether this service should be hidden from graphs"; - default = true; + default = false; type = types.bool; }; diff --git a/topology/topology/default.nix b/topology/topology/default.nix index cc2a0da..e295283 100644 --- a/topology/topology/default.nix +++ b/topology/topology/default.nix @@ -57,6 +57,14 @@ in { defaultText = literalExpression ''config.renderers.${config.renderer}.output''; }; + lib = lib.mkOption { + default = {}; + type = lib.types.attrsOf lib.types.attrs; + description = lib.mdDoc '' + This option allows modules to define helper functions, constants, etc. + ''; + }; + assertions = mkOption { internal = true; default = []; diff --git a/topology/topology/renderers/d2/default.nix b/topology/topology/renderers/d2/default.nix index 9e7e77a..315a6a0 100644 --- a/topology/topology/renderers/d2/default.nix +++ b/topology/topology/renderers/d2/default.nix @@ -17,7 +17,7 @@ in { }; }; - config.renderers.d2.output = pkgs.runCommand "build-d2-topology" {} '' + config.renderers.d2.output = pkgs.runCommand "topology-d2" {} '' mkdir -p $out # cp ${import ./network.nix args} $out/network.d2 ln -s ${import ./network.nix args} $out/svgs diff --git a/topology/topology/renderers/d2/network.nix b/topology/topology/renderers/d2/network.nix index b2fec5a..1e406a2 100644 --- a/topology/topology/renderers/d2/network.nix +++ b/topology/topology/renderers/d2/network.nix @@ -8,50 +8,8 @@ (lib) attrValues concatLines - filter - hasSuffix - head - optionalString - splitString - tail ; - getIcon = registry: iconName: - if iconName == null - then null - else config.icons.${registry}.${iconName}.file or null; - - mkImage = twAttrs: file: - if file == null - then '' -
- '' - else if hasSuffix ".svg" file - then let - withoutPrefix = head (tail (splitString "" withoutPrefix); - in '''' - else if hasSuffix ".png" file - # FIXME: TODO png, jpg, ... - then '' - " - '' - else builtins.throw "Unsupported icon file type: ${file}"; - - mkSpacer = name: - /* - html - */ - '' -
-
-
- ${name} -
-
-
- ''; - netToD2 = net: '' ${net.id}: ${net.name} { info: |md @@ -79,117 +37,13 @@ # ${node.id}.${interface.id} -- ${x.node}.${x.interface} #'')); - nodeInterfaceHtmlSpacing = '' -
- ''; - nodeInterfaceHtml = interface: let - color = - if interface.virtual - then "#242931" - else "#70a5eb"; - in - /* - html - */ - '' -
-
-
- ${mkImage "w-6 h-6 mr-2" (getIcon "interfaces" interface.icon)} - ${interface.id} -
- addrs: ${toString interface.addresses} -
- ''; - - nodeServiceDetailsHeader = - /* - html - */ - '' -
- ''; - - nodeServiceDetail = detail: - /* - html - */ - '' -
- ${detail.name} - ${detail.text} -
- ''; - - nodeServiceDetails = service: - optionalString (service.details != {}) nodeServiceDetailsHeader - # FIXME: order not respected - + concatLines ((map nodeServiceDetail) (attrValues service.details)); - - nodeServiceHtml = service: - /* - html - */ - '' -
-
- ${mkImage "w-16 h-16 mr-4 rounded-lg" (getIcon "services" service.icon)} -
-

${service.name}

- ${optionalString (service.info != "") ''

${service.info}

''} -
-
- ${nodeServiceDetails service} -
- ''; - - nodeHtml = node: let - services = filter (x: !x.hidden) (attrValues node.services); - in - /* - html - */ - '' -
-
-
-

${node.name}

-
-

${node.type}

-
- - ${optionalString (node.interfaces != {}) (mkSpacer "Interfaces" + nodeInterfaceHtmlSpacing)} - ${concatLines (map nodeInterfaceHtml (attrValues node.interfaces))} - ${optionalString (node.interfaces != {}) nodeInterfaceHtmlSpacing} - - ${optionalString (services != []) (mkSpacer "Services")} - ${concatLines (map nodeServiceHtml services)} - -
-
-
- ''; - nodeToD2 = node: '' ${node.id}: ${node.name} {} ${concatLines (map (nodeInterfaceToD2 node) (attrValues node.interfaces))} ''; - - generateNodeSvg = node: '' - ${lib.getExe pkgs.html-to-svg} \ - --font ${pkgs.jetbrains-mono}/share/fonts/truetype/JetBrainsMono-Regular.ttf \ - --font-bold ${pkgs.jetbrains-mono}/share/fonts/truetype/JetBrainsMono-Bold.ttf \ - --width 680 \ - ${pkgs.writeText "${node.name}.html" (nodeHtml node)} \ - $out/${node.name}.svg - ''; in - #pkgs.writeText "network.d2" '' - # ${concatLines (map netToD2 (attrValues config.networks))} - # ${concatLines (map nodeToD2 (attrValues config.nodes))} - #'' - pkgs.runCommand "generate-node-svgs" {} '' - mkdir -p $out - ${concatLines (map generateNodeSvg (attrValues config.nodes))} + pkgs.writeText "network.d2" '' + ${concatLines (map netToD2 (attrValues config.networks))} + ${concatLines (map nodeToD2 (attrValues config.nodes))} '' diff --git a/topology/topology/renderers/svg/default.nix b/topology/topology/renderers/svg/default.nix new file mode 100644 index 0000000..f56c10e --- /dev/null +++ b/topology/topology/renderers/svg/default.nix @@ -0,0 +1,221 @@ +{ + config, + lib, + pkgs, + ... +}: let + inherit + (lib) + attrValues + concatLines + filter + flip + hasSuffix + head + mkOption + optionalString + splitString + tail + types + ; + + htmlToSvgCommand = inFile: outFile: '' + ${lib.getExe pkgs.html-to-svg} \ + --font ${pkgs.jetbrains-mono}/share/fonts/truetype/JetBrainsMono-Regular.ttf \ + --font-bold ${pkgs.jetbrains-mono}/share/fonts/truetype/JetBrainsMono-Bold.ttf \ + --width 680 \ + ${inFile} ${outFile} + ''; + + renderHtmlToSvg = html: name: let + drv = pkgs.runCommand "generate-svg-${name}" {} '' + mkdir -p $out + ${htmlToSvgCommand (pkgs.writeText "${name}.html" html) "$out/${name}.svg"} + ''; + in "${drv}/${name}.svg"; + + getIcon = registry: iconName: + if iconName == null + then null + else config.icons.${registry}.${iconName}.file or null; + + html = rec { + mkImage = twAttrs: file: + if file == null + then '' +
+ '' + else if hasSuffix ".svg" file + then let + withoutPrefix = head (tail (splitString "" withoutPrefix); + in '''' + else if hasSuffix ".png" file + # FIXME: TODO png, jpg, ... + then '' + " + '' + else builtins.throw "Unsupported icon file type: ${file}"; + + mkSpacer = name: + /* + html + */ + '' +
+
+
+ ${name} +
+
+
+ ''; + + mkRootContainer = contents: + /* + html + */ + '' +
+ ${contents} +
+ ''; + + mkRootCard = twAttrs: contents: + mkRootContainer + /* + html + */ + '' +
+ ${contents} +
+ ''; + + spacingMt2 = '' +
+ ''; + + node = rec { + mkInterface = interface: let + color = + if interface.virtual + then "#242931" + else "#70a5eb"; + in + /* + html + */ + '' +
+
+
+ ${mkImage "w-6 h-6 mr-2" (getIcon "interfaces" interface.icon)} + ${interface.id} +
+ addrs: ${toString interface.addresses} +
+ ''; + + serviceDetail = detail: + /* + html + */ + '' +
+ ${detail.name} + ${detail.text} +
+ ''; + + serviceDetails = service: + optionalString (service.details != {}) ''
'' + # FIXME: order not respected + + concatLines ((map serviceDetail) (attrValues service.details)); + + mkService = service: + /* + html + */ + '' +
+
+ ${mkImage "w-16 h-16 mr-4 rounded-lg" (getIcon "services" service.icon)} +
+

${service.name}

+ ${optionalString (service.info != "") ''

${service.info}

''} +
+
+ ${serviceDetails service} +
+ ''; + + mkTitle = node: + /* + html + */ + '' +
+

${node.name}

+
+

${node.type}

+
+ ''; + + mkInfoCardTitle = node: + mkRootCard "rounded-t-xl" + /* + html + */ + '' + ${mkTitle node} + ''; + + mkInfoCardFull = node: let + services = filter (x: !x.hidden) (attrValues node.services); + in + mkRootCard "rounded-xl" + /* + html + */ + '' + ${mkTitle node} + + ${optionalString (node.interfaces != {}) (mkSpacer "Interfaces" + spacingMt2)} + ${concatLines (map mkInterface (attrValues node.interfaces))} + ${optionalString (node.interfaces != {}) spacingMt2} + + ${optionalString (services != []) (mkSpacer "Services")} + ${concatLines (map mkService services)} + +
+ ''; + }; + }; +in { + options.renderers.svg = { + # FIXME: colors.bg0 = mkColorOption "bg0" "#"; + + output = mkOption { + description = "The derivation containing the rendered output"; + type = types.path; + readOnly = true; + }; + }; + + config = { + lib.renderers.svg.node = { + mkInfoCardFull = node: renderHtmlToSvg (html.node.mkInfoCardFull node) node.name; + }; + + renderers.svg.output = pkgs.runCommand "topology-svgs" {} '' + mkdir -p $out/nodes + ${concatLines (flip map (attrValues config.nodes) ( + node: + htmlToSvgCommand ( + pkgs.writeText "node-${node.name}.html" (html.node.mkInfoCardFull node) + ) "$out/nodes/${node.name}.svg" + ))} + ''; + }; +}