From e444936c04643c7a76c7c35d430ca3db5fabb194 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Nov 2025 13:01:39 +0000 Subject: [PATCH 02/21] Add environment variables to override admin panel interface settings Co-authored-by: kaaax0815 <32197462+kaaax0815@users.noreply.github.com> --- .../advanced/config/optional-config.md | 22 ++++++++++++ src/server/api/admin/interface/index.get.ts | 3 +- src/server/api/admin/interface/index.post.ts | 15 +++++++- src/server/utils/WireGuard.ts | 35 ++++++++++++------- src/server/utils/config.ts | 27 ++++++++++++++ 5 files changed, 87 insertions(+), 15 deletions(-) diff --git a/docs/content/advanced/config/optional-config.md b/docs/content/advanced/config/optional-config.md index c58f5636..e2a20da8 100644 --- a/docs/content/advanced/config/optional-config.md +++ b/docs/content/advanced/config/optional-config.md @@ -20,3 +20,25 @@ You will however still see a IPv6 address in the Web UI, but it won't be used. This option can be removed in the future, as more devices support IPv6. /// + +## Configuration Overrides + +These environment variables allow you to override settings that would normally be configured through the Admin Panel. When set, these values take precedence over database settings and cannot be changed through the Web UI. + +| Env | Example | Description | +| ---------------------------- | ------- | ----------------------------------------------------- | +| `OVERRIDE_INTERFACE_PORT` | `51820` | Override the WireGuard interface listening port | +| `OVERRIDE_INTERFACE_DEVICE` | `eth1` | Override the network device/interface | +| `OVERRIDE_INTERFACE_MTU` | `1420` | Override the MTU (Maximum Transmission Unit) setting | + +/// warning | Override Behavior + +When these override environment variables are set: +- The specified values will be used instead of database settings +- Changes made through the Web UI to these fields will not take effect +- The Web UI will still display the overridden values +- Updates to these fields via the API will be ignored + +These overrides are useful for containerized environments where configuration should be controlled externally. + +/// diff --git a/src/server/api/admin/interface/index.get.ts b/src/server/api/admin/interface/index.get.ts index 7161aecc..d0ddceff 100644 --- a/src/server/api/admin/interface/index.get.ts +++ b/src/server/api/admin/interface/index.get.ts @@ -1,8 +1,9 @@ export default definePermissionEventHandler('admin', 'any', async () => { const wgInterface = await Database.interfaces.get(); + const wgInterfaceWithOverrides = applyInterfaceOverrides(wgInterface); return { - ...wgInterface, + ...wgInterfaceWithOverrides, privateKey: undefined, }; }); diff --git a/src/server/api/admin/interface/index.post.ts b/src/server/api/admin/interface/index.post.ts index d6beedbe..ba5bda29 100644 --- a/src/server/api/admin/interface/index.post.ts +++ b/src/server/api/admin/interface/index.post.ts @@ -8,7 +8,20 @@ export default definePermissionEventHandler( event, validateZod(InterfaceUpdateSchema, event) ); - await Database.interfaces.update(data); + + // Remove overridden fields from the update data + const updateData = { ...data }; + if (WG_OVERRIDE_ENV.INTERFACE_PORT !== undefined) { + delete updateData.port; + } + if (WG_OVERRIDE_ENV.INTERFACE_DEVICE !== undefined) { + delete updateData.device; + } + if (WG_OVERRIDE_ENV.INTERFACE_MTU !== undefined) { + delete updateData.mtu; + } + + await Database.interfaces.update(updateData); await WireGuard.saveConfig(); return { success: true }; } diff --git a/src/server/utils/WireGuard.ts b/src/server/utils/WireGuard.ts index 2da26fce..61101249 100644 --- a/src/server/utils/WireGuard.ts +++ b/src/server/utils/WireGuard.ts @@ -14,8 +14,9 @@ class WireGuard { */ async saveConfig() { const wgInterface = await Database.interfaces.get(); - await this.#saveWireguardConfig(wgInterface); - await this.#syncWireguardConfig(wgInterface); + const wgInterfaceWithOverrides = applyInterfaceOverrides(wgInterface); + await this.#saveWireguardConfig(wgInterfaceWithOverrides); + await this.#syncWireguardConfig(wgInterfaceWithOverrides); } /** @@ -151,6 +152,7 @@ class WireGuard { async getClientConfiguration({ clientId }: { clientId: ID }) { const wgInterface = await Database.interfaces.get(); + const wgInterfaceWithOverrides = applyInterfaceOverrides(wgInterface); const userConfig = await Database.userConfigs.get(); const client = await Database.clients.get(clientId); @@ -159,9 +161,14 @@ class WireGuard { throw new Error('Client not found'); } - return wg.generateClientConfig(wgInterface, userConfig, client, { - enableIpv6: !WG_ENV.DISABLE_IPV6, - }); + return wg.generateClientConfig( + wgInterfaceWithOverrides, + userConfig, + client, + { + enableIpv6: !WG_ENV.DISABLE_IPV6, + } + ); } async getClientQRCodeSVG({ clientId }: { clientId: ID }) { @@ -217,25 +224,27 @@ class WireGuard { Database.interfaces.update(wgInterface); } - WG_DEBUG(`Starting Wireguard Interface ${wgInterface.name}...`); - await this.#saveWireguardConfig(wgInterface); - await wg.down(wgInterface.name).catch(() => {}); - await wg.up(wgInterface.name).catch((err) => { + const wgInterfaceWithOverrides = applyInterfaceOverrides(wgInterface); + + WG_DEBUG(`Starting Wireguard Interface ${wgInterfaceWithOverrides.name}...`); + await this.#saveWireguardConfig(wgInterfaceWithOverrides); + await wg.down(wgInterfaceWithOverrides.name).catch(() => {}); + await wg.up(wgInterfaceWithOverrides.name).catch((err) => { if ( err && err.message && - err.message.includes(`Cannot find device "${wgInterface.name}"`) + err.message.includes(`Cannot find device "${wgInterfaceWithOverrides.name}"`) ) { throw new Error( - `WireGuard exited with the error: Cannot find device "${wgInterface.name}"\nThis usually means that your host's kernel does not support WireGuard!`, + `WireGuard exited with the error: Cannot find device "${wgInterfaceWithOverrides.name}"\nThis usually means that your host's kernel does not support WireGuard!`, { cause: err.message } ); } throw err; }); - await this.#syncWireguardConfig(wgInterface); - WG_DEBUG(`Wireguard Interface ${wgInterface.name} started successfully.`); + await this.#syncWireguardConfig(wgInterfaceWithOverrides); + WG_DEBUG(`Wireguard Interface ${wgInterfaceWithOverrides.name} started successfully.`); WG_DEBUG('Starting Cron Job...'); await this.startCronJob(); diff --git a/src/server/utils/config.ts b/src/server/utils/config.ts index 322b7ae6..e6d63079 100644 --- a/src/server/utils/config.ts +++ b/src/server/utils/config.ts @@ -54,6 +54,19 @@ export const WG_INITIAL_ENV = { : undefined, }; +export const WG_OVERRIDE_ENV = { + /** Override the WireGuard interface port */ + INTERFACE_PORT: process.env.OVERRIDE_INTERFACE_PORT + ? Number.parseInt(process.env.OVERRIDE_INTERFACE_PORT, 10) + : undefined, + /** Override the network device/interface */ + INTERFACE_DEVICE: process.env.OVERRIDE_INTERFACE_DEVICE, + /** Override the MTU setting */ + INTERFACE_MTU: process.env.OVERRIDE_INTERFACE_MTU + ? Number.parseInt(process.env.OVERRIDE_INTERFACE_MTU, 10) + : undefined, +}; + function assertEnv(env: T) { const val = process.env[env]; @@ -63,3 +76,17 @@ function assertEnv(env: T) { return val; } + +/** + * Apply environment variable overrides to an interface object + */ +export function applyInterfaceOverrides< + T extends { port: number; device: string; mtu: number }, +>(wgInterface: T): T { + return { + ...wgInterface, + port: WG_OVERRIDE_ENV.INTERFACE_PORT ?? wgInterface.port, + device: WG_OVERRIDE_ENV.INTERFACE_DEVICE ?? wgInterface.device, + mtu: WG_OVERRIDE_ENV.INTERFACE_MTU ?? wgInterface.mtu, + }; +} From 5fd3ee9843ca84788a54ad3cc4cb0ccb26e973c8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Nov 2025 13:04:57 +0000 Subject: [PATCH 03/21] Format documentation with prettier Co-authored-by: kaaax0815 <32197462+kaaax0815@users.noreply.github.com> --- docs/content/advanced/config/optional-config.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/content/advanced/config/optional-config.md b/docs/content/advanced/config/optional-config.md index e2a20da8..791acbe3 100644 --- a/docs/content/advanced/config/optional-config.md +++ b/docs/content/advanced/config/optional-config.md @@ -25,15 +25,16 @@ This option can be removed in the future, as more devices support IPv6. These environment variables allow you to override settings that would normally be configured through the Admin Panel. When set, these values take precedence over database settings and cannot be changed through the Web UI. -| Env | Example | Description | -| ---------------------------- | ------- | ----------------------------------------------------- | -| `OVERRIDE_INTERFACE_PORT` | `51820` | Override the WireGuard interface listening port | -| `OVERRIDE_INTERFACE_DEVICE` | `eth1` | Override the network device/interface | -| `OVERRIDE_INTERFACE_MTU` | `1420` | Override the MTU (Maximum Transmission Unit) setting | +| Env | Example | Description | +| --------------------------- | ------- | ---------------------------------------------------- | +| `OVERRIDE_INTERFACE_PORT` | `51820` | Override the WireGuard interface listening port | +| `OVERRIDE_INTERFACE_DEVICE` | `eth1` | Override the network device/interface | +| `OVERRIDE_INTERFACE_MTU` | `1420` | Override the MTU (Maximum Transmission Unit) setting | /// warning | Override Behavior When these override environment variables are set: + - The specified values will be used instead of database settings - Changes made through the Web UI to these fields will not take effect - The Web UI will still display the overridden values From 67185192fde84a979d2022508f4bd1eab03479e9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Nov 2025 13:25:22 +0000 Subject: [PATCH 04/21] Refactor environment variables with shorter names and add comprehensive override support - Rename OVERRIDE_INTERFACE_* to WG_* (shorter, non-conflicting) - Add interface overrides: WG_PORT, WG_DEVICE, WG_MTU, WG_IPV4_CIDR, WG_IPV6_CIDR, WG_ENABLED - Add client config overrides: WG_HOST, WG_CLIENT_PORT, WG_DEFAULT_DNS, WG_DEFAULT_ALLOWED_IPS, WG_DEFAULT_MTU, WG_DEFAULT_PERSISTENT_KEEPALIVE - Add general config overrides: WG_SESSION_TIMEOUT, WG_METRICS_PROMETHEUS, WG_METRICS_JSON - Update all API endpoints to apply and filter overrides - Update documentation with all new environment variables - Exclude AmneziaWG parameters (j*, s*, i*, h*) as requested Co-authored-by: kaaax0815 <32197462+kaaax0815@users.noreply.github.com> --- .../advanced/config/optional-config.md | 42 +++++++- src/server/api/admin/general.get.ts | 3 +- src/server/api/admin/general.post.ts | 15 ++- src/server/api/admin/interface/cidr.post.ts | 11 ++- src/server/api/admin/interface/index.post.ts | 15 ++- src/server/api/admin/userconfig.get.ts | 3 +- src/server/api/admin/userconfig.post.ts | 24 ++++- src/server/utils/WireGuard.ts | 3 +- src/server/utils/config.ts | 96 +++++++++++++++++-- 9 files changed, 189 insertions(+), 23 deletions(-) diff --git a/docs/content/advanced/config/optional-config.md b/docs/content/advanced/config/optional-config.md index 791acbe3..acaa89f5 100644 --- a/docs/content/advanced/config/optional-config.md +++ b/docs/content/advanced/config/optional-config.md @@ -25,11 +25,35 @@ This option can be removed in the future, as more devices support IPv6. These environment variables allow you to override settings that would normally be configured through the Admin Panel. When set, these values take precedence over database settings and cannot be changed through the Web UI. -| Env | Example | Description | -| --------------------------- | ------- | ---------------------------------------------------- | -| `OVERRIDE_INTERFACE_PORT` | `51820` | Override the WireGuard interface listening port | -| `OVERRIDE_INTERFACE_DEVICE` | `eth1` | Override the network device/interface | -| `OVERRIDE_INTERFACE_MTU` | `1420` | Override the MTU (Maximum Transmission Unit) setting | +### Interface Settings + +| Env | Example | Description | +| -------------- | ----------------- | ---------------------------------- | +| `WG_PORT` | `51820` | WireGuard interface listening port | +| `WG_DEVICE` | `eth0` | Network device/interface | +| `WG_MTU` | `1420` | Maximum Transmission Unit | +| `WG_IPV4_CIDR` | `10.8.0.0/24` | IPv4 CIDR range | +| `WG_IPV6_CIDR` | `fdcc::/112` | IPv6 CIDR range | +| `WG_ENABLED` | `true` or `false` | Whether the interface is enabled | + +### Client Connection Settings + +| Env | Example | Description | +| --------------------------------- | ----------------- | ---------------------------------------- | +| `WG_HOST` | `vpn.example.com` | Host clients will connect to | +| `WG_CLIENT_PORT` | `51820` | Port clients will connect to | +| `WG_DEFAULT_DNS` | `1.1.1.1,8.8.8.8` | Default DNS servers for clients | +| `WG_DEFAULT_ALLOWED_IPS` | `0.0.0.0/0,::/0` | Default allowed IPs for clients | +| `WG_DEFAULT_MTU` | `1420` | Default MTU for clients | +| `WG_DEFAULT_PERSISTENT_KEEPALIVE` | `25` | Default persistent keepalive for clients | + +### General Settings + +| Env | Example | Description | +| ----------------------- | ----------------- | -------------------------- | +| `WG_SESSION_TIMEOUT` | `3600` | Session timeout in seconds | +| `WG_METRICS_PROMETHEUS` | `true` or `false` | Enable Prometheus metrics | +| `WG_METRICS_JSON` | `true` or `false` | Enable JSON metrics | /// warning | Override Behavior @@ -43,3 +67,11 @@ When these override environment variables are set: These overrides are useful for containerized environments where configuration should be controlled externally. /// + +/// note | Note on Port Variables + +- `WG_PORT` - The port WireGuard listens on (interface port) +- `WG_CLIENT_PORT` - The port clients connect to (endpoint port, usually same as `WG_PORT`) +- `PORT` - The port the Web UI listens on (HTTP server port) + +/// diff --git a/src/server/api/admin/general.get.ts b/src/server/api/admin/general.get.ts index 075eefce..c99f77f7 100644 --- a/src/server/api/admin/general.get.ts +++ b/src/server/api/admin/general.get.ts @@ -1,4 +1,5 @@ export default definePermissionEventHandler('admin', 'any', async () => { const generalConfig = await Database.general.getConfig(); - return generalConfig; + const generalConfigWithOverrides = applyGeneralOverrides(generalConfig); + return generalConfigWithOverrides; }); diff --git a/src/server/api/admin/general.post.ts b/src/server/api/admin/general.post.ts index 414af6d7..f43f47fc 100644 --- a/src/server/api/admin/general.post.ts +++ b/src/server/api/admin/general.post.ts @@ -8,7 +8,20 @@ export default definePermissionEventHandler( event, validateZod(GeneralUpdateSchema, event) ); - await Database.general.update(data); + + // Remove overridden fields from the update data + const updateData = { ...data }; + if (WG_GENERAL_OVERRIDE_ENV.SESSION_TIMEOUT !== undefined) { + delete updateData.sessionTimeout; + } + if (WG_GENERAL_OVERRIDE_ENV.METRICS_PROMETHEUS !== undefined) { + delete updateData.metricsPrometheus; + } + if (WG_GENERAL_OVERRIDE_ENV.METRICS_JSON !== undefined) { + delete updateData.metricsJson; + } + + await Database.general.update(updateData); return { success: true }; } ); diff --git a/src/server/api/admin/interface/cidr.post.ts b/src/server/api/admin/interface/cidr.post.ts index 95e239cf..2c4f4096 100644 --- a/src/server/api/admin/interface/cidr.post.ts +++ b/src/server/api/admin/interface/cidr.post.ts @@ -9,7 +9,16 @@ export default definePermissionEventHandler( validateZod(InterfaceCidrUpdateSchema, event) ); - await Database.interfaces.updateCidr(data); + // Remove overridden fields from the update data + const updateData = { ...data }; + if (WG_OVERRIDE_ENV.IPV4_CIDR !== undefined) { + delete updateData.ipv4Cidr; + } + if (WG_OVERRIDE_ENV.IPV6_CIDR !== undefined) { + delete updateData.ipv6Cidr; + } + + await Database.interfaces.updateCidr(updateData); await WireGuard.saveConfig(); return { success: true }; } diff --git a/src/server/api/admin/interface/index.post.ts b/src/server/api/admin/interface/index.post.ts index ba5bda29..7a19eed5 100644 --- a/src/server/api/admin/interface/index.post.ts +++ b/src/server/api/admin/interface/index.post.ts @@ -11,15 +11,24 @@ export default definePermissionEventHandler( // Remove overridden fields from the update data const updateData = { ...data }; - if (WG_OVERRIDE_ENV.INTERFACE_PORT !== undefined) { + if (WG_OVERRIDE_ENV.PORT !== undefined) { delete updateData.port; } - if (WG_OVERRIDE_ENV.INTERFACE_DEVICE !== undefined) { + if (WG_OVERRIDE_ENV.DEVICE !== undefined) { delete updateData.device; } - if (WG_OVERRIDE_ENV.INTERFACE_MTU !== undefined) { + if (WG_OVERRIDE_ENV.MTU !== undefined) { delete updateData.mtu; } + if (WG_OVERRIDE_ENV.IPV4_CIDR !== undefined) { + delete updateData.ipv4Cidr; + } + if (WG_OVERRIDE_ENV.IPV6_CIDR !== undefined) { + delete updateData.ipv6Cidr; + } + if (WG_OVERRIDE_ENV.ENABLED !== undefined) { + delete updateData.enabled; + } await Database.interfaces.update(updateData); await WireGuard.saveConfig(); diff --git a/src/server/api/admin/userconfig.get.ts b/src/server/api/admin/userconfig.get.ts index b41a81bd..acac9086 100644 --- a/src/server/api/admin/userconfig.get.ts +++ b/src/server/api/admin/userconfig.get.ts @@ -1,4 +1,5 @@ export default definePermissionEventHandler('admin', 'any', async () => { const userConfig = await Database.userConfigs.get(); - return userConfig; + const userConfigWithOverrides = applyUserConfigOverrides(userConfig); + return userConfigWithOverrides; }); diff --git a/src/server/api/admin/userconfig.post.ts b/src/server/api/admin/userconfig.post.ts index ff150b0c..68d63de4 100644 --- a/src/server/api/admin/userconfig.post.ts +++ b/src/server/api/admin/userconfig.post.ts @@ -8,7 +8,29 @@ export default definePermissionEventHandler( event, validateZod(UserConfigUpdateSchema, event) ); - await Database.userConfigs.update(data); + + // Remove overridden fields from the update data + const updateData = { ...data }; + if (WG_CLIENT_OVERRIDE_ENV.HOST !== undefined) { + delete updateData.host; + } + if (WG_CLIENT_OVERRIDE_ENV.CLIENT_PORT !== undefined) { + delete updateData.port; + } + if (WG_CLIENT_OVERRIDE_ENV.DEFAULT_DNS !== undefined) { + delete updateData.defaultDns; + } + if (WG_CLIENT_OVERRIDE_ENV.DEFAULT_ALLOWED_IPS !== undefined) { + delete updateData.defaultAllowedIps; + } + if (WG_CLIENT_OVERRIDE_ENV.DEFAULT_MTU !== undefined) { + delete updateData.defaultMtu; + } + if (WG_CLIENT_OVERRIDE_ENV.DEFAULT_PERSISTENT_KEEPALIVE !== undefined) { + delete updateData.defaultPersistentKeepalive; + } + + await Database.userConfigs.update(updateData); await WireGuard.saveConfig(); return { success: true }; } diff --git a/src/server/utils/WireGuard.ts b/src/server/utils/WireGuard.ts index 61101249..27dbad50 100644 --- a/src/server/utils/WireGuard.ts +++ b/src/server/utils/WireGuard.ts @@ -154,6 +154,7 @@ class WireGuard { const wgInterface = await Database.interfaces.get(); const wgInterfaceWithOverrides = applyInterfaceOverrides(wgInterface); const userConfig = await Database.userConfigs.get(); + const userConfigWithOverrides = applyUserConfigOverrides(userConfig); const client = await Database.clients.get(clientId); @@ -163,7 +164,7 @@ class WireGuard { return wg.generateClientConfig( wgInterfaceWithOverrides, - userConfig, + userConfigWithOverrides, client, { enableIpv6: !WG_ENV.DISABLE_IPV6, diff --git a/src/server/utils/config.ts b/src/server/utils/config.ts index e6d63079..d72cf997 100644 --- a/src/server/utils/config.ts +++ b/src/server/utils/config.ts @@ -56,15 +56,59 @@ export const WG_INITIAL_ENV = { export const WG_OVERRIDE_ENV = { /** Override the WireGuard interface port */ - INTERFACE_PORT: process.env.OVERRIDE_INTERFACE_PORT - ? Number.parseInt(process.env.OVERRIDE_INTERFACE_PORT, 10) + PORT: process.env.WG_PORT + ? Number.parseInt(process.env.WG_PORT, 10) : undefined, /** Override the network device/interface */ - INTERFACE_DEVICE: process.env.OVERRIDE_INTERFACE_DEVICE, + DEVICE: process.env.WG_DEVICE, /** Override the MTU setting */ - INTERFACE_MTU: process.env.OVERRIDE_INTERFACE_MTU - ? Number.parseInt(process.env.OVERRIDE_INTERFACE_MTU, 10) + MTU: process.env.WG_MTU + ? Number.parseInt(process.env.WG_MTU, 10) : undefined, + /** Override the IPv4 CIDR */ + IPV4_CIDR: process.env.WG_IPV4_CIDR, + /** Override the IPv6 CIDR */ + IPV6_CIDR: process.env.WG_IPV6_CIDR, + /** Override the enabled status */ + ENABLED: process.env.WG_ENABLED === 'true' ? true : + process.env.WG_ENABLED === 'false' ? false : + undefined, +}; + +export const WG_CLIENT_OVERRIDE_ENV = { + /** Override the client connection host */ + HOST: process.env.WG_HOST, + /** Override the client connection port (different from WG_PORT which is the interface port) */ + CLIENT_PORT: process.env.WG_CLIENT_PORT + ? Number.parseInt(process.env.WG_CLIENT_PORT, 10) + : undefined, + /** Override default client DNS servers */ + DEFAULT_DNS: process.env.WG_DEFAULT_DNS?.split(',').map((x) => x.trim()), + /** Override default client allowed IPs */ + DEFAULT_ALLOWED_IPS: process.env.WG_DEFAULT_ALLOWED_IPS?.split(',').map((x) => x.trim()), + /** Override default client MTU */ + DEFAULT_MTU: process.env.WG_DEFAULT_MTU + ? Number.parseInt(process.env.WG_DEFAULT_MTU, 10) + : undefined, + /** Override default client persistent keepalive */ + DEFAULT_PERSISTENT_KEEPALIVE: process.env.WG_DEFAULT_PERSISTENT_KEEPALIVE + ? Number.parseInt(process.env.WG_DEFAULT_PERSISTENT_KEEPALIVE, 10) + : undefined, +}; + +export const WG_GENERAL_OVERRIDE_ENV = { + /** Override session timeout */ + SESSION_TIMEOUT: process.env.WG_SESSION_TIMEOUT + ? Number.parseInt(process.env.WG_SESSION_TIMEOUT, 10) + : undefined, + /** Override metrics Prometheus enabled status */ + METRICS_PROMETHEUS: process.env.WG_METRICS_PROMETHEUS === 'true' ? true : + process.env.WG_METRICS_PROMETHEUS === 'false' ? false : + undefined, + /** Override metrics JSON enabled status */ + METRICS_JSON: process.env.WG_METRICS_JSON === 'true' ? true : + process.env.WG_METRICS_JSON === 'false' ? false : + undefined, }; function assertEnv(env: T) { @@ -81,12 +125,46 @@ function assertEnv(env: T) { * Apply environment variable overrides to an interface object */ export function applyInterfaceOverrides< - T extends { port: number; device: string; mtu: number }, + T extends { port: number; device: string; mtu: number; ipv4Cidr: string; ipv6Cidr: string; enabled: boolean }, >(wgInterface: T): T { return { ...wgInterface, - port: WG_OVERRIDE_ENV.INTERFACE_PORT ?? wgInterface.port, - device: WG_OVERRIDE_ENV.INTERFACE_DEVICE ?? wgInterface.device, - mtu: WG_OVERRIDE_ENV.INTERFACE_MTU ?? wgInterface.mtu, + port: WG_OVERRIDE_ENV.PORT ?? wgInterface.port, + device: WG_OVERRIDE_ENV.DEVICE ?? wgInterface.device, + mtu: WG_OVERRIDE_ENV.MTU ?? wgInterface.mtu, + ipv4Cidr: WG_OVERRIDE_ENV.IPV4_CIDR ?? wgInterface.ipv4Cidr, + ipv6Cidr: WG_OVERRIDE_ENV.IPV6_CIDR ?? wgInterface.ipv6Cidr, + enabled: WG_OVERRIDE_ENV.ENABLED ?? wgInterface.enabled, + }; +} + +/** + * Apply environment variable overrides to a user config object + */ +export function applyUserConfigOverrides< + T extends { host: string; port: number; defaultDns: string[]; defaultAllowedIps: string[]; defaultMtu: number; defaultPersistentKeepalive: number }, +>(userConfig: T): T { + return { + ...userConfig, + host: WG_CLIENT_OVERRIDE_ENV.HOST ?? userConfig.host, + port: WG_CLIENT_OVERRIDE_ENV.CLIENT_PORT ?? userConfig.port, + defaultDns: WG_CLIENT_OVERRIDE_ENV.DEFAULT_DNS ?? userConfig.defaultDns, + defaultAllowedIps: WG_CLIENT_OVERRIDE_ENV.DEFAULT_ALLOWED_IPS ?? userConfig.defaultAllowedIps, + defaultMtu: WG_CLIENT_OVERRIDE_ENV.DEFAULT_MTU ?? userConfig.defaultMtu, + defaultPersistentKeepalive: WG_CLIENT_OVERRIDE_ENV.DEFAULT_PERSISTENT_KEEPALIVE ?? userConfig.defaultPersistentKeepalive, + }; +} + +/** + * Apply environment variable overrides to a general config object + */ +export function applyGeneralOverrides< + T extends { sessionTimeout: number; metricsPrometheus: boolean; metricsJson: boolean }, +>(generalConfig: T): T { + return { + ...generalConfig, + sessionTimeout: WG_GENERAL_OVERRIDE_ENV.SESSION_TIMEOUT ?? generalConfig.sessionTimeout, + metricsPrometheus: WG_GENERAL_OVERRIDE_ENV.METRICS_PROMETHEUS ?? generalConfig.metricsPrometheus, + metricsJson: WG_GENERAL_OVERRIDE_ENV.METRICS_JSON ?? generalConfig.metricsJson, }; } From 11ab71b5d2cfc996f259f781d24106957bbd30af Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Nov 2025 13:41:33 +0000 Subject: [PATCH 05/21] Allow admin panel updates to be saved while overrides remain effective; remove WG_ENABLED - Remove WG_ENABLED environment variable (interface cannot be disabled) - Allow all admin panel updates to be saved to database - Environment variable overrides take precedence at runtime only - Users can now update values in admin panel even when overridden - Updated documentation to clarify override behavior Co-authored-by: kaaax0815 <32197462+kaaax0815@users.noreply.github.com> --- .../advanced/config/optional-config.md | 51 +++++++++---------- src/server/api/admin/general.post.ts | 16 ++---- src/server/api/admin/interface/cidr.post.ts | 13 ++--- src/server/api/admin/interface/index.post.ts | 25 ++------- src/server/api/admin/userconfig.post.ts | 25 ++------- src/server/utils/config.ts | 7 +-- 6 files changed, 38 insertions(+), 99 deletions(-) diff --git a/docs/content/advanced/config/optional-config.md b/docs/content/advanced/config/optional-config.md index acaa89f5..805f3f16 100644 --- a/docs/content/advanced/config/optional-config.md +++ b/docs/content/advanced/config/optional-config.md @@ -23,46 +23,45 @@ This option can be removed in the future, as more devices support IPv6. ## Configuration Overrides -These environment variables allow you to override settings that would normally be configured through the Admin Panel. When set, these values take precedence over database settings and cannot be changed through the Web UI. +These environment variables allow you to override settings that would normally be configured through the Admin Panel. When set, these values take precedence over database settings at runtime. ### Interface Settings -| Env | Example | Description | -| -------------- | ----------------- | ---------------------------------- | -| `WG_PORT` | `51820` | WireGuard interface listening port | -| `WG_DEVICE` | `eth0` | Network device/interface | -| `WG_MTU` | `1420` | Maximum Transmission Unit | -| `WG_IPV4_CIDR` | `10.8.0.0/24` | IPv4 CIDR range | -| `WG_IPV6_CIDR` | `fdcc::/112` | IPv6 CIDR range | -| `WG_ENABLED` | `true` or `false` | Whether the interface is enabled | +| Env | Example | Description | +| -------------- | ------------- | ------------------------- | +| `WG_PORT` | `51820` | WireGuard interface port | +| `WG_DEVICE` | `eth0` | Network device/interface | +| `WG_MTU` | `1420` | Maximum Transmission Unit | +| `WG_IPV4_CIDR` | `10.8.0.0/24` | IPv4 CIDR range | +| `WG_IPV6_CIDR` | `fdcc::/112` | IPv6 CIDR range | ### Client Connection Settings -| Env | Example | Description | -| --------------------------------- | ----------------- | ---------------------------------------- | -| `WG_HOST` | `vpn.example.com` | Host clients will connect to | -| `WG_CLIENT_PORT` | `51820` | Port clients will connect to | -| `WG_DEFAULT_DNS` | `1.1.1.1,8.8.8.8` | Default DNS servers for clients | -| `WG_DEFAULT_ALLOWED_IPS` | `0.0.0.0/0,::/0` | Default allowed IPs for clients | -| `WG_DEFAULT_MTU` | `1420` | Default MTU for clients | -| `WG_DEFAULT_PERSISTENT_KEEPALIVE` | `25` | Default persistent keepalive for clients | +| Env | Example | Description | +| --------------------------------- | ----------------- | ------------------------------- | +| `WG_HOST` | `vpn.example.com` | Host clients will connect to | +| `WG_CLIENT_PORT` | `51820` | Port clients will connect to | +| `WG_DEFAULT_DNS` | `1.1.1.1,8.8.8.8` | Default DNS servers for clients | +| `WG_DEFAULT_ALLOWED_IPS` | `0.0.0.0/0,::/0` | Default allowed IPs for clients | +| `WG_DEFAULT_MTU` | `1420` | Default MTU for clients | +| `WG_DEFAULT_PERSISTENT_KEEPALIVE` | `25` | Default persistent keepalive | ### General Settings -| Env | Example | Description | -| ----------------------- | ----------------- | -------------------------- | -| `WG_SESSION_TIMEOUT` | `3600` | Session timeout in seconds | -| `WG_METRICS_PROMETHEUS` | `true` or `false` | Enable Prometheus metrics | -| `WG_METRICS_JSON` | `true` or `false` | Enable JSON metrics | +| Env | Example | Description | +| ----------------------- | ----------------- | ------------------------- | +| `WG_SESSION_TIMEOUT` | `3600` | Session timeout (seconds) | +| `WG_METRICS_PROMETHEUS` | `true` or `false` | Enable Prometheus metrics | +| `WG_METRICS_JSON` | `true` or `false` | Enable JSON metrics | /// warning | Override Behavior When these override environment variables are set: -- The specified values will be used instead of database settings -- Changes made through the Web UI to these fields will not take effect -- The Web UI will still display the overridden values -- Updates to these fields via the API will be ignored +- The specified values will be used at runtime instead of database settings +- You can still update these fields through the Web UI and they will be saved to the database +- However, the overridden values from environment variables will always take precedence +- The Web UI will display the overridden (effective) values These overrides are useful for containerized environments where configuration should be controlled externally. diff --git a/src/server/api/admin/general.post.ts b/src/server/api/admin/general.post.ts index f43f47fc..606c73fb 100644 --- a/src/server/api/admin/general.post.ts +++ b/src/server/api/admin/general.post.ts @@ -9,19 +9,9 @@ export default definePermissionEventHandler( validateZod(GeneralUpdateSchema, event) ); - // Remove overridden fields from the update data - const updateData = { ...data }; - if (WG_GENERAL_OVERRIDE_ENV.SESSION_TIMEOUT !== undefined) { - delete updateData.sessionTimeout; - } - if (WG_GENERAL_OVERRIDE_ENV.METRICS_PROMETHEUS !== undefined) { - delete updateData.metricsPrometheus; - } - if (WG_GENERAL_OVERRIDE_ENV.METRICS_JSON !== undefined) { - delete updateData.metricsJson; - } - - await Database.general.update(updateData); + // Allow all updates to be saved to database + // Overrides will be applied when reading/using the values + await Database.general.update(data); return { success: true }; } ); diff --git a/src/server/api/admin/interface/cidr.post.ts b/src/server/api/admin/interface/cidr.post.ts index 2c4f4096..00bd4048 100644 --- a/src/server/api/admin/interface/cidr.post.ts +++ b/src/server/api/admin/interface/cidr.post.ts @@ -9,16 +9,9 @@ export default definePermissionEventHandler( validateZod(InterfaceCidrUpdateSchema, event) ); - // Remove overridden fields from the update data - const updateData = { ...data }; - if (WG_OVERRIDE_ENV.IPV4_CIDR !== undefined) { - delete updateData.ipv4Cidr; - } - if (WG_OVERRIDE_ENV.IPV6_CIDR !== undefined) { - delete updateData.ipv6Cidr; - } - - await Database.interfaces.updateCidr(updateData); + // Allow all updates to be saved to database + // Overrides will be applied when reading/using the values + await Database.interfaces.updateCidr(data); await WireGuard.saveConfig(); return { success: true }; } diff --git a/src/server/api/admin/interface/index.post.ts b/src/server/api/admin/interface/index.post.ts index 7a19eed5..a2658378 100644 --- a/src/server/api/admin/interface/index.post.ts +++ b/src/server/api/admin/interface/index.post.ts @@ -9,28 +9,9 @@ export default definePermissionEventHandler( validateZod(InterfaceUpdateSchema, event) ); - // Remove overridden fields from the update data - const updateData = { ...data }; - if (WG_OVERRIDE_ENV.PORT !== undefined) { - delete updateData.port; - } - if (WG_OVERRIDE_ENV.DEVICE !== undefined) { - delete updateData.device; - } - if (WG_OVERRIDE_ENV.MTU !== undefined) { - delete updateData.mtu; - } - if (WG_OVERRIDE_ENV.IPV4_CIDR !== undefined) { - delete updateData.ipv4Cidr; - } - if (WG_OVERRIDE_ENV.IPV6_CIDR !== undefined) { - delete updateData.ipv6Cidr; - } - if (WG_OVERRIDE_ENV.ENABLED !== undefined) { - delete updateData.enabled; - } - - await Database.interfaces.update(updateData); + // Allow all updates to be saved to database + // Overrides will be applied when reading/using the values + await Database.interfaces.update(data); await WireGuard.saveConfig(); return { success: true }; } diff --git a/src/server/api/admin/userconfig.post.ts b/src/server/api/admin/userconfig.post.ts index 68d63de4..d023c460 100644 --- a/src/server/api/admin/userconfig.post.ts +++ b/src/server/api/admin/userconfig.post.ts @@ -9,28 +9,9 @@ export default definePermissionEventHandler( validateZod(UserConfigUpdateSchema, event) ); - // Remove overridden fields from the update data - const updateData = { ...data }; - if (WG_CLIENT_OVERRIDE_ENV.HOST !== undefined) { - delete updateData.host; - } - if (WG_CLIENT_OVERRIDE_ENV.CLIENT_PORT !== undefined) { - delete updateData.port; - } - if (WG_CLIENT_OVERRIDE_ENV.DEFAULT_DNS !== undefined) { - delete updateData.defaultDns; - } - if (WG_CLIENT_OVERRIDE_ENV.DEFAULT_ALLOWED_IPS !== undefined) { - delete updateData.defaultAllowedIps; - } - if (WG_CLIENT_OVERRIDE_ENV.DEFAULT_MTU !== undefined) { - delete updateData.defaultMtu; - } - if (WG_CLIENT_OVERRIDE_ENV.DEFAULT_PERSISTENT_KEEPALIVE !== undefined) { - delete updateData.defaultPersistentKeepalive; - } - - await Database.userConfigs.update(updateData); + // Allow all updates to be saved to database + // Overrides will be applied when reading/using the values + await Database.userConfigs.update(data); await WireGuard.saveConfig(); return { success: true }; } diff --git a/src/server/utils/config.ts b/src/server/utils/config.ts index d72cf997..5e93e51e 100644 --- a/src/server/utils/config.ts +++ b/src/server/utils/config.ts @@ -69,10 +69,6 @@ export const WG_OVERRIDE_ENV = { IPV4_CIDR: process.env.WG_IPV4_CIDR, /** Override the IPv6 CIDR */ IPV6_CIDR: process.env.WG_IPV6_CIDR, - /** Override the enabled status */ - ENABLED: process.env.WG_ENABLED === 'true' ? true : - process.env.WG_ENABLED === 'false' ? false : - undefined, }; export const WG_CLIENT_OVERRIDE_ENV = { @@ -125,7 +121,7 @@ function assertEnv(env: T) { * Apply environment variable overrides to an interface object */ export function applyInterfaceOverrides< - T extends { port: number; device: string; mtu: number; ipv4Cidr: string; ipv6Cidr: string; enabled: boolean }, + T extends { port: number; device: string; mtu: number; ipv4Cidr: string; ipv6Cidr: string }, >(wgInterface: T): T { return { ...wgInterface, @@ -134,7 +130,6 @@ export function applyInterfaceOverrides< mtu: WG_OVERRIDE_ENV.MTU ?? wgInterface.mtu, ipv4Cidr: WG_OVERRIDE_ENV.IPV4_CIDR ?? wgInterface.ipv4Cidr, ipv6Cidr: WG_OVERRIDE_ENV.IPV6_CIDR ?? wgInterface.ipv6Cidr, - enabled: WG_OVERRIDE_ENV.ENABLED ?? wgInterface.enabled, }; } From 268916782d47abe9a6a163adb6bb5c9aa131a4ad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Nov 2025 13:52:54 +0000 Subject: [PATCH 06/21] Return database values in GET endpoints; add overrides info route; remove comments - GET endpoints now return raw database values (not overridden values) - Created /api/admin/overrides route that returns which fields are overridden by env vars - Removed comments from POST endpoints - Admin panel can now show database values and display indicators for overridden fields Co-authored-by: kaaax0815 <32197462+kaaax0815@users.noreply.github.com> --- src/server/api/admin/general.get.ts | 3 +-- src/server/api/admin/general.post.ts | 3 --- src/server/api/admin/interface/cidr.post.ts | 3 --- src/server/api/admin/interface/index.get.ts | 3 +-- src/server/api/admin/interface/index.post.ts | 3 --- src/server/api/admin/overrides.get.ts | 24 ++++++++++++++++++++ src/server/api/admin/userconfig.get.ts | 3 +-- src/server/api/admin/userconfig.post.ts | 3 --- 8 files changed, 27 insertions(+), 18 deletions(-) create mode 100644 src/server/api/admin/overrides.get.ts diff --git a/src/server/api/admin/general.get.ts b/src/server/api/admin/general.get.ts index c99f77f7..075eefce 100644 --- a/src/server/api/admin/general.get.ts +++ b/src/server/api/admin/general.get.ts @@ -1,5 +1,4 @@ export default definePermissionEventHandler('admin', 'any', async () => { const generalConfig = await Database.general.getConfig(); - const generalConfigWithOverrides = applyGeneralOverrides(generalConfig); - return generalConfigWithOverrides; + return generalConfig; }); diff --git a/src/server/api/admin/general.post.ts b/src/server/api/admin/general.post.ts index 606c73fb..414af6d7 100644 --- a/src/server/api/admin/general.post.ts +++ b/src/server/api/admin/general.post.ts @@ -8,9 +8,6 @@ export default definePermissionEventHandler( event, validateZod(GeneralUpdateSchema, event) ); - - // Allow all updates to be saved to database - // Overrides will be applied when reading/using the values await Database.general.update(data); return { success: true }; } diff --git a/src/server/api/admin/interface/cidr.post.ts b/src/server/api/admin/interface/cidr.post.ts index 00bd4048..a9abcdfc 100644 --- a/src/server/api/admin/interface/cidr.post.ts +++ b/src/server/api/admin/interface/cidr.post.ts @@ -8,9 +8,6 @@ export default definePermissionEventHandler( event, validateZod(InterfaceCidrUpdateSchema, event) ); - - // Allow all updates to be saved to database - // Overrides will be applied when reading/using the values await Database.interfaces.updateCidr(data); await WireGuard.saveConfig(); return { success: true }; diff --git a/src/server/api/admin/interface/index.get.ts b/src/server/api/admin/interface/index.get.ts index d0ddceff..7161aecc 100644 --- a/src/server/api/admin/interface/index.get.ts +++ b/src/server/api/admin/interface/index.get.ts @@ -1,9 +1,8 @@ export default definePermissionEventHandler('admin', 'any', async () => { const wgInterface = await Database.interfaces.get(); - const wgInterfaceWithOverrides = applyInterfaceOverrides(wgInterface); return { - ...wgInterfaceWithOverrides, + ...wgInterface, privateKey: undefined, }; }); diff --git a/src/server/api/admin/interface/index.post.ts b/src/server/api/admin/interface/index.post.ts index a2658378..d6beedbe 100644 --- a/src/server/api/admin/interface/index.post.ts +++ b/src/server/api/admin/interface/index.post.ts @@ -8,9 +8,6 @@ export default definePermissionEventHandler( event, validateZod(InterfaceUpdateSchema, event) ); - - // Allow all updates to be saved to database - // Overrides will be applied when reading/using the values await Database.interfaces.update(data); await WireGuard.saveConfig(); return { success: true }; diff --git a/src/server/api/admin/overrides.get.ts b/src/server/api/admin/overrides.get.ts new file mode 100644 index 00000000..dda4bb8a --- /dev/null +++ b/src/server/api/admin/overrides.get.ts @@ -0,0 +1,24 @@ +export default definePermissionEventHandler('admin', 'any', async () => { + return { + interface: { + port: WG_OVERRIDE_ENV.PORT !== undefined, + device: WG_OVERRIDE_ENV.DEVICE !== undefined, + mtu: WG_OVERRIDE_ENV.MTU !== undefined, + ipv4Cidr: WG_OVERRIDE_ENV.IPV4_CIDR !== undefined, + ipv6Cidr: WG_OVERRIDE_ENV.IPV6_CIDR !== undefined, + }, + userConfig: { + host: WG_CLIENT_OVERRIDE_ENV.HOST !== undefined, + port: WG_CLIENT_OVERRIDE_ENV.CLIENT_PORT !== undefined, + defaultDns: WG_CLIENT_OVERRIDE_ENV.DEFAULT_DNS !== undefined, + defaultAllowedIps: WG_CLIENT_OVERRIDE_ENV.DEFAULT_ALLOWED_IPS !== undefined, + defaultMtu: WG_CLIENT_OVERRIDE_ENV.DEFAULT_MTU !== undefined, + defaultPersistentKeepalive: WG_CLIENT_OVERRIDE_ENV.DEFAULT_PERSISTENT_KEEPALIVE !== undefined, + }, + general: { + sessionTimeout: WG_GENERAL_OVERRIDE_ENV.SESSION_TIMEOUT !== undefined, + metricsPrometheus: WG_GENERAL_OVERRIDE_ENV.METRICS_PROMETHEUS !== undefined, + metricsJson: WG_GENERAL_OVERRIDE_ENV.METRICS_JSON !== undefined, + }, + }; +}); diff --git a/src/server/api/admin/userconfig.get.ts b/src/server/api/admin/userconfig.get.ts index acac9086..b41a81bd 100644 --- a/src/server/api/admin/userconfig.get.ts +++ b/src/server/api/admin/userconfig.get.ts @@ -1,5 +1,4 @@ export default definePermissionEventHandler('admin', 'any', async () => { const userConfig = await Database.userConfigs.get(); - const userConfigWithOverrides = applyUserConfigOverrides(userConfig); - return userConfigWithOverrides; + return userConfig; }); diff --git a/src/server/api/admin/userconfig.post.ts b/src/server/api/admin/userconfig.post.ts index d023c460..ff150b0c 100644 --- a/src/server/api/admin/userconfig.post.ts +++ b/src/server/api/admin/userconfig.post.ts @@ -8,9 +8,6 @@ export default definePermissionEventHandler( event, validateZod(UserConfigUpdateSchema, event) ); - - // Allow all updates to be saved to database - // Overrides will be applied when reading/using the values await Database.userConfigs.update(data); await WireGuard.saveConfig(); return { success: true }; From c1d5822f417c61f9d7bab3baa15c7dbb8bf5dd48 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Nov 2025 14:10:07 +0000 Subject: [PATCH 07/21] Add hooks overrides support with environment variables - Added WG_PRE_UP, WG_POST_UP, WG_PRE_DOWN, WG_POST_DOWN environment variables - Created applyHooksOverrides() helper function - Updated WireGuard service to apply hooks overrides when generating config - Updated /api/admin/overrides endpoint to include hooks override information - Updated documentation with hooks environment variables Co-authored-by: kaaax0815 <32197462+kaaax0815@users.noreply.github.com> --- .../advanced/config/optional-config.md | 9 +++++++ src/server/api/admin/overrides.get.ts | 6 +++++ src/server/utils/WireGuard.ts | 3 ++- src/server/utils/config.ts | 26 +++++++++++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/docs/content/advanced/config/optional-config.md b/docs/content/advanced/config/optional-config.md index 805f3f16..967e64a0 100644 --- a/docs/content/advanced/config/optional-config.md +++ b/docs/content/advanced/config/optional-config.md @@ -54,6 +54,15 @@ These environment variables allow you to override settings that would normally b | `WG_METRICS_PROMETHEUS` | `true` or `false` | Enable Prometheus metrics | | `WG_METRICS_JSON` | `true` or `false` | Enable JSON metrics | +### Hooks + +| Env | Example | Description | +| -------------- | ------------------------- | --------------------- | +| `WG_PRE_UP` | `echo "Starting WG"` | PreUp hook command | +| `WG_POST_UP` | `iptables -A FORWARD ...` | PostUp hook command | +| `WG_PRE_DOWN` | `echo "Stopping WG"` | PreDown hook command | +| `WG_POST_DOWN` | `iptables -D FORWARD ...` | PostDown hook command | + /// warning | Override Behavior When these override environment variables are set: diff --git a/src/server/api/admin/overrides.get.ts b/src/server/api/admin/overrides.get.ts index dda4bb8a..ae3b2ef0 100644 --- a/src/server/api/admin/overrides.get.ts +++ b/src/server/api/admin/overrides.get.ts @@ -20,5 +20,11 @@ export default definePermissionEventHandler('admin', 'any', async () => { metricsPrometheus: WG_GENERAL_OVERRIDE_ENV.METRICS_PROMETHEUS !== undefined, metricsJson: WG_GENERAL_OVERRIDE_ENV.METRICS_JSON !== undefined, }, + hooks: { + preUp: WG_HOOKS_OVERRIDE_ENV.PRE_UP !== undefined, + postUp: WG_HOOKS_OVERRIDE_ENV.POST_UP !== undefined, + preDown: WG_HOOKS_OVERRIDE_ENV.PRE_DOWN !== undefined, + postDown: WG_HOOKS_OVERRIDE_ENV.POST_DOWN !== undefined, + }, }; }); diff --git a/src/server/utils/WireGuard.ts b/src/server/utils/WireGuard.ts index 27dbad50..8f4d875a 100644 --- a/src/server/utils/WireGuard.ts +++ b/src/server/utils/WireGuard.ts @@ -27,10 +27,11 @@ class WireGuard { async #saveWireguardConfig(wgInterface: InterfaceType) { const clients = await Database.clients.getAll(); const hooks = await Database.hooks.get(); + const hooksWithOverrides = applyHooksOverrides(hooks); const result = []; result.push( - wg.generateServerInterface(wgInterface, hooks, { + wg.generateServerInterface(wgInterface, hooksWithOverrides, { enableIpv6: !WG_ENV.DISABLE_IPV6, }) ); diff --git a/src/server/utils/config.ts b/src/server/utils/config.ts index 5e93e51e..0a8e29fd 100644 --- a/src/server/utils/config.ts +++ b/src/server/utils/config.ts @@ -107,6 +107,17 @@ export const WG_GENERAL_OVERRIDE_ENV = { undefined, }; +export const WG_HOOKS_OVERRIDE_ENV = { + /** Override PreUp hook */ + PRE_UP: process.env.WG_PRE_UP, + /** Override PostUp hook */ + POST_UP: process.env.WG_POST_UP, + /** Override PreDown hook */ + PRE_DOWN: process.env.WG_PRE_DOWN, + /** Override PostDown hook */ + POST_DOWN: process.env.WG_POST_DOWN, +}; + function assertEnv(env: T) { const val = process.env[env]; @@ -163,3 +174,18 @@ export function applyGeneralOverrides< metricsJson: WG_GENERAL_OVERRIDE_ENV.METRICS_JSON ?? generalConfig.metricsJson, }; } + +/** + * Apply environment variable overrides to a hooks object + */ +export function applyHooksOverrides< + T extends { preUp: string; postUp: string; preDown: string; postDown: string }, +>(hooks: T): T { + return { + ...hooks, + preUp: WG_HOOKS_OVERRIDE_ENV.PRE_UP ?? hooks.preUp, + postUp: WG_HOOKS_OVERRIDE_ENV.POST_UP ?? hooks.postUp, + preDown: WG_HOOKS_OVERRIDE_ENV.PRE_DOWN ?? hooks.preDown, + postDown: WG_HOOKS_OVERRIDE_ENV.POST_DOWN ?? hooks.postDown, + }; +} From fbf24410dbd1ab8b7c49d139d6696552dcb511de Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Nov 2025 14:30:56 +0000 Subject: [PATCH 08/21] Add metrics password override and implement frontend UI indicators Backend changes: - Added WG_METRICS_PASSWORD environment variable override - Updated applyGeneralOverrides() to include metrics password - Updated /api/admin/overrides endpoint to include metrics password Frontend changes: - Added override indicators (warning icons with tooltips) to all form fields - Updated TextField, NumberField, NullTextField, SwitchField, HostField, ArrayField components - Added overridden prop support to all form components - Fetched /api/admin/overrides in all admin pages (interface, general, config, hooks) - Warning icon displays when field is overridden by environment variable - ArrayField shows banner when overridden - Updated documentation with WG_METRICS_PASSWORD Co-authored-by: kaaax0815 <32197462+kaaax0815@users.noreply.github.com> --- docs/content/advanced/config/optional-config.md | 1 + src/app/components/Form/ArrayField.vue | 10 +++++++++- src/app/components/Form/HostField.vue | 4 ++++ src/app/components/Form/NullTextField.vue | 4 ++++ src/app/components/Form/NumberField.vue | 10 +++++++++- src/app/components/Form/SwitchField.vue | 10 +++++++++- src/app/components/Form/TextField.vue | 4 ++++ src/app/pages/admin/config.vue | 17 ++++++++++++++++- src/app/pages/admin/general.vue | 11 +++++++++++ src/app/pages/admin/hooks.vue | 10 ++++++++++ src/app/pages/admin/interface.vue | 9 +++++++++ src/server/api/admin/overrides.get.ts | 1 + src/server/utils/config.ts | 5 ++++- 13 files changed, 91 insertions(+), 5 deletions(-) diff --git a/docs/content/advanced/config/optional-config.md b/docs/content/advanced/config/optional-config.md index 967e64a0..436703e1 100644 --- a/docs/content/advanced/config/optional-config.md +++ b/docs/content/advanced/config/optional-config.md @@ -51,6 +51,7 @@ These environment variables allow you to override settings that would normally b | Env | Example | Description | | ----------------------- | ----------------- | ------------------------- | | `WG_SESSION_TIMEOUT` | `3600` | Session timeout (seconds) | +| `WG_METRICS_PASSWORD` | `mypassword123` | Metrics endpoint password | | `WG_METRICS_PROMETHEUS` | `true` or `false` | Enable Prometheus metrics | | `WG_METRICS_JSON` | `true` or `false` | Enable JSON metrics | diff --git a/src/app/components/Form/ArrayField.vue b/src/app/components/Form/ArrayField.vue index 497949ca..745e2414 100644 --- a/src/app/components/Form/ArrayField.vue +++ b/src/app/components/Form/ArrayField.vue @@ -1,5 +1,9 @@ diff --git a/src/app/components/Form/TextField.vue b/src/app/components/Form/TextField.vue index e62de448..3c442f62 100644 --- a/src/app/components/Form/TextField.vue +++ b/src/app/components/Form/TextField.vue @@ -6,6 +6,9 @@ + + + (); const data = defineModel(); diff --git a/src/app/pages/admin/config.vue b/src/app/pages/admin/config.vue index 40146ae2..84e7e0e6 100644 --- a/src/app/pages/admin/config.vue +++ b/src/app/pages/admin/config.vue @@ -9,12 +9,14 @@ :label="$t('general.host')" :description="$t('admin.config.hostDesc')" url="/api/admin/ip-info" + :overridden="overrides.host" /> @@ -24,13 +26,18 @@ {{ $t('general.dns') }} - + {{ $t('form.sectionAdvanced') }} @@ -39,12 +46,14 @@ v-model="data.defaultMtu" :label="$t('general.mtu')" :description="$t('admin.config.mtuDesc')" + :overridden="overrides.defaultMtu" /> @@ -118,6 +127,12 @@ const { data: _data, refresh } = await useFetch(`/api/admin/userconfig`, { method: 'get', }); +const { data: overridesData } = await useFetch(`/api/admin/overrides`, { + method: 'get', +}); + +const overrides = computed(() => overridesData.value?.userConfig || {}); + const data = toRef(_data.value); const _submit = useSubmit( diff --git a/src/app/pages/admin/general.vue b/src/app/pages/admin/general.vue index 9515e456..49a57cd0 100644 --- a/src/app/pages/admin/general.vue +++ b/src/app/pages/admin/general.vue @@ -7,6 +7,7 @@ v-model="data.sessionTimeout" :label="$t('admin.general.sessionTimeout')" :description="$t('admin.general.sessionTimeoutDesc')" + :overridden="overrides.sessionTimeout" /> @@ -16,18 +17,21 @@ v-model="data.metricsPassword" :label="$t('admin.general.metricsPassword')" :description="$t('admin.general.metricsPasswordDesc')" + :overridden="overrides.metricsPassword" /> @@ -43,6 +47,13 @@ const { data: _data, refresh } = await useFetch(`/api/admin/general`, { method: 'get', }); + +const { data: overridesData } = await useFetch(`/api/admin/overrides`, { + method: 'get', +}); + +const overrides = computed(() => overridesData.value?.general || {}); + const data = toRef(_data.value); const _submit = useSubmit( diff --git a/src/app/pages/admin/hooks.vue b/src/app/pages/admin/hooks.vue index b8dda4b6..19d81bf4 100644 --- a/src/app/pages/admin/hooks.vue +++ b/src/app/pages/admin/hooks.vue @@ -6,21 +6,25 @@ id="PreUp" v-model="data.preUp" :label="$t('hooks.preUp')" + :overridden="overrides.preUp" /> @@ -37,6 +41,12 @@ const { data: _data, refresh } = await useFetch(`/api/admin/hooks`, { method: 'get', }); +const { data: overridesData } = await useFetch(`/api/admin/overrides`, { + method: 'get', +}); + +const overrides = computed(() => overridesData.value?.hooks || {}); + const data = toRef(_data.value); const _submit = useSubmit( diff --git a/src/app/pages/admin/interface.vue b/src/app/pages/admin/interface.vue index fd4d7eeb..6d5c56e4 100644 --- a/src/app/pages/admin/interface.vue +++ b/src/app/pages/admin/interface.vue @@ -7,18 +7,21 @@ v-model="data.mtu" :label="$t('general.mtu')" :description="$t('admin.interface.mtuDesc')" + :overridden="overrides.mtu" /> @@ -164,6 +167,12 @@ const { data: _data, refresh } = await useFetch(`/api/admin/interface`, { method: 'get', }); +const { data: overridesData } = await useFetch(`/api/admin/overrides`, { + method: 'get', +}); + +const overrides = computed(() => overridesData.value?.interface || {}); + const data = toRef(_data.value); const _submit = useSubmit( diff --git a/src/server/api/admin/overrides.get.ts b/src/server/api/admin/overrides.get.ts index ae3b2ef0..45e3bbce 100644 --- a/src/server/api/admin/overrides.get.ts +++ b/src/server/api/admin/overrides.get.ts @@ -17,6 +17,7 @@ export default definePermissionEventHandler('admin', 'any', async () => { }, general: { sessionTimeout: WG_GENERAL_OVERRIDE_ENV.SESSION_TIMEOUT !== undefined, + metricsPassword: WG_GENERAL_OVERRIDE_ENV.METRICS_PASSWORD !== undefined, metricsPrometheus: WG_GENERAL_OVERRIDE_ENV.METRICS_PROMETHEUS !== undefined, metricsJson: WG_GENERAL_OVERRIDE_ENV.METRICS_JSON !== undefined, }, diff --git a/src/server/utils/config.ts b/src/server/utils/config.ts index 0a8e29fd..0eb3bd1b 100644 --- a/src/server/utils/config.ts +++ b/src/server/utils/config.ts @@ -97,6 +97,8 @@ export const WG_GENERAL_OVERRIDE_ENV = { SESSION_TIMEOUT: process.env.WG_SESSION_TIMEOUT ? Number.parseInt(process.env.WG_SESSION_TIMEOUT, 10) : undefined, + /** Override metrics password */ + METRICS_PASSWORD: process.env.WG_METRICS_PASSWORD, /** Override metrics Prometheus enabled status */ METRICS_PROMETHEUS: process.env.WG_METRICS_PROMETHEUS === 'true' ? true : process.env.WG_METRICS_PROMETHEUS === 'false' ? false : @@ -165,11 +167,12 @@ export function applyUserConfigOverrides< * Apply environment variable overrides to a general config object */ export function applyGeneralOverrides< - T extends { sessionTimeout: number; metricsPrometheus: boolean; metricsJson: boolean }, + T extends { sessionTimeout: number; metricsPassword: string | null; metricsPrometheus: boolean; metricsJson: boolean }, >(generalConfig: T): T { return { ...generalConfig, sessionTimeout: WG_GENERAL_OVERRIDE_ENV.SESSION_TIMEOUT ?? generalConfig.sessionTimeout, + metricsPassword: WG_GENERAL_OVERRIDE_ENV.METRICS_PASSWORD ?? generalConfig.metricsPassword, metricsPrometheus: WG_GENERAL_OVERRIDE_ENV.METRICS_PROMETHEUS ?? generalConfig.metricsPrometheus, metricsJson: WG_GENERAL_OVERRIDE_ENV.METRICS_JSON ?? generalConfig.metricsJson, }; From 993c130f65b837b2ccc43d01e34d97854d5f376c Mon Sep 17 00:00:00 2001 From: Bernd Storath <999999bst@gmail.com> Date: Fri, 14 Nov 2025 15:41:48 +0100 Subject: [PATCH 09/21] format code --- src/app/components/Form/ArrayField.vue | 5 +- src/app/components/Form/HostField.vue | 7 +- src/app/components/Form/NullTextField.vue | 7 +- src/app/components/Form/NumberField.vue | 7 +- src/app/components/Form/SwitchField.vue | 7 +- src/app/components/Form/TextField.vue | 7 +- src/server/api/admin/overrides.get.ts | 9 ++- src/server/utils/WireGuard.ts | 12 +++- src/server/utils/config.ts | 78 +++++++++++++++++------ 9 files changed, 102 insertions(+), 37 deletions(-) diff --git a/src/app/components/Form/ArrayField.vue b/src/app/components/Form/ArrayField.vue index 745e2414..d7de0195 100644 --- a/src/app/components/Form/ArrayField.vue +++ b/src/app/components/Form/ArrayField.vue @@ -1,6 +1,9 @@