mirror of
https://github.com/sol1/rustguac.git
synced 2026-09-10 01:26:06 +00:00
v0.5.1: RDP resize fix, new themes, Docker config persistence
- Fix RDP display resize for FreeRDP 3.x (patch 004: config.h struct layout) - Add aurora theme (midnight blue with ambient glow gradients) - Add jaguar theme (racing green & gold with subtle gradients) - Add bg_pattern support for CSS gradient backgrounds in themes - Fix Docker config.toml persistence across rebuilds (#38) - Add Docker Compose volume mount documentation - Increase API rate limit to 5/s burst 30 (fix spurious 429s) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
Generated
+1
-1
@@ -3015,7 +3015,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustguac"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"axum-server",
|
||||
|
||||
+1
-1
@@ -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"
|
||||
|
||||
|
||||
+11
-4
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -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:
|
||||
|
||||
+80
-29
@@ -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<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub bg_pattern: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub logo_url: Option<String>,
|
||||
}
|
||||
|
||||
@@ -631,6 +681,7 @@ impl ThemeConfig {
|
||||
apply!(type_web_fg);
|
||||
apply!(hop_bg);
|
||||
apply!(hop_fg);
|
||||
apply!(bg_pattern);
|
||||
|
||||
(preset_name.to_string(), colors)
|
||||
}
|
||||
|
||||
+1
-1
@@ -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(),
|
||||
|
||||
+2
-2
@@ -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");
|
||||
|
||||
+20
-8
@@ -4,8 +4,8 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>rustguac - Address Book</title>
|
||||
<style>
|
||||
:root { --primary: #e94560; --primary-hover: #c73652; --accent: #5bc0be; --accent-hover: #4aa3a1; --bg: #1a1a2e; --surface: #16213e; --input: #0f3460; --text: #e0e0e0; --text-muted: #aaa; --border: #333; --text-dim: #666; --text-on-primary: #fff; --btn-disabled: #555; --status-pending: #f0c040; --status-active: #5bc0be; --status-completed: #888; --status-error: #e94560; --status-expired: #666; --type-ssh-bg: #1b4332; --type-ssh-fg: #52b788; --type-rdp-bg: #3d1f00; --type-rdp-fg: #f0a050; --type-vnc-bg: #2d1b4e; --type-vnc-fg: #b07ff0; --type-web-bg: #1a1a4e; --type-web-fg: #7b8ff0; --hop-bg: #1b4332; --hop-fg: #52b788; }
|
||||
body { font-family: monospace; background: var(--bg); color: var(--text); padding: 2em; font-size: 18px; }
|
||||
:root { --primary: #e94560; --primary-hover: #c73652; --accent: #5bc0be; --accent-hover: #4aa3a1; --bg: #1a1a2e; --surface: #16213e; --input: #0f3460; --text: #e0e0e0; --text-muted: #aaa; --border: #333; --text-dim: #666; --text-on-primary: #fff; --btn-disabled: #555; --status-pending: #f0c040; --status-active: #5bc0be; --status-completed: #888; --status-error: #e94560; --status-expired: #666; --type-ssh-bg: #1b4332; --type-ssh-fg: #52b788; --type-rdp-bg: #3d1f00; --type-rdp-fg: #f0a050; --type-vnc-bg: #2d1b4e; --type-vnc-fg: #b07ff0; --type-web-bg: #1a1a4e; --type-web-fg: #7b8ff0; --hop-bg: #1b4332; --hop-fg: #52b788; --bg-pattern: none; }
|
||||
body { font-family: monospace; background-color: var(--bg); background-image: var(--bg-pattern); background-attachment: fixed; min-height: 100vh; color: var(--text); padding: 2em; font-size: 18px; }
|
||||
h1 { color: var(--primary); }
|
||||
a { color: var(--accent); }
|
||||
nav { margin-bottom: 1.5em; font-size: 0.95em; }
|
||||
@@ -243,6 +243,7 @@
|
||||
.flow-node-target { background: var(--input); color: var(--accent); font-weight: bold; }
|
||||
.flow-arrow { color: var(--text-muted); margin: 0 0.3em; }
|
||||
</style>
|
||||
<script>(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]);if(o.bg_pattern&&o.bg_pattern!=='none'){var s=document.createElement('style');s.id='bg-pattern-style';s.textContent='body{background-image:'+o.bg_pattern+';background-attachment:fixed}';document.head.appendChild(s)}}catch(e){}}})();</script>
|
||||
</head>
|
||||
<body>
|
||||
<img id="site-logo" src="/logo.svg" style="max-height:40px;vertical-align:middle;margin-right:0.5em" alt=""><h1 style="display:inline;vertical-align:middle">rustguac</h1>
|
||||
@@ -542,10 +543,9 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(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){
|
||||
@@ -557,9 +557,9 @@
|
||||
if (!apiKey) {
|
||||
fetch('/api/me', { credentials: 'same-origin' })
|
||||
.then(function(res) {
|
||||
if (!res.ok) window.location.href = '/';
|
||||
if (res.status === 401 || res.status === 403) window.location.href = '/';
|
||||
})
|
||||
.catch(function() { window.location.href = '/'; });
|
||||
.catch(function() {});
|
||||
}
|
||||
|
||||
document.getElementById('logout-item').addEventListener('click', function() {
|
||||
@@ -592,8 +592,13 @@
|
||||
// Check user role and vault status
|
||||
function init() {
|
||||
fetch('/api/me', { headers: apiHeaders(), credentials: 'same-origin' })
|
||||
.then(function(res) { return res.json(); })
|
||||
.then(function(res) {
|
||||
if (res.status === 401 || res.status === 403) { window.location.href = '/'; return; }
|
||||
if (!res.ok) throw new Error('server returned ' + res.status);
|
||||
return res.json();
|
||||
})
|
||||
.then(function(data) {
|
||||
if (!data) return;
|
||||
isAdmin = data.role === 'admin';
|
||||
var level = roleLevel[data.role] || 0;
|
||||
if (level < 3) {
|
||||
@@ -829,6 +834,13 @@
|
||||
btn.textContent = 'Connecting...';
|
||||
clearError();
|
||||
var body = extraBody || {};
|
||||
// Send browser dimensions so RDP sessions start at correct resolution
|
||||
var bw = window.innerWidth, bh = window.innerHeight;
|
||||
if (!body.width && bw > 0 && bh > 0) {
|
||||
body.width = bw;
|
||||
body.height = bh;
|
||||
if (!body.dpi) body.dpi = Math.round((window.devicePixelRatio || 1) * 96);
|
||||
}
|
||||
fetch('/api/addressbook/folders/' + encodeURIComponent(scope) + '/' + encodeURIComponent(folder) + '/entries/' + encodeURIComponent(name) + '/connect', {
|
||||
method: 'POST',
|
||||
headers: apiHeaders({ 'Content-Type': 'application/json' }),
|
||||
|
||||
+8
-7
@@ -4,8 +4,8 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>rustguac - Admin</title>
|
||||
<style>
|
||||
:root { --primary: #e94560; --primary-hover: #c73652; --accent: #5bc0be; --accent-hover: #4aa3a1; --bg: #1a1a2e; --surface: #16213e; --input: #0f3460; --text: #e0e0e0; --text-muted: #aaa; --border: #333; --text-dim: #666; --text-on-primary: #fff; --btn-disabled: #555; --status-pending: #f0c040; --status-active: #5bc0be; --status-completed: #888; --status-error: #e94560; --status-expired: #666; --type-ssh-bg: #1b4332; --type-ssh-fg: #52b788; --type-rdp-bg: #3d1f00; --type-rdp-fg: #f0a050; --type-vnc-bg: #2d1b4e; --type-vnc-fg: #b07ff0; --type-web-bg: #1a1a4e; --type-web-fg: #7b8ff0; --hop-bg: #1b4332; --hop-fg: #52b788; }
|
||||
body { font-family: monospace; background: var(--bg); color: var(--text); padding: 2em; font-size: 18px; }
|
||||
:root { --primary: #e94560; --primary-hover: #c73652; --accent: #5bc0be; --accent-hover: #4aa3a1; --bg: #1a1a2e; --surface: #16213e; --input: #0f3460; --text: #e0e0e0; --text-muted: #aaa; --border: #333; --text-dim: #666; --text-on-primary: #fff; --btn-disabled: #555; --status-pending: #f0c040; --status-active: #5bc0be; --status-completed: #888; --status-error: #e94560; --status-expired: #666; --type-ssh-bg: #1b4332; --type-ssh-fg: #52b788; --type-rdp-bg: #3d1f00; --type-rdp-fg: #f0a050; --type-vnc-bg: #2d1b4e; --type-vnc-fg: #b07ff0; --type-web-bg: #1a1a4e; --type-web-fg: #7b8ff0; --hop-bg: #1b4332; --hop-fg: #52b788; --bg-pattern: none; }
|
||||
body { font-family: monospace; background-color: var(--bg); background-image: var(--bg-pattern); background-attachment: fixed; min-height: 100vh; color: var(--text); padding: 2em; font-size: 18px; }
|
||||
h1 { color: var(--primary); }
|
||||
h2 { color: var(--primary); font-size: 1em; margin-top: 2em; }
|
||||
a { color: var(--accent); }
|
||||
@@ -80,6 +80,7 @@
|
||||
.audit-row { font-size: 0.9em; }
|
||||
.audit-row td { padding: 0.3em 0.8em; }
|
||||
</style>
|
||||
<script>(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]);if(o.bg_pattern&&o.bg_pattern!=='none'){var s=document.createElement('style');s.id='bg-pattern-style';s.textContent='body{background-image:'+o.bg_pattern+';background-attachment:fixed}';document.head.appendChild(s)}}catch(e){}}})();</script>
|
||||
</head>
|
||||
<body>
|
||||
<img id="site-logo" src="/logo.svg" style="max-height:40px;vertical-align:middle;margin-right:0.5em" alt=""><h1 style="display:inline;vertical-align:middle">rustguac</h1>
|
||||
@@ -168,10 +169,9 @@
|
||||
<div id="error"></div>
|
||||
|
||||
<script>
|
||||
(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){
|
||||
@@ -191,7 +191,8 @@
|
||||
function checkAdmin() {
|
||||
fetch('/api/me', { headers: apiHeaders(), 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) {
|
||||
@@ -204,7 +205,7 @@
|
||||
loadAllTokens();
|
||||
loadAuditLog();
|
||||
})
|
||||
.catch(function() { window.location.href = '/'; });
|
||||
.catch(function() {});
|
||||
}
|
||||
checkAdmin();
|
||||
|
||||
|
||||
+6
-4
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>rustguac - SSH Session</title>
|
||||
<style>
|
||||
:root { --primary: #e94560; --primary-hover: #c73652; --accent: #5bc0be; --accent-hover: #4aa3a1; --bg: #1a1a2e; --surface: #16213e; --input: #0f3460; --text: #e0e0e0; --text-muted: #aaa; --border: #333; --text-dim: #888; --text-on-primary: #fff; --btn-disabled: #555; --status-pending: #f0c040; --status-active: #5bc0be; --status-completed: #888; --status-error: #e94560; --status-expired: #666; --type-ssh-bg: #1a3a2a; --type-ssh-fg: #5bc0be; --type-rdp-bg: #2a1a3a; --type-rdp-fg: #a78bfa; --type-vnc-bg: #3a2a1a; --type-vnc-fg: #f0c040; --type-web-bg: #1a2a3a; --type-web-fg: #60a5fa; --hop-bg: #1e3a5f; --hop-fg: #7ec8e3; }
|
||||
:root { --primary: #e94560; --primary-hover: #c73652; --accent: #5bc0be; --accent-hover: #4aa3a1; --bg: #1a1a2e; --surface: #16213e; --input: #0f3460; --text: #e0e0e0; --text-muted: #aaa; --border: #333; --text-dim: #888; --text-on-primary: #fff; --btn-disabled: #555; --status-pending: #f0c040; --status-active: #5bc0be; --status-completed: #888; --status-error: #e94560; --status-expired: #666; --type-ssh-bg: #1a3a2a; --type-ssh-fg: #5bc0be; --type-rdp-bg: #2a1a3a; --type-rdp-fg: #a78bfa; --type-vnc-bg: #3a2a1a; --type-vnc-fg: #f0c040; --type-web-bg: #1a2a3a; --type-web-fg: #60a5fa; --hop-bg: #1e3a5f; --hop-fg: #7ec8e3; --bg-pattern: none; }
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -69,6 +69,7 @@
|
||||
}
|
||||
#banner-continue:hover { background: var(--primary-hover); }
|
||||
</style>
|
||||
<script>(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]);if(o.bg_pattern&&o.bg_pattern!=='none'){var s=document.createElement('style');s.id='bg-pattern-style';s.textContent='body{background-image:'+o.bg_pattern+';background-attachment:fixed}';document.head.appendChild(s)}}catch(e){}}})();</script>
|
||||
|
||||
<!-- Guacamole common JS modules -->
|
||||
<script src="/guac/Namespace.js"></script>
|
||||
@@ -116,8 +117,7 @@
|
||||
<div id="display"></div>
|
||||
|
||||
<script>
|
||||
(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);}
|
||||
fetch('/api/auth/status').then(function(r){return r.json()}).then(function(d){
|
||||
@@ -773,7 +773,9 @@
|
||||
clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(function() {
|
||||
var anyPanelOpen = panelOpen || filePanelOpen;
|
||||
client.sendSize(anyPanelOpen ? window.innerWidth - 380 : window.innerWidth, window.innerHeight);
|
||||
var sw = anyPanelOpen ? window.innerWidth - 380 : window.innerWidth;
|
||||
var sh = window.innerHeight;
|
||||
client.sendSize(sw, sh);
|
||||
}, 250);
|
||||
});
|
||||
|
||||
|
||||
+9
-9
@@ -4,9 +4,8 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>rustguac - Docs</title>
|
||||
<style>
|
||||
:root { --primary: #e94560; --primary-hover: #c73652; --accent: #5bc0be; --accent-hover: #4aa3a1; --bg: #1a1a2e; --surface: #16213e; --input: #0f3460; --text: #e0e0e0; --text-muted: #aaa; --border: #333; --text-dim: #666; --text-on-primary: #fff; --btn-disabled: #555; --status-pending: #f0c040; --status-active: #5bc0be; --status-completed: #888; --status-error: #e94560; --status-expired: #666; --type-ssh-bg: #1b4332; --type-ssh-fg: #52b788; --type-rdp-bg: #3d1f00; --type-rdp-fg: #f0a050; --type-vnc-bg: #2d1b4e; --type-vnc-fg: #b07ff0; --type-web-bg: #1a1a4e; --type-web-fg: #7b8ff0; --hop-bg: #1b4332; --hop-fg: #52b788; }
|
||||
* { box-sizing: border-box; }
|
||||
body { font-family: monospace; background: var(--bg); color: var(--text); padding: 2em; font-size: 18px; margin: 0; }
|
||||
:root { --primary: #e94560; --primary-hover: #c73652; --accent: #5bc0be; --accent-hover: #4aa3a1; --bg: #1a1a2e; --surface: #16213e; --input: #0f3460; --text: #e0e0e0; --text-muted: #aaa; --border: #333; --text-dim: #666; --text-on-primary: #fff; --btn-disabled: #555; --status-pending: #f0c040; --status-active: #5bc0be; --status-completed: #888; --status-error: #e94560; --status-expired: #666; --type-ssh-bg: #1b4332; --type-ssh-fg: #52b788; --type-rdp-bg: #3d1f00; --type-rdp-fg: #f0a050; --type-vnc-bg: #2d1b4e; --type-vnc-fg: #b07ff0; --type-web-bg: #1a1a4e; --type-web-fg: #7b8ff0; --hop-bg: #1b4332; --hop-fg: #52b788; --bg-pattern: none; }
|
||||
body { font-family: monospace; background-color: var(--bg); background-image: var(--bg-pattern); background-attachment: fixed; min-height: 100vh; color: var(--text); padding: 2em; font-size: 18px; }
|
||||
h1 { color: var(--primary); }
|
||||
nav { margin-bottom: 1.5em; font-size: 0.95em; }
|
||||
nav a { margin-right: 1.5em; text-decoration: none; color: var(--accent); }
|
||||
@@ -29,7 +28,7 @@
|
||||
.um-logout:hover { color:var(--primary); background:var(--input); }
|
||||
|
||||
/* Layout */
|
||||
.docs-layout { display: flex; min-height: calc(100vh - 140px); border-top: 1px solid var(--border); margin-top: 1em; }
|
||||
.docs-layout { display: flex; min-height: calc(100vh - 160px); border-top: 1px solid var(--border); margin-top: 1em; }
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
@@ -110,6 +109,7 @@
|
||||
.content { padding: 1em; }
|
||||
}
|
||||
</style>
|
||||
<script>(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]);if(o.bg_pattern&&o.bg_pattern!=='none'){var s=document.createElement('style');s.id='bg-pattern-style';s.textContent='body{background-image:'+o.bg_pattern+';background-attachment:fixed}';document.head.appendChild(s)}}catch(e){}}})();</script>
|
||||
</head>
|
||||
<body>
|
||||
<img id="site-logo" src="/logo.svg" style="max-height:40px;vertical-align:middle;margin-right:0.5em" alt=""><h1 style="display:inline;vertical-align:middle">rustguac</h1>
|
||||
@@ -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');
|
||||
|
||||
+4
-4
@@ -4,8 +4,8 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>rustguac</title>
|
||||
<style>
|
||||
:root { --primary: #e94560; --primary-hover: #c73652; --accent: #5bc0be; --accent-hover: #4aa3a1; --bg: #1a1a2e; --surface: #16213e; --input: #0f3460; --text: #e0e0e0; --text-muted: #aaa; --border: #333; --text-dim: #888; --text-on-primary: #fff; --btn-disabled: #555; --status-pending: #f0c040; --status-active: #5bc0be; --status-completed: #888; --status-error: #e94560; --status-expired: #666; --type-ssh-bg: #1a3a2a; --type-ssh-fg: #5bc0be; --type-rdp-bg: #2a1a3a; --type-rdp-fg: #a78bfa; --type-vnc-bg: #3a2a1a; --type-vnc-fg: #f0c040; --type-web-bg: #1a2a3a; --type-web-fg: #60a5fa; --hop-bg: #1e3a5f; --hop-fg: #7ec8e3; }
|
||||
body { font-family: monospace; background: var(--bg); color: var(--text); padding: 2em; font-size: 18px; }
|
||||
:root { --primary: #e94560; --primary-hover: #c73652; --accent: #5bc0be; --accent-hover: #4aa3a1; --bg: #1a1a2e; --surface: #16213e; --input: #0f3460; --text: #e0e0e0; --text-muted: #aaa; --border: #333; --text-dim: #888; --text-on-primary: #fff; --btn-disabled: #555; --status-pending: #f0c040; --status-active: #5bc0be; --status-completed: #888; --status-error: #e94560; --status-expired: #666; --type-ssh-bg: #1a3a2a; --type-ssh-fg: #5bc0be; --type-rdp-bg: #2a1a3a; --type-rdp-fg: #a78bfa; --type-vnc-bg: #3a2a1a; --type-vnc-fg: #f0c040; --type-web-bg: #1a2a3a; --type-web-fg: #60a5fa; --hop-bg: #1e3a5f; --hop-fg: #7ec8e3; --bg-pattern: none; }
|
||||
body { font-family: monospace; background-color: var(--bg); background-image: var(--bg-pattern); background-attachment: fixed; min-height: 100vh; color: var(--text); padding: 2em; font-size: 18px; }
|
||||
h1 { color: var(--primary); }
|
||||
#error { color: var(--primary); margin-top: 0.8em; }
|
||||
|
||||
@@ -80,6 +80,7 @@
|
||||
#login-form button:hover { background: var(--primary-hover); }
|
||||
#login-form button:disabled { background: var(--btn-disabled); cursor: default; }
|
||||
</style>
|
||||
<script>(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]);if(o.bg_pattern&&o.bg_pattern!=='none'){var s=document.createElement('style');s.id='bg-pattern-style';s.textContent='body{background-image:'+o.bg_pattern+';background-attachment:fixed}';document.head.appendChild(s)}}catch(e){}}})();</script>
|
||||
</head>
|
||||
<body>
|
||||
<img id="site-logo" src="/logo.svg" style="max-height:64px;margin-bottom:0.5em" alt="">
|
||||
@@ -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=''}}}
|
||||
|
||||
|
||||
+14
-10
@@ -4,8 +4,8 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>rustguac - Recordings</title>
|
||||
<style>
|
||||
:root { --primary: #e94560; --primary-hover: #c73652; --accent: #5bc0be; --accent-hover: #4aa3a1; --bg: #1a1a2e; --surface: #16213e; --input: #0f3460; --text: #e0e0e0; --text-muted: #aaa; --border: #333; --text-dim: #666; --text-on-primary: #fff; --btn-disabled: #555; --status-pending: #f0c040; --status-active: #5bc0be; --status-completed: #888; --status-error: #e94560; --status-expired: #666; --type-ssh-bg: #1b4332; --type-ssh-fg: #52b788; --type-rdp-bg: #3d1f00; --type-rdp-fg: #f0a050; --type-vnc-bg: #2d1b4e; --type-vnc-fg: #b07ff0; --type-web-bg: #1a1a4e; --type-web-fg: #7b8ff0; --hop-bg: #1b4332; --hop-fg: #52b788; }
|
||||
body { font-family: monospace; background: var(--bg); color: var(--text); padding: 2em; font-size: 18px; }
|
||||
:root { --primary: #e94560; --primary-hover: #c73652; --accent: #5bc0be; --accent-hover: #4aa3a1; --bg: #1a1a2e; --surface: #16213e; --input: #0f3460; --text: #e0e0e0; --text-muted: #aaa; --border: #333; --text-dim: #666; --text-on-primary: #fff; --btn-disabled: #555; --status-pending: #f0c040; --status-active: #5bc0be; --status-completed: #888; --status-error: #e94560; --status-expired: #666; --type-ssh-bg: #1b4332; --type-ssh-fg: #52b788; --type-rdp-bg: #3d1f00; --type-rdp-fg: #f0a050; --type-vnc-bg: #2d1b4e; --type-vnc-fg: #b07ff0; --type-web-bg: #1a1a4e; --type-web-fg: #7b8ff0; --hop-bg: #1b4332; --hop-fg: #52b788; --bg-pattern: none; }
|
||||
body { font-family: monospace; background-color: var(--bg); background-image: var(--bg-pattern); background-attachment: fixed; min-height: 100vh; color: var(--text); padding: 2em; font-size: 18px; }
|
||||
h1 { color: var(--primary); }
|
||||
a { color: var(--accent); }
|
||||
nav { margin-bottom: 1.5em; font-size: 0.95em; }
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
#player-section {
|
||||
display: none;
|
||||
margin-top: 2em;
|
||||
margin: 1em 0 1.5em 0;
|
||||
background: var(--surface);
|
||||
border-radius: 6px;
|
||||
padding: 1em;
|
||||
@@ -121,6 +121,7 @@
|
||||
}
|
||||
#player-time { color: var(--text-muted); font-size: 0.85em; white-space: nowrap; }
|
||||
</style>
|
||||
<script>(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]);if(o.bg_pattern&&o.bg_pattern!=='none'){var s=document.createElement('style');s.id='bg-pattern-style';s.textContent='body{background-image:'+o.bg_pattern+';background-attachment:fixed}';document.head.appendChild(s)}}catch(e){}}})();</script>
|
||||
|
||||
<!-- Guacamole JS modules (same as client.html) -->
|
||||
<script src="/guac/Namespace.js"></script>
|
||||
@@ -170,7 +171,6 @@
|
||||
</nav>
|
||||
|
||||
<strong>Recordings</strong>
|
||||
<div id="recording-list"></div>
|
||||
|
||||
<div id="player-section">
|
||||
<div id="player-header">
|
||||
@@ -188,11 +188,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="recording-list"></div>
|
||||
|
||||
<script>
|
||||
(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){
|
||||
@@ -209,7 +210,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) {
|
||||
@@ -221,7 +223,7 @@
|
||||
document.getElementById('sessions-link').style.display = 'none';
|
||||
}
|
||||
})
|
||||
.catch(function() { window.location.href = '/'; });
|
||||
.catch(function() {});
|
||||
}
|
||||
|
||||
document.getElementById('logout-item').addEventListener('click', function() {
|
||||
@@ -350,7 +352,9 @@
|
||||
closePlayer();
|
||||
|
||||
document.getElementById('player-title').textContent = name;
|
||||
document.getElementById('player-section').style.display = 'block';
|
||||
var playerSection = document.getElementById('player-section');
|
||||
playerSection.style.display = 'block';
|
||||
playerSection.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
|
||||
var headers = {};
|
||||
if (apiKey) headers['Authorization'] = 'Bearer ' + apiKey;
|
||||
|
||||
+17
-9
@@ -4,8 +4,8 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>rustguac - Sessions</title>
|
||||
<style>
|
||||
:root { --primary: #e94560; --primary-hover: #c73652; --accent: #5bc0be; --accent-hover: #4aa3a1; --bg: #1a1a2e; --surface: #16213e; --input: #0f3460; --text: #e0e0e0; --text-muted: #aaa; --border: #333; --text-dim: #666; --text-on-primary: #fff; --btn-disabled: #555; --status-pending: #f0c040; --status-active: #5bc0be; --status-completed: #888; --status-error: #e94560; --status-expired: #666; --type-ssh-bg: #1b4332; --type-ssh-fg: #52b788; --type-rdp-bg: #3d1f00; --type-rdp-fg: #f0a050; --type-vnc-bg: #2d1b4e; --type-vnc-fg: #b07ff0; --type-web-bg: #1a1a4e; --type-web-fg: #7b8ff0; --hop-bg: #1b4332; --hop-fg: #52b788; }
|
||||
body { font-family: monospace; background: var(--bg); color: var(--text); padding: 2em; font-size: 18px; }
|
||||
:root { --primary: #e94560; --primary-hover: #c73652; --accent: #5bc0be; --accent-hover: #4aa3a1; --bg: #1a1a2e; --surface: #16213e; --input: #0f3460; --text: #e0e0e0; --text-muted: #aaa; --border: #333; --text-dim: #666; --text-on-primary: #fff; --btn-disabled: #555; --status-pending: #f0c040; --status-active: #5bc0be; --status-completed: #888; --status-error: #e94560; --status-expired: #666; --type-ssh-bg: #1b4332; --type-ssh-fg: #52b788; --type-rdp-bg: #3d1f00; --type-rdp-fg: #f0a050; --type-vnc-bg: #2d1b4e; --type-vnc-fg: #b07ff0; --type-web-bg: #1a1a4e; --type-web-fg: #7b8ff0; --hop-bg: #1b4332; --hop-fg: #52b788; --bg-pattern: none; }
|
||||
body { font-family: monospace; background-color: var(--bg); background-image: var(--bg-pattern); background-attachment: fixed; min-height: 100vh; color: var(--text); padding: 2em; font-size: 18px; }
|
||||
h1 { color: var(--primary); }
|
||||
h2 { color: var(--primary); font-size: 1em; margin-top: 2em; }
|
||||
a { color: var(--accent); }
|
||||
@@ -72,7 +72,7 @@
|
||||
text-align: left; padding: 0.4em 0.8em; border-bottom: 1px solid var(--border);
|
||||
}
|
||||
#sessions th { color: var(--text-muted); font-size: 0.85em; }
|
||||
#sessions a { text-decoration: none; }
|
||||
#sessions a { text-decoration: none; font-size: 0.9em; }
|
||||
#sessions a:hover { text-decoration: underline; }
|
||||
.status-pending { color: var(--status-pending); }
|
||||
.status-active { color: var(--status-active); }
|
||||
@@ -81,7 +81,7 @@
|
||||
.status-expired { color: var(--status-expired); }
|
||||
.btn-small {
|
||||
background: none; border: none; color: var(--primary);
|
||||
cursor: pointer; font-family: monospace; padding: 0; font-size: 0.9em;
|
||||
cursor: pointer; font-family: monospace; padding: 0; margin: 0; font-size: 0.9em;
|
||||
}
|
||||
.btn-small:hover { text-decoration: underline; }
|
||||
.btn-share { color: var(--accent); }
|
||||
@@ -193,6 +193,7 @@
|
||||
margin-top: 0.3em;
|
||||
}
|
||||
</style>
|
||||
<script>(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]);if(o.bg_pattern&&o.bg_pattern!=='none'){var s=document.createElement('style');s.id='bg-pattern-style';s.textContent='body{background-image:'+o.bg_pattern+';background-attachment:fixed}';document.head.appendChild(s)}}catch(e){}}})();</script>
|
||||
</head>
|
||||
<body>
|
||||
<img id="site-logo" src="/logo.svg" style="max-height:40px;vertical-align:middle;margin-right:0.5em" alt=""><h1 style="display:inline;vertical-align:middle">rustguac</h1>
|
||||
@@ -316,10 +317,9 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(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){
|
||||
@@ -333,7 +333,8 @@
|
||||
function checkRole() {
|
||||
fetch('/api/me', { headers: apiHeaders(), 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) {
|
||||
@@ -354,7 +355,7 @@
|
||||
document.getElementById('session-form').style.display = '';
|
||||
}
|
||||
})
|
||||
.catch(function() { window.location.href = '/'; });
|
||||
.catch(function() {});
|
||||
}
|
||||
if (apiKey) {
|
||||
// API key users are always admin
|
||||
@@ -638,6 +639,13 @@
|
||||
if (hops.length > 0) body.jump_hosts = hops;
|
||||
var banner = document.getElementById('banner').value;
|
||||
if (banner) body.banner = banner;
|
||||
// Send browser dimensions so RDP sessions start at correct resolution
|
||||
var bw = window.innerWidth, bh = window.innerHeight;
|
||||
if (bw > 0 && bh > 0) {
|
||||
body.width = bw;
|
||||
body.height = bh;
|
||||
body.dpi = Math.round((window.devicePixelRatio || 1) * 96);
|
||||
}
|
||||
fetch('/api/sessions', {
|
||||
method: 'POST',
|
||||
headers: apiHeaders({ 'Content-Type': 'application/json' }),
|
||||
|
||||
+8
-7
@@ -4,8 +4,8 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>rustguac - API Tokens</title>
|
||||
<style>
|
||||
:root { --primary: #e94560; --primary-hover: #c73652; --accent: #5bc0be; --accent-hover: #4aa3a1; --bg: #1a1a2e; --surface: #16213e; --input: #0f3460; --text: #e0e0e0; --text-muted: #aaa; --border: #333; --text-dim: #666; --text-on-primary: #fff; --btn-disabled: #555; --status-pending: #f0c040; --status-active: #5bc0be; --status-completed: #888; --status-error: #e94560; --status-expired: #666; --type-ssh-bg: #1b4332; --type-ssh-fg: #52b788; --type-rdp-bg: #3d1f00; --type-rdp-fg: #f0a050; --type-vnc-bg: #2d1b4e; --type-vnc-fg: #b07ff0; --type-web-bg: #1a1a4e; --type-web-fg: #7b8ff0; --hop-bg: #1b4332; --hop-fg: #52b788; }
|
||||
body { font-family: monospace; background: var(--bg); color: var(--text); padding: 2em; font-size: 18px; }
|
||||
:root { --primary: #e94560; --primary-hover: #c73652; --accent: #5bc0be; --accent-hover: #4aa3a1; --bg: #1a1a2e; --surface: #16213e; --input: #0f3460; --text: #e0e0e0; --text-muted: #aaa; --border: #333; --text-dim: #666; --text-on-primary: #fff; --btn-disabled: #555; --status-pending: #f0c040; --status-active: #5bc0be; --status-completed: #888; --status-error: #e94560; --status-expired: #666; --type-ssh-bg: #1b4332; --type-ssh-fg: #52b788; --type-rdp-bg: #3d1f00; --type-rdp-fg: #f0a050; --type-vnc-bg: #2d1b4e; --type-vnc-fg: #b07ff0; --type-web-bg: #1a1a4e; --type-web-fg: #7b8ff0; --hop-bg: #1b4332; --hop-fg: #52b788; --bg-pattern: none; }
|
||||
body { font-family: monospace; background-color: var(--bg); background-image: var(--bg-pattern); background-attachment: fixed; min-height: 100vh; color: var(--text); padding: 2em; font-size: 18px; }
|
||||
h1 { color: var(--primary); }
|
||||
h2 { color: var(--primary); font-size: 1em; margin-top: 2em; }
|
||||
a { color: var(--accent); }
|
||||
@@ -78,6 +78,7 @@
|
||||
color: var(--text-muted); margin-top: 1em; max-width: 500px;
|
||||
}
|
||||
</style>
|
||||
<script>(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]);if(o.bg_pattern&&o.bg_pattern!=='none'){var s=document.createElement('style');s.id='bg-pattern-style';s.textContent='body{background-image:'+o.bg_pattern+';background-attachment:fixed}';document.head.appendChild(s)}}catch(e){}}})();</script>
|
||||
</head>
|
||||
<body>
|
||||
<img id="site-logo" src="/logo.svg" style="max-height:40px;vertical-align:middle;margin-right:0.5em" alt=""><h1 style="display:inline;vertical-align:middle">rustguac</h1>
|
||||
@@ -131,10 +132,9 @@
|
||||
<div id="error"></div>
|
||||
|
||||
<script>
|
||||
(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){
|
||||
@@ -175,7 +175,8 @@
|
||||
|
||||
fetch('/api/me', { headers: apiHeaders(), 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) {
|
||||
@@ -206,7 +207,7 @@
|
||||
|
||||
loadTokens();
|
||||
})
|
||||
.catch(function() { window.location.href = '/'; });
|
||||
.catch(function() {});
|
||||
}
|
||||
|
||||
function populateMaxRoles(userRole) {
|
||||
|
||||
Reference in New Issue
Block a user