Add start of POC

This commit is contained in:
Melora Hugues 2024-09-17 18:39:53 +02:00
parent 7228f8b7c0
commit 25adf0f374
7 changed files with 1082 additions and 0 deletions

3
go.mod Normal file
View file

@ -0,0 +1,3 @@
module git.faercol.me/faercol/topology-map
go 1.23.1

212
main.go Normal file
View file

@ -0,0 +1,212 @@
package main
import (
"encoding/json"
"fmt"
"html/template"
"io"
"net/http"
"strconv"
)
const apiKey = "4e75b8927940adc29e2e1eac042bf92bcddd57fe"
const netboxBaseURL = "https://netbox.internal.faercol.me/api/"
type Device struct {
ID int `json:"id"`
Name string `json:"name"`
}
type Object struct {
Device Device `json:"device"`
}
type CableTermination struct {
Object Object `json:"object"`
}
type Cable struct {
ID int `json:"id"`
ATerminations []CableTermination `json:"a_terminations"`
BTerminations []CableTermination `json:"b_terminations"`
}
type VM struct {
ID int `json:"id"`
Name string `json:"name"`
Device Device `json:"device"`
}
type vmsResponse struct {
Results []VM `json:"results"`
}
type deviceResponse struct {
Results []Device `json:"results"`
}
type cableResponse struct {
Results []Cable `json:"results"`
}
type ElementData struct {
ID string `json:"id"`
Source string `json:"source,omitempty"`
Target string `json:"target,omitempty"`
Parent string `json:"parent,omitempty"`
}
type Element struct {
Data ElementData `json:"data"`
Classes []string `json:"classes"`
}
func GetDevices() ([]Device, error) {
query, err := http.NewRequest("GET", netboxBaseURL+"dcim/devices", nil)
if err != nil {
return nil, err
}
query.Header.Set("Authorization", "Token "+apiKey)
resp, err := http.DefaultClient.Do(query)
if err != nil {
return nil, err
}
var res deviceResponse
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if err := json.Unmarshal(respBody, &res); err != nil {
return nil, err
}
return res.Results, nil
}
func GetCables() ([]Cable, error) {
query, err := http.NewRequest("GET", netboxBaseURL+"dcim/cables", nil)
if err != nil {
return nil, err
}
query.Header.Set("Authorization", "Token "+apiKey)
resp, err := http.DefaultClient.Do(query)
if err != nil {
return nil, err
}
var res cableResponse
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if err := json.Unmarshal(respBody, &res); err != nil {
return nil, err
}
return res.Results, nil
}
func GetVMs() ([]VM, error) {
query, err := http.NewRequest("GET", netboxBaseURL+"virtualization/virtual-machines", nil)
if err != nil {
return nil, err
}
query.Header.Set("Authorization", "Token "+apiKey)
resp, err := http.DefaultClient.Do(query)
if err != nil {
return nil, err
}
var res vmsResponse
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if err := json.Unmarshal(respBody, &res); err != nil {
return nil, err
}
return res.Results, nil
}
func main() {
srv := http.NewServeMux()
srv.HandleFunc("/static/", func(w http.ResponseWriter, r *http.Request) {
fmt.Println("Serving static file")
fs := http.FileServer(http.Dir("./static"))
http.StripPrefix("/static", fs).ServeHTTP(w, r)
})
srv.HandleFunc("/api/data", func(w http.ResponseWriter, r *http.Request) {
fmt.Println("Serving API route")
devices, err := GetDevices()
if err != nil {
fmt.Printf("Failed to get devices: %s\n", err)
w.WriteHeader(500)
return
}
cables, err := GetCables()
if err != nil {
fmt.Printf("Failed to get cables: %s\n", err)
w.WriteHeader(500)
return
}
vms, err := GetVMs()
if err != nil {
fmt.Printf("Failed to get VMs: %s\n", err)
w.WriteHeader(500)
return
}
resp := []Element{}
for _, d := range devices {
resp = append(resp, Element{Data: ElementData{ID: d.Name}})
}
for _, c := range cables {
resp = append(resp, Element{Data: ElementData{ID: "link-" + strconv.FormatInt(int64(c.ID), 10), Source: c.ATerminations[0].Object.Device.Name, Target: c.BTerminations[0].Object.Device.Name}})
}
for _, v := range vms {
resp = append(resp, Element{Data: ElementData{ID: v.Name}}, Element{Data: ElementData{ID: "vm-" + strconv.FormatInt(int64(v.ID), 10), Source: v.Name, Target: v.Device.Name}, Classes: []string{"edge", "virtual-edge"}})
}
respBody, err := json.Marshal(resp)
if err != nil {
fmt.Printf("Failed to serialize data: %s\n", err)
w.WriteHeader(500)
return
}
w.Write(respBody)
})
srv.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Println("Serving main root")
tpl, err := template.New("index.html").ParseFiles("templates/index.html")
if err != nil {
fmt.Printf("Failed to read template: %s\n", err)
w.WriteHeader(500)
return
}
if err := tpl.Execute(w, nil); err != nil {
fmt.Printf("Failed to execute template: %s\n", err)
w.WriteHeader(500)
return
}
})
if err := http.ListenAndServe("127.0.0.1:5000", srv); err != nil {
panic(err)
}
}

