@@ -11,11 +11,13 @@ import (
1111 "github.com/pkg/errors"
1212 "github.com/sargassum-world/godest/handling"
1313 "github.com/sargassum-world/godest/turbostreams"
14+ "golang.org/x/sync/errgroup"
1415
1516 "github.com/sargassum-world/fluitans/internal/app/fluitans/auth"
16- "github.com/sargassum-world/fluitans/internal/clients/desec"
17+ desecc "github.com/sargassum-world/fluitans/internal/clients/desec"
1718 ztc "github.com/sargassum-world/fluitans/internal/clients/zerotier"
1819 "github.com/sargassum-world/fluitans/internal/clients/ztcontrollers"
20+ "github.com/sargassum-world/fluitans/pkg/desec"
1921 "github.com/sargassum-world/fluitans/pkg/zerotier"
2022)
2123
@@ -59,13 +61,13 @@ func (ss StringSet) Equals(set StringSet) bool {
5961 return ss .Contains (set ) && set .Contains (ss )
6062}
6163
62- // Authorization
64+ // Devices
6365
6466const devicesListPartial = "networks/devices-list.partial.tmpl"
6567
6668func replaceDevicesListStream (
6769 ctx context.Context , controllerAddress , networkID string , a auth.Auth ,
68- c * ztc.Client , cc * ztcontrollers.Client , dc * desec .Client ,
70+ c * ztc.Client , cc * ztcontrollers.Client , dc * desecc .Client ,
6971) (turbostreams.Message , error ) {
7072 networkViewData , err := getNetworkViewData (ctx , controllerAddress , networkID , c , cc , dc )
7173 if err != nil {
@@ -134,33 +136,32 @@ func (h *Handlers) HandleDevicesPub() turbostreams.HandlerFunc {
134136 return func (c * turbostreams.Context ) error {
135137 // Make change trackers
136138 initialized := false
137- var prevDevices StringSet
139+ var devices StringSet
138140
139141 // Parse params
140142 networkID := c .Param ("id" )
141143 controllerAddress := ztc .GetControllerAddress (networkID )
142144
143145 // Publish periodically
144- const pubInterval = 2 * time .Second
145- return handling .Repeat (c .Context (), pubInterval , func () (done bool , err error ) {
146+ const pubInterval = 5 * time .Second
147+ return handling .RepeatImmediate (c .Context (), pubInterval , func () (done bool , err error ) {
146148 // Check for changes
147- changed , devices , err := checkDevicesList (
148- c .Context (), controllerAddress , networkID , prevDevices , h .ztc , h .ztcc ,
149+ changed , updatedDevices , err := checkDevicesList (
150+ c .Context (), controllerAddress , networkID , devices , h .ztc , h .ztcc ,
149151 )
150152 if err != nil {
151153 return false , err
152154 }
155+ devices = updatedDevices
153156 if ! changed {
154157 return false , nil
155158 }
156159 if ! initialized {
157160 // We just started publishing because a page added a subscription, so there's no need to
158161 // send the devices list again - that page already has the latest version
159- prevDevices = devices
160162 initialized = true
161163 return false , nil
162164 }
163- prevDevices = devices
164165
165166 // Publish changes
166167 message , err := replaceDevicesListStream (
@@ -211,15 +212,234 @@ func (h *Handlers) HandleDevicesPost() auth.HTTPHandlerFunc {
211212 }
212213
213214 // Redirect user
214- return c .Redirect (
215- http .StatusSeeOther , fmt .Sprintf ("/networks/%s#device-%s" , networkID , memberAddress ),
215+ return c .Redirect (http .StatusSeeOther , fmt .Sprintf (
216+ "/networks/%s#/networks/%s/devices/%s" , networkID , networkID , memberAddress ,
217+ ))
218+ }
219+ }
220+
221+ // Device
222+
223+ const devicePartial = "networks/device.partial.tmpl"
224+
225+ type DeviceViewData struct {
226+ Member Member
227+ DomainNames []string
228+ Network zerotier.ControllerNetwork
229+ NetworkDNSNamed bool
230+ }
231+
232+ func getDeviceViewData (
233+ ctx context.Context , address , networkID , memberAddress string ,
234+ c * ztc.Client , cc * ztcontrollers.Client , dc * desecc.Client ,
235+ ) (vd DeviceViewData , err error ) {
236+ controller , err := cc .FindControllerByAddress (ctx , address )
237+ if err != nil {
238+ return DeviceViewData {}, err
239+ }
240+ if controller == nil {
241+ return DeviceViewData {}, echo .NewHTTPError (http .StatusNotFound , "controller not found" )
242+ }
243+
244+ eg , egctx := errgroup .WithContext (ctx )
245+ var network * zerotier.ControllerNetwork
246+ var subnameRRsets map [string ][]desec.RRset
247+ eg .Go (func () (err error ) {
248+ network , err = c .GetNetwork (ctx , * controller , networkID )
249+ return err
250+ })
251+ eg .Go (func () (err error ) {
252+ subnameRRsets , err = dc .GetRRsets (ctx )
253+ return err
254+ })
255+ if err = eg .Wait (); err != nil {
256+ return DeviceViewData {}, err
257+ }
258+ if network == nil {
259+ return DeviceViewData {}, echo .NewHTTPError (http .StatusNotFound , "zerotier network not found" )
260+ }
261+ vd .Network = * network
262+
263+ members , err := getMemberRecords (
264+ ctx , dc .Config .DomainName , * controller , * network , []string {memberAddress }, subnameRRsets , c ,
265+ )
266+ if err != nil {
267+ return DeviceViewData {}, err
268+ }
269+ var ok bool
270+ if vd .Member , ok = members [memberAddress ]; ! ok {
271+ return DeviceViewData {}, echo .NewHTTPError (
272+ http .StatusNotFound , "zerotier network member not found" ,
216273 )
217274 }
275+
276+ if vd .NetworkDNSNamed , err = checkNamedByDNS (egctx , * network .Name , * network .Id , dc ); err != nil {
277+ return DeviceViewData {}, err
278+ }
279+
280+ return vd , nil
281+ }
282+
283+ func replaceDeviceStream (
284+ ctx context.Context , controllerAddress , networkID , memberAddress string , a auth.Auth ,
285+ c * ztc.Client , cc * ztcontrollers.Client , dc * desecc.Client ,
286+ ) (turbostreams.Message , error ) {
287+ deviceViewData , err := getDeviceViewData (
288+ ctx , controllerAddress , networkID , memberAddress , c , cc , dc ,
289+ )
290+ if err != nil {
291+ return turbostreams.Message {}, err
292+ }
293+ return turbostreams.Message {
294+ Action : turbostreams .ActionReplace ,
295+ Target : "/networks/" + networkID + "/devices/" + memberAddress ,
296+ Template : devicePartial ,
297+ Data : map [string ]interface {}{
298+ "Member" : deviceViewData .Member ,
299+ "DomainNames" : deviceViewData .DomainNames ,
300+ "Network" : deviceViewData .Network ,
301+ "NetworkDNSNamed" : deviceViewData .NetworkDNSNamed ,
302+ "Auth" : a ,
303+ },
304+ }, nil
305+ }
306+
307+ func (h * Handlers ) HandleDeviceSub () turbostreams.HandlerFunc {
308+ return func (c * turbostreams.Context ) error {
309+ // Parse params
310+ networkID := c .Param ("id" )
311+ controllerAddress := ztc .GetControllerAddress (networkID )
312+ memberAddress := c .Param ("address" )
313+
314+ // Run queries
315+ ctx := c .Context ()
316+ controller , err := h .ztcc .FindControllerByAddress (ctx , controllerAddress )
317+ if err != nil {
318+ return err
319+ }
320+ member , err := h .ztc .GetNetworkMember (ctx , * controller , networkID , memberAddress )
321+ if err != nil {
322+ return err
323+ }
324+ if member == nil {
325+ return errors .Errorf ("network %s member %s not found" , networkID , memberAddress )
326+ }
327+
328+ // Allow subscription
329+ return nil
330+ }
331+ }
332+
333+ func checkNetwork (
334+ ctx context.Context , controllerAddress , networkID string , prevNetwork zerotier.ControllerNetwork ,
335+ c * ztc.Client , cc * ztcontrollers.Client ,
336+ ) (changed bool , updatedNetwork zerotier.ControllerNetwork , err error ) {
337+ controller , err := cc .FindControllerByAddress (ctx , controllerAddress )
338+ if err != nil {
339+ return false , updatedNetwork , err
340+ }
341+ member , err := c .GetNetwork (ctx , * controller , networkID )
342+ if err != nil {
343+ return false , updatedNetwork , err
344+ }
345+ updatedNetwork = * member
346+ sixplaneChanged := prevNetwork .V6AssignMode == nil ||
347+ prevNetwork .V6AssignMode .N6plane == nil ||
348+ * updatedNetwork .V6AssignMode .N6plane != * prevNetwork .V6AssignMode .N6plane
349+ rfc4193Changed := prevNetwork .V6AssignMode == nil ||
350+ prevNetwork .V6AssignMode .Rfc4193 == nil ||
351+ * updatedNetwork .V6AssignMode .Rfc4193 != * prevNetwork .V6AssignMode .Rfc4193
352+ if ! sixplaneChanged && ! rfc4193Changed {
353+ prevNetwork .V6AssignMode = updatedNetwork .V6AssignMode
354+ return false , prevNetwork , nil
355+ }
356+ return true , updatedNetwork , nil
357+ }
358+
359+ func checkDevice (
360+ ctx context.Context , controllerAddress , networkID , memberAddress string ,
361+ prevDevice zerotier.ControllerNetworkMember ,
362+ c * ztc.Client , cc * ztcontrollers.Client ,
363+ ) (changed bool , updatedDevice zerotier.ControllerNetworkMember , err error ) {
364+ controller , err := cc .FindControllerByAddress (ctx , controllerAddress )
365+ if err != nil {
366+ return false , updatedDevice , err
367+ }
368+ member , err := c .GetNetworkMember (ctx , * controller , networkID , memberAddress )
369+ if err != nil {
370+ return false , updatedDevice , err
371+ }
372+ updatedDevice = * member
373+ revisionChanged := prevDevice .Revision == nil ||
374+ * updatedDevice .Revision != * prevDevice .Revision
375+ // TODO: do we need to check whether the IP assignments list has changed?
376+ if ! revisionChanged {
377+ return false , prevDevice , nil
378+ }
379+ return true , updatedDevice , nil
218380}
219381
382+ func (h * Handlers ) HandleDevicePub () turbostreams.HandlerFunc {
383+ t := devicesListPartial
384+ h .r .MustHave (t )
385+ return func (c * turbostreams.Context ) error {
386+ // Make change trackers
387+ initialized := false
388+ var device zerotier.ControllerNetworkMember
389+ var network zerotier.ControllerNetwork
390+
391+ // Parse params
392+ networkID := c .Param ("id" )
393+ controllerAddress := ztc .GetControllerAddress (networkID )
394+ memberAddress := c .Param ("address" )
395+
396+ // Publish periodically
397+ const pubInterval = 5 * time .Second
398+ return handling .RepeatImmediate (c .Context (), pubInterval , func () (done bool , err error ) {
399+ // Check for changes
400+ networkChanged , updatedNetwork , err := checkNetwork (
401+ c .Context (), controllerAddress , networkID , network , h .ztc , h .ztcc ,
402+ )
403+ if err != nil {
404+ return false , err
405+ }
406+ network = updatedNetwork
407+ deviceChanged , updatedDevice , err := checkDevice (
408+ c .Context (), controllerAddress , networkID , memberAddress , device , h .ztc , h .ztcc ,
409+ )
410+ if err != nil {
411+ return false , err
412+ }
413+ device = updatedDevice
414+
415+ if ! deviceChanged && ! networkChanged {
416+ return false , nil
417+ }
418+ if ! initialized {
419+ // We just started publishing because a page added a subscription, so there's no need to
420+ // send the devices list again - that page already has the latest version
421+ initialized = true
422+ return false , nil
423+ }
424+
425+ // Publish changes
426+ message , err := replaceDeviceStream (
427+ c .Context (), controllerAddress , networkID , memberAddress , auth.Auth {}, h .ztc , h .ztcc , h .dc ,
428+ )
429+ if err != nil {
430+ return false , err
431+ }
432+ c .Publish (message )
433+ return false , nil
434+ })
435+ }
436+ }
437+
438+ // Authorization
439+
220440func setMemberAuthorization (
221- ctx context.Context , controller ztcontrollers.Controller , networkID string ,
222- memberAddress string , authorized bool , c * ztc.Client ,
441+ ctx context.Context , controller ztcontrollers.Controller , networkID , memberAddress string ,
442+ authorized bool , c * ztc.Client ,
223443) error {
224444 auth := authorized
225445 if err := c .UpdateMember (
@@ -243,7 +463,7 @@ func (h *Handlers) HandleDeviceAuthorizationPost() auth.HTTPHandlerFunc {
243463 networkID := c .Param ("id" )
244464 controllerAddress := ztc .GetControllerAddress (networkID )
245465 memberAddress := c .Param ("address" )
246- authorization := strings .ToLower (c .FormValue ("authorization" )) == "true"
466+ authorization := strings .ToLower (c .FormValue ("authorization" )) == checkboxTrueValue
247467
248468 // Run queries
249469 ctx := c .Request ().Context ()
@@ -274,7 +494,7 @@ func (h *Handlers) HandleDeviceAuthorizationPost() auth.HTTPHandlerFunc {
274494
275495 // Redirect user
276496 return c .Redirect (http .StatusSeeOther , fmt .Sprintf (
277- "/networks/%s#device-%s" , networkID , memberAddress ,
497+ "/networks/%s#/networks/%s/devices/%s" , networkID , networkID , memberAddress ,
278498 ))
279499 }
280500}
@@ -283,7 +503,7 @@ func (h *Handlers) HandleDeviceAuthorizationPost() auth.HTTPHandlerFunc {
283503
284504func confirmMemberNameManageable (
285505 ctx context.Context , controller ztcontrollers.Controller , networkID string ,
286- memberAddress , memberName string , c * ztc.Client , dc * desec .Client ,
506+ memberAddress , memberName string , c * ztc.Client , dc * desecc .Client ,
287507) (memberSubname string , err error ) {
288508 network , memberAddresses , err := c .GetNetworkInfo (ctx , controller , networkID )
289509 if err != nil {
@@ -317,7 +537,7 @@ func confirmMemberNameManageable(
317537
318538func setMemberName (
319539 ctx context.Context , controller ztcontrollers.Controller , networkID string ,
320- memberAddress , memberName string , c * ztc.Client , dc * desec .Client ,
540+ memberAddress , memberName string , c * ztc.Client , dc * desecc .Client ,
321541) error {
322542 memberSubname , err := confirmMemberNameManageable (
323543 ctx , controller , networkID , memberAddress , memberName , c , dc ,
@@ -341,7 +561,7 @@ func setMemberName(
341561
342562func unsetMemberName (
343563 ctx context.Context , controller ztcontrollers.Controller , networkID string ,
344- memberAddress , memberName string , c * ztc.Client , dc * desec .Client ,
564+ memberAddress , memberName string , c * ztc.Client , dc * desecc .Client ,
345565) error {
346566 memberSubname , err := confirmMemberNameManageable (
347567 ctx , controller , networkID , memberAddress , memberName , c , dc ,
@@ -405,7 +625,7 @@ func (h *Handlers) HandleDeviceNamePost() auth.HTTPHandlerFunc {
405625
406626 // Redirect user
407627 return c .Redirect (http .StatusSeeOther , fmt .Sprintf (
408- "/networks/%s#device- %s" , networkID , memberAddress ,
628+ "/networks/%s#/networks/%s/ device/ %s" , networkID , networkID , memberAddress ,
409629 ))
410630 }
411631}
0 commit comments