Browse Source

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 <[email protected]>
copilot/add-env-variables-admin-panel
copilot-swe-agent[bot] 7 months ago
parent
commit
fbf24410db
  1. 1
      docs/content/advanced/config/optional-config.md
  2. 10
      src/app/components/Form/ArrayField.vue
  3. 4
      src/app/components/Form/HostField.vue
  4. 4
      src/app/components/Form/NullTextField.vue
  5. 10
      src/app/components/Form/NumberField.vue
  6. 10
      src/app/components/Form/SwitchField.vue
  7. 4
      src/app/components/Form/TextField.vue
  8. 17
      src/app/pages/admin/config.vue
  9. 11
      src/app/pages/admin/general.vue
  10. 10
      src/app/pages/admin/hooks.vue
  11. 9
      src/app/pages/admin/interface.vue
  12. 1
      src/server/api/admin/overrides.get.ts
  13. 5
      src/server/utils/config.ts

1
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 |

10
src/app/components/Form/ArrayField.vue

@ -1,5 +1,9 @@
<template>
<div class="flex flex-col gap-2">
<div v-if="overridden" class="flex items-center gap-2 p-2 bg-amber-50 dark:bg-amber-900/20 rounded-lg text-amber-700 dark:text-amber-400 text-sm">
<IconsWarning class="size-4" />
<span>This field is overridden by an environment variable</span>
</div>
<div v-if="data?.length === 0">
{{ emptyText || $t('form.noItems') }}
</div>
@ -35,7 +39,11 @@
<script lang="ts" setup>
const data = defineModel<string[]>();
defineProps<{ emptyText?: string[]; name: string }>();
defineProps<{
emptyText?: string[];
name: string;
overridden?: boolean;
}>();
function update(e: Event, i: number) {
const v = (e.target as HTMLInputElement).value;

4
src/app/components/Form/HostField.vue

@ -6,6 +6,9 @@
<BaseTooltip v-if="description" :text="description">
<IconsInfo class="size-4" />
</BaseTooltip>
<BaseTooltip v-if="overridden" text="This field is overridden by an environment variable">
<IconsWarning class="size-4 ml-1 text-amber-500" />
</BaseTooltip>
</div>
<div class="flex gap-1">
<BaseInput
@ -38,6 +41,7 @@ defineProps<{
description?: string;
placeholder?: string;
url: '/api/admin/ip-info' | '/api/setup/4';
overridden?: boolean;
}>();
const data = defineModel<string | null>({

4
src/app/components/Form/NullTextField.vue

@ -6,6 +6,9 @@
<BaseTooltip v-if="description" :text="description">
<IconsInfo class="size-4" />
</BaseTooltip>
<BaseTooltip v-if="overridden" text="This field is overridden by an environment variable">
<IconsWarning class="size-4 ml-1 text-amber-500" />
</BaseTooltip>
</div>
<BaseInput
:id="id"
@ -24,6 +27,7 @@ defineProps<{
description?: string;
autocomplete?: string;
placeholder?: string;
overridden?: boolean;
}>();
const data = defineModel<string | null>({

10
src/app/components/Form/NumberField.vue

@ -6,12 +6,20 @@
<BaseTooltip v-if="description" :text="description">
<IconsInfo class="size-4" />
</BaseTooltip>
<BaseTooltip v-if="overridden" text="This field is overridden by an environment variable">
<IconsWarning class="size-4 ml-1 text-amber-500" />
</BaseTooltip>
</div>
<BaseInput :id="id" v-model.number="data" :name="id" type="number" />
</template>
<script lang="ts" setup>
defineProps<{ id: string; label: string; description?: string }>();
defineProps<{
id: string;
label: string;
description?: string;
overridden?: boolean;
}>();
const data = defineModel<number>();
</script>

10
src/app/components/Form/SwitchField.vue

@ -6,11 +6,19 @@
<BaseTooltip v-if="description" :text="description">
<IconsInfo class="size-4" />
</BaseTooltip>
<BaseTooltip v-if="overridden" text="This field is overridden by an environment variable">
<IconsWarning class="size-4 ml-1 text-amber-500" />
</BaseTooltip>
</div>
<BaseSwitch :id="id" v-model="data" />
</template>
<script lang="ts" setup>
defineProps<{ id: string; label: string; description?: string }>();
defineProps<{
id: string;
label: string;
description?: string;
overridden?: boolean;
}>();
const data = defineModel<boolean>();
</script>

4
src/app/components/Form/TextField.vue

@ -6,6 +6,9 @@
<BaseTooltip v-if="description" :text="description">
<IconsInfo class="size-4" />
</BaseTooltip>
<BaseTooltip v-if="overridden" text="This field is overridden by an environment variable">
<IconsWarning class="size-4 ml-1 text-amber-500" />
</BaseTooltip>
</div>
<BaseInput
:id="id"
@ -24,6 +27,7 @@ defineProps<{
description?: string;
autocomplete?: string;
disabled?: boolean;
overridden?: boolean;
}>();
const data = defineModel<string>();

17
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"
/>
<FormNumberField
id="port"
v-model="data.port"
:label="$t('general.port')"
:description="$t('admin.config.portDesc')"
:overridden="overrides.port"
/>
</FormGroup>
<FormGroup>
@ -24,13 +26,18 @@
<FormArrayField
v-model="data.defaultAllowedIps"
name="defaultAllowedIps"
:overridden="overrides.defaultAllowedIps"
/>
</FormGroup>
<FormGroup>
<FormHeading :description="$t('admin.config.dnsDesc')">
{{ $t('general.dns') }}
</FormHeading>
<FormArrayField v-model="data.defaultDns" name="defaultDns" />
<FormArrayField
v-model="data.defaultDns"
name="defaultDns"
:overridden="overrides.defaultDns"
/>
</FormGroup>
<FormGroup>
<FormHeading>{{ $t('form.sectionAdvanced') }}</FormHeading>
@ -39,12 +46,14 @@
v-model="data.defaultMtu"
:label="$t('general.mtu')"
:description="$t('admin.config.mtuDesc')"
:overridden="overrides.defaultMtu"
/>
<FormNumberField
id="defaultPersistentKeepalive"
v-model="data.defaultPersistentKeepalive"
:label="$t('general.persistentKeepalive')"
:description="$t('admin.config.persistentKeepaliveDesc')"
:overridden="overrides.defaultPersistentKeepalive"
/>
</FormGroup>
<FormGroup v-if="globalStore.information?.isAwg">
@ -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(

11
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"
/>
</FormGroup>
<FormGroup>
@ -16,18 +17,21 @@
v-model="data.metricsPassword"
:label="$t('admin.general.metricsPassword')"
:description="$t('admin.general.metricsPasswordDesc')"
:overridden="overrides.metricsPassword"
/>
<FormSwitchField
id="prometheus"
v-model="data.metricsPrometheus"
:label="$t('admin.general.prometheus')"
:description="$t('admin.general.prometheusDesc')"
:overridden="overrides.metricsPrometheus"
/>
<FormSwitchField
id="json"
v-model="data.metricsJson"
:label="$t('admin.general.json')"
:description="$t('admin.general.jsonDesc')"
:overridden="overrides.metricsJson"
/>
</FormGroup>
<FormGroup>
@ -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(

10
src/app/pages/admin/hooks.vue

@ -6,21 +6,25 @@
id="PreUp"
v-model="data.preUp"
:label="$t('hooks.preUp')"
:overridden="overrides.preUp"
/>
<FormTextField
id="PostUp"
v-model="data.postUp"
:label="$t('hooks.postUp')"
:overridden="overrides.postUp"
/>
<FormTextField
id="PreDown"
v-model="data.preDown"
:label="$t('hooks.preDown')"
:overridden="overrides.preDown"
/>
<FormTextField
id="PostDown"
v-model="data.postDown"
:label="$t('hooks.postDown')"
:overridden="overrides.postDown"
/>
</FormGroup>
<FormGroup>
@ -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(

9
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"
/>
<FormNumberField
id="port"
v-model="data.port"
:label="$t('general.port')"
:description="$t('admin.interface.portDesc')"
:overridden="overrides.port"
/>
<FormTextField
id="device"
v-model="data.device"
:label="$t('admin.interface.device')"
:description="$t('admin.interface.deviceDesc')"
:overridden="overrides.device"
/>
</FormGroup>
<FormGroup v-if="globalStore.information?.isAwg">
@ -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(

1
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,
},

5
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,
};

Loading…
Cancel
Save