config: take error messages as a list and rotate them

This commit is contained in:
4cuck committed 2026-08-29 05:40:46 -04:00
1 parent 02ba12c9a9
commit 6943e19733
4 files changed
+46 -34

No files matched your search

+14 -9
View File
@@ -82,6 +82,7 @@
}:
let
cfg = config.services.teapot;
messageType = lib.types.listOf lib.types.str;
configFile = (pkgs.formats.toml { }).generate "teapot.toml" (
lib.recursiveUpdate {
inherit (cfg) cache preferences gifTranscoding;
@@ -216,19 +217,23 @@
'';
};
clientBudgetMessage = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Shown when a caller outruns its own budget.";
type = messageType;
default = [ ];
example = [
"Slow down."
"Still too fast."
];
description = "Shown when a caller outruns its own budget. A list is rotated through.";
};
rateLimitedMessage = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Shown when the instance is out of upstream quota.";
type = messageType;
default = [ ];
description = "Shown when the instance is out of upstream quota. A list is rotated through.";
};
internalErrorMessage = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Shown when a request fails for any other reason.";
type = messageType;
default = [ ];
description = "Shown when a request fails for any other reason. A list is rotated through.";
};
proxy = lib.mkOption {
type = lib.types.str;
+3 -3
View File
@@ -123,11 +123,11 @@ pub struct AppConfig {
#[serde(default, rename = "trustedProxies")]
pub trusted_proxies: Vec<String>,
#[serde(default, rename = "rateLimitedMessage")]
pub rate_limited_message: Option<String>,
pub rate_limited_message: Vec<String>,
#[serde(default, rename = "clientBudgetMessage")]
pub client_budget_message: Option<String>,
pub client_budget_message: Vec<String>,
#[serde(default, rename = "internalErrorMessage")]
pub internal_error_message: Option<String>,
pub internal_error_message: Vec<String>,
#[serde(default, rename = "enableDebug")]
pub enable_debug: bool,
#[serde(default, rename = "debugToken")]
+26 -19
View File
@@ -1,7 +1,13 @@
use std::{
io,
result,
sync::OnceLock,
sync::{
OnceLock,
atomic::{
AtomicUsize,
Ordering,
},
},
};
use axum::{
@@ -29,9 +35,9 @@ pub type Result<T> = result::Result<T, Error>;
/// the two cannot drift.
#[derive(Debug, Default)]
pub struct Messages {
pub client_budget: Option<String>,
pub rate_limited: Option<String>,
pub internal_error: Option<String>,
pub client_budget: Vec<String>,
pub rate_limited: Vec<String>,
pub internal_error: Vec<String>,
}
static MESSAGES: OnceLock<Messages> = OnceLock::new();
@@ -43,13 +49,23 @@ pub fn set_messages(messages: Messages) {
fn configured() -> &'static Messages {
static EMPTY: Messages = Messages {
client_budget: None,
rate_limited: None,
internal_error: None,
client_budget: Vec::new(),
rate_limited: Vec::new(),
internal_error: Vec::new(),
};
MESSAGES.get().unwrap_or(&EMPTY)
}
/// Rotate through the configured wordings, one per refusal.
fn pick(configured: &'static [String], fallback: &'static str) -> &'static str {
static NEXT: AtomicUsize = AtomicUsize::new(0);
match *configured {
[] => fallback,
[ref only] => only,
ref many => &many[NEXT.fetch_add(1, Ordering::Relaxed) % many.len()],
}
}
pub const CLIENT_BUDGET_MESSAGE: &str = "You are requesting uncached pages faster than this \
instance can fetch them. Cached pages still work.";
pub const RATE_LIMITED_MESSAGE: &str =
@@ -59,18 +75,12 @@ pub const UNAVAILABLE_MESSAGE: &str = "The service has no available upstream ses
#[must_use]
pub fn client_budget_message() -> &'static str {
configured()
.client_budget
.as_deref()
.unwrap_or(CLIENT_BUDGET_MESSAGE)
pick(&configured().client_budget, CLIENT_BUDGET_MESSAGE)
}
#[must_use]
pub fn rate_limited_message() -> &'static str {
configured()
.rate_limited
.as_deref()
.unwrap_or(RATE_LIMITED_MESSAGE)
pick(&configured().rate_limited, RATE_LIMITED_MESSAGE)
}
#[must_use]
@@ -80,10 +90,7 @@ pub const fn unavailable_message() -> &'static str {
#[must_use]
pub fn internal_error_message() -> &'static str {
configured()
.internal_error
.as_deref()
.unwrap_or(INTERNAL_ERROR_MESSAGE)
pick(&configured().internal_error, INTERNAL_ERROR_MESSAGE)
}
#[derive(Error, Debug)]
+3 -3
View File
@@ -41,9 +41,9 @@ fn test_config() -> Config {
enable_rss: true,
client_budget: false,
trusted_proxies: Vec::new(),
client_budget_message: None,
rate_limited_message: None,
internal_error_message: None,
client_budget_message: Vec::new(),
rate_limited_message: Vec::new(),
internal_error_message: Vec::new(),
enable_debug: false,
debug_token: String::new(),
proxy: String::new(),