Browse Source

improve client edit page, add mtu

pull/1397/head
Bernd Storath 5 months ago
parent
commit
23b4bd85c8
  1. 16
      src/app/components/base/Switch.vue
  2. 11
      src/app/components/form/ActionField.vue
  3. 38
      src/app/components/form/ArrayField.vue
  4. 9
      src/app/components/form/Group.vue
  5. 5
      src/app/components/form/Heading.vue
  6. 17
      src/app/components/form/NumberField.vue
  7. 11
      src/app/components/form/SwitchField.vue
  8. 17
      src/app/components/form/TextField.vue
  9. 2
      src/app/components/ui/UserMenu.vue
  10. 131
      src/app/pages/clients/[id].vue
  11. 1
      src/server/api/setup/migrate.post.ts
  12. 1
      src/server/utils/WireGuard.ts
  13. 3
      src/server/utils/ip.ts
  14. 2
      src/server/utils/wgHelper.ts
  15. 1
      src/services/database/repositories/client.ts

16
src/app/components/base/Switch.vue

@ -0,0 +1,16 @@
<template>
<SwitchRoot
:id="id"
v-model:checked="data"
class="w-[40px] h-[24px] focus-within:outline focus-within:outline-red-700 flex bg-gray-200 dark:bg-neutral-400 shadow-sm rounded-full relative data-[state=checked]:bg-red-800 cursor-default"
>
<SwitchThumb
class="block w-[16px] h-[16px] my-auto bg-white shadow-sm rounded-full transition-transform duration-100 translate-x-1 will-change-transform data-[state=checked]:translate-x-[20px]"
/>
</SwitchRoot>
</template>
<script lang="ts" setup>
defineProps<{ id: string }>();
const data = defineModel<boolean>();
</script>

11
src/app/components/form/ActionField.vue

@ -0,0 +1,11 @@
<template>
<input
:value="label"
type="button"
class="dark:bg-neutral-700 text-gray-500 col-span-2 py-2 dark:text-neutral-200 border-2 border-gray-100 dark:border-neutral-800 rounded-lg focus:border-red-800 dark:placeholder:text-neutral-400 focus:outline-0 focus:ring-0"
/>
</template>
<script lang="ts" setup>
defineProps<{ label: string }>();
</script>

38
src/app/components/form/ArrayField.vue

@ -0,0 +1,38 @@
<template>
<div class="flex flex-col">
<div v-for="(item, i) in data" :key="item">
<input
:value="item"
type="text"
class="dark:bg-neutral-700 text-gray-500 dark:text-neutral-200 border-2 border-gray-100 dark:border-neutral-800 rounded-lg focus:border-red-800 dark:placeholder:text-neutral-400 focus:outline-0 focus:ring-0"
@input="update(i)"
/>
<input type="button" value="-" @click="del(i)" />
</div>
<input type="button" value="Add" @click="add" />
</div>
</template>
<script lang="ts" setup>
const data = defineModel<string[]>();
function update(i: number) {
return (v: string) => {
if (!data.value) {
return;
}
data.value[i] = v;
};
}
function add() {
data.value?.push('');
}
function del(i: number) {
if (!data.value) {
return;
}
data.value.splice(i, 1);
}
</script>

9
src/app/components/form/Group.vue

@ -0,0 +1,9 @@
<template>
<section class="grid grid-cols-1 gap-4 md:grid-cols-2">
<slot />
<Separator
decorative
class="w-full h-px bg-gray-100 dark:bg-neutral-600 col-span-2"
/>
</section>
</template>

5
src/app/components/form/Heading.vue

@ -0,0 +1,5 @@
<template>
<h4 class="text-2xl col-span-full py-6">
<slot />
</h4>
</template>

17
src/app/components/form/NumberField.vue

@ -0,0 +1,17 @@
<template>
<Label :for="id" class="font-semibold md:align-middle md:leading-10">
{{ label }}
</Label>
<input
:id="id"
v-model.number="data"
type="number"
class="dark:bg-neutral-700 text-gray-500 dark:text-neutral-200 border-2 border-gray-100 dark:border-neutral-800 rounded-lg focus:border-red-800 dark:placeholder:text-neutral-400 focus:outline-0 focus:ring-0"
/>
</template>
<script lang="ts" setup>
defineProps<{ id: string; label: string }>();
const data = defineModel<number>();
</script>

11
src/app/components/form/SwitchField.vue

