diff --git a/CLAUDE.md b/CLAUDE.md index 9137d25..1977883 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -73,7 +73,7 @@ Optional `[oidc]` section enables OpenID Connect authentication. Key settings: ` - **Bare metal**: `sudo ./install.sh` on Debian 13. Installs to `/opt/rustguac`, creates `rustguac` system user with home dir, sets up systemd services. - **Docker**: `docker build -t rustguac .` — multi-stage, debian:trixie-slim runtime. -- **Remote test machine**: `root@solace.sol1.net` — Debian 13 VM (no GPU). Binary at `/opt/rustguac/bin/rustguac`, config at `/opt/rustguac/config.toml`. +- **Remote test machine**: See project memory for connection details. Binary at `/opt/rustguac/bin/rustguac`, config at `/opt/rustguac/config.toml`. ## Build notes diff --git a/Cargo.lock b/Cargo.lock index 718482f..02c5488 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3015,7 +3015,7 @@ dependencies = [ [[package]] name = "rustguac" -version = "0.5.0" +version = "0.5.1" dependencies = [ "axum", "axum-server", diff --git a/Cargo.toml b/Cargo.toml index 3109241..60381cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustguac" -version = "0.5.0" +version = "0.5.1" edition = "2021" description = "Lightweight Rust replacement for Apache Guacamole client" diff --git a/Dockerfile b/Dockerfile index 4dafdfc..3b6aad1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -112,8 +112,8 @@ RUN mkdir -p /opt/rustguac/data /opt/rustguac/recordings /opt/rustguac/tls # Generate self-signed cert for guacd TLS (internal loopback encryption) RUN /opt/rustguac/bin/rustguac generate-cert --hostname localhost --out-dir /opt/rustguac/tls -# Default config (guacd TLS enabled by default) -RUN cat > /opt/rustguac/config.toml <<'EOF' +# Default config template (copied to config.toml on first run if not mounted) +RUN cat > /opt/rustguac/config.toml.default <<'EOF' listen_addr = "0.0.0.0:8089" guacd_addr = "127.0.0.1:4822" recording_path = "/opt/rustguac/recordings" @@ -136,11 +136,18 @@ RUN cat > /opt/rustguac/entrypoint.sh <<'SCRIPT' #!/bin/sh set -e +# Copy default config on first run (if no config file is mounted/present) +CONFIG_PATH="/opt/rustguac/config.toml" +if [ ! -f "$CONFIG_PATH" ]; then + echo "No config.toml found — copying default configuration." + cp /opt/rustguac/config.toml.default "$CONFIG_PATH" +fi + # Create admin API key on first run (if no DB exists yet) DB_PATH="/opt/rustguac/data/rustguac.db" if [ ! -f "$DB_PATH" ]; then echo "First run detected — creating admin API key..." - /opt/rustguac/bin/rustguac --config /opt/rustguac/config.toml add-admin --name docker-admin + /opt/rustguac/bin/rustguac --config "$CONFIG_PATH" add-admin --name docker-admin echo "" echo "==> SAVE THE API KEY ABOVE — it is only shown once! <==" echo "" @@ -166,7 +173,7 @@ trap 'kill $GUACD_PID 2>/dev/null; wait; exit 0' TERM INT # Run rustguac in foreground echo "Starting rustguac..." -exec /opt/rustguac/bin/rustguac --config /opt/rustguac/config.toml serve +exec /opt/rustguac/bin/rustguac --config "$CONFIG_PATH" serve SCRIPT RUN chmod +x /opt/rustguac/entrypoint.sh diff --git a/docs/installation.md b/docs/installation.md index 7a86690..e9d7c67 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -133,6 +133,27 @@ docker exec rustguac /opt/rustguac/bin/rustguac \ --config /opt/rustguac/config.toml add-admin --name my-admin ``` +### Customizing the configuration + +To persist config changes across container restarts, bind-mount a local `config.toml` into the container: + +1. **Copy the default config** from the image: + +```bash +docker run --rm sol1/rustguac:latest cat /opt/rustguac/config.toml.default > config.toml +``` + +2. **Edit** `config.toml` as needed (see [Configuration](configuration.md)): + +```toml +# Example: allow SSH to private networks +ssh_allowed_networks = ["127.0.0.0/8", "::1/128", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] +``` + +3. **Mount it** in your Docker Compose file or `docker run` command (see below). + +If no config file is mounted, the container uses a built-in default on first start. + ### Docker Compose example ```yaml @@ -142,6 +163,7 @@ services: ports: - "8089:8089" volumes: + - ./config.toml:/opt/rustguac/config.toml - rustguac-data:/opt/rustguac/data - rustguac-recordings:/opt/rustguac/recordings environment: @@ -209,3 +231,4 @@ The patches fix: 1. **Autoconf `-Werror` vs deprecated FreeRDP headers** — FreeRDP 3.15 deprecates `codecs_free()`, breaking compile tests 2. **Deprecated function pointer API** — replaces `->input->MouseEvent()` etc. with safe FreeRDP 3.x functions 3. **NULL pointer dereference** — FreeRDP 3.x fires PubSub events before `guac_rdp_disp` is allocated +4. **Struct layout mismatch** — channel source files missing `config.h` see wrong field offsets when SSH support is enabled diff --git a/patches/004-config-h-struct-layout.patch b/patches/004-config-h-struct-layout.patch new file mode 100644 index 0000000..800c7c9 --- /dev/null +++ b/patches/004-config-h-struct-layout.patch @@ -0,0 +1,117 @@ +diff --git a/src/protocols/rdp/channels/common-svc.c b/src/protocols/rdp/channels/common-svc.c +index 774316f4..d4e70243 100644 +--- a/src/protocols/rdp/channels/common-svc.c ++++ b/src/protocols/rdp/channels/common-svc.c +@@ -17,6 +17,7 @@ + * under the License. + */ + ++#include "config.h" + #include "channels/common-svc.h" + #include "plugins/channels.h" + #include "rdp.h" +diff --git a/src/protocols/rdp/channels/disp.c b/src/protocols/rdp/channels/disp.c +index da1ca800..616b77d7 100644 +--- a/src/protocols/rdp/channels/disp.c ++++ b/src/protocols/rdp/channels/disp.c +@@ -17,6 +17,7 @@ + * under the License. + */ + ++#include "config.h" + #include "channels/disp.h" + #include "plugins/channels.h" + #include "fs.h" +@@ -85,6 +86,10 @@ static void guac_rdp_disp_channel_connected(rdpContext* context, + if (strcmp(args->name, DISP_DVC_CHANNEL_NAME) != 0) + return; + ++ /* Abort if display update module is not yet initialized */ ++ if (guac_disp == NULL) ++ return; ++ + /* Init module with current display size */ + guac_rdp_disp_set_size(guac_disp, rdp_client->settings, + context->instance, guac_rdp_get_width(context->instance), +@@ -127,6 +132,10 @@ static void guac_rdp_disp_channel_disconnected(rdpContext* context, + if (strcmp(args->name, DISP_DVC_CHANNEL_NAME) != 0) + return; + ++ /* Abort if display update module is not yet initialized */ ++ if (guac_disp == NULL) ++ return; ++ + /* Channel is no longer connected */ + guac_disp->disp = NULL; + +@@ -153,6 +162,10 @@ void guac_rdp_disp_load_plugin(rdpContext* context) { + void guac_rdp_disp_set_size(guac_rdp_disp* disp, guac_rdp_settings* settings, + freerdp* rdp_inst, int width, int height) { + ++ /* Abort if display module or settings are not yet initialized */ ++ if (disp == NULL || settings == NULL) ++ return; ++ + guac_rect resize = { + .left = 0, + .top = 0, +diff --git a/src/protocols/rdp/channels/pipe-svc.c b/src/protocols/rdp/channels/pipe-svc.c +index 68d43488..4a5a3e9e 100644 +--- a/src/protocols/rdp/channels/pipe-svc.c ++++ b/src/protocols/rdp/channels/pipe-svc.c +@@ -17,6 +17,7 @@ + * under the License. + */ + ++#include "config.h" + #include "channels/common-svc.h" + #include "channels/pipe-svc.h" + #include "common/list.h" +diff --git a/src/protocols/rdp/channels/rdpei.c b/src/protocols/rdp/channels/rdpei.c +index a94faa9f..317e39dd 100644 +--- a/src/protocols/rdp/channels/rdpei.c ++++ b/src/protocols/rdp/channels/rdpei.c +@@ -17,6 +17,7 @@ + * under the License. + */ + ++#include "config.h" + #include "channels/rdpei.h" + #include "plugins/channels.h" + #include "rdp.h" +diff --git a/src/protocols/rdp/channels/rdpgfx.c b/src/protocols/rdp/channels/rdpgfx.c +index 0fae972f..327e7c2b 100644 +--- a/src/protocols/rdp/channels/rdpgfx.c ++++ b/src/protocols/rdp/channels/rdpgfx.c +@@ -17,6 +17,7 @@ + * under the License. + */ + ++#include "config.h" + #include "channels/rdpgfx.h" + #include "plugins/channels.h" + #include "rdp.h" +diff --git a/src/protocols/rdp/input.c b/src/protocols/rdp/input.c +index 34f9e7c4..5d4d5edf 100644 +--- a/src/protocols/rdp/input.c ++++ b/src/protocols/rdp/input.c +@@ -17,6 +17,7 @@ + * under the License. + */ + ++#include "config.h" + #include "channels/disp.h" + #include "channels/rdpei.h" + #include "input.h" +@@ -106,6 +107,11 @@ int guac_rdp_user_size_handler(guac_user* user, int width, int height) { + guac_rdp_settings* settings = rdp_client->settings; + freerdp* rdp_inst = rdp_client->rdp_inst; + ++ /* Abort if not yet fully initialized (browser may send size instruction ++ * before the RDP connection is fully established) */ ++ if (settings == NULL || rdp_client->disp == NULL) ++ return 0; ++ + /* Convert client pixels to remote pixels */ + width = width * settings->resolution / user->info.optimal_resolution; + height = height * settings->resolution / user->info.optimal_resolution; diff --git a/patches/README.md b/patches/README.md index 1593769..d1fdd33 100644 --- a/patches/README.md +++ b/patches/README.md @@ -49,6 +49,32 @@ Three new connection parameters: **Requires:** FreeRDP 3.x built with Kerberos support (`-DWITH_KRB5=ON`). Debian 13's `freerdp3-dev` includes this by default. +## 003-null-guard-disp-size.patch + +**Problem:** Browser may send `size` instructions before the RDP connection is fully established, or FreeRDP 3.x may fire PubSub events before `guac_rdp_disp` is fully initialized. This causes NULL pointer dereferences. + +**Files patched:** + +| File | Fix | +|------|-----| +| `src/protocols/rdp/channels/disp.c` | Add NULL guard for `guac_disp` in `guac_rdp_disp_channel_connected()`, `guac_rdp_disp_channel_disconnected()`, and `guac_rdp_disp_set_size()` | +| `src/protocols/rdp/input.c` | Add NULL guard for `settings` and `rdp_client->disp` in `guac_rdp_user_size_handler()` | + +## 004-config-h-struct-layout.patch + +**Problem:** Several RDP channel source files and `input.c` do not include `config.h`, so `ENABLE_COMMON_SSH` is undefined in those compilation units. This causes the `guac_rdp_client` struct to have a different layout (missing 3 SSH pointer fields = 24 bytes), making all field accesses after the `#ifdef ENABLE_COMMON_SSH` block read/write wrong memory offsets. Specifically, `rdp_client->disp` reads NULL (actually the `recording` field), so **RDP display resizing silently fails**. + +**Files patched:** + +| File | Fix | +|------|-----| +| `src/protocols/rdp/channels/disp.c` | Add `#include "config.h"` | +| `src/protocols/rdp/channels/common-svc.c` | Add `#include "config.h"` | +| `src/protocols/rdp/channels/pipe-svc.c` | Add `#include "config.h"` | +| `src/protocols/rdp/channels/rdpei.c` | Add `#include "config.h"` | +| `src/protocols/rdp/channels/rdpgfx.c` | Add `#include "config.h"` | +| `src/protocols/rdp/input.c` | Add `#include "config.h"` | + ## Applying patches Patches are applied automatically by all build scripts (`build-deb.sh`, `build-rpm.sh`, `install.sh`, `dev.sh`, `Dockerfile`). To apply manually: diff --git a/src/config.rs b/src/config.rs index a1f6804..402d7ac 100644 --- a/src/config.rs +++ b/src/config.rs @@ -274,6 +274,13 @@ pub struct ThemeColors { pub type_web_fg: String, pub hop_bg: String, pub hop_fg: String, + /// CSS background-image value (gradient, pattern, or "none"). + #[serde(default = "default_bg_pattern")] + pub bg_pattern: String, +} + +fn default_bg_pattern() -> String { + "none".into() } /// Returns all 6 built-in theme presets. @@ -310,6 +317,7 @@ pub fn builtin_presets() -> Vec<(&'static str, ThemeColors)> { type_web_fg: "#7b8ff0".into(), hop_bg: "#1b4332".into(), hop_fg: "#52b788".into(), + bg_pattern: "none".into(), }, ), ( @@ -343,6 +351,7 @@ pub fn builtin_presets() -> Vec<(&'static str, ThemeColors)> { type_web_fg: "#1e40af".into(), hop_bg: "#dcfce7".into(), hop_fg: "#166534".into(), + bg_pattern: "none".into(), }, ), ( @@ -376,6 +385,7 @@ pub fn builtin_presets() -> Vec<(&'static str, ThemeColors)> { type_web_fg: "#6699ff".into(), hop_bg: "#003300".into(), hop_fg: "#00ff66".into(), + bg_pattern: "none".into(), }, ), ( @@ -409,6 +419,7 @@ pub fn builtin_presets() -> Vec<(&'static str, ThemeColors)> { type_web_fg: "#6699ff".into(), hop_bg: "#0a200a".into(), hop_fg: "#33ff33".into(), + bg_pattern: "none".into(), }, ), ( @@ -442,6 +453,7 @@ pub fn builtin_presets() -> Vec<(&'static str, ThemeColors)> { type_web_fg: "#88c0d0".into(), hop_bg: "#384838".into(), hop_fg: "#a3be8c".into(), + bg_pattern: "none".into(), }, ), ( @@ -475,39 +487,75 @@ pub fn builtin_presets() -> Vec<(&'static str, ThemeColors)> { type_web_fg: "#60a5fa".into(), hop_bg: "#14532d".into(), hop_fg: "#4ade80".into(), + bg_pattern: "none".into(), }, ), ( - "avocado", + "jaguar", ThemeColors { - primary: "#d4883c".into(), - primary_hover: "#b8742f".into(), - accent: "#c5d455".into(), - accent_hover: "#a8b83e".into(), - bg: "#151a0e".into(), - surface: "#1e2414".into(), - input: "#2a321c".into(), - text: "#eef0e0".into(), - text_muted: "#a0a888".into(), - border: "#3a4228".into(), - text_dim: "#5a6240".into(), - text_on_primary: "#151a0e".into(), - btn_disabled: "#3a4228".into(), - status_pending: "#d4883c".into(), - status_active: "#c5d455".into(), - status_completed: "#6a7252".into(), - status_error: "#c0392b".into(), - status_expired: "#3a4228".into(), - type_ssh_bg: "#1e2a14".into(), - type_ssh_fg: "#8cb832".into(), - type_rdp_bg: "#2a2014".into(), - type_rdp_fg: "#d4a050".into(), - type_vnc_bg: "#221e2a".into(), - type_vnc_fg: "#b07ff0".into(), - type_web_bg: "#1a1e2a".into(), - type_web_fg: "#7b8ff0".into(), - hop_bg: "#1e2a14".into(), - hop_fg: "#8cb832".into(), + primary: "#d4a853".into(), + primary_hover: "#b89040".into(), + accent: "#50c878".into(), + accent_hover: "#3dab60".into(), + bg: "#0a100e".into(), + surface: "#121c18".into(), + input: "#1a2c26".into(), + text: "#dce4e0".into(), + text_muted: "#8a9e96".into(), + border: "#243830".into(), + text_dim: "#4a6058".into(), + text_on_primary: "#0a100e".into(), + btn_disabled: "#2a3e36".into(), + status_pending: "#d4a853".into(), + status_active: "#50c878".into(), + status_completed: "#5a7068".into(), + status_error: "#e05050".into(), + status_expired: "#2a3e36".into(), + type_ssh_bg: "#0e1e16".into(), + type_ssh_fg: "#50c878".into(), + type_rdp_bg: "#1e1a0e".into(), + type_rdp_fg: "#d4a853".into(), + type_vnc_bg: "#1a142a".into(), + type_vnc_fg: "#a080d0".into(), + type_web_bg: "#0e1a2a".into(), + type_web_fg: "#6098d0".into(), + hop_bg: "#0e1e16".into(), + hop_fg: "#50c878".into(), + bg_pattern: "radial-gradient(ellipse at 20% 80%, rgba(80, 200, 120, 0.08) 0%, transparent 50%), radial-gradient(ellipse at 80% 10%, rgba(212, 168, 83, 0.06) 0%, transparent 45%)".into(), + }, + ), + ( + "aurora", + ThemeColors { + primary: "#3b82f6".into(), + primary_hover: "#2563eb".into(), + accent: "#22d3ee".into(), + accent_hover: "#06b6d4".into(), + bg: "#0b1120".into(), + surface: "#111827".into(), + input: "#1e293b".into(), + text: "#e2e8f0".into(), + text_muted: "#94a3b8".into(), + border: "#1e3a5f".into(), + text_dim: "#64748b".into(), + text_on_primary: "#fff".into(), + btn_disabled: "#334155".into(), + status_pending: "#f59e0b".into(), + status_active: "#22d3ee".into(), + status_completed: "#64748b".into(), + status_error: "#ef4444".into(), + status_expired: "#334155".into(), + type_ssh_bg: "#0d2818".into(), + type_ssh_fg: "#34d399".into(), + type_rdp_bg: "#1e1b4b".into(), + type_rdp_fg: "#818cf8".into(), + type_vnc_bg: "#2a1f0e".into(), + type_vnc_fg: "#fbbf24".into(), + type_web_bg: "#0c2340".into(), + type_web_fg: "#60a5fa".into(), + hop_bg: "#0d2818".into(), + hop_fg: "#34d399".into(), + bg_pattern: "radial-gradient(ellipse at 15% 0%, rgba(59, 130, 246, 0.15) 0%, transparent 50%), radial-gradient(ellipse at 85% 100%, rgba(34, 211, 238, 0.10) 0%, transparent 50%), radial-gradient(ellipse at 50% 50%, rgba(30, 58, 138, 0.12) 0%, transparent 70%)".into(), }, ), ] @@ -575,6 +623,8 @@ pub struct ThemeConfig { #[serde(skip_serializing_if = "Option::is_none")] pub hop_fg: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub bg_pattern: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub logo_url: Option, } @@ -631,6 +681,7 @@ impl ThemeConfig { apply!(type_web_fg); apply!(hop_bg); apply!(hop_fg); + apply!(bg_pattern); (preset_name.to_string(), colors) } diff --git a/src/guacd.rs b/src/guacd.rs index eb16703..96bf771 100644 --- a/src/guacd.rs +++ b/src/guacd.rs @@ -205,7 +205,7 @@ pub async fn connect_and_handshake( "width" => p.width.to_string(), "height" => p.height.to_string(), "dpi" => p.dpi.to_string(), - "color-depth" => "24".into(), + "color-depth" => "32".into(), "ignore-cert" => if p.ignore_cert { "true" } else { "false" }.into(), "disable-auth" => "false".into(), "cursor" => "local".into(), diff --git a/src/main.rs b/src/main.rs index fa27a61..116f9d6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -666,8 +666,8 @@ async fn run_server(config: Config, database: Db) { // Rate limit configs let api_governor_conf = GovernorConfigBuilder::default() - .per_second(1) - .burst_size(10) + .per_second(5) + .burst_size(30) .key_extractor(SmartIpKeyExtractor) .finish() .expect("Failed to build API rate limit config"); diff --git a/static/addressbook.html b/static/addressbook.html index ba236ae..5847fe9 100644 --- a/static/addressbook.html +++ b/static/addressbook.html @@ -4,8 +4,8 @@ rustguac - Address Book +

