@@ -50,6 +50,8 @@ class Topology extends Window {
5050 this . SetupToolbar ( ) ;
5151
5252 this . startButton = this . AddToolbarButton ( "Start discovery" , "mono/play.svg?light" ) ;
53+ this . syncButton = this . AddToolbarButton ( "Save uplinks to Devices" , "mono/upload.svg?light" ) ;
54+ this . syncButton . disabled = true ;
5355 this . AddToolbarSeparator ( ) ;
5456
5557 this . findButton = this . AddToolbarButton ( "Find" , "mono/search.svg?light" ) ;
@@ -128,6 +130,7 @@ class Topology extends Window {
128130 this . content . onmouseup = event => this . Topology_onmouseup ( event ) ;
129131
130132 this . startButton . onclick = ( ) => this . StartDialog ( ) ;
133+ this . syncButton . onclick = ( ) => this . UpdateDeviceUplink ( ) ;
131134 this . findButton . onclick = ( ) => this . FindMode ( ) ;
132135 this . vlanButton . onclick = ( ) => this . VlanMode ( ) ;
133136 this . trafficButton . onclick = ( ) => this . TrafficMode ( ) ;
@@ -492,6 +495,7 @@ class Topology extends Window {
492495 this . ws = new WebSocket ( `${ KEEP . isSecure ? "wss://" : "ws://" } ${ server } /ws/topology` ) ;
493496
494497 this . ws . onopen = ( ) => {
498+ this . syncButton . disabled = true ;
495499 this . startButton . disabled = true ;
496500 this . vlanButton . disabled = true ;
497501 this . trafficButton . disabled = true ;
@@ -500,6 +504,7 @@ class Topology extends Window {
500504 } ;
501505
502506 this . ws . onclose = ( ) => {
507+ this . syncButton . disabled = false ;
503508 this . startButton . disabled = false ;
504509 this . startButton . setAttribute ( "tip-below" , "Re-discover" ) ;
505510 this . startButton . style . backgroundImage = "url(mono/restart.svg?light)" ;
@@ -601,6 +606,7 @@ class Topology extends Window {
601606
602607 this . ws . onerror = ( ) => {
603608 this . startButton . disabled = false ;
609+ this . syncButton . disabled = false ;
604610 } ;
605611 }
606612
@@ -2691,8 +2697,8 @@ class Topology extends Window {
26912697 updateDbButton . style . backgroundRepeat = "no-repeat" ;
26922698 updateDbButton . style . backgroundImage = "url(mono/upload.svg)" ;
26932699 optionsBox . appendChild ( updateDbButton ) ;
2694-
2695- updateDbButton . onclick = ( ) => this . UpdateDatabaseDialog ( file ) ;
2700+
2701+ updateDbButton . onclick = ( ) => this . UpdateDeviceUplink ( file ) ;
26962702 }
26972703
26982704 const overwriteProtocol = { } ;
@@ -2894,97 +2900,126 @@ class Topology extends Window {
28942900 }
28952901 }
28962902
2897- UpdateDatabaseDialog ( file ) {
2898- const dialog = this . DialogBox ( "640px " ) ;
2903+ UpdateDeviceUplink ( target = "*" ) {
2904+ const dialog = this . DialogBox ( "calc(100% - 40px) " ) ;
28992905 if ( dialog === null ) return ;
29002906
29012907 const { okButton, innerBox} = dialog ;
29022908 okButton . value = "Sync database uplinks" ;
29032909
2904- innerBox . parentElement . style . maxWidth = "640px" ;
2905-
2906- innerBox . style . padding = "16px 32px" ;
2910+ innerBox . parentElement . style . maxWidth = "720px" ;
29072911
2908- const device = this . devices [ file ] ;
2912+ innerBox . style . border = "2px solid var(--clr-control)" ;
2913+ innerBox . style . borderRadius = "4px" ;
2914+ innerBox . style . margin = "20px 20px 8px 20px" ;
29092915
29102916 const list = [ ] ;
2917+
2918+ for ( const file in this . devices ) {
2919+ if ( target !== "*" && file !== target ) continue ;
29112920
2912- for ( const portIndex in device . lldp . localPortName ) {
2913- const portName = device . lldp . localPortName [ portIndex ] || portIndex ;
2914-
2915- const entry = device . lldp . entry [ portIndex ] || [ device ?. dot1tp ?. entry [ portIndex ] ] ;
2916- if ( ! entry || entry . length === 0 ) continue ;
2917-
2918- for ( let i = 0 ; i < entry . length ; i ++ ) {
2919- if ( ! entry [ i ] ) continue ;
2920-
2921- const dbEntry = LOADER . devices . data [ entry [ i ] ] ;
2922-
2923- let checked = true ;
2924- if ( "type" in dbEntry && dbEntry . type . v . toLowerCase ( ) === "switch" ) {
2925- checked = false ;
2926- }
2927-
2928- const newItem = document . createElement ( "div" ) ;
2929- newItem . className = "topology-find-listitem" ;
2930- newItem . style . paddingLeft = "4px" ;
2931- newItem . style . overflow = "hidden" ;
2932- innerBox . appendChild ( newItem ) ;
2933-
2934- const toggle = this . CreateToggle ( portName , checked , newItem ) ;
2935- toggle . label . style . marginRight = "12px" ;
2936- toggle . label . style . marginTop = "4px" ;
2937- toggle . label . style . width = "128px" ;
2938-
2939- const deviceLabel = document . createElement ( "div" ) ;
2940- deviceLabel . style . display = "inline-block" ;
2941- deviceLabel . style . transform = "translateY(4px)" ;
2942- deviceLabel . style . width = "calc(100% - 225px)" ;
2943- deviceLabel . style . backgroundImage = "url(mono/gear.svg)" ;
2944- deviceLabel . style . backgroundSize = "20px 20px" ;
2945- deviceLabel . style . backgroundPosition = "2px 50%" ;
2946- deviceLabel . style . backgroundRepeat = "no-repeat" ;
2947- deviceLabel . style . paddingLeft = "28px" ;
2948- deviceLabel . style . overflow = "hidden" ;
2949- deviceLabel . style . textOverflow = "ellipsis" ;
2950- deviceLabel . style . whiteSpace = "nowrap" ;
2951- newItem . appendChild ( deviceLabel ) ;
2921+ const device = this . devices [ file ] ;
29522922
2953- if ( "hostname" in dbEntry && dbEntry . hostname . v . length > 0 ) {
2954- deviceLabel . textContent = dbEntry . hostname . v ;
2955- }
2956- else if ( "name" in dbEntry && dbEntry . name . v . length > 0 ) {
2957- deviceLabel . textContent = dbEntry . name . v ;
2958- }
2959- else if ( "mac address" in dbEntry && dbEntry [ "mac address" ] . v . length > 0 ) {
2960- deviceLabel . textContent = dbEntry [ "mac address" ] . v ;
2961- }
2962- else if ( "ip" in dbEntry && dbEntry . ip . v . length > 0 ) {
2963- deviceLabel . textContent = dbEntry . ip . v ;
2964- }
2923+ if ( device . isUnmanaged ) continue ;
2924+ if ( ! device . lldp || device . lldp . localPortName === 0 ) continue ;
29652925
2966- if ( "type" in dbEntry ) {
2967- const type = dbEntry . type . v . toLowerCase ( ) ;
2968- deviceLabel . style . backgroundImage = `url( ${ type in LOADER . deviceIcons ? LOADER . deviceIcons [ type ] : "mono/gear.svg" } )` ;
2969- }
2926+ const switchBox = document . createElement ( "div" ) ;
2927+ switchBox . style . border = "2px solid var(--clr-control)" ;
2928+ switchBox . style . borderRadius = "4px" ;
2929+ switchBox . style . margin = "20px" ;
29702930
2971- list . push ( {
2972- toggle : toggle ,
2973- file : entry [ i ] ,
2974- port : portName
2975- } ) ;
2931+ const titleBox = document . createElement ( "div" ) ;
2932+ titleBox . textContent = device . initial . hostname ;
2933+ titleBox . style . fontWeight = "bold" ;
2934+ titleBox . style . backgroundColor = "var(--clr-control)" ;
2935+ titleBox . style . padding = "4px" ;
29762936
2977- newItem . onclick = ( ) => {
2978- toggle . checkbox . checked = ! toggle . checkbox . checked ;
2979- } ;
2937+ const contentBox = document . createElement ( "div" ) ;
2938+
2939+ switchBox . append ( titleBox , contentBox ) ;
2940+
2941+ for ( const portIndex in device . lldp . localPortName ) {
2942+ const portName = device . lldp . localPortName [ portIndex ] || portIndex ;
2943+
2944+ const entry = device . lldp . entry [ portIndex ] || [ device ?. dot1tp ?. entry [ portIndex ] ] ;
2945+ if ( ! entry || entry . length === 0 ) continue ;
2946+
2947+ for ( let i = 0 ; i < entry . length ; i ++ ) {
2948+ if ( ! entry [ i ] ) continue ;
2949+
2950+ const dbEntry = LOADER . devices . data [ entry [ i ] ] ;
2951+ if ( ! dbEntry ) continue ;
2952+
2953+ let checked = true ;
2954+ if ( "type" in dbEntry && dbEntry . type . v . toLowerCase ( ) === "switch" ) {
2955+ checked = false ;
2956+ }
2957+
2958+ const newItem = document . createElement ( "div" ) ;
2959+ newItem . className = "topology-find-listitem" ;
2960+ newItem . style . paddingLeft = "4px" ;
2961+ newItem . style . overflow = "hidden" ;
2962+ contentBox . appendChild ( newItem ) ;
2963+
2964+ const toggle = this . CreateToggle ( portName , checked , newItem ) ;
2965+ toggle . label . style . marginRight = "12px" ;
2966+ toggle . label . style . marginTop = "4px" ;
2967+ toggle . label . style . width = "128px" ;
2968+
2969+ const deviceLabel = document . createElement ( "div" ) ;
2970+ deviceLabel . style . display = "inline-block" ;
2971+ deviceLabel . style . transform = "translateY(4px)" ;
2972+ deviceLabel . style . width = "calc(100% - 225px)" ;
2973+ deviceLabel . style . backgroundImage = "url(mono/gear.svg)" ;
2974+ deviceLabel . style . backgroundSize = "20px 20px" ;
2975+ deviceLabel . style . backgroundPosition = "2px 50%" ;
2976+ deviceLabel . style . backgroundRepeat = "no-repeat" ;
2977+ deviceLabel . style . paddingLeft = "28px" ;
2978+ deviceLabel . style . overflow = "hidden" ;
2979+ deviceLabel . style . textOverflow = "ellipsis" ;
2980+ deviceLabel . style . whiteSpace = "nowrap" ;
2981+ newItem . appendChild ( deviceLabel ) ;
2982+
2983+ if ( "hostname" in dbEntry && dbEntry . hostname . v . length > 0 ) {
2984+ deviceLabel . textContent = dbEntry . hostname . v ;
2985+ }
2986+ else if ( "name" in dbEntry && dbEntry . name . v . length > 0 ) {
2987+ deviceLabel . textContent = dbEntry . name . v ;
2988+ }
2989+ else if ( "mac address" in dbEntry && dbEntry [ "mac address" ] . v . length > 0 ) {
2990+ deviceLabel . textContent = dbEntry [ "mac address" ] . v ;
2991+ }
2992+ else if ( "ip" in dbEntry && dbEntry . ip . v . length > 0 ) {
2993+ deviceLabel . textContent = dbEntry . ip . v ;
2994+ }
2995+
2996+ if ( "type" in dbEntry ) {
2997+ const type = dbEntry . type . v . toLowerCase ( ) ;
2998+ deviceLabel . style . backgroundImage = `url(${ type in LOADER . deviceIcons ? LOADER . deviceIcons [ type ] : "mono/gear.svg" } )` ;
2999+ }
3000+
3001+ list . push ( {
3002+ toggle : toggle ,
3003+ file : entry [ i ] ,
3004+ port : portName
3005+ } ) ;
3006+
3007+ newItem . onclick = ( ) => {
3008+ toggle . checkbox . checked = ! toggle . checkbox . checked ;
3009+ } ;
3010+ }
3011+ }
3012+
3013+ if ( contentBox . childNodes . length > 0 ) {
3014+ innerBox . appendChild ( switchBox ) ;
29803015 }
29813016 }
29823017
29833018 okButton . onclick = async ( ) => {
29843019 const mods = { } ;
29853020 for ( let i = 0 ; i < list . length ; i ++ ) {
29863021 if ( ! list [ i ] . toggle . checkbox . checked ) continue ;
2987-
3022+
29883023 mods [ list [ i ] . file ] = {
29893024 uplink : JSON . stringify ( {
29903025 device : file ,
0 commit comments