mirror of
https://github.com/Pacerino/CaddyProxyManager.git
synced 2026-08-04 11:15:08 -04:00
Introduce a Provider interface selectable via CPM_CADDY_MODE: - CaddyfileProvider renders per-host snippets (template whitespace fixed) - APIProvider manages Caddy's JSON config via the admin API Add configurable reload strategy (systemd/exec/api/none) and a configurable listen address for the bootstrapped server in API mode. Includes unit tests for the JSON builder, provider factory and the API provider (httptest).
104 lines
2.6 KiB
Go
104 lines
2.6 KiB
Go
package caddy
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/Pacerino/CaddyProxyManager/internal/config"
|
|
"github.com/Pacerino/CaddyProxyManager/internal/logger"
|
|
"github.com/coreos/go-systemd/v22/dbus"
|
|
)
|
|
|
|
// Reload triggers Caddy to pick up configuration changes using the strategy
|
|
// configured via CPM_CADDY_RELOADSTRATEGY.
|
|
func Reload() error {
|
|
switch strings.ToLower(config.Configuration.Caddy.ReloadStrategy) {
|
|
case "", "systemd":
|
|
return reloadSystemd()
|
|
case "exec":
|
|
return reloadExec()
|
|
case "api":
|
|
return reloadAPI()
|
|
case "none":
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("unknown reload strategy %q (expected systemd, exec, api or none)", config.Configuration.Caddy.ReloadStrategy)
|
|
}
|
|
}
|
|
|
|
// reloadSystemd reloads the caddy unit over the systemd D-Bus interface.
|
|
func reloadSystemd() error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
conn, err := dbus.NewSystemdConnectionContext(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer conn.Close()
|
|
resChan := make(chan string)
|
|
_, err = conn.ReloadOrRestartUnitContext(ctx, config.Configuration.Caddy.Service, "replace", resChan)
|
|
if err != nil {
|
|
logger.Error("CaddyReloadError", err)
|
|
return err
|
|
}
|
|
<-resChan
|
|
return nil
|
|
}
|
|
|
|
// reloadExec runs `caddy reload --config <Caddyfile>` to apply changes.
|
|
func reloadExec() error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
cmd := exec.CommandContext(ctx,
|
|
config.Configuration.Caddy.Binary,
|
|
"reload",
|
|
"--config", config.Configuration.CaddyFile,
|
|
)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
if err := cmd.Run(); err != nil {
|
|
logger.Error("CaddyReloadError", err)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// reloadAPI loads the on-disk Caddyfile through the admin API's /load endpoint.
|
|
func reloadAPI() error {
|
|
data, err := os.ReadFile(config.Configuration.CaddyFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
|
defer cancel()
|
|
|
|
url := strings.TrimRight(config.Configuration.Caddy.AdminURL, "/") + "/load"
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(data))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Set("Content-Type", "text/caddyfile")
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
logger.Error("CaddyReloadError", err)
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode >= 300 {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return fmt.Errorf("caddy /load returned %d: %s", resp.StatusCode, string(body))
|
|
}
|
|
return nil
|
|
}
|