rustguac

@@ -542,10 +543,9 @@

rustguac

@@ -168,10 +169,9 @@
@@ -116,8 +117,7 @@

rustguac

@@ -196,10 +196,9 @@ }); // Theme and auth - (function(){var c=localStorage.getItem('rustguac_theme_colors');if(c){try{var o=JSON.parse(c),r=document.documentElement.style;for(var k in o)r.setProperty('--'+k.replace(/_/g,'-'),o[k])}catch(e){}}})(); - function applyThemeColors(colors){var r=document.documentElement.style;for(var k in colors)r.setProperty('--'+k.replace(/_/g,'-'),colors[k]);localStorage.setItem('rustguac_theme_colors',JSON.stringify(colors))} + function applyThemeColors(colors){var r=document.documentElement.style;for(var k in colors)r.setProperty('--'+k.replace(/_/g,'-'),colors[k]);var s=document.getElementById('bg-pattern-style');if(!s){s=document.createElement('style');s.id='bg-pattern-style';document.head.appendChild(s)}s.textContent=colors.bg_pattern&&colors.bg_pattern!=='none'?'body{background-image:'+colors.bg_pattern+';background-attachment:fixed}':'';localStorage.setItem('rustguac_theme_colors',JSON.stringify(colors))} var _themePresets={},_adminPreset='dark'; - var _themeDescriptions={dark:'Navy & cyan \u2014 the default',light:'Clean white & blue','high-contrast':'Maximum readability',terminal:'Retro green-on-black',nord:'Arctic, muted blues',corporate:'Slate & steel blue',avocado:'Natural greens & warm brown'}; + var _themeDescriptions={dark:'Navy & cyan \u2014 the default',light:'Clean white & blue','high-contrast':'Maximum readability',terminal:'Retro green-on-black',nord:'Arctic, muted blues',corporate:'Slate & steel blue',aurora:'Midnight blue with ambient glow',jaguar:'Racing green & gold'}; function initTheme(t){if(!t)return;_themePresets=t.presets||{};_adminPreset=t.admin_preset||'dark';var u=localStorage.getItem('rustguac_theme'),active=u&&_themePresets[u]?u:_adminPreset,colors=(active===_adminPreset)?t.admin_colors:_themePresets[active];if(colors)applyThemeColors(colors);if(t.logo_url){var l=document.getElementById('site-logo');if(l){l.src=t.logo_url;l.style.display=''}}var menu=document.getElementById('um-theme-list');if(menu){menu.innerHTML='';Object.keys(_themePresets).forEach(function(name){var item=document.createElement('div');item.className='um-item'+(name===active?' active':'');var sw=document.createElement('span');sw.className='um-swatch';var p=_themePresets[name];sw.style.background='linear-gradient(135deg,'+p.primary+' 50%,'+p.accent+' 50%)';item.appendChild(sw);var info=document.createElement('div');info.className='um-theme-info';var nm=document.createElement('span');nm.className='um-theme-name';nm.textContent=name;info.appendChild(nm);var desc=document.createElement('span');desc.className='um-theme-desc';desc.textContent=_themeDescriptions[name]||'';info.appendChild(desc);item.appendChild(info);item.addEventListener('click',function(){localStorage.setItem('rustguac_theme',name);applyThemeColors(_themePresets[name]);menu.querySelectorAll('.um-item').forEach(function(el){el.classList.remove('active')});item.classList.add('active');document.getElementById('user-menu').style.display='none'});menu.appendChild(item)})}} var _ub=document.getElementById('user-menu-btn');if(_ub)_ub.addEventListener('click',function(e){e.stopPropagation();var m=document.getElementById('user-menu');m.style.display=m.style.display==='block'?'none':'block'});document.addEventListener('click',function(){var m=document.getElementById('user-menu');if(m)m.style.display='none'}); fetch('/api/auth/status').then(function(r){return r.json()}).then(function(d){ @@ -214,7 +213,8 @@ } else { fetch('/api/me', { credentials: 'same-origin' }) .then(function(res) { - if (!res.ok) { window.location.href = '/'; return; } + if (res.status === 401 || res.status === 403) { window.location.href = '/'; return; } + if (!res.ok) return; return res.json(); }) .then(function(data) { @@ -226,7 +226,7 @@ document.getElementById('sessions-link').style.display = 'none'; } }) - .catch(function() { window.location.href = '/'; }); + .catch(function() {}); } document.getElementById('logout-item').addEventListener('click', function() { sessionStorage.removeItem('rustguac_api_key'); diff --git a/static/index.html b/static/index.html index 94c1ffa..9e90c18 100644 --- a/static/index.html +++ b/static/index.html @@ -4,8 +4,8 @@ rustguac + @@ -122,8 +123,7 @@ if (res.ok) window.location.href = '/addressbook.html'; }); - (function(){var c=localStorage.getItem('rustguac_theme_colors');if(c){try{var o=JSON.parse(c),r=document.documentElement.style;for(var k in o)r.setProperty('--'+k.replace(/_/g,'-'),o[k])}catch(e){}}})(); - function applyThemeColors(colors){var r=document.documentElement.style;for(var k in colors)r.setProperty('--'+k.replace(/_/g,'-'),colors[k]);localStorage.setItem('rustguac_theme_colors',JSON.stringify(colors))} + function applyThemeColors(colors){var r=document.documentElement.style;for(var k in colors)r.setProperty('--'+k.replace(/_/g,'-'),colors[k]);var s=document.getElementById('bg-pattern-style');if(!s){s=document.createElement('style');s.id='bg-pattern-style';document.head.appendChild(s)}s.textContent=colors.bg_pattern&&colors.bg_pattern!=='none'?'body{background-image:'+colors.bg_pattern+';background-attachment:fixed}':'';localStorage.setItem('rustguac_theme_colors',JSON.stringify(colors))} var _themePresets={},_adminPreset='dark'; function initTheme(t){if(!t)return;_themePresets=t.presets||{};_adminPreset=t.admin_preset||'dark';var u=localStorage.getItem('rustguac_theme'),active=u&&_themePresets[u]?u:_adminPreset,colors=(active===_adminPreset)?t.admin_colors:_themePresets[active];if(colors)applyThemeColors(colors);if(t.logo_url){var l=document.getElementById('site-logo');if(l){l.src=t.logo_url;l.style.display=''}}} diff --git a/static/recordings.html b/static/recordings.html index 27fb81d..f61513d 100644 --- a/static/recordings.html +++ b/static/recordings.html @@ -4,8 +4,8 @@ rustguac - Recordings + @@ -170,7 +171,6 @@ Recordings -
@@ -188,11 +188,12 @@
+
+

rustguac

@@ -316,10 +317,9 @@

rustguac

@@ -131,10 +132,9 @@