@ -0,0 +1,11 @@
<template>
<Label :for="id" class="font-semibold md:align-middle md:leading-10">
{{ label }}
</Label>
<BaseSwitch :id="id" v-model="data" />
</template>
<script lang="ts" setup>
defineProps<{ id: string; label: string }>();
const data = defineModel<boolean>();
</script>

17
src/app/components/form/TextField.vue

@ -0,0 +1,17 @@
<template>
<Label :for="id" class="font-semibold md:align-middle md:leading-10">
{{ label }}
</Label>
<input
:id="id"
v-model="data"
type="text"
class="dark:bg-neutral-700 text-gray-500 dark:text-neutral-200 border-2 border-gray-100 dark:border-neutral-800 rounded-lg focus:border-red-800 dark:placeholder:text-neutral-400 focus:outline-0 focus:ring-0"
/>
</template>
<script lang="ts" setup>
defineProps<{ id: string; label: string }>();
const data = defineModel<string>();
</script>

2
src/app/components/ui/UserMenu.vue

@ -9,7 +9,7 @@
class="inline-flex h-8 w-8 select-none items-center justify-center overflow-hidden rounded-full align-middle mr-2"
>
<AvatarFallback
class="text-grass11 leading-1 flex h-full w-full items-center justify-center bg-white text-[15px] font-medium"
class="leading-1 flex h-full w-full items-center justify-center bg-white text-[15px] font-medium"
:delay-ms="600"
>
{{ fallbackName }}

131
src/app/pages/clients/[id].vue

@ -5,108 +5,57 @@
<PanelHeadTitle :text="data.name" />
</PanelHead>
<PanelBody>
<section class="grid grid-cols-1 gap-4 md:grid-cols-2">
<h4 class="text-2xl col-span-full py-6">
<FormGroup>
<FormHeading>
{{ $t('me.sectionGeneral') }}
</h4>
<Label for="name" class="font-semibold md:align-middle md:leading-10">
{{ 'Name' }}
</Label>
<input
id="name"
v-model.trim="data.name"
type="text"
class="dark:bg-neutral-700 text-gray-500 dark:text-neutral-200 border-2 border-gray-100 dark:border-neutral-800 rounded-lg focus:border-red-800 dark:placeholder:text-neutral-400 focus:outline-0 focus:ring-0"
/>
<Label
for="enabled"
class="font-semibold md:align-middle md:leading-10"
>
{{ 'Enabled' }}
</Label>
<input
</FormHeading>
<FormTextField id="name" v-model.trim="data.name" label="Name" />
<FormSwitchField
id="enabled"
v-model.trim="data.enabled"
type="checkbox"
class="dark:bg-neutral-700 text-gray-500 dark:text-neutral-200 border-2 border-gray-100 dark:border-neutral-800 rounded-lg focus:border-red-800 dark:placeholder:text-neutral-400 focus:outline-0 focus:ring-0"
/>
</section>
<section class="grid grid-cols-1 gap-4 md:grid-cols-2">
<h4 class="text-2xl col-span-full py-6">
{{ 'Address' }}
</h4>
<Label for="ipv4" class="font-semibold md:align-middle md:leading-10">
{{ 'IPv4' }}
</Label>
<input
id="ipv4"
v-model.trim="data.address4"
type="text"
class="dark:bg-neutral-700 text-gray-500 dark:text-neutral-200 border-2 border-gray-100 dark:border-neutral-800 rounded-lg focus:border-red-800 dark:placeholder:text-neutral-400 focus:outline-0 focus:ring-0"
/>
<Label for="ipv6" class="font-semibold md:align-middle md:leading-10">
{{ 'IPv6' }}
</Label>
<input
id="ipv6"
v-model.trim="data.address6"
type="text"
class="dark:bg-neutral-700 text-gray-500 dark:text-neutral-200 border-2 border-gray-100 dark:border-neutral-800 rounded-lg focus:border-red-800 dark:placeholder:text-neutral-400 focus:outline-0 focus:ring-0"
v-model="data.enabled"
label="Enabled"
/>
</section>
<section class="grid grid-cols-1 gap-4 md:grid-cols-2">
<h4 class="text-2xl col-span-full py-6">
{{ 'Advanced' }}
</h4>
<Label
for="keepalive"
class="font-semibold md:align-middle md:leading-10"
>
{{ 'Persistent Keepalive' }}
</Label>
<input
</FormGroup>
<FormGroup>
<FormHeading>Address</FormHeading>
<FormTextField id="ipv4" v-model.trim="data.address4" label="IPv4" />
<FormTextField id="ipv6" v-model.trim="data.address6" label="IPv6" />
</FormGroup>
<FormGroup>
<FormHeading>Allowed IPs</FormHeading>
<FormArrayField v-model="data.allowedIPs" />
</FormGroup>
<FormGroup>
<FormHeading>Advanced</FormHeading>
<FormNumberField id="mtu" v-model="data.mtu" label="MTU" />
<FormNumberField
id="keepalive"
v-model.number="data.persistentKeepalive"
type="number"
class="dark:bg-neutral-700 text-gray-500 dark:text-neutral-200 border-2 border-gray-100 dark:border-neutral-800 rounded-lg focus:border-red-800 dark:placeholder:text-neutral-400 focus:outline-0 focus:ring-0"
v-model="data.persistentKeepalive"
label="Persistent Keepalive"
/>
</section>
<section class="grid grid-cols-1 gap-4 md:grid-cols-2">
<h4 class="text-2xl col-span-full py-6">
{{ 'Action' }}
</h4>
<Label
for="rotateprivkey"
class="font-semibold md:align-middle md:leading-10"
>
{{ 'Rotate Private Key' }}
</Label>
<input
id="rotateprivkey"
value="Do !"
type="button"
class="dark:bg-neutral-700 text-gray-500 dark:text-neutral-200 border-2 border-gray-100 dark:border-neutral-800 rounded-lg focus:border-red-800 dark:placeholder:text-neutral-400 focus:outline-0 focus:ring-0"
/>
<Label
for="delete"
class="font-semibold md:align-middle md:leading-10"
>
{{ 'Delete' }}
</Label>
<input
id="delete"
value="Do !"
type="button"
class="dark:bg-neutral-700 text-gray-500 dark:text-neutral-200 border-2 border-gray-100 dark:border-neutral-800 rounded-lg focus:border-red-800 dark:placeholder:text-neutral-400 focus:outline-0 focus:ring-0"
/>
</section>
</FormGroup>
<FormGroup>
<FormHeading>Actions</FormHeading>
<FormActionField label="Delete!" />
<FormActionField label="Revert!" @click="revert" />
</FormGroup>
</PanelBody>
</Panel>
</main>
</template>
<script lang="ts" setup>
const authStore = useAuthStore();
authStore.update();
const route = useRoute();
const id = route.params.id as string;
const { data } = await useFetch(`/api/client/${id}`, { method: 'get' });
const { data: _data, refresh } = await useFetch(`/api/client/${id}`, {
method: 'get',
});
const data = toRef(_data.value);
async function revert() {
await refresh();
data.value = toRef(_data.value).value;
}
</script>

