Browse Source

be able to change interface and general

pull/1572/head
Bernd Storath 3 months ago
parent
commit
7e2277ac0a
No known key found for this signature in database GPG Key ID: D6C85685A555540F
  1. 54
      src/app/pages/admin/config.vue
  2. 62
      src/app/pages/admin/index.vue
  3. 71
      src/app/pages/admin/interface.vue
  4. 3
      src/i18n/locales/en.json
  5. 5
      src/nuxt.config.ts
  6. 8
      src/server/api/admin/general.post.ts
  7. 5
      src/server/api/admin/interface.get.ts
  8. 8
      src/server/api/admin/interface.post.ts
  9. 10
      src/server/utils/types.ts
  10. 24
      src/services/database/lowdb.ts
  11. 2
      src/services/database/migrations/1.ts
  12. 9
      src/services/database/repositories/system.ts

54
src/app/pages/admin/config.vue

@ -1,31 +1,33 @@
<template>
<main v-if="data">
<FormGroup>
<FormHeading>Connection</FormHeading>
<FormTextField id="host" v-model="data.host" label="Host" />
<FormNumberField id="port" v-model="data.port" label="Port" />
</FormGroup>
<FormGroup>
<FormHeading>Allowed IPs</FormHeading>
<FormArrayField v-model="data.allowedIps" name="allowedIps" />
</FormGroup>
<FormGroup>
<FormHeading>DNS</FormHeading>
<FormArrayField v-model="data.defaultDns" name="defaultDns" />
</FormGroup>
<FormGroup>
<FormHeading>Advanced</FormHeading>
<FormNumberField id="mtu" v-model="data.mtu" label="MTU" />
<FormNumberField
id="keepalive"
v-model="data.persistentKeepalive"
label="Persistent Keepalive"
/>
</FormGroup>
<FormGroup>
<FormHeading>Actions</FormHeading>
<FormActionField label="Revert!" @click="revert" />
</FormGroup>
<FormElement>
<FormGroup>
<FormHeading>Connection</FormHeading>
<FormTextField id="host" v-model="data.host" label="Host" />
<FormNumberField id="port" v-model="data.port" label="Port" />
</FormGroup>
<FormGroup>
<FormHeading>Allowed IPs</FormHeading>
<FormArrayField v-model="data.allowedIps" name="allowedIps" />
</FormGroup>
<FormGroup>
<FormHeading>DNS</FormHeading>
<FormArrayField v-model="data.defaultDns" name="defaultDns" />
</FormGroup>
<FormGroup>
<FormHeading>Advanced</FormHeading>
<FormNumberField id="mtu" v-model="data.mtu" label="MTU" />
<FormNumberField
id="keepalive"
v-model="data.persistentKeepalive"
label="Persistent Keepalive"
/>
</FormGroup>
<FormGroup>
<FormHeading>Actions</FormHeading>
<FormActionField label="Revert!" @click="revert" />
</FormGroup>
</FormElement>
</main>
</template>

62
src/app/pages/admin/index.vue

@ -1,9 +1,59 @@
<template>
<div>
<FormGroup>
<FormNumberField id="session" label="Session Timeout" />
</FormGroup>
</div>
<main v-if="data">
<FormElement @submit.prevent="submit">
<FormGroup>
<FormNumberField
id="session"
v-model="data.sessionTimeout"
label="Session Timeout"
/>
</FormGroup>
<FormGroup>
<FormHeading>Actions</FormHeading>
<FormActionField type="submit" label="Save" />
<FormActionField label="Revert" @click="revert" />
</FormGroup>
</FormElement>
</main>
</template>
<script setup lang="ts"></script>
<script setup lang="ts">
const toast = useToast();
const { data: _data, refresh } = await useFetch(`/api/admin/general`, {
method: 'get',
});
const data = toRef(_data.value);
async function submit() {
try {
const res = await $fetch(`/api/admin/general`, {
method: 'post',
body: data.value,
});
toast.showToast({
type: 'success',
title: 'Success',
message: 'Saved',
});
if (!res.success) {
throw new Error('Failed to save');
}
await refreshNuxtData();
} catch (e) {
if (e instanceof Error) {
toast.showToast({
type: 'error',
title: 'Error',
message: e.message,
});
}
}
}
async function revert() {
await refresh();
data.value = toRef(_data.value).value;
}
</script>

71
src/app/pages/admin/interface.vue

@ -1,19 +1,58 @@
<template>
<div>
<FormGroup>
<FormHeading>Interface Settings</FormHeading>
<FormNumberField id="mtu" label="MTU" />
<FormNumberField id="port" label="Port" />
<FormTextField id="device" label="Device" />
</FormGroup>
<FormGroup>
<FormHeading>Scripts</FormHeading>
<FormTextField id="mtu" label="PreUp" />
<FormTextField id="port" label="PostUp" />
<FormTextField id="device" label="PreDown" />
<FormTextField id="device" label="PostDown" />
</FormGroup>
</div>
<main v-if="data">
<FormElement @submit.prevent="submit">
<FormGroup>
<FormHeading>Interface Settings</FormHeading>
<FormNumberField id="mtu" v-model="data.mtu" label="MTU" />
<FormNumberField id="port" v-model="data.port" label="Port" />
<FormTextField id="device" v-model="data.device" label="Device" />
</FormGroup>
<FormGroup>
<FormHeading>Actions</FormHeading>
<FormActionField type="submit" label="Save" />
<FormActionField label="Revert" @click="revert" />
</FormGroup>
</FormElement>
</main>
</template>
<script setup lang="ts"></script>
<script setup lang="ts">
const toast = useToast();
const { data: _data, refresh } = await useFetch(`/api/admin/interface`, {
method: 'get',
});
const data = toRef(_data.value);
async function submit() {
try {
const res = await $fetch(`/api/admin/interface`, {
method: 'post',
body: data.value,
});
toast.showToast({
type: 'success',
title: 'Success',
message: 'Saved',
});
if (!res.success) {
throw new Error('Failed to save');
}
await refreshNuxtData();
} catch (e) {
if (e instanceof Error) {
toast.showToast({
type: 'error',
title: 'Error',
message: e.message,
});
}
}
}
async function revert() {
await refresh();
data.value = toRef(_data.value).value;
}
</script>

