Compare commits
3 Commits
6f3c0b01b0
...
6f8ba5f5ae
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f8ba5f5ae | ||
|
|
c05ceec0c2 | ||
|
|
62e462d732 |
@@ -1812,9 +1812,9 @@ directory is also loaded automatically via
|
|||||||
| `PORT` | int | `8080` | HTTP listen port |
|
| `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`. |
|
| `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) |
|
| `DEBUG` | bool | `false` | Enable debug logging (verbose request/response logging) |
|
||||||
| `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. |
|
| `MESSAGE_MAX_AGE` | int | `2592000` | Maximum age of messages in seconds (30 days). Messages older than this are pruned. |
|
||||||
| `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. |
|
| `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` | 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. |
|
| `QUEUE_MAX_AGE` | int | `2592000` | Maximum age of client queue entries in seconds (30 days). Entries older than this are pruned. |
|
||||||
| `MAX_MESSAGE_SIZE` | int | `4096` | Maximum message body size in bytes (planned enforcement) |
|
| `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) |
|
| `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` |
|
| `MOTD` | string | `""` | Message of the day, shown to clients via `GET /api/v1/server` |
|
||||||
|
|||||||
@@ -38,9 +38,9 @@ type Config struct {
|
|||||||
MetricsUsername string
|
MetricsUsername string
|
||||||
Port int
|
Port int
|
||||||
SentryDSN string
|
SentryDSN string
|
||||||
MessageMaxAge string
|
MessageMaxAge int
|
||||||
MaxMessageSize int
|
MaxMessageSize int
|
||||||
QueueMaxAge string
|
QueueMaxAge int
|
||||||
MOTD string
|
MOTD string
|
||||||
ServerName string
|
ServerName string
|
||||||
FederationKey string
|
FederationKey string
|
||||||
@@ -69,9 +69,9 @@ func New(
|
|||||||
viper.SetDefault("SENTRY_DSN", "")
|
viper.SetDefault("SENTRY_DSN", "")
|
||||||
viper.SetDefault("METRICS_USERNAME", "")
|
viper.SetDefault("METRICS_USERNAME", "")
|
||||||
viper.SetDefault("METRICS_PASSWORD", "")
|
viper.SetDefault("METRICS_PASSWORD", "")
|
||||||
viper.SetDefault("MESSAGE_MAX_AGE", "720h")
|
viper.SetDefault("MESSAGE_MAX_AGE", "2592000")
|
||||||
viper.SetDefault("MAX_MESSAGE_SIZE", "4096")
|
viper.SetDefault("MAX_MESSAGE_SIZE", "4096")
|
||||||
viper.SetDefault("QUEUE_MAX_AGE", "720h")
|
viper.SetDefault("QUEUE_MAX_AGE", "2592000")
|
||||||
viper.SetDefault("MOTD", defaultMOTD)
|
viper.SetDefault("MOTD", defaultMOTD)
|
||||||
viper.SetDefault("SERVER_NAME", "")
|
viper.SetDefault("SERVER_NAME", "")
|
||||||
viper.SetDefault("FEDERATION_KEY", "")
|
viper.SetDefault("FEDERATION_KEY", "")
|
||||||
@@ -94,9 +94,9 @@ func New(
|
|||||||
MaintenanceMode: viper.GetBool("MAINTENANCE_MODE"),
|
MaintenanceMode: viper.GetBool("MAINTENANCE_MODE"),
|
||||||
MetricsUsername: viper.GetString("METRICS_USERNAME"),
|
MetricsUsername: viper.GetString("METRICS_USERNAME"),
|
||||||
MetricsPassword: viper.GetString("METRICS_PASSWORD"),
|
MetricsPassword: viper.GetString("METRICS_PASSWORD"),
|
||||||
MessageMaxAge: viper.GetString("MESSAGE_MAX_AGE"),
|
MessageMaxAge: viper.GetInt("MESSAGE_MAX_AGE"),
|
||||||
MaxMessageSize: viper.GetInt("MAX_MESSAGE_SIZE"),
|
MaxMessageSize: viper.GetInt("MAX_MESSAGE_SIZE"),
|
||||||
QueueMaxAge: viper.GetString("QUEUE_MAX_AGE"),
|
QueueMaxAge: viper.GetInt("QUEUE_MAX_AGE"),
|
||||||
MOTD: viper.GetString("MOTD"),
|
MOTD: viper.GetString("MOTD"),
|
||||||
ServerName: viper.GetString("SERVER_NAME"),
|
ServerName: viper.GetString("SERVER_NAME"),
|
||||||
FederationKey: viper.GetString("FEDERATION_KEY"),
|
FederationKey: viper.GetString("FEDERATION_KEY"),
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
// Package db provides database access and migration management.
|
|
||||||
package db
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"modernc.org/sqlite"
|
|
||||||
sqlite3 "modernc.org/sqlite/lib"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsUniqueConstraintError reports whether err is a SQLite
|
|
||||||
// unique-constraint violation.
|
|
||||||
func IsUniqueConstraintError(err error) bool {
|
|
||||||
var sqliteErr *sqlite.Error
|
|
||||||
if !errors.As(err, &sqliteErr) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return sqliteErr.Code() == sqlite3.SQLITE_CONSTRAINT_UNIQUE
|
|
||||||
}
|
|
||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.eeqj.de/sneak/neoirc/internal/db"
|
|
||||||
"git.eeqj.de/sneak/neoirc/internal/irc"
|
"git.eeqj.de/sneak/neoirc/internal/irc"
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
)
|
)
|
||||||
@@ -200,7 +199,7 @@ func (hdlr *Handlers) handleCreateSessionError(
|
|||||||
request *http.Request,
|
request *http.Request,
|
||||||
err error,
|
err error,
|
||||||
) {
|
) {
|
||||||
if db.IsUniqueConstraintError(err) {
|
if strings.Contains(err.Error(), "UNIQUE") {
|
||||||
hdlr.respondError(
|
hdlr.respondError(
|
||||||
writer, request,
|
writer, request,
|
||||||
"nick already taken",
|
"nick already taken",
|
||||||
@@ -1428,7 +1427,7 @@ func (hdlr *Handlers) executeNickChange(
|
|||||||
request.Context(), sessionID, newNick,
|
request.Context(), sessionID, newNick,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if db.IsUniqueConstraintError(err) {
|
if strings.Contains(err.Error(), "UNIQUE") {
|
||||||
hdlr.respondIRCError(
|
hdlr.respondIRCError(
|
||||||
writer, request, clientID, sessionID,
|
writer, request, clientID, sessionID,
|
||||||
irc.ErrNicknameInUse, nick, []string{newNick},
|
irc.ErrNicknameInUse, nick, []string{newNick},
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.eeqj.de/sneak/neoirc/internal/db"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const minPasswordLength = 8
|
const minPasswordLength = 8
|
||||||
@@ -96,7 +94,7 @@ func (hdlr *Handlers) handleRegisterError(
|
|||||||
request *http.Request,
|
request *http.Request,
|
||||||
err error,
|
err error,
|
||||||
) {
|
) {
|
||||||
if db.IsUniqueConstraintError(err) {
|
if strings.Contains(err.Error(), "UNIQUE") {
|
||||||
hdlr.respondError(
|
hdlr.respondError(
|
||||||
writer, request,
|
writer, request,
|
||||||
"nick already taken",
|
"nick already taken",
|
||||||
|
|||||||
@@ -204,39 +204,16 @@ func (hdlr *Handlers) runCleanup(
|
|||||||
hdlr.pruneQueuesAndMessages(ctx)
|
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
|
// pruneQueuesAndMessages removes old client_queues entries
|
||||||
// per QUEUE_MAX_AGE and prunes messages per MESSAGE_MAX_AGE.
|
// per QUEUE_MAX_AGE and prunes messages per MESSAGE_MAX_AGE.
|
||||||
func (hdlr *Handlers) pruneQueuesAndMessages(
|
func (hdlr *Handlers) pruneQueuesAndMessages(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
) {
|
) {
|
||||||
queueMaxAge := hdlr.parseDurationConfig(
|
queueMaxAge := hdlr.params.Config.QueueMaxAge
|
||||||
"QUEUE_MAX_AGE",
|
|
||||||
hdlr.params.Config.QueueMaxAge,
|
|
||||||
)
|
|
||||||
if queueMaxAge > 0 {
|
if queueMaxAge > 0 {
|
||||||
queueCutoff := time.Now().Add(-queueMaxAge)
|
queueCutoff := time.Now().Add(
|
||||||
|
-time.Duration(queueMaxAge) * time.Second,
|
||||||
|
)
|
||||||
|
|
||||||
pruned, err := hdlr.params.Database.
|
pruned, err := hdlr.params.Database.
|
||||||
PruneOldQueueEntries(ctx, queueCutoff)
|
PruneOldQueueEntries(ctx, queueCutoff)
|
||||||
@@ -252,12 +229,11 @@ func (hdlr *Handlers) pruneQueuesAndMessages(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
messageMaxAge := hdlr.parseDurationConfig(
|
messageMaxAge := hdlr.params.Config.MessageMaxAge
|
||||||
"MESSAGE_MAX_AGE",
|
|
||||||
hdlr.params.Config.MessageMaxAge,
|
|
||||||
)
|
|
||||||
if messageMaxAge > 0 {
|
if messageMaxAge > 0 {
|
||||||
msgCutoff := time.Now().Add(-messageMaxAge)
|
msgCutoff := time.Now().Add(
|
||||||
|
-time.Duration(messageMaxAge) * time.Second,
|
||||||
|
)
|
||||||
|
|
||||||
pruned, err := hdlr.params.Database.
|
pruned, err := hdlr.params.Database.
|
||||||
PruneOldMessages(ctx, msgCutoff)
|
PruneOldMessages(ctx, msgCutoff)
|
||||||
|
|||||||
Reference in New Issue
Block a user