1
src/server/api/setup/migrate.post.ts

@ -78,6 +78,7 @@ export default defineEventHandler(async (event) => {
serverAllowedIPs: [],
persistentKeepalive: 0,
address6: address6,
mtu: 1420,
});
}

1
src/server/utils/WireGuard.ts

@ -152,6 +152,7 @@ class WireGuard {
allowedIPs: [...system.userConfig.allowedIps],
serverAllowedIPs: null,
persistentKeepalive: system.userConfig.persistentKeepalive,
mtu: system.userConfig.mtu,
};
if (expireDate) {

3
src/server/utils/ip.ts

@ -17,6 +17,9 @@ export function nextIPv6(
return nextIP(6, system, clients);
}
// TODO: above functions should probably have a lock
// TODO(general): what happens if multiple users create client at the same time?
function nextIP(
version: 4 | 6,
system: DeepReadonly<Database['system']>,

2
src/server/utils/wgHelper.ts

@ -48,7 +48,7 @@ PostDown = ${system.iptables.PostDown}`;
PrivateKey = ${client.privateKey}
Address = ${client.address4}/${cidr4Block}, ${client.address6}/${cidr6Block}
DNS = ${system.userConfig.defaultDns.join(', ')}
MTU = ${system.userConfig.mtu}
MTU = ${client.mtu}
[Peer]
PublicKey = ${system.interface.publicKey}

1
src/services/database/repositories/client.ts

@ -25,6 +25,7 @@ export type Client = {
updatedAt: string;
enabled: boolean;
persistentKeepalive: number;
mtu: number;
};
export type NewClient = Omit<Client, 'createdAt' | 'updatedAt'>;

Loading…
Cancel
Save