feat: add option to listen on tailscale

This commit is contained in:
Stavros
2026-06-21 16:20:31 +03:00
parent 8e35631ec8
commit 2ab24432bb
3 changed files with 36 additions and 104 deletions
+21 -26
View File
@@ -46,18 +46,17 @@ type Services struct {
} }
type BootstrapApp struct { type BootstrapApp struct {
config model.Config config model.Config
runtime model.RuntimeConfig runtime model.RuntimeConfig
services Services services Services
log *logger.Logger log *logger.Logger
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
queries repository.Store queries repository.Store
router *gin.Engine router *gin.Engine
db *sql.DB db *sql.DB
ding *ding.Ding ding *ding.Ding
listeners []Listener dig *dig.Container
dig *dig.Container
} }
func NewBootstrapApp(config model.Config) *BootstrapApp { func NewBootstrapApp(config model.Config) *BootstrapApp {
@@ -285,11 +284,11 @@ func (app *BootstrapApp) Setup() error {
app.runtime.ConfiguredProviders = configuredProviders app.runtime.ConfiguredProviders = configuredProviders
// replace the default app url with the tailscale hostname if tailscale is enabled // force tailscale app url if listening on a tailscale address
if app.services.tailscaleService != nil { if app.services.tailscaleService != nil && app.config.Tailscale.Listen {
tailscaleUrl := "https://" + app.services.tailscaleService.GetHostname() tailscaleUrl := "https://" + app.services.tailscaleService.GetHostname()
if tailscaleUrl != app.runtime.AppURL { if tailscaleUrl != app.runtime.AppURL {
app.log.App.Info().Msg("Tailscale is enabled, replacing app url with tailscale hostname") app.log.App.Info().Msg("Listening on tailscale, replacing app url with tailscale hostname")
app.runtime.AppURL = tailscaleUrl app.runtime.AppURL = tailscaleUrl
} }
} }
@@ -311,19 +310,15 @@ func (app *BootstrapApp) Setup() error {
app.ding.Go(app.heartbeatRoutine, ding.RingMinor) app.ding.Go(app.heartbeatRoutine, ding.RingMinor)
} }
// setup listeners // get listener
app.listeners = app.calculateListenerPolicy() listenerFunc := app.getListenerFunc()
if app.config.Server.ConcurrentListenersEnabled { // run listener
app.log.App.Info().Msg("Concurrent listeners enabled, will run on all available listeners") lec := make(chan error, 1)
}
// run listeners app.ding.Go(func(ctx context.Context) {
lec, err := app.runListeners() lec <- listenerFunc(ctx)
}, ding.RingNormal)
if err != nil {
return fmt.Errorf("failed to run listeners: %w", err)
}
// monitor cancellation and server errors // monitor cancellation and server errors
for { for {
+9 -71
View File
@@ -9,7 +9,6 @@ import (
"os" "os"
"time" "time"
"github.com/steveiliop56/ding"
"github.com/tinyauthapp/tinyauth/internal/controller" "github.com/tinyauthapp/tinyauth/internal/controller"
"github.com/tinyauthapp/tinyauth/internal/middleware" "github.com/tinyauthapp/tinyauth/internal/middleware"
"github.com/tinyauthapp/tinyauth/internal/model" "github.com/tinyauthapp/tinyauth/internal/model"
@@ -18,14 +17,6 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
type Listener int
const (
ListenerHTTP Listener = iota
ListenerUnix
ListenerTailscale
)
func (app *BootstrapApp) setupRouter() error { func (app *BootstrapApp) setupRouter() error {
// we don't want gin debug mode // we don't want gin debug mode
gin.SetMode(gin.ReleaseMode) gin.SetMode(gin.ReleaseMode)
@@ -134,73 +125,20 @@ func (app *BootstrapApp) setupRouter() error {
return nil return nil
} }
func (app *BootstrapApp) runListeners() (chan error, error) { // Top down
// lec -> listener error channel // 1. Tailscale (if tailscale.listen)
lec := make(chan error, len(app.listeners)) // 2. Unix socket (if server.socketPath)
// 3. HTTP - default
for _, listenerType := range app.listeners { func (app *BootstrapApp) getListenerFunc() func(ctx context.Context) error {
listenerFunc, err := app.listenerFromType(listenerType) if app.services.tailscaleService != nil && app.config.Tailscale.Listen {
return app.serveTailscale
if err != nil {
return nil, fmt.Errorf("failed to get listener function: %w", err)
}
app.ding.Go(func(ctx context.Context) {
lec <- listenerFunc(ctx)
}, ding.RingNormal)
}
return lec, nil
}
// The way we calculate listeners is as follows:
// If concurrent listeners are disabled, we pick the first available listener, so:
// 1. If tailscale is enabled, we use tailscale
// 2. If socket path is configured, we use unix socket
// 3. Finally if none is configured we use http
// If concurrent listeners are enabled, we add all available listeners in the following order
func (app *BootstrapApp) calculateListenerPolicy() []Listener {
l := []Listener{}
if !app.config.Server.ConcurrentListenersEnabled {
if app.services.tailscaleService != nil {
l = append(l, ListenerTailscale)
return l
}
if app.config.Server.SocketPath != "" {
l = append(l, ListenerUnix)
return l
}
l = append(l, ListenerHTTP)
return l
} }
if app.config.Server.SocketPath != "" { if app.config.Server.SocketPath != "" {
l = append(l, ListenerUnix) return app.serveUnix
} }
if app.services.tailscaleService != nil { return app.serveHTTP
l = append(l, ListenerTailscale)
}
l = append(l, ListenerHTTP)
return l
}
func (app *BootstrapApp) listenerFromType(listenerType Listener) (func(ctx context.Context) error, error) {
switch listenerType {
case ListenerHTTP:
return app.serveHTTP, nil
case ListenerUnix:
return app.serveUnix, nil
case ListenerTailscale:
return app.serveTailscale, nil
default:
return nil, fmt.Errorf("invalid listener type: %d", listenerType)
}
} }
func (app *BootstrapApp) serveHTTP(ctx context.Context) error { func (app *BootstrapApp) serveHTTP(ctx context.Context) error {
+6 -7
View File
@@ -15,9 +15,8 @@ func NewDefaultConfiguration() *Config {
Path: "./resources", Path: "./resources",
}, },
Server: ServerConfig{ Server: ServerConfig{
Port: 3000, Port: 3000,
Address: "0.0.0.0", Address: "0.0.0.0",
ConcurrentListenersEnabled: false,
}, },
Auth: AuthConfig{ Auth: AuthConfig{
SubdomainsEnabled: true, SubdomainsEnabled: true,
@@ -104,10 +103,9 @@ type ResourcesConfig struct {
} }
type ServerConfig struct { type ServerConfig struct {
Port int `description:"The port on which the server listens." yaml:"port"` Port int `description:"The port on which the server listens." yaml:"port"`
Address string `description:"The address on which the server listens." yaml:"address"` Address string `description:"The address on which the server listens." yaml:"address"`
SocketPath string `description:"The path to the Unix socket." yaml:"socketPath"` SocketPath string `description:"The path to the Unix socket." yaml:"socketPath"`
ConcurrentListenersEnabled bool `description:"Enable listening on both TCP and Unix socket at the same time." yaml:"concurrentListenersEnabled"`
} }
type AuthConfig struct { type AuthConfig struct {
@@ -218,6 +216,7 @@ type TailscaleConfig struct {
Hostname string `description:"Tailscale hostname." yaml:"hostname"` Hostname string `description:"Tailscale hostname." yaml:"hostname"`
AuthKey string `description:"Tailscale auth key." yaml:"authKey"` AuthKey string `description:"Tailscale auth key." yaml:"authKey"`
Ephemeral bool `description:"Use ephemeral Tailscale node." yaml:"ephemeral"` Ephemeral bool `description:"Use ephemeral Tailscale node." yaml:"ephemeral"`
Listen bool `description:"Listen on the Tailscale address instead of standard address." yaml:"listen"`
} }
// OAuth/OIDC config // OAuth/OIDC config