diff --git a/CHANGELOG.md b/CHANGELOG.md index bfb1883..d94e387 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,8 @@ Release history focused on **features, settings, default behavior, compatibility - **Appearance copy:** removed the experimental/default-state sentence from the SVG icon option help text. The option remains account-scoped and off by default. - **On-demand live logs:** the panel status SSE uses `/api/events?...&logs=false`; `/api/logs` refreshes only while the Live Logs window is open or docked and actively following the bounded buffer. Closing, pausing or expanding the history view stops periodic log reads; reopening/resuming triggers an immediate refresh. Existing `/api/events` API consumers remain compatible because the new `logs` query parameter defaults to `true`. - **OPNsense certificate deploy:** the source release now includes the previously referenced `rustproxy-acme.conf.example` and `actions_rustproxy_cert.conf`; the integration guide explicitly requires certificate scope plus `allow_deploy` for the deployment credential. -- **Upgrade notes:** no configuration or database changes are required. +- **Preference persistence fix:** expanded the per-administrator UI preference document limit from 4 KiB to 64 KiB. Customized Overview layouts can exceed the old limit once widget widths are stored; affected saves previously surfaced as `auth.unavailable` even though authentication was healthy. +- **Upgrade notes:** the SQLite schema is migrated automatically to expand administrator preference storage; proxy configuration is unchanged. ## 0.36.0 — leaner administration polling diff --git a/SHA256SUMS b/SHA256SUMS index a1e3d78..4592a74 100644 --- a/SHA256SUMS +++ b/SHA256SUMS @@ -2,7 +2,7 @@ 4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example e76e688874828e3743d03693b1d2872b9633b4db09909673786caf30f7cfe489 .gitignore fded6c62b3bbf31e80efa6a5411431625fb06a3be044eb03399de2e09d02c1d6 AGENTS.md -ad23fed76aa9795ce6a34272504607454a8b60aa4293faf1b5be32526e9313f3 CHANGELOG.md +9e2912307564ea75dafd974af97d681208ff1a6babeab1955f22cee958535cb0 CHANGELOG.md a9309ef711e6daa6ca9bf7f3d7d9bfffe2411a5557be90d7eceeea06e3f5bd1d Cargo.lock a27d4c4f02f92ef1d41a96800ec46a9c835d4fe5edc2184f0babfa28e6c053a6 Cargo.toml 25c7f3c8162cbf5cc156439774118a81dae9fdc52b611ec7e40c230fc99a772c Dockerfile @@ -103,8 +103,9 @@ faa7e50922bf180c363f7627fbcfe8079831e36282220d17481cf69bc64b57f1 migrations/000 f093aef9639a959a2522946995a02d2e67c464e794c3ef665261a9a5b348c968 migrations/0007_secret_store.sql 90f6dce865496e018c047133d3bc1721fc2a19b2f4ced22e2a3625d9e9ff18ac migrations/0008_config_history.sql fc7585fdcbe99fe3496f0e62f4c4e2161f4a0173222ba4950331aec6c44e50f8 migrations/0009_certificate_api_grants.sql -a8b1e5ec3236a0c2681dfa1746c526eeef77e8bcb5ea8634d954ac398b4642bb migrations/0010_dashboard_presets.sql +d58a464870895cbefcaa2b4adfd6fcd0d5182d19f2528eef6509e190046cc9fe migrations/0010_dashboard_presets.sql e56ac3718ea7d73fd9afb1c3b44ca95c9bea424c32166e37ce913bee0e67a277 migrations/0011_tokens.sql +273ce62a518657853428ca44dfe4dd2a56abbbbba684e905a2d0147d08359888 migrations/0012_admin_preferences_size.sql 478220552ffeea3de00e2866d427d9f87f4cfc4ae5232e5c3a774e62425b1e0c nagios/README.md 91233910aa4dab07ae917fef93d018a7a0beaab697f9a0bb889ac613e595cca4 nagios/check_rustproxy.py a42e01b341725c9da0928edf40c8c3b9c814ef90592f208c52b1f2c56a2ad383 nagios/check_rustproxy_api @@ -170,7 +171,7 @@ f7a356109786635dd58cba590c71c796dbf36b47f25845190c75ee5c6a9fcb86 src/admin/oper f5127855c2994d052678865df36d703b74f4a0afc633daf7718b10da18d26e37 src/admin/panel_content.rs 9080babb5094d24c2cd4b8e6efb6a516142263ff9f45219a13857dfc55f33fa4 src/admin/preferences.rs 35a7f99c7448c90b8637f4b3a7becca138f1f378fe5dc3f87122151525bb20f9 src/admin/preferences_columns.rs -4bc49f367ea0d7e387151434498366e61836d3132e12657cd2b28073f668f80c src/admin/preferences_tests.rs +1a7b2c2ab9c9cb00f48be34640c4f430ec0a3bc4ee3202db63b804f64e32f7b6 src/admin/preferences_tests.rs 62ecbae5dee00aeeca3eb806e2605372471dfab971cde8f66f11e711df8fcea1 src/admin/public/mod.rs 7a00e4ab943f74d2b1bdcbfab7f9cd3f584e9f59536b4aca6c4c31f5f5508d12 src/admin/public/tests.rs 6930b4f4b45257772b321b20a9becdf9bc4610274852948ba38c2d0cdf02dfaf src/admin/refresh_preferences.rs @@ -180,7 +181,7 @@ e135431cb078ddb483165581123c64af8f00ea67acb3a395ec3e53c85d0d0d45 src/admin/sess 2d5b4c28fd0bd48b06890fa8ec44a2296de948c1f88eb2909f4b495710d4e13e src/admin/status_live.rs 8315c856cd50f1072593ecb30c274d42d82f078b5fb93e7c68a8857cd81b0841 src/admin/status_system.rs cadd7dca29b9aad8c77c7bc23dd249ea37a15adfa4d636a8ef00273a91ce35dc src/admin/tail.rs -0d5bd0e7fe4428959a90256ae1cbdb8ec6ffbc829214907670779564e1b1b2f8 src/admin/tests.rs +eafb1afb4bf3dd967c18313d4725cc69edf7ad9d351b89f4e1da545f08e7e941 src/admin/tests.rs c18c369124368f58524652565102817ed69fd25b222b0c5e737ef7632b45dcfb src/admin/web/acl-picker.js 48fcd818c522fe80f2c7b2e218f87de6a7772ffd0a24e93502780b03ec78e335 src/admin/web/acme.js 86abf50f39862d821cd2e43ca425671aa85289272915132c5a9a5dd1fc4182e2 src/admin/web/api-tokens.js @@ -192,7 +193,7 @@ fcce3b0204ca2c679f5d8049d020619664a3c0a45889975681258d82ae329988 src/admin/web/ d695ec90fb5c98c27d7ba1d2bebf54f9840985fc530efcec3fcaf48d74b36c6b src/admin/web/app/dashboard-inline-layout.js e4238322a985d3e8b7f2de94b7af31e1c718775590b2c7b3428b878cd43337b7 src/admin/web/app/dashboard-layout-editor.js a184c50e1ca6bb5a14954e31b15f09ff9978fb939064ca724851bbb4e661dc56 src/admin/web/app/dashboard-presets.js -6c1321059662a15998bba60190419469c04b71be346fb9c2adfd8b83a4ea696b src/admin/web/app/icons.js +07d4bdafa83e591fbe90d2f6199d4efa2e7207973c83c402c43d3631e64539c7 src/admin/web/app/icons.js 32aa11f041eafdf16e66adef6f5847a67413785224da2c19ecbf825996622aff src/admin/web/app/overview-extra-metrics.js 5e62cccf6a9507fe208e9f41bbd08453c3d8c6a6b83c56d93a2bb22550ea6e9a src/admin/web/app/overview-masonry.js 4d1d4760cdff44771c9d880a7c0da81d7233ef6e3a4868111a522703831a7a54 src/admin/web/app/overview-samples.js @@ -209,11 +210,11 @@ b7ead49224a4ec67181d3f24f9d9634bd6d76756f826a4d5d993300637d1d8f3 src/admin/web/ f5855db3327f51170a28114e07d207745b4d6c8822d88d24c1a81eb92f345b14 src/admin/web/cache-panel.js 9f323507649910561518b63cf6632e81b3ecc4b09501ae53b635705f34ea6587 src/admin/web/certificate-api.js a09f060b47771c56e9b9e57f1b896a32a2f87f1fb33401ac76932a91d247c67e src/admin/web/css/00-tokens.css -922e1bc7631f9e942c31cb8b130bb377791cdc0a3248513c80698af9ed4c8b7b src/admin/web/css/10-base.css +8a008583f2e15dddb6e5a2f45a91a21784bc2c49357f92d49ba49da4b6ae0602 src/admin/web/css/10-base.css 6ed54b27a4a81b073f1d30cf61e366f3d5c1f6f8495f0d0a8af48e061937eb4e src/admin/web/css/20-shell.css -321f8c767f1bb82d2b684ed72506b753fc01d8d9a81676f28a84dcaa580d5fc8 src/admin/web/css/30-overview.css +a07b840826c97706b3bab9ae805445531faaceb033ca61034dd8428c3af56d3c src/admin/web/css/30-overview.css dd82b5e3ead91c20bb08cf8d71bbba31ea8e3d108c417913c63409f3e34da7e1 src/admin/web/css/40-editors.css -89e33ec7004dda531a15b0b210a59aa125f5954a382e6f7def429293cbdf0f4b src/admin/web/css/50-settings.css +24defe4c76df881da43498e2fc9ebd2927178f4a6f4dc964dbbae03353634008 src/admin/web/css/50-settings.css f8b515c63080c71ee647cfd8c36628271e8357b31cd84f00cf591e9b752b44fe src/admin/web/css/60-monitoring.css 33c556cd4be0310117ea5324d27e622b0b5ca2190c8d6f70aa551d94fb411d38 src/admin/web/css/70-responsive.css 8009afdb04d5170687816730cc930a22fead1fe0248835ca7e8de81d6907395d src/admin/web/css/README.md @@ -242,14 +243,14 @@ e6c4d91d1542d0a5b47bb8736f9539d43a6713df5a473b635519d5b7eac2eb9c src/admin/web/ 0a5fff35555153f59d4346933030a67b6e1a8768cee7a47cfa2fcf1a2c564f63 src/admin/web/favicon.svg 7cbdef9192a9dee1f8eac42b85f52b3190f7f6efae50004aa7ad116a910e4036 src/admin/web/file-logs.js 3f0525ca1d4e088ccd4988b8a117b26a8cf8c12213b24c37081d41b68d2b0ff2 src/admin/web/host404.js -c6e8e98a17126b24da815f0d8d55cbc47b451b046aea922638dff9cc9f4397d7 src/admin/web/index.html +1809fefeccaf951a81d685445334d324de4c0aaf4cbe8d1f92bfe17fabee90ab src/admin/web/index.html 7979c490b7fcfcd8f5e52e99fcf7757d78509337a625341c98f62c6da50da374 src/admin/web/integrations.js 7a12f020fc587205694978ab41b660e9a80487d413fbf6e34fc525eabc58308f src/admin/web/login-keys.json ac9f667273918e18406df06cb2613c923f50a88897693a03a18f500e22dc6ec2 src/admin/web/login.html c2e5f554810b8e34fb47361fb90a01073a2e332ee26d82e030956fad3ec1f83e src/admin/web/login.js 16d848a25f744e55d18757ce82cfd80e85bcc5b1777342c41e7bd732e13d8913 src/admin/web/management.js 1f3d10e389c9c20e6eb019509fa6e82b50c6b43bbf988c1210a4d42bb73985d0 src/admin/web/partials/overview-charts.html -5b98f30f2d0da378221b029e7f7fa81d81fc725424a2242a336f1cf5025e6f87 src/admin/web/partials/overview-metrics.html +72d151410dbbad1572309da366c19343587fa72fdf47a1849fcf2ae9b6bab93e src/admin/web/partials/overview-metrics.html 3928afce2ed6d5f2d3e29475dd29e4c3bd3c6df180ad5119beaff0c7a58e3b3b src/admin/web/prometheus-token.js 2d491fe9a547fe109dc5db6a74806d50b08c3daca15a6622c79c9fea33b10c63 src/admin/web/redirects.js 76a01f590d49649dcb1f554c221290919b2a8438e6001737f3c096fa6daec74d src/admin/web/settings.js @@ -268,7 +269,7 @@ dafce3a59a6007e59d7a4018fce96a14cd8e8cedb3ce77f02ee229a280c122d8 src/admin/web/ 2d40c2cb32d7365df0c9cd6c8cb6f0483c7a799786c648f4836a0d9740c6c5ef src/auth/files/mod.rs b2f948bcc3dbd24952c7b1b02d41d06757f49f0c38a78caf51f8f8a687d8480d src/auth/files/tests.rs 50ef648351271d78310e00caa6d246e0009171ed036a3e4e1144737df8c064c2 src/auth/mod.rs -25b9f78a7eee277902d375266bd0f023b79794bcdf646214fc161c5e6dab72d1 src/auth/store.rs +cc4ddd57a264d12bffab131bf04b5cd22df75d38b61e21ce669f5a8a97b109ac src/auth/store.rs 745226cabb007819370fc6aa680995a03a8a13fa057dacfab0fcc21d0c43bbe4 src/auth/tests.rs a4c14df927c22a7fa46023f25cfe48d3aa3c245bec97fce834999d9bcc8d75e1 src/auth_provider/choice.rs 514e99e0d3da21abd633d53872234bc4d7d0178583ab291e53cf4b1802fd5337 src/auth_provider/errors.rs @@ -346,7 +347,7 @@ cc75698be2828000e88f2c482bd26f2fa4050e3432bec1b5a581dca17fef4a37 src/config/mod dd9a6ad8f58e0cdd1ae74f3f972fee0ac14383cd8b8f518403b9a36c169f35bd src/config/snapshot.rs 477cddaa1e1c0b9b605a2f550971a04cf30287519cba70cfbb2e1860e3a93ff2 src/config/tests.rs f35de0ee1516ece6bbfd2b2a7db7d1e0f5048b0ec47cf2a3765dffb799abfd93 src/config/transport_tests.rs -e0f46d885ed81ce306a110560cdd2c217dadde054986d89fcec835ebf6939d65 src/db.rs +76bf74a51fa5c4d12c5fe3e5ba066fbf7a73d83bd62f349b18be100930f204a8 src/db.rs c290d0cb02dfa5d2c60ecabac1cfb20c8884e3d14cca1944906c6d8e677f1784 src/db/api_tokens.rs 892ec0e5985700cca399c861197f6c9d2f1c823d8d95b88abac9d27ae15a069a src/db/certificate_api.rs 5491dd7bb9b3fceac97c4dea63aba6d5c7abcb0fbd06e36ae986e00c79bc07d0 src/db/dashboard_presets.rs diff --git a/migrations/0010_dashboard_presets.sql b/migrations/0010_dashboard_presets.sql index 0ffa73a..f258b84 100644 --- a/migrations/0010_dashboard_presets.sql +++ b/migrations/0010_dashboard_presets.sql @@ -1,5 +1,5 @@ -- Named, administrator-scoped Overview presets. Keep this separate from the --- existing 4096-byte appearance preferences document and proxy configuration. +-- appearance preferences document and proxy configuration. CREATE TABLE IF NOT EXISTS dashboard_presets ( administrator_id INTEGER NOT NULL CHECK (administrator_id = 1), name TEXT NOT NULL CHECK (length(name) BETWEEN 1 AND 64), diff --git a/migrations/0012_admin_preferences_size.sql b/migrations/0012_admin_preferences_size.sql new file mode 100644 index 0000000..fea4a5a --- /dev/null +++ b/migrations/0012_admin_preferences_size.sql @@ -0,0 +1,13 @@ +-- Expand administrator UI preference storage. Current dashboard layouts can exceed +-- the original 4096-byte limit once per-widget widths are persisted. +CREATE TABLE admin_preferences_v2 ( + administrator_id INTEGER PRIMARY KEY CHECK (administrator_id = 1), + document TEXT NOT NULL CHECK (length(document) BETWEEN 2 AND 65536), + FOREIGN KEY (administrator_id) REFERENCES administrator(id) ON DELETE CASCADE +); + +INSERT INTO admin_preferences_v2(administrator_id, document) +SELECT administrator_id, document FROM admin_preferences; + +DROP TABLE admin_preferences; +ALTER TABLE admin_preferences_v2 RENAME TO admin_preferences; diff --git a/src/admin/preferences_tests.rs b/src/admin/preferences_tests.rs index e0e9e62..d2f5fcc 100644 --- a/src/admin/preferences_tests.rs +++ b/src/admin/preferences_tests.rs @@ -292,3 +292,22 @@ fn dashboard_width_preferences_preserve_legacy_and_allow_custom_grids() { .contains_key("not_a_card")); assert!(validate_admin_preferences(&prefs).is_ok()); } + +#[test] +fn customized_dashboard_preferences_fit_persistent_storage_limit() { + let mut prefs = AdminPreferences { + dashboard_widget_sizes: Some(std::collections::BTreeMap::new()), + ..Default::default() + }; + for widget in DASHBOARD_WIDGETS.iter() { + prefs + .dashboard_widget_sizes + .as_mut() + .unwrap() + .insert(widget.id.clone(), 12); + } + assert!(validate_admin_preferences(&prefs).is_ok()); + let document = serde_json::to_string(&prefs).unwrap(); + assert!(document.len() > 4096); + assert!(document.len() <= crate::db::ADMIN_PREFERENCES_MAX_BYTES); +} diff --git a/src/admin/tests.rs b/src/admin/tests.rs index 90505ca..c184b1f 100644 --- a/src/admin/tests.rs +++ b/src/admin/tests.rs @@ -3518,6 +3518,7 @@ mod tests { .unwrap(); assert_eq!(invalid.status(), StatusCode::BAD_REQUEST); let bad_sse = router + .clone() .oneshot( request("/api/events?interval=4") .header("cookie", &cookie) diff --git a/src/admin/web/css/30-overview.css b/src/admin/web/css/30-overview.css index 9376cc2..ca195bb 100644 --- a/src/admin/web/css/30-overview.css +++ b/src/admin/web/css/30-overview.css @@ -617,15 +617,21 @@ body[data-density=compact] .pool-card .table-scroll { #panel-overview .stat-with-action .stat-corner-badge { min-height: 0; border-radius: 999px; + border-color: var(--line); background: transparent; + color: var(--muted); + font-size: 9px; + font-weight: 400; + letter-spacing: 0; cursor: pointer; text-transform: none; } #panel-overview .stat-with-action .stat-corner-badge:hover, #panel-overview .stat-with-action .stat-corner-badge:focus-visible { - border-color: var(--accent); - background: var(--notice-bg); + border-color: var(--line); + background: var(--panel2); + color: var(--text); } #panel-overview .dashboard-overview-grid.is-unlocked .stat-with-action .stat-corner-action { diff --git a/src/auth/store.rs b/src/auth/store.rs index e08b631..2f6e058 100644 --- a/src/auth/store.rs +++ b/src/auth/store.rs @@ -174,7 +174,7 @@ impl Store { } pub async fn save_admin_preferences(&self, username: &str, document: &str) -> Result { ensure!( - (2..=4096).contains(&document.len()), + (2..=crate::db::ADMIN_PREFERENCES_MAX_BYTES).contains(&document.len()), "administrator preferences document is too large" ); let result = sqlx::query( diff --git a/src/db.rs b/src/db.rs index 7656e35..f66fe4b 100644 --- a/src/db.rs +++ b/src/db.rs @@ -13,7 +13,8 @@ use sqlx::{ }; use std::{path::Path, time::Duration}; -pub const SCHEMA_VERSION: i64 = 11; +pub const SCHEMA_VERSION: i64 = 12; +pub const ADMIN_PREFERENCES_MAX_BYTES: usize = 64 * 1024; pub const CERTIFICATE_API_CREDENTIAL_CAPACITY: i64 = 32; pub const AUDIT_CAPACITY: i64 = 10_000; @@ -129,6 +130,11 @@ impl Store { sqlx::raw_sql(include_str!("../migrations/0011_tokens.sql")) .execute(&mut *tx) .await?; + if version < 12 { + sqlx::raw_sql(include_str!("../migrations/0012_admin_preferences_size.sql")) + .execute(&mut *tx) + .await?; + } sqlx::query("INSERT OR IGNORE INTO configuration(id, revision, document) VALUES(1, 0, ?)") .bind(serde_json::to_string(initial)?) .execute(&mut *tx) @@ -139,7 +145,7 @@ impl Store { .bind(crate::auth::now()) .execute(&mut *tx) .await?; - sqlx::query("PRAGMA user_version = 11") + sqlx::query("PRAGMA user_version = 12") .execute(&mut *tx) .await?; tx.commit().await?;