Remove the no-op MAINTENANCE_MODE setting (closes #317) #430

Merged
clawbot merged 1 commits from issue-317-remove-maintenance-mode into next 2026-10-02 13:27:43 +02:00
7 changed files with 10 additions and 48 deletions
Showing only changes of commit 0824ae9c2f - Show all commits
+3 -4
View File
@@ -135,7 +135,6 @@ TTY detection, and security headers are always applied.
| `BIND_ADDRESS` | IP address the HTTP listener binds. Loopback by default, so the cleartext listener is not published on every interface. The Docker image ships `0.0.0.0` instead. See [Bind address](#bind-address) | `127.0.0.1` (image: `0.0.0.0`) |
| `DATA_DIR` | Directory for all SQLite databases | `/var/lib/webhooker` |
| `DEBUG` | Enable debug logging | `false` |
| `MAINTENANCE_MODE` | Report `maintenanceMode: true` in the healthcheck JSON. It does not change how any request is served — no maintenance page exists | `false` |
| `METRICS_USERNAME` | Basic auth username for `/metrics`. Must be set together with `METRICS_PASSWORD`; one without the other fails startup | `""` |
| `METRICS_PASSWORD` | Basic auth password for `/metrics`. Must be set together with `METRICS_USERNAME`; one without the other fails startup | `""` |
| `SENTRY_DSN` | Sentry error reporting DSN. Unset leaves error reporting off; a value the Sentry SDK cannot parse fails startup rather than serving with reporting silently off | `""` |
@@ -515,8 +514,8 @@ no report is being sent — which is why it aborts rather than starting
with reporting off. Leaving it unset is not a mistake and not affected:
error reporting is simply off and startup is normal.
Boolean variables (`DEBUG`, `MAINTENANCE_MODE`) accept exactly the
spellings Go's `strconv.ParseBool` accepts — `1`, `t`, `T`, `TRUE`,
The boolean variable `DEBUG` accepts exactly the spellings Go's
`strconv.ParseBool` accepts — `1`, `t`, `T`, `TRUE`,
`true`, `True`, `0`, `f`, `F`, `FALSE`, `false`, `False` — and nothing
else. `yes`, `on`, and `off` are rejected rather than quietly treated
as false.
@@ -2775,7 +2774,7 @@ abuse limit later; they are tracked as future work.
| Method | Path | Description |
| ------ | --------------------------- | ----------- |
| `GET` | `/` | Root redirect, 303 (authenticated → `/hooks`, unauthenticated → `/pages/login`) |
| `GET` | `/.well-known/healthcheck` | Health check (JSON: `status`, `now`, `uptimeSeconds`, `uptimeHuman`, `version`, `appname`, `maintenanceMode`) |
| `GET` | `/.well-known/healthcheck` | Health check (JSON: `status`, `now`, `uptimeSeconds`, `uptimeHuman`, `version`, `appname`) |
| `GET`, `HEAD` | `/s/*` | Static file serving (embedded CSS, JS). `GET` and `HEAD` only — `POST`, `PUT`, `PATCH`, `DELETE`, `OPTIONS`, `TRACE` and `CONNECT` are answered `405 Method Not Allowed` with `Allow: GET, HEAD`. Any other method (such as `PROPFIND`) is refused by chi before it reaches this route, and gets `405` without an `Allow` header. Pinned by `TestStaticServesOnlyGetAndHead` |
| `POST` | `/h/{uuid}` | Webhook receiver endpoint. `POST` only — every other method is answered `405 Method Not Allowed` with `Allow: POST`. Rate limited (see [Rate Limiting](#rate-limiting)) |
-8
View File
@@ -149,7 +149,6 @@ type ConfigParams struct {
type Config struct {
DataDir string
Debug bool
MaintenanceMode bool
Environment string
MetricsPassword string
MetricsUsername string
@@ -658,11 +657,6 @@ func loadFromEnv() (*Config, error) {
return nil, err
}
maintenanceMode, err := envBool("MAINTENANCE_MODE", false)
if err != nil {
return nil, err
}
retentionSweepInterval, err := envPositiveDuration(
"RETENTION_SWEEP_INTERVAL",
defaultRetentionSweepInterval,
@@ -712,7 +706,6 @@ func loadFromEnv() (*Config, error) {
return &Config{
DataDir: DataDir(),
Debug: debug,
MaintenanceMode: maintenanceMode,
Environment: environment,
MetricsUsername: metricsUsername,
MetricsPassword: metricsPassword,
@@ -799,7 +792,6 @@ func New(lc fx.Lifecycle, params ConfigParams) (*Config, error) {
// host can reach the admin UI.
"bindAddress", s.BindAddress,
"debug", s.Debug,
"maintenanceMode", s.MaintenanceMode,
"dataDir", s.DataDir,
"retentionSweepInterval", s.RetentionSweepInterval.String(),
// Logged because a perfectly valid non-positive value here
+4 -13
View File
@@ -18,10 +18,9 @@ const testEnvKey = "WEBHOOKER_TEST_VALUE"
// Real configuration variables exercised by the config.New tests.
const (
envKeyPort = "PORT"
envKeyDebug = "DEBUG"
envKeyMaintenanceMode = "MAINTENANCE_MODE"
envKeyBindAddress = "BIND_ADDRESS"
envKeyPort = "PORT"
envKeyDebug = "DEBUG"
envKeyBindAddress = "BIND_ADDRESS"
)
// Sample BIND_ADDRESS values used by the tables below.
@@ -604,12 +603,6 @@ func flagEnvValueCases() []badEnvValueCase {
value: "ture",
expectError: true,
},
{
name: "unparseable MAINTENANCE_MODE aborts startup",
key: envKeyMaintenanceMode,
value: "sometimes",
expectError: true,
},
}
}
@@ -656,8 +649,7 @@ func TestNewUsesDefaultsWhenUnset(t *testing.T) {
t.Setenv("WEBHOOKER_ENVIRONMENT", "dev")
for _, key := range []string{
envKeyPort, envKeyDebug, envKeyMaintenanceMode,
envKeyBindAddress, envKeySentryDSN,
envKeyPort, envKeyDebug, envKeyBindAddress, envKeySentryDSN,
} {
require.NoError(t, os.Unsetenv(key))
}
@@ -668,7 +660,6 @@ func TestNewUsesDefaultsWhenUnset(t *testing.T) {
assert.Equal(t, 8080, cfg.Port)
assert.False(t, cfg.Debug)
assert.False(t, cfg.MaintenanceMode)
// Loopback, not the wildcard: the default must not publish the
// cleartext admin UI and the unauthenticated receiver on every
-7
View File
@@ -53,13 +53,6 @@ func settingRows(cfg *config.Config) []settingRow {
},
{"DATA_DIR", "Directory for all SQLite databases", cfg.DataDir},
{"DEBUG", "Enable debug logging", strconv.FormatBool(cfg.Debug)},
{
"MAINTENANCE_MODE",
"Report maintenanceMode: true in the healthcheck JSON. " +
"It does not change how any request is served — no " +
"maintenance page exists",
strconv.FormatBool(cfg.MaintenanceMode),
},
{
"METRICS_USERNAME",
"Basic auth username for /metrics. Must be set together " +
+3 -6
View File
@@ -62,14 +62,12 @@ func settingsShown(
func TestSettingsPageShowsLoadedConfiguration(t *testing.T) {
t.Parallel()
// DEBUG and MAINTENANCE_MODE get opposite values, and each of
// METRICS_USERNAME, METRICS_PASSWORD and SENTRY_DSN is the only one
// of the three set in one of the content tests, so each row is
// checked against its own field.
// Each of METRICS_USERNAME, METRICS_PASSWORD and SENTRY_DSN is the
// only one of the three set in one of the content tests, so each
// row is checked against its own field.
cfg := &config.Config{
DataDir: t.TempDir(),
Debug: true,
MaintenanceMode: false,
Environment: config.EnvironmentDev,
MetricsUsername: "scraper",
MetricsPassword: "",
@@ -96,7 +94,6 @@ func TestSettingsPageShowsLoadedConfiguration(t *testing.T) {
"BIND_ADDRESS": "192.0.2.10",
"DATA_DIR": cfg.DataDir,
"DEBUG": "true",
"MAINTENANCE_MODE": "false",
"METRICS_USERNAME": "scraper",
"METRICS_PASSWORD": "not set",
"SENTRY_DSN": "not set",
-4
View File
@@ -7,7 +7,6 @@ import (
"time"
"go.uber.org/fx"
"sneak.berlin/go/webhooker/internal/config"
"sneak.berlin/go/webhooker/internal/database"
"sneak.berlin/go/webhooker/internal/globals"
"sneak.berlin/go/webhooker/internal/logger"
@@ -18,7 +17,6 @@ type HealthcheckParams struct {
fx.In
Globals *globals.Globals
Config *config.Config
Logger *logger.Logger
Database *database.Database
}
@@ -64,7 +62,6 @@ func (s *Healthcheck) Healthcheck() *Response {
UptimeHuman: s.uptime().String(),
Appname: s.params.Globals.Appname,
Version: s.params.Globals.Version,
Maintenance: s.params.Config.MaintenanceMode,
}
return resp
@@ -78,7 +75,6 @@ type Response struct {
UptimeHuman string `json:"uptimeHuman"`
Version string `json:"version"`
Appname string `json:"appname"`
Maintenance bool `json:"maintenanceMode"`
}
func (s *Healthcheck) uptime() time.Duration {
-6
View File
@@ -159,12 +159,6 @@ func (s *Server) Run() {
s.serve()
}
// MaintenanceMode returns whether the server is in maintenance
// mode.
func (s *Server) MaintenanceMode() bool {
return s.params.Config.MaintenanceMode
}
// enableSentry initialises the Sentry SDK when error reporting is
// configured, and reports the failure when it is configured and cannot
// be initialised. A DSN that is not set is not a failure: reporting