diff --git a/Makefile b/Makefile index f865431..4e8f091 100644 --- a/Makefile +++ b/Makefile @@ -100,6 +100,8 @@ dev: docker_start server_start server_start: TSDPROXY_DataDir=./dev/data TSDPROXY_LOG_LEVEL=debug DOCKER_HOST=unix:///var/run/docker.sock \ TSDPROXY_AUTHKEYFILE=./dev/KEY_FILE \ + TSDPROXY_DASHBOARD_ENABLED=true \ + TSDPROXY_DASHBOARD_NAME=DASH1 \ wgo run -file=.go -file=.yaml -file=.env -file=.json -file=.toml ${MAIN_PACKAGE_PATH} ## docker_start: start the docker containers @@ -117,6 +119,23 @@ docker_stop: .PHONY: stop stop: dev_kill docker_stop +## docker_image: Create docker image +.PHONY: docker_image +docker_image: + docker buildx build -t "tsdproxy:latest" . + +## docker_local image start +.PHONY: docker_image_start +docker_image_start: + docker compose -f dev/docker-compose.yaml up -d + +## docker_local image stop +.PHONY: docker_image_stop +docker_image_stop: + docker compose -f dev/docker-compose.yaml down + + + # ==================================================================================== # # QUALITY CONTROL # ==================================================================================== # diff --git a/cmd/server/main.go b/cmd/server/main.go index 0256730..8ea0093 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -15,6 +15,7 @@ import ( "github.com/docker/docker/client" "github.com/almeidapaulopt/tsdproxy/internal/core" + "github.com/almeidapaulopt/tsdproxy/internal/dashboard" pm "github.com/almeidapaulopt/tsdproxy/internal/proxymanager" ) @@ -25,6 +26,7 @@ type WebApp struct { Health *core.Health Docker *client.Client ProxyManager *pm.ProxyManager + Dashboard *dashboard.Dashboard } func InitializeApp() (*WebApp, error) { @@ -47,6 +49,10 @@ func InitializeApp() (*WebApp, error) { // proxymanager := pm.NewProxyManager(docker, logger, config) + // init Dashboard + // + dash := dashboard.NewDashboard(httpServer, logger, config, proxymanager.Proxies) + webApp := &WebApp{ Config: config, Log: logger, @@ -54,6 +60,7 @@ func InitializeApp() (*WebApp, error) { Health: health, Docker: docker, ProxyManager: proxymanager, + Dashboard: dash, } return webApp, nil } @@ -95,6 +102,10 @@ func (app *WebApp) Start() { // go app.ProxyManager.WatchDockerEvents(ctx) + // Start Dashboard + // + app.Dashboard.AddRoutes() + // Start the webserver // go func() { diff --git a/internal/dashboard/dash.go b/internal/dashboard/dash.go new file mode 100644 index 0000000..7dc4aae --- /dev/null +++ b/internal/dashboard/dash.go @@ -0,0 +1,43 @@ +package dashboard + +import ( + "net/http" + + "github.com/almeidapaulopt/tsdproxy/internal/core" + "github.com/almeidapaulopt/tsdproxy/internal/proxymanager" +) + +type Dashboard struct { + Log *core.Logger + HTTP *core.HTTPServer + Config *core.Config + proxies proxymanager.ProxyList +} + +func NewDashboard(http *core.HTTPServer, log *core.Logger, cfg *core.Config, pl proxymanager.ProxyList) *Dashboard { + return &Dashboard{ + Log: log, + HTTP: http, + Config: cfg, + proxies: pl, + } +} + +func (dash *Dashboard) AddRoutes() { + dash.HTTP.Handle("GET /", dash.index()) +} + +func (dash *Dashboard) index() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(` + + + + + `)) + for _, p := range dash.proxies { + w.Write([]byte(p.URL.String() + "\n")) + } + w.Write([]byte(``)) + } +} diff --git a/internal/proxymanager/proxymanager.go b/internal/proxymanager/proxymanager.go index bda751c..1239cae 100644 --- a/internal/proxymanager/proxymanager.go +++ b/internal/proxymanager/proxymanager.go @@ -22,8 +22,10 @@ import ( "github.com/almeidapaulopt/tsdproxy/internal/tailscale" ) +type ProxyList map[string]*Proxy + type ProxyManager struct { - proxies map[string]*Proxy + Proxies ProxyList docker *client.Client Log *core.Logger config *core.Config @@ -39,7 +41,7 @@ type Proxy struct { func NewProxyManager(cli *client.Client, logger *core.Logger, config *core.Config) *ProxyManager { return &ProxyManager{ - proxies: make(map[string]*Proxy), + Proxies: make(ProxyList), docker: cli, config: config, Log: logger, @@ -50,21 +52,21 @@ func (pm *ProxyManager) AddProxy(proxy *Proxy) { pm.mutex.Lock() defer pm.mutex.Unlock() - pm.proxies[proxy.container.ID] = proxy + pm.Proxies[proxy.container.ID] = proxy } func (pm *ProxyManager) RemoveProxy(containerID string) { pm.mutex.Lock() defer pm.mutex.Unlock() - if proxy, exists := pm.proxies[containerID]; exists { + if proxy, exists := pm.Proxies[containerID]; exists { if err := proxy.TsServer.Close(); err != nil { pm.Log.Error().Err(err).Str("containerID", containerID).Msg("Error shutting down proxy server") } else { pm.Log.Info().Str("containerID", containerID).Msg("Proxy server shut down successfully") } - delete(pm.proxies, containerID) + delete(pm.Proxies, containerID) pm.Log.Info().Str("containerID", containerID[:12]).Msg("Removed proxy for container") } } @@ -146,13 +148,12 @@ func (pm *ProxyManager) SetupProxy(ctx context.Context, containerID string) { // Create the tsnet server // - server := tailscale.NewTsNetServer(proxyURL.Hostname(), pm.config, pm.Log) - defer server.Close() - - if err := server.Start(ctx); err != nil { + server, err := tailscale.NewTsNetServer(proxyURL.Hostname(), pm.config, pm.Log) + if err != nil { pm.Log.Error().Err(err).Str("containerID", containerID).Str("containerName", container.GetName()).Msg("Error starting server") return } + defer server.Close() // Create the TLS listener // @@ -223,7 +224,7 @@ func (pm *ProxyManager) WatchDockerEvents(ctx context.Context) { func (pm *ProxyManager) StopAll() { pm.Log.Info().Msg("Shutdown all proxies") - for id := range pm.proxies { + for id := range pm.Proxies { pm.RemoveProxy(id) } } diff --git a/internal/tailscale/tailscale.go b/internal/tailscale/tailscale.go index fb14b74..45598f0 100644 --- a/internal/tailscale/tailscale.go +++ b/internal/tailscale/tailscale.go @@ -1,48 +1,45 @@ package tailscale import ( - "context" "fmt" "path/filepath" "github.com/almeidapaulopt/tsdproxy/internal/core" + tsclient "tailscale.com/client/tailscale" "tailscale.com/tsnet" ) type TsNetServer struct { - TsServer *tsnet.Server + TsServer *tsnet.Server + LocalClient *tsclient.LocalClient } -func NewTsNetServer(hostname string, config *core.Config, logger *core.Logger) *TsNetServer { - return &TsNetServer{ - &tsnet.Server{ - Hostname: hostname, - AuthKey: config.AuthKey, - Dir: filepath.Join(config.DataDir, hostname), - Ephemeral: true, - Logf: func(format string, args ...any) { - logger.Trace().Msgf(format, args...) - }, - UserLogf: func(format string, args ...any) { - logger.Trace().Msgf(format, args...) - }, +func NewTsNetServer(hostname string, config *core.Config, logger *core.Logger) (*TsNetServer, error) { + tserver := &tsnet.Server{ + Hostname: hostname, + AuthKey: config.AuthKey, + Dir: filepath.Join(config.DataDir, hostname), + Ephemeral: true, + RunWebClient: true, + Logf: func(format string, args ...any) { + logger.Trace().Msgf(format, args...) + }, + UserLogf: func(format string, args ...any) { + logger.Trace().Msgf(format, args...) }, } -} - -func (tn *TsNetServer) Close() error { - return tn.TsServer.Close() -} -func (tn *TsNetServer) Start(ctx context.Context) error { - if err := tn.TsServer.Start(); err != nil { - return fmt.Errorf("error starting server: %w", err) + lc, err := tserver.LocalClient() + if err != nil { + return nil, fmt.Errorf("error starting tailscale server: %w", err) } - // Wait for tailscale to come up... - if _, err := tn.TsServer.Up(ctx); err != nil { - return fmt.Errorf("error to come up server: %w", err) - } + return &TsNetServer{ + tserver, + lc, + }, nil +} - return tn.TsServer.Start() +func (tn *TsNetServer) Close() error { + return tn.TsServer.Close() }