refactor: remove standalone proxies, add Traefik provider with Docker labels
Standalone proxy removal: - Delete store, API handlers, proxy manager, health monitor, validator, hints - Delete frontend pages (proxies list, create, edit) and components (ProxyCard, ProxyForm, ProxyFilter, ProxyGroup, ValidationChecklist) - Remove proxy routes from router, nav items, dashboard references - Clean up SystemHealthCard to remove proxy section Traefik provider: - Add TraefikProvider implementing proxy.Provider via Docker labels - ContainerLabels() returns traefik.enable, router rule, entrypoints, service port, TLS cert resolver, docker network - ConfigureRoute() returns router name (labels handle routing at container creation) - DeleteRoute() is no-op (container removal auto-deregisters) - Ping() checks Traefik API health (optional) - Wire ContainerLabels into deployer (executeDeploy + blueGreenDeploy) - Add Traefik settings: entrypoint, cert_resolver, network, api_url - Add traefik option to proxy provider selector in settings UI - Show conditional Traefik config fields - Add i18n keys (EN + RU)
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TraefikProvider manages proxy routes via Docker labels.
|
||||
// Traefik auto-discovers containers with the appropriate labels.
|
||||
type TraefikProvider struct {
|
||||
entrypoint string
|
||||
certResolver string
|
||||
network string // Docker network for traefik.docker.network label
|
||||
apiURL string // Traefik API URL for health checks (optional)
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// NewTraefikProvider creates a Traefik-backed proxy provider.
|
||||
func NewTraefikProvider(entrypoint, certResolver, network, apiURL string) *TraefikProvider {
|
||||
if entrypoint == "" {
|
||||
entrypoint = "websecure"
|
||||
}
|
||||
return &TraefikProvider{
|
||||
entrypoint: entrypoint,
|
||||
certResolver: certResolver,
|
||||
network: network,
|
||||
apiURL: strings.TrimRight(apiURL, "/"),
|
||||
httpClient: &http.Client{Timeout: 5 * time.Second},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TraefikProvider) Name() string { return "traefik" }
|
||||
|
||||
// ConfigureRoute for Traefik is a no-op for deploy-managed containers.
|
||||
// Labels are set at container creation time via ContainerLabels().
|
||||
// Returns a route ID for tracking.
|
||||
func (t *TraefikProvider) ConfigureRoute(_ context.Context, domain, _ string, _ int, _ RouteOptions) (string, error) {
|
||||
routerName := sanitizeDomain(domain)
|
||||
return routerName, nil
|
||||
}
|
||||
|
||||
// DeleteRoute for Traefik is a no-op — removing the container removes the labels,
|
||||
// and Traefik automatically de-registers the route.
|
||||
func (t *TraefikProvider) DeleteRoute(_ context.Context, _ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContainerLabels returns Docker labels for Traefik auto-discovery.
|
||||
func (t *TraefikProvider) ContainerLabels(domain string, port int) map[string]string {
|
||||
name := sanitizeDomain(domain)
|
||||
labels := map[string]string{
|
||||
"traefik.enable": "true",
|
||||
fmt.Sprintf("traefik.http.routers.%s.rule", name): fmt.Sprintf("Host(`%s`)", domain),
|
||||
fmt.Sprintf("traefik.http.routers.%s.entrypoints", name): t.entrypoint,
|
||||
fmt.Sprintf("traefik.http.services.%s.loadbalancer.server.port", name): fmt.Sprintf("%d", port),
|
||||
}
|
||||
if t.certResolver != "" {
|
||||
labels[fmt.Sprintf("traefik.http.routers.%s.tls.certresolver", name)] = t.certResolver
|
||||
}
|
||||
if t.network != "" {
|
||||
labels["traefik.docker.network"] = t.network
|
||||
}
|
||||
return labels
|
||||
}
|
||||
|
||||
// Ping checks Traefik API connectivity if a URL is configured.
|
||||
func (t *TraefikProvider) Ping(ctx context.Context) error {
|
||||
if t.apiURL == "" {
|
||||
return nil // No API URL configured, skip health check.
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, t.apiURL+"/api/overview", nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create traefik ping request: %w", err)
|
||||
}
|
||||
resp, err := t.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("traefik ping: %w", err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode >= 400 {
|
||||
return fmt.Errorf("traefik api returned status %d", resp.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// sanitizeDomain converts a domain to a safe Traefik router name.
|
||||
func sanitizeDomain(domain string) string {
|
||||
r := strings.NewReplacer(".", "-", ":", "-", "*", "wildcard")
|
||||
return r.Replace(strings.ToLower(domain))
|
||||
}
|
||||
Reference in New Issue
Block a user