From 6f3c0b01b01b2af3defc9c108d808ee2a878fb24 Mon Sep 17 00:00:00 2001 From: clawbot Date: Tue, 10 Mar 2026 04:05:18 -0700 Subject: [PATCH] refactor: use Go duration strings for QUEUE_MAX_AGE and MESSAGE_MAX_AGE Change config from integer seconds to Go duration strings (e.g. '720h') parsed via time.ParseDuration, consistent with SESSION_IDLE_TIMEOUT. Default remains 30 days (720h). --- README.md | 4 ++-- internal/config/config.go | 12 +++++------ internal/handlers/handlers.go | 40 ++++++++++++++++++++++++++++------- 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 1ff5192..42bac72 100644 --- a/README.md +++ b/README.md @@ -1812,9 +1812,9 @@ directory is also loaded automatically via | `PORT` | int | `8080` | HTTP listen port | | `DBURL` | string | `file:///var/lib/neoirc/state.db?_journal_mode=WAL` | SQLite connection string. For file-based: `file:///path/to/db.db?_journal_mode=WAL`. For in-memory (testing): `file::memory:?cache=shared`. | | `DEBUG` | bool | `false` | Enable debug logging (verbose request/response logging) | -| `MESSAGE_MAX_AGE` | int | `2592000` | Maximum age of messages in seconds (30 days). Messages older than this are pruned. | +| `MESSAGE_MAX_AGE` | string | `720h` | Maximum age of messages as a Go duration string (e.g. `720h`, `24h`). Messages older than this are pruned. Default is 30 days. | | `SESSION_IDLE_TIMEOUT` | string | `720h` | Session idle timeout as a Go duration string (e.g. `720h`, `24h`). Sessions with no activity for this long are expired and the nick is released. Default is 30 days. | -| `QUEUE_MAX_AGE` | int | `2592000` | Maximum age of client queue entries in seconds (30 days). Entries older than this are pruned. | +| `QUEUE_MAX_AGE` | string | `720h` | Maximum age of client queue entries as a Go duration string (e.g. `720h`, `24h`). Entries older than this are pruned. Default is 30 days. | | `MAX_MESSAGE_SIZE` | int | `4096` | Maximum message body size in bytes (planned enforcement) | | `LONG_POLL_TIMEOUT`| int | `15` | Default long-poll timeout in seconds (client can override via query param, server caps at 30) | | `MOTD` | string | `""` | Message of the day, shown to clients via `GET /api/v1/server` | diff --git a/internal/config/config.go b/internal/config/config.go index bd3b7b7..855b55a 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -38,9 +38,9 @@ type Config struct { MetricsUsername string Port int SentryDSN string - MessageMaxAge int + MessageMaxAge string MaxMessageSize int - QueueMaxAge int + QueueMaxAge string MOTD string ServerName string FederationKey string @@ -69,9 +69,9 @@ func New( viper.SetDefault("SENTRY_DSN", "") viper.SetDefault("METRICS_USERNAME", "") viper.SetDefault("METRICS_PASSWORD", "") - viper.SetDefault("MESSAGE_MAX_AGE", "2592000") + viper.SetDefault("MESSAGE_MAX_AGE", "720h") viper.SetDefault("MAX_MESSAGE_SIZE", "4096") - viper.SetDefault("QUEUE_MAX_AGE", "2592000") + viper.SetDefault("QUEUE_MAX_AGE", "720h") viper.SetDefault("MOTD", defaultMOTD) viper.SetDefault("SERVER_NAME", "") viper.SetDefault("FEDERATION_KEY", "") @@ -94,9 +94,9 @@ func New( MaintenanceMode: viper.GetBool("MAINTENANCE_MODE"), MetricsUsername: viper.GetString("METRICS_USERNAME"), MetricsPassword: viper.GetString("METRICS_PASSWORD"), - MessageMaxAge: viper.GetInt("MESSAGE_MAX_AGE"), + MessageMaxAge: viper.GetString("MESSAGE_MAX_AGE"), MaxMessageSize: viper.GetInt("MAX_MESSAGE_SIZE"), - QueueMaxAge: viper.GetInt("QUEUE_MAX_AGE"), + QueueMaxAge: viper.GetString("QUEUE_MAX_AGE"), MOTD: viper.GetString("MOTD"), ServerName: viper.GetString("SERVER_NAME"), FederationKey: viper.GetString("FEDERATION_KEY"), diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index 7ee70d5..66d6824 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -204,16 +204,39 @@ func (hdlr *Handlers) runCleanup( hdlr.pruneQueuesAndMessages(ctx) } +// parseDurationConfig parses a Go duration string, +// returning zero on empty input and logging on error. +func (hdlr *Handlers) parseDurationConfig( + name, raw string, +) time.Duration { + if raw == "" { + return 0 + } + + dur, err := time.ParseDuration(raw) + if err != nil { + hdlr.log.Error( + "invalid duration config, skipping", + "name", name, "value", raw, "error", err, + ) + + return 0 + } + + return dur +} + // pruneQueuesAndMessages removes old client_queues entries // per QUEUE_MAX_AGE and prunes messages per MESSAGE_MAX_AGE. func (hdlr *Handlers) pruneQueuesAndMessages( ctx context.Context, ) { - queueMaxAge := hdlr.params.Config.QueueMaxAge + queueMaxAge := hdlr.parseDurationConfig( + "QUEUE_MAX_AGE", + hdlr.params.Config.QueueMaxAge, + ) if queueMaxAge > 0 { - queueCutoff := time.Now().Add( - -time.Duration(queueMaxAge) * time.Second, - ) + queueCutoff := time.Now().Add(-queueMaxAge) pruned, err := hdlr.params.Database. PruneOldQueueEntries(ctx, queueCutoff) @@ -229,11 +252,12 @@ func (hdlr *Handlers) pruneQueuesAndMessages( } } - messageMaxAge := hdlr.params.Config.MessageMaxAge + messageMaxAge := hdlr.parseDurationConfig( + "MESSAGE_MAX_AGE", + hdlr.params.Config.MessageMaxAge, + ) if messageMaxAge > 0 { - msgCutoff := time.Now().Add( - -time.Duration(messageMaxAge) * time.Second, - ) + msgCutoff := time.Now().Add(-messageMaxAge) pruned, err := hdlr.params.Database. PruneOldMessages(ctx, msgCutoff)