Skip to content

Commit

Permalink
chore: Update dependencies and refactor transporter package for impro…
Browse files Browse the repository at this point in the history
…ved performance and readability (#369)
  • Loading branch information
Ehco1996 authored Sep 1, 2024
1 parent a0fe6b5 commit 54e5279
Show file tree
Hide file tree
Showing 9 changed files with 273 additions and 182 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ cmd/test/
localdev/

.vscode/settings.json
.zed/
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/hashicorp/go-retryablehttp v0.7.7
github.com/juju/ratelimit v1.0.2
github.com/labstack/echo/v4 v4.12.0
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.20.0
github.com/prometheus/client_model v0.6.1
github.com/prometheus/common v0.55.0
Expand Down
109 changes: 84 additions & 25 deletions internal/web/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.uber.org/zap"

Expand All @@ -24,6 +25,14 @@ import (
//go:embed templates/*.html
var templatesFS embed.FS

const (
metricsPath = "/metrics/"
indexPath = "/"
connectionsPath = "/connections/"
rulesPath = "/rules/"
apiPrefix = "/api/v1"
)

type Server struct {
glue.Reloader
glue.HealthChecker
Expand All @@ -50,15 +59,53 @@ func NewServer(
healthChecker glue.HealthChecker,
connMgr cmgr.Cmgr,
) (*Server, error) {
if err := validateConfig(cfg); err != nil {
return nil, errors.Wrap(err, "invalid configuration")
}

l := zap.S().Named("web")

templates := template.Must(template.ParseFS(templatesFS, "templates/*.html"))
for _, temp := range templates.Templates() {
l.Debug("template name: ", temp.Name())
}
e := NewEchoServer()
if err := setupMiddleware(e, cfg, l); err != nil {
return nil, errors.Wrap(err, "failed to setup middleware")
}

if err := setupTemplates(e, l); err != nil {
return nil, errors.Wrap(err, "failed to setup templates")
}

if err := setupMetrics(cfg); err != nil {
return nil, errors.Wrap(err, "failed to setup metrics")
}

s := &Server{
Reloader: relayReloader,
HealthChecker: healthChecker,

e: e,
l: l,
cfg: cfg,
connMgr: connMgr,
addr: net.JoinHostPort(cfg.WebHost, fmt.Sprintf("%d", cfg.WebPort)),
}

setupRoutes(s)

return s, nil
}

func validateConfig(cfg *config.Config) error {
// Add validation logic here
if cfg.WebPort <= 0 || cfg.WebPort > 65535 {
return errors.New("invalid web port")
}
// Add more validations as needed
return nil
}

func setupMiddleware(e *echo.Echo, cfg *config.Config, l *zap.SugaredLogger) error {
e.Use(NginxLogMiddleware(l))
e.Renderer = &echoTemplate{templates: templates}

if cfg.WebToken != "" {
e.Use(middleware.KeyAuthWithConfig(middleware.KeyAuthConfig{
KeyLookup: "query:token",
Expand All @@ -70,7 +117,6 @@ func NewServer(

if cfg.WebAuthUser != "" && cfg.WebAuthPass != "" {
e.Use(middleware.BasicAuth(func(username, password string, c echo.Context) (bool, error) {
// Be careful to use constant time comparison to prevent timing attacks
if subtle.ConstantTimeCompare([]byte(username), []byte(cfg.WebAuthUser)) == 1 &&
subtle.ConstantTimeCompare([]byte(password), []byte(cfg.WebAuthPass)) == 1 {
return true, nil
Expand All @@ -79,38 +125,51 @@ func NewServer(
}))
}

return nil
}

func setupTemplates(e *echo.Echo, l *zap.SugaredLogger) error {
funcMap := template.FuncMap{
"sub": func(a, b int) int { return a - b },
"add": func(a, b int) int { return a + b },
}
tmpl, err := template.New("").Funcs(funcMap).ParseFS(templatesFS, "templates/*.html")
if err != nil {
return errors.Wrap(err, "failed to parse templates")
}
templates := template.Must(tmpl, nil)
for _, temp := range templates.Templates() {
l.Debug("template name: ", temp.Name())
}
e.Renderer = &echoTemplate{templates: templates}
return nil
}

func setupMetrics(cfg *config.Config) error {
if err := metrics.RegisterEhcoMetrics(cfg); err != nil {
return nil, err
return errors.Wrap(err, "failed to register Ehco metrics")
}
if err := metrics.RegisterNodeExporterMetrics(cfg); err != nil {
return nil, err
return errors.Wrap(err, "failed to register Node Exporter metrics")
}
s := &Server{
Reloader: relayReloader,
HealthChecker: healthChecker,
return nil
}

e: e,
l: l,
cfg: cfg,
connMgr: connMgr,
addr: net.JoinHostPort(cfg.WebHost, fmt.Sprintf("%d", cfg.WebPort)),
}
func setupRoutes(s *Server) {
e := s.e

// register handler
e.GET("/metrics/", echo.WrapHandler(promhttp.Handler()))
e.GET(metricsPath, echo.WrapHandler(promhttp.Handler()))
e.GET("/debug/pprof/*", echo.WrapHandler(http.DefaultServeMux))

e.GET("/", s.index)
e.GET("/connections/", s.ListConnections)
e.GET("/rules/", s.ListRules)
e.GET(indexPath, s.index)
e.GET(connectionsPath, s.ListConnections)
e.GET(rulesPath, s.ListRules)

// api group
api := e.Group("/api/v1")
api := e.Group(apiPrefix)
api.GET("/config/", s.CurrentConfig)
api.POST("/config/reload/", s.HandleReload)
api.GET("/health_check/", s.HandleHealthCheck)
api.GET("/node_metrics/", s.GetNodeMetrics)
return s, nil
}

func (s *Server) Start() error {
Expand Down
13 changes: 13 additions & 0 deletions internal/web/templates/_head.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<head>
<title>Ehco Web</title>
<meta charset="UTF-8" />
<meta name="description" content="ehco web" />
<meta name="keywords" content="ehco-relay" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/1.0.1/css/bulma.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/4.6.13/flatpickr.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/4.6.13/flatpickr.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
File renamed without changes.
58 changes: 58 additions & 0 deletions internal/web/templates/_navbar.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="container">
<div class="navbar-brand">
<a class="navbar-item" href="/">
<h1 class="title is-4">Ehco Relay</h1>
</a>
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>

<div class="navbar-menu">
<div class="navbar-start">
<a href="/rules/" class="navbar-item">
<span class="icon"><i class="fas fa-list"></i></span>
<span>Rule List</span>
</a>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">
<span class="icon"><i class="fas fa-link"></i></span>
<span>Connections</span>
</a>
<div class="navbar-dropdown">
<a href="/connections/?conn_type=active" class="navbar-item">Active</a>
<a href="/connections/?conn_type=closed" class="navbar-item">Closed</a>
</div>
</div>
</div>

<div class="navbar-end">
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">
<span class="icon"><i class="fas fa-external-link-alt"></i></span>
<span>Quick Links</span>
</a>
<div class="navbar-dropdown">
<a href="/metrics/" class="navbar-item">
<span class="icon"><i class="fas fa-chart-line"></i></span>
<span>Metrics</span>
</a>
<a href="/debug/pprof/" class="navbar-item">
<span class="icon"><i class="fas fa-bug"></i></span>
<span>Debug</span>
</a>
<a href="/api/v1/config/" class="navbar-item">
<span class="icon"><i class="fas fa-cog"></i></span>
<span>Config</span>
</a>
</div>
</div>
</div>
</div>
</div>
</nav>
<hr />
78 changes: 53 additions & 25 deletions internal/web/templates/connection.html
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="ehco web">
<meta name="keywords" content="ehco-relay">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/1.0.1/css/bulma.min.css">
<title>Connections</title>
</head>
{{template "_head.html" .}}
<body>
{{ template "_navbar.html" . }}
<section class="section">
<div class="container">
<h1 class="title">ALL Connections: {{.AllCount}}</h1>
<h1 class="title">Connections</h1>
<h2 class="subtitle">Total: {{.AllCount}}</h2>

<div class="tabs is-boxed is-centered">
<ul>
<li class="{{if eq .ConnType "active"}}is-active{{end}}">
<a href="?conn_type=active&page=1&page_size={{.PageSize}}">Active({{.ActiveCount}})</a>
<a href="?conn_type=active&page=1&page_size={{.PageSize}}">Active ({{.ActiveCount}})</a>
</li>
<li class="{{if eq .ConnType "closed"}}is-active{{end}}">
<a href="?conn_type=closed&page=1&page_size={{.PageSize}}">Closed({{.ClosedCount}})</a>
<a href="?conn_type=closed&page=1&page_size={{.PageSize}}">Closed ({{.ClosedCount}})</a>
</li>
</ul>
</div>

{{if gt (len .ConnectionList) 0}}
<div class="table-container">
<table class="table is-striped is-hoverable is-fullwidth">
<table class="table is-striped is-hoverable is-fullwidth is-responsive">
<thead>
<tr>
<th>Label</th>
Expand All @@ -37,39 +34,70 @@ <h1 class="title">ALL Connections: {{.AllCount}}</h1>
<tbody>
{{range .ConnectionList}}
<tr>
<td>{{.RelayLabel}}</td>
<td>{{.ConnType}}</td>
<td>{{.GetFlow}}</td>
<td>{{.Stats}}</td>
<td>{{.GetTime}}</td>
<td data-label="Label">{{.RelayLabel}}</td>
<td data-label="Type">{{.ConnType}}</td>
<td data-label="Flow">{{.GetFlow}}</td>
<td data-label="Stats">{{.Stats}}</td>
<td data-label="Time">{{.GetTime}}</td>
</tr>
{{end}}
</tbody>
</table>
</div>
{{else}}
<div class="notification is-info">
<p>No connections available.</p>
<div class="notification is-info is-light">
<p class="has-text-centered">No {{.ConnType}} connections available.</p>
</div>
{{end}}

{{if gt .TotalPage 1}}
<nav class="pagination is-centered" role="navigation" aria-label="pagination" style="margin-top: 20px;">
<a class="pagination-previous" {{if eq .Prev 0}}disabled{{end}}
<a class="pagination-previous" {{if le .CurrentPage 1}}disabled{{end}}
href="?conn_type={{.ConnType}}&page={{.Prev}}&page_size={{.PageSize}}">Previous</a>
<a class="pagination-next" {{if eq .Next 0}}disabled{{end}}
<a class="pagination-next" {{if ge .CurrentPage .TotalPage}}disabled{{end}}
href="?conn_type={{.ConnType}}&page={{.Next}}&page_size={{.PageSize}}">Next page</a>
<ul class="pagination-list">
{{if gt .CurrentPage 1}}
{{if gt .CurrentPage 2}}
<li><a class="pagination-link" href="?conn_type={{.ConnType}}&page=1&page_size={{.PageSize}}">1</a></li>
{{if gt .CurrentPage 3}}<li><span class="pagination-ellipsis">&hellip;</span></li>{{end}}
{{end}}
{{if gt .CurrentPage 1}}
<li><a class="pagination-link" href="?conn_type={{.ConnType}}&page={{sub .CurrentPage 1}}&page_size={{.PageSize}}">{{sub .CurrentPage 1}}</a></li>
{{end}}
<li><span class="pagination-ellipsis"></span></li>
<li><a class="pagination-link is-current" aria-label="Page {{.CurrentPage}}" aria-current="page">{{.CurrentPage}}</a></li>
<li><span class="pagination-ellipsis"></span></li>
{{if lt .CurrentPage .TotalPage}}
<li><a class="pagination-link" href="?conn_type={{.ConnType}}&page={{.TotalPage}}&page_size={{.PageSize}}">{{.TotalPage}}</a></li>
<li><a class="pagination-link" href="?conn_type={{.ConnType}}&page={{add .CurrentPage 1}}&page_size={{.PageSize}}">{{add .CurrentPage 1}}</a></li>
{{if lt .CurrentPage (sub .TotalPage 1)}}<li><span class="pagination-ellipsis">&hellip;</span></li>{{end}}
{{end}}
{{if lt .CurrentPage (sub .TotalPage 1)}}
<li><a class="pagination-link" href="?conn_type={{.ConnType}}&page={{.TotalPage}}&page_size={{.PageSize}}">{{.TotalPage}}</a></li>
{{end}}
</ul>
</nav>
{{end}}
</div>
</section>

<div id="loading" class="modal">
<div class="modal-background"></div>
<div class="modal-content">
<div class="loader is-loading"></div>
</div>
</div>

<script>
document.addEventListener('DOMContentLoaded', () => {
const links = document.querySelectorAll('a:not([target="_blank"])');
const loading = document.getElementById('loading');

links.forEach(link => {
link.addEventListener('click', (e) => {
if (!link.classList.contains('disabled')) {
loading.classList.add('is-active');
}
});
});
});
</script>
</body>
</html>
Loading

0 comments on commit 54e5279

Please sign in to comment.