View file

@ -0,0 +1,753 @@
(function webpackUniversalModuleDefinition(root, factory) {
if (typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("webcola"));
else if (typeof define === 'function' && define.amd)
define(["webcola"], factory);
else if (typeof exports === 'object')
exports["cytoscapeCola"] = factory(require("webcola"));
else
root["cytoscapeCola"] = factory(root["webcola"]);
})(this, function (__WEBPACK_EXTERNAL_MODULE_5__) {
return /******/ (function (modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if (installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/
}
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/
};
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/
}
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // identity function for calling harmony imports with the correct context
/******/ __webpack_require__.i = function (value) { return value; };
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function (exports, name, getter) {
/******/ if (!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/
});
/******/
}
/******/
};
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function (module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/
};
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function (object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 3);
/******/
})
/************************************************************************/
/******/([
/* 0 */
/***/ (function (module, exports, __webpack_require__) {
"use strict";
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var assign = __webpack_require__(1);
var defaults = __webpack_require__(2);
var cola = __webpack_require__(5) || (typeof window !== 'undefined' ? window.cola : null);
var raf = __webpack_require__(4);
var isString = function isString(o) {
return (typeof o === 'undefined' ? 'undefined' : _typeof(o)) === _typeof('');
};
var isNumber = function isNumber(o) {
return (typeof o === 'undefined' ? 'undefined' : _typeof(o)) === _typeof(0);
};
var isObject = function isObject(o) {
return o != null && (typeof o === 'undefined' ? 'undefined' : _typeof(o)) === _typeof({});
};
var isFunction = function isFunction(o) {
return o != null && (typeof o === 'undefined' ? 'undefined' : _typeof(o)) === _typeof(function () { });
};
var nop = function nop() { };
var getOptVal = function getOptVal(val, ele) {
if (isFunction(val)) {
var fn = val;
return fn.apply(ele, [ele]);
} else {
return val;
}
};
// constructor
// options : object containing layout options
function ColaLayout(options) {
this.options = assign({}, defaults, options);
}
// runs the layout
ColaLayout.prototype.run = function () {
var layout = this;
var options = this.options;
layout.manuallyStopped = false;
var cy = options.cy; // cy is automatically populated for us in the constructor
var eles = options.eles;
var nodes = eles.nodes();
var edges = eles.edges();
var ready = false;
var isParent = function isParent(ele) {
return ele.isParent();
};
var parentNodes = nodes.filter(isParent);
var nonparentNodes = nodes.subtract(parentNodes);
var bb = options.boundingBox || { x1: 0, y1: 0, w: cy.width(), h: cy.height() };
if (bb.x2 === undefined) {
bb.x2 = bb.x1 + bb.w;
}
if (bb.w === undefined) {
bb.w = bb.x2 - bb.x1;
}
if (bb.y2 === undefined) {
bb.y2 = bb.y1 + bb.h;
}
if (bb.h === undefined) {
bb.h = bb.y2 - bb.y1;
}
var updateNodePositions = function updateNodePositions() {
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
var dimensions = node.layoutDimensions(options);
var scratch = node.scratch('cola');
// update node dims
if (!scratch.updatedDims) {
var padding = getOptVal(options.nodeSpacing, node);
scratch.width = dimensions.w + 2 * padding;
scratch.height = dimensions.h + 2 * padding;
}
}
nodes.positions(function (node) {
var scratch = node.scratch().cola;
var retPos = void 0;
if (!node.grabbed() && nonparentNodes.contains(node)) {
retPos = {
x: bb.x1 + scratch.x,
y: bb.y1 + scratch.y
};
if (!isNumber(retPos.x) || !isNumber(retPos.y)) {
retPos = undefined;
}
}
return retPos;
});
nodes.updateCompoundBounds(); // because the way this layout sets positions is buggy for some reason; ref #878
if (!ready) {
onReady();
ready = true;
}
if (options.fit) {
cy.fit(options.padding);
}
};
var onDone = function onDone() {
if (options.ungrabifyWhileSimulating) {
grabbableNodes.grabify();
}
cy.off('destroy', destroyHandler);
nodes.off('grab free position', grabHandler);
nodes.off('lock unlock', lockHandler);
// trigger layoutstop when the layout stops (e.g. finishes)
layout.one('layoutstop', options.stop);
layout.trigger({ type: 'layoutstop', layout: layout });
};
var onReady = function onReady() {
// trigger layoutready when each node has had its position set at least once
layout.one('layoutready', options.ready);
layout.trigger({ type: 'layoutready', layout: layout });
};
var ticksPerFrame = options.refresh;
if (options.refresh < 0) {
ticksPerFrame = 1;
} else {
ticksPerFrame = Math.max(1, ticksPerFrame); // at least 1
}
var adaptor = layout.adaptor = cola.adaptor({
trigger: function trigger(e) {
// on sim event
var TICK = cola.EventType ? cola.EventType.tick : null;
var END = cola.EventType ? cola.EventType.end : null;
switch (e.type) {
case 'tick':
case TICK:
if (options.animate) {
updateNodePositions();
}
break;
case 'end':
case END:
updateNodePositions();
if (!options.infinite) {
onDone();
}
break;
}
},
kick: function kick() {
// kick off the simulation
//let skip = 0;
var firstTick = true;
var inftick = function inftick() {
if (layout.manuallyStopped) {
onDone();
return true;
}
var ret = adaptor.tick();
if (!options.infinite && !firstTick) {
adaptor.convergenceThreshold(options.convergenceThreshold);
}
firstTick = false;
if (ret && options.infinite) {
// resume layout if done
adaptor.resume(); // resume => new kick
}
return ret; // allow regular finish b/c of new kick
};
var multitick = function multitick() {
// multiple ticks in a row
var ret = void 0;
for (var i = 0; i < ticksPerFrame && !ret; i++) {
ret = ret || inftick(); // pick up true ret vals => sim done
}
return ret;
};
if (options.animate) {
var frame = function frame() {
if (multitick()) {
return;
}
raf(frame);
};
raf(frame);
} else {
while (!inftick()) {
// keep going...
}
}
},
on: nop, // dummy; not needed
drag: nop // not needed for our case
});
layout.adaptor = adaptor;
// if set no grabbing during layout
var grabbableNodes = nodes.filter(':grabbable');
if (options.ungrabifyWhileSimulating) {
grabbableNodes.ungrabify();
}
var destroyHandler = void 0;
cy.one('destroy', destroyHandler = function destroyHandler() {
layout.stop();
});
// handle node dragging
var grabHandler = void 0;
nodes.on('grab free position', grabHandler = function grabHandler(e) {
var node = this;
var scrCola = node.scratch().cola;
var pos = node.position();
var nodeIsTarget = e.cyTarget === node || e.target === node;
if (!nodeIsTarget) {
return;
}
switch (e.type) {
case 'grab':
adaptor.dragstart(scrCola);
break;
case 'free':
adaptor.dragend(scrCola);
break;
case 'position':
// only update when different (i.e. manual .position() call or drag) so we don't loop needlessly
if (scrCola.px !== pos.x - bb.x1 || scrCola.py !== pos.y - bb.y1) {
scrCola.px = pos.x - bb.x1;
scrCola.py = pos.y - bb.y1;
}
break;
}
});
var lockHandler = void 0;
nodes.on('lock unlock', lockHandler = function lockHandler() {
var node = this;
var scrCola = node.scratch().cola;
scrCola.fixed = node.locked();
if (node.locked()) {
adaptor.dragstart(scrCola);
} else {
adaptor.dragend(scrCola);
}
});
// add nodes to cola
adaptor.nodes(nonparentNodes.map(function (node, i) {
var padding = getOptVal(options.nodeSpacing, node);
var pos = node.position();
var dimensions = node.layoutDimensions(options);
var struct = node.scratch().cola = {
x: options.randomize && !node.locked() || pos.x === undefined ? Math.round(Math.random() * bb.w) : pos.x,
y: options.randomize && !node.locked() || pos.y === undefined ? Math.round(Math.random() * bb.h) : pos.y,
width: dimensions.w + 2 * padding,
height: dimensions.h + 2 * padding,
index: i,
fixed: node.locked()
};
return struct;
}));
// the constraints to be added on nodes
var constraints = [];
if (options.alignment) {
// then set alignment constraints
if (options.alignment.vertical) {
var verticalAlignments = options.alignment.vertical;
verticalAlignments.forEach(function (alignment) {
var offsetsX = [];
alignment.forEach(function (nodeData) {
var node = nodeData.node;
var scrCola = node.scratch().cola;
var index = scrCola.index;
offsetsX.push({
node: index,
offset: nodeData.offset ? nodeData.offset : 0
});
});
constraints.push({
type: 'alignment',
axis: 'x',
offsets: offsetsX
});
});
}
if (options.alignment.horizontal) {
var horizontalAlignments = options.alignment.horizontal;
horizontalAlignments.forEach(function (alignment) {
var offsetsY = [];
alignment.forEach(function (nodeData) {
var node = nodeData.node;
var scrCola = node.scratch().cola;
var index = scrCola.index;
offsetsY.push({
node: index,
offset: nodeData.offset ? nodeData.offset : 0
});
});
constraints.push({
type: 'alignment',
axis: 'y',
offsets: offsetsY
});
});
}
}
// if gapInequalities variable is set add each inequality constraint to list of constraints
if (options.gapInequalities) {
options.gapInequalities.forEach(function (inequality) {
// for the constraints to be passed to cola layout adaptor use indices of nodes,
// not the nodes themselves
var leftIndex = inequality.left.scratch().cola.index;
var rightIndex = inequality.right.scratch().cola.index;
constraints.push({
axis: inequality.axis,
left: leftIndex,
right: rightIndex,
gap: inequality.gap,
equality: inequality.equality
});
});
}
// add constraints if any
if (constraints.length > 0) {
adaptor.constraints(constraints);
}
// add compound nodes to cola
adaptor.groups(parentNodes.map(function (node, i) {
// add basic group incl leaf nodes
var optPadding = getOptVal(options.nodeSpacing, node);
var getPadding = function getPadding(d) {
return parseFloat(node.style('padding-' + d));
};
var pleft = getPadding('left') + optPadding;
var pright = getPadding('right') + optPadding;
var ptop = getPadding('top') + optPadding;
var pbottom = getPadding('bottom') + optPadding;
node.scratch().cola = {
index: i,
padding: Math.max(pleft, pright, ptop, pbottom),
// leaves should only contain direct descendants (children),
// not the leaves of nested compound nodes or any nodes that are compounds themselves
leaves: node.children().intersection(nonparentNodes).map(function (child) {
return child[0].scratch().cola.index;
}),
fixed: node.locked()
};
return node;
}).map(function (node) {
// add subgroups
node.scratch().cola.groups = node.children().intersection(parentNodes).map(function (child) {
return child.scratch().cola.index;
});
return node.scratch().cola;
}));
// get the edge length setting mechanism
var length = void 0;
var lengthFnName = void 0;
if (options.edgeLength != null) {
length = options.edgeLength;
lengthFnName = 'linkDistance';
} else if (options.edgeSymDiffLength != null) {
length = options.edgeSymDiffLength;
lengthFnName = 'symmetricDiffLinkLengths';
} else if (options.edgeJaccardLength != null) {
length = options.edgeJaccardLength;
lengthFnName = 'jaccardLinkLengths';
} else {
length = 100;
lengthFnName = 'linkDistance';
}
var lengthGetter = function lengthGetter(link) {
return link.calcLength;
};
// add the edges to cola
adaptor.links(edges.stdFilter(function (edge) {
return nonparentNodes.contains(edge.source()) && nonparentNodes.contains(edge.target());
}).map(function (edge) {
var c = edge.scratch().cola = {
source: edge.source()[0].scratch().cola.index,
target: edge.target()[0].scratch().cola.index
};
if (length != null) {
c.calcLength = getOptVal(length, edge);
}
return c;
}));
adaptor.size([bb.w, bb.h]);
if (length != null) {
adaptor[lengthFnName](lengthGetter);
}
// set the flow of cola
if (options.flow) {
var flow = void 0;
var defAxis = 'y';
var defMinSep = 50;
if (isString(options.flow)) {
flow = {
axis: options.flow,
minSeparation: defMinSep
};
} else if (isNumber(options.flow)) {
flow = {
axis: defAxis,
minSeparation: options.flow
};
} else if (isObject(options.flow)) {
flow = options.flow;
flow.axis = flow.axis || defAxis;
flow.minSeparation = flow.minSeparation != null ? flow.minSeparation : defMinSep;
} else {
// e.g. options.flow: true
flow = {
axis: defAxis,
minSeparation: defMinSep
};
}
adaptor.flowLayout(flow.axis, flow.minSeparation);
}
layout.trigger({ type: 'layoutstart', layout: layout });
adaptor.avoidOverlaps(options.avoidOverlap).handleDisconnected(options.handleDisconnected).start(options.unconstrIter, options.userConstIter, options.allConstIter, undefined, // gridSnapIterations = 0
undefined, // keepRunning = true
options.centerGraph);
if (!options.infinite) {
setTimeout(function () {
if (!layout.manuallyStopped) {
adaptor.stop();
}
}, options.maxSimulationTime);
}
return this; // chaining
};
// called on continuous layouts to stop them before they finish
ColaLayout.prototype.stop = function () {
if (this.adaptor) {
this.manuallyStopped = true;
this.adaptor.stop();
}
return this; // chaining
};
module.exports = ColaLayout;
/***/
}),
/* 1 */
/***/ (function (module, exports, __webpack_require__) {
"use strict";
// Simple, internal Object.assign() polyfill for options objects etc.
module.exports = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
for (var _len = arguments.length, srcs = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
srcs[_key - 1] = arguments[_key];
}
srcs.filter(function (src) {
return src != null;
}).forEach(function (src) {
Object.keys(src).forEach(function (k) {
return tgt[k] = src[k];
});
});
return tgt;
};
/***/
}),
/* 2 */
/***/ (function (module, exports, __webpack_require__) {
"use strict";
// default layout options
var defaults = {
animate: true, // whether to show the layout as it's running
refresh: 1, // number of ticks per frame; higher is faster but more jerky
maxSimulationTime: 4000, // max length in ms to run the layout
ungrabifyWhileSimulating: false, // so you can't drag nodes during layout
fit: true, // on every layout reposition of nodes, fit the viewport
padding: 30, // padding around the simulation
boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
nodeDimensionsIncludeLabels: false, // whether labels should be included in determining the space used by a node
// layout event callbacks
ready: function ready() { }, // on layoutready
stop: function stop() { }, // on layoutstop
// positioning options
randomize: false, // use random node positions at beginning of layout
avoidOverlap: true, // if true, prevents overlap of node bounding boxes
handleDisconnected: true, // if true, avoids disconnected components from overlapping
convergenceThreshold: 0.01, // when the alpha value (system energy) falls below this value, the layout stops
nodeSpacing: function nodeSpacing(node) {
return 10;
}, // extra spacing around nodes
flow: undefined, // use DAG/tree flow layout if specified, e.g. { axis: 'y', minSeparation: 30 }
alignment: undefined, // relative alignment constraints on nodes, e.g. function( node ){ return { x: 0, y: 1 } }
gapInequalities: undefined, // list of inequality constraints for the gap between the nodes, e.g. [{"axis":"y", "left":node1, "right":node2, "gap":25}]
centerGraph: true, // adjusts the node positions initially to center the graph (pass false if you want to start the layout from the current position)
// different methods of specifying edge length
// each can be a constant numerical value or a function like `function( edge ){ return 2; }`
edgeLength: undefined, // sets edge length directly in simulation
edgeSymDiffLength: undefined, // symmetric diff edge length in simulation
edgeJaccardLength: undefined, // jaccard edge length in simulation
// iterations of cola algorithm; uses default values on undefined
unconstrIter: undefined, // unconstrained initial layout iterations
userConstIter: undefined, // initial layout iterations with user-specified constraints
allConstIter: undefined, // initial layout iterations with all constraints including non-overlap
// infinite layout options
infinite: false // overrides all other options for a forces-all-the-time mode
};
module.exports = defaults;
/***/
}),
/* 3 */
/***/ (function (module, exports, __webpack_require__) {
"use strict";
var impl = __webpack_require__(0);
// registers the extension on a cytoscape lib ref
var register = function register(cytoscape) {
if (!cytoscape) {
return;
} // can't register if cytoscape unspecified
cytoscape('layout', 'cola', impl); // register with cytoscape.js
};
if (typeof cytoscape !== 'undefined') {
// expose to global cytoscape (i.e. window.cytoscape)
register(cytoscape);
}
module.exports = register;
/***/
}),
/* 4 */
/***/ (function (module, exports, __webpack_require__) {
"use strict";
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var raf = void 0;
if ((typeof window === "undefined" ? "undefined" : _typeof(window)) !== (true ? "undefined" : _typeof(undefined))) {
raf = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || function (fn) {
return setTimeout(fn, 16);
};
} else {
// if not available, all you get is immediate calls
raf = function raf(cb) {
cb();
};
}
module.exports = raf;
/***/
}),
/* 5 */
/***/ (function (module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE_5__;
/***/
})
/******/]);
});

