github.com/sdqri/sequined@v0.0.0-20240421190656-fc6bf956f4d8/internal/graphmultiplexer/graph_multiplexer.go (about)

     1  package graphmultiplexer
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"net/http"
     7  	"time"
     8  
     9  	dsh "github.com/sdqri/sequined/internal/dashboard"
    10  	ggr "github.com/sdqri/sequined/internal/graphgenerator"
    11  	hyr "github.com/sdqri/sequined/internal/hyperrenderer"
    12  	obs "github.com/sdqri/sequined/internal/observer"
    13  )
    14  
    15  type Middleware func(http.HandlerFunc) http.HandlerFunc
    16  
    17  type GraphMux struct {
    18  	Root     *hyr.Webpage
    19  	RouteMap map[string]hyr.HyperRenderer
    20  	Observer *obs.Observer
    21  
    22  	*http.ServeMux
    23  	middlewareChain  []Middleware
    24  	GraphHandlerFunc http.HandlerFunc
    25  }
    26  
    27  type GraphMuxOption func(*GraphMux)
    28  
    29  func New(root *hyr.Webpage, opts ...GraphMuxOption) (*GraphMux, error) {
    30  	routeMap := hyr.CreatePathMap(root)
    31  
    32  	mux := GraphMux{
    33  		Root:     root,
    34  		RouteMap: routeMap,
    35  
    36  		ServeMux:        http.NewServeMux(),
    37  		middlewareChain: make([]Middleware, 0),
    38  	}
    39  
    40  	for _, opt := range opts {
    41  		opt(&mux)
    42  	}
    43  
    44  	if mux.Observer != nil {
    45  		mux.middlewareChain = append(mux.middlewareChain, VisitLoggerMiddleware(&mux))
    46  
    47  		var err error
    48  		hyr.Traverse(mux.Root, func(node hyr.HyperRenderer) bool {
    49  			currentPage, ok := node.(*hyr.Webpage)
    50  			if !ok {
    51  				err = ggr.ErrUnexpectedNodeType
    52  				return true
    53  			}
    54  			mux.logNodeCreation(currentPage)
    55  			return false
    56  		})
    57  		if err != nil {
    58  			return nil, err
    59  		}
    60  	}
    61  
    62  	mux.GraphHandlerFunc = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    63  		mux.HandleGraphHttpRequest(w, r)
    64  	})
    65  
    66  	for _, mw := range mux.middlewareChain {
    67  		mux.GraphHandlerFunc = mw(mux.GraphHandlerFunc)
    68  	}
    69  
    70  	mux.Handle("/", mux.GraphHandlerFunc)
    71  
    72  	return &mux, nil
    73  }
    74  
    75  func WithObserver(observer *obs.Observer) GraphMuxOption {
    76  	return func(mux *GraphMux) {
    77  		mux.Observer = observer
    78  	}
    79  }
    80  
    81  func WithMiddleware(mw Middleware) GraphMuxOption {
    82  	return func(mux *GraphMux) {
    83  		mux.middlewareChain = append(mux.middlewareChain, mw)
    84  	}
    85  }
    86  
    87  func (mux *GraphMux) Use(mw Middleware) {
    88  	mux.GraphHandlerFunc = mw(mux.GraphHandlerFunc)
    89  }
    90  
    91  func (mux *GraphMux) HandleGraphHttpRequest(w http.ResponseWriter, r *http.Request) {
    92  	page, ok := mux.RouteMap[r.URL.Path]
    93  	if ok {
    94  		err := page.Render(w)
    95  		if err != nil {
    96  			fmt.Println(err.Error())
    97  		}
    98  	} else {
    99  		http.NotFound(w, r)
   100  	}
   101  }
   102  
   103  func (mux *GraphMux) SyncGraph(updateChan chan ggr.UpdateMessage, errChan chan error) {
   104  	for {
   105  		select {
   106  		case updateMsg, ok := <-updateChan:
   107  			if !ok {
   108  				//TODO: not do
   109  			}
   110  			mux.RouteMap = hyr.CreatePathMap(mux.Root)
   111  			switch updateMsg.Type {
   112  			case ggr.UpdateTypeCreate:
   113  				mux.logNodeCreation(updateMsg.Webpage)
   114  			case ggr.UpdateTypeDelete:
   115  				mux.logNodeDeletion(updateMsg.Webpage)
   116  			}
   117  			// case err, ok <- errChan:
   118  
   119  		}
   120  	}
   121  }
   122  
   123  func (mux *GraphMux) logNodeCreation(webpage hyr.HyperRenderer) {
   124  	if mux.Observer != nil {
   125  		mux.Observer.LogNode(obs.NodeLog{
   126  			ID:        obs.NodeID(webpage.GetID()),
   127  			CreatedAt: time.Now().UTC(),
   128  			DeletedAt: nil,
   129  		})
   130  	}
   131  }
   132  
   133  func (mux *GraphMux) logNodeDeletion(webpage hyr.HyperRenderer) {
   134  	if mux.Observer != nil {
   135  		now := time.Now().UTC()
   136  		if logNode, ok := mux.Observer.NodeLogMap[obs.NodeID(webpage.GetID())]; ok {
   137  			logNode.DeletedAt = &now
   138  		}
   139  	}
   140  }
   141  
   142  func (mux *GraphMux) logVisit(req *http.Request) {
   143  	if mux.Observer == nil {
   144  		return
   145  	}
   146  
   147  	ip, _, _ := net.SplitHostPort(req.RemoteAddr)
   148  	if node, ok := mux.RouteMap[req.URL.Path]; ok {
   149  		if currentPage, ok := node.(*hyr.Webpage); ok {
   150  			mux.Observer.LogVisit(obs.VisitLog{
   151  				RemoteAddr: obs.IPAddr(ip),
   152  				NodeID:     obs.NodeID(currentPage.GetID()),
   153  				VisitedAt:  time.Now().UTC(),
   154  			})
   155  		}
   156  	}
   157  }
   158  
   159  func VisitLoggerMiddleware(mux *GraphMux) Middleware {
   160  	return func(next http.HandlerFunc) http.HandlerFunc {
   161  		return func(w http.ResponseWriter, r *http.Request) {
   162  			next(w, r)
   163  
   164  			mux.logVisit(r)
   165  		}
   166  	}
   167  }
   168  
   169  func (mux *GraphMux) ActivateDashboard(dashboard *dsh.Dashboard) {
   170  	dashboard.HandleBy(mux.ServeMux)
   171  }