3
src/i18n/locales/en.json

@ -81,7 +81,8 @@
"hostMin": "Host must contain at least 1 character",
"port": "Port must be a valid number",
"portMin": "Port must be at least 1",
"portMax": "Port must be at most 65535"
"portMax": "Port must be at most 65535",
"sessionTimeout": "Session Timeout must be a valid number"
},
"name": "Name",
"username": "Username",

5
src/nuxt.config.ts

@ -29,11 +29,6 @@ export default defineNuxtConfig({
language: 'en-US',
name: 'English',
},
{
code: 'de',
language: 'de-DE',
name: 'Deutsch',
},
],
defaultLocale: 'en',
vueI18n: './i18n.config.ts',

8
src/server/api/admin/general.post.ts

@ -0,0 +1,8 @@
export default defineEventHandler(async (event) => {
const data = await readValidatedBody(
event,
validateZod(generalUpdateType, event)
);
await Database.system.updateGeneral(data);
return { success: true };
});

5
src/server/api/admin/interface.get.ts

@ -0,0 +1,5 @@
export default defineEventHandler(async () => {
const system = await Database.system.get();
// TODO: handle through wireguard to update conf accordingly
return system.interface;
});

8
src/server/api/admin/interface.post.ts

@ -0,0 +1,8 @@
export default defineEventHandler(async (event) => {
const data = await readValidatedBody(
event,
validateZod(interfaceUpdateType, event)
);
await Database.system.updateInterface(data);
return { success: true };
});

10
src/server/utils/types.ts

@ -157,6 +157,16 @@ export const clientUpdateType = z.object({
persistentKeepalive: z.number({ message: 'zod.persistentKeepalive' }),
});
export const generalUpdateType = z.object({
sessionTimeout: z.number({ message: 'zod.sessionTimeout' }),
});
export const interfaceUpdateType = z.object({
mtu: z.number({ message: 'zod.mtu' }),
port: port,
device: z.string({ message: 'zod.device' }),
});
// from https://github.com/airjp73/rvf/blob/7e7c35d98015ea5ecff5affaf89f78296e84e8b9/packages/zod-form-data/src/helpers.ts#L117
type FormDataLikeInput = {
[Symbol.iterator](): IterableIterator<[string, FormDataEntryValue]>;

24
src/services/database/lowdb.ts

@ -19,7 +19,11 @@ import {
type CreateClient,
type OneTimeLink,
} from './repositories/client';
import { SystemRepository } from './repositories/system';
import {
SystemRepository,
type General,
type UpdateWGInterface,
} from './repositories/system';
import { SetupRepository, type Steps } from './repositories/setup';
import type { DeepReadonly } from 'vue';
@ -81,6 +85,24 @@ class LowDBSystem extends SystemRepository {
v.system.userConfig.port = port;
});
}
async updateGeneral(general: General) {
DEBUG('Update General');
this.#db.update((v) => {
v.system.general = general;
});
}
async updateInterface(wgInterface: UpdateWGInterface) {
DEBUG('Update Interface');
this.#db.update((v) => {
const oldInterface = v.system.interface;
v.system.interface = {
...oldInterface,
...wgInterface,
};
});
}
}
class LowDBUser extends UserRepository {

2
src/services/database/migrations/1.ts

@ -64,6 +64,8 @@ export async function run1(db: Low<Database>) {
clients: {},
};
// TODO: be able to regenerate this on changes
database.system.iptables.PostUp =
`iptables -t nat -A POSTROUTING -s ${database.system.userConfig.address4Range} -o ${database.system.interface.device} -j MASQUERADE;
iptables -A INPUT -p udp -m udp --dport ${database.system.interface.port} -j ACCEPT;

9
src/services/database/repositories/system.ts

@ -63,6 +63,11 @@ export type System = {
sessionConfig: SessionConfig;
};
export type UpdateWGInterface = Omit<
WGInterface,
'privateKey' | 'publicKey' | 'address4' | 'address6'
>;
/**
* Interface for system-related database operations.
* This interface provides methods for retrieving system configuration data
@ -72,4 +77,8 @@ export abstract class SystemRepository {
abstract get(): Promise<DeepReadonly<System>>;
abstract updateClientsHostPort(host: string, port: number): Promise<void>;
abstract updateGeneral(general: General): Promise<void>;
abstract updateInterface(wgInterface: UpdateWGInterface): Promise<void>;
}

Loading…
Cancel
Save