77
static/scripts/index.js Normal file
View file

@ -0,0 +1,77 @@
// import cola from 'cytoscape-cola';
async function getData() {
try {
const resp = await fetch("/api/data");
if (!resp.ok) {
throw new Error(`Response status: ${resp.status}`);
}
const json = await resp.json();
return json;
} catch (error) {
console.error(error);
}
}
function setupGraph() {
getData()
.catch(function (err) { console.error(err) })
.then(function (elements) {
var cy = window.cy = cytoscape({
container: document.getElementById('map-container'), // container to render in
elements: elements,
// elements: [ // list of graph elements to start with
// { // node a
// data: { id: 'a' }
// },
// { // node b
// data: { id: 'b' }
// },
// { // edge ab
// data: { id: 'ab', source: 'a', target: 'b' }
// }
// ],
style: [ // the stylesheet for the graph
{
selector: 'node',
style: {
'background-color': '#666',
'label': 'data(id)'
}
},
{
selector: 'edge',
style: {
'width': 1,
'line-color': '#1E88E5',
'target-arrow-color': '#1E88E5',
'target-arrow-shape': 'none',
'curve-style': 'bezier'
}
},
{
selector: ".virtual-edge",
style: {
"line-color": "#BDBDBD",
"line-style": "dashed",
}
}
],
layout: {
name: 'cola',
}
});
});
}
document.addEventListener('DOMContentLoaded', setupGraph);

9
static/style/main.css Normal file
View file

@ -0,0 +1,9 @@
#map-container {
width: 80%;
height: 600px;
display: block;
}
.edge {
color: pink;
}

28
templates/index.html Normal file
View file

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="/static/style/main.css">
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.30.2/cytoscape.min.js"
integrity="sha512-EY3U1MWdgKx0P1dqTE4inlKz2cpXtWpsR1YUyD855Hs6RL/A0cyvrKh60EpE8wDZ814cTe1KgRK+sG0Rn792vQ=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.30.2/cytoscape.umd.js"
integrity="sha512-j0/d+W9EL6BFFGcM3YGwI10MsaTihhvlrvZoTLff+plAOEC78IgiJZGx/0ZGMpjHqrlp5si4JpdNtcDMywep4A=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://unpkg.com/webcola/WebCola/cola.min.js"></script>
<script src="/static/scripts/cytoscape-cola.js"></script>
<script src="/static/scripts/index.js"></script>
</head>
<body>
<h1>Coucou</h1>
<div id="map-container"></div>
</body>
</html>

BIN
topology-map Executable file

Binary file not shown.