mirror of https://github.com/wg-easy/wg-easy
37 changed files with 295 additions and 383 deletions
@ -1,60 +0,0 @@ |
|||||
<template> |
|
||||
<span class="group"> |
|
||||
<!-- Show --> |
|
||||
<input |
|
||||
v-show="clientEditAddress4Id === client.id" |
|
||||
ref="clientAddress4Input" |
|
||||
v-model="clientEditAddress4" |
|
||||
class="rounded border-2 dark:bg-neutral-700 border-gray-100 dark:border-neutral-600 focus:border-gray-200 dark:focus:border-neutral-500 outline-none w-20 text-black dark:text-neutral-300 dark:placeholder:text-neutral-500" |
|
||||
@keyup.enter=" |
|
||||
updateClientAddress4(client, clientEditAddress4); |
|
||||
clientEditAddress4 = null; |
|
||||
clientEditAddress4Id = null; |
|
||||
" |
|
||||
@keyup.escape=" |
|
||||
clientEditAddress4 = null; |
|
||||
clientEditAddress4Id = null; |
|
||||
" |
|
||||
/> |
|
||||
<span v-show="clientEditAddress4Id !== client.id" class="inline-block">{{ |
|
||||
client.address4 |
|
||||
}}</span> |
|
||||
|
|
||||
<!-- Edit --> |
|
||||
<span |
|
||||
v-show="clientEditAddress4Id !== client.id" |
|
||||
class="cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity" |
|
||||
@click=" |
|
||||
clientEditAddress4 = client.address4; |
|
||||
clientEditAddress4Id = client.id; |
|
||||
nextTick(() => clientAddress4Input?.select()); |
|
||||
" |
|
||||
> |
|
||||
<IconsEdit |
|
||||
class="h-4 w-4 inline align-middle opacity-25 hover:opacity-100" |
|
||||
/> |
|
||||
</span> |
|
||||
</span> |
|
||||
</template> |
|
||||
|
|
||||
<script setup lang="ts"> |
|
||||
defineProps<{ |
|
||||
client: LocalClient; |
|
||||
}>(); |
|
||||
|
|
||||
const clientsStore = useClientsStore(); |
|
||||
|
|
||||
const clientAddress4Input = ref<HTMLInputElement | null>(null); |
|
||||
const clientEditAddress4 = ref<null | string>(null); |
|
||||
const clientEditAddress4Id = ref<null | string>(null); |
|
||||
|
|
||||
function updateClientAddress4(client: WGClient, address4: string | null) { |
|
||||
if (address4 === null) { |
|
||||
return; |
|
||||
} |
|
||||
api |
|
||||
.updateClientAddress4({ clientId: client.id, address4 }) |
|
||||
.catch((err) => alert(err.message || err.toString())) |
|
||||
.finally(() => clientsStore.refresh().catch(console.error)); |
|
||||
} |
|
||||
</script> |
|
@ -1,17 +0,0 @@ |
|||||
<template> |
|
||||
<button |
|
||||
class="align-middle bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 hover:bg-red-800 dark:hover:bg-red-800 hover:text-white dark:hover:text-white p-2 rounded transition" |
|
||||
:title="$t('deleteClient')" |
|
||||
@click="modalStore.clientDelete = client" |
|
||||
> |
|
||||
<IconsDelete class="w-5" /> |
|
||||
</button> |
|
||||
</template> |
|
||||
|
|
||||
<script setup lang="ts"> |
|
||||
defineProps<{ |
|
||||
client: LocalClient; |
|
||||
}>(); |
|
||||
|
|
||||
const modalStore = useModalStore(); |
|
||||
</script> |
|
@ -1,91 +0,0 @@ |
|||||
<template> |
|
||||
<div |
|
||||
v-show="globalStore.features.clientExpiration.enabled" |
|
||||
class="block md:inline-block pb-1 md:pb-0 text-gray-500 dark:text-neutral-400 text-xs" |
|
||||
> |
|
||||
<span class="group"> |
|
||||
<!-- Show --> |
|
||||
<input |
|
||||
v-show="clientEditExpireDateId === client.id" |
|
||||
ref="clientExpireDateInput" |
|
||||
v-model="clientEditExpireDate" |
|
||||
type="text" |
|
||||
class="rounded border-2 dark:bg-neutral-700 border-gray-100 dark:border-neutral-600 focus:border-gray-200 dark:focus:border-neutral-500 outline-none w-70 text-black dark:text-neutral-300 dark:placeholder:text-neutral-500 text-xs p-0" |
|
||||
@keyup.enter=" |
|
||||
updateClientExpireDate(client, clientEditExpireDate); |
|
||||
clientEditExpireDate = null; |
|
||||
clientEditExpireDateId = null; |
|
||||
" |
|
||||
@keyup.escape=" |
|
||||
clientEditExpireDate = null; |
|
||||
clientEditExpireDateId = null; |
|
||||
" |
|
||||
/> |
|
||||
<span |
|
||||
v-show="clientEditExpireDateId !== client.id" |
|
||||
class="inline-block" |
|
||||
>{{ expiredDateFormat(client.expiresAt) }}</span |
|
||||
> |
|
||||
|
|
||||
<!-- Edit --> |
|
||||
<span |
|
||||
v-show="clientEditExpireDateId !== client.id" |
|
||||
class="cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity" |
|
||||
@click=" |
|
||||
clientEditExpireDate = client.expiresAt |
|
||||
? client.expiresAt.slice(0, 10) |
|
||||
: 'yyyy-mm-dd'; |
|
||||
clientEditExpireDateId = client.id; |
|
||||
nextTick(() => clientExpireDateInput?.select()); |
|
||||
" |
|
||||
> |
|
||||
<svg |
|
||||
xmlns="http://www.w3.org/2000/svg" |
|
||||
class="h-4 w-4 inline align-middle opacity-25 hover:opacity-100" |
|
||||
fill="none" |
|
||||
viewBox="0 0 24 24" |
|
||||
stroke="currentColor" |
|
||||
> |
|
||||
<path |
|
||||
stroke-linecap="round" |
|
||||
stroke-linejoin="round" |
|
||||
stroke-width="2" |
|
||||
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" |
|
||||
/> |
|
||||
</svg> |
|
||||
</span> |
|
||||
</span> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script setup lang="ts"> |
|
||||
defineProps<{ client: LocalClient }>(); |
|
||||
|
|
||||
const globalStore = useGlobalStore(); |
|
||||
const clientsStore = useClientsStore(); |
|
||||
const clientEditExpireDate = ref<string | null>(null); |
|
||||
const clientEditExpireDateId = ref<string | null>(null); |
|
||||
const { t, locale } = useI18n(); |
|
||||
|
|
||||
const clientExpireDateInput = ref<HTMLInputElement | null>(null); |
|
||||
|
|
||||
function updateClientExpireDate( |
|
||||
client: LocalClient, |
|
||||
expireDate: string | null |
|
||||
) { |
|
||||
api |
|
||||
.updateClientExpireDate({ clientId: client.id, expireDate }) |
|
||||
.catch((err) => alert(err.message || err.toString())) |
|
||||
.finally(() => clientsStore.refresh().catch(console.error)); |
|
||||
} |
|
||||
|
|
||||
function expiredDateFormat(value: string | null) { |
|
||||
if (value === null) return t('Permanent'); |
|
||||
const dateTime = new Date(value); |
|
||||
return dateTime.toLocaleDateString(locale.value, { |
|
||||
year: 'numeric', |
|
||||
month: 'long', |
|
||||
day: 'numeric', |
|
||||
}); |
|
||||
} |
|
||||
</script> |
|
@ -1,65 +0,0 @@ |
|||||
<template> |
|
||||
<div |
|
||||
class="text-gray-700 dark:text-neutral-200 group text-sm md:text-base" |
|
||||
:title="$t('createdOn') + dateTime(new Date(client.createdAt))" |
|
||||
> |
|
||||
<!-- Show --> |
|
||||
<input |
|
||||
v-show="clientEditNameId === client.id" |
|
||||
ref="clientNameInput" |
|
||||
v-model="clientEditName" |
|
||||
class="rounded px-1 border-2 dark:bg-neutral-700 border-gray-100 dark:border-neutral-600 focus:border-gray-200 dark:focus:border-neutral-500 dark:placeholder:text-neutral-500 outline-none w-30" |
|
||||
@keyup.enter=" |
|
||||
updateClientName(client, clientEditName); |
|
||||
clientEditName = null; |
|
||||
clientEditNameId = null; |
|
||||
" |
|
||||
@keyup.escape=" |
|
||||
clientEditName = null; |
|
||||
clientEditNameId = null; |
|
||||
" |
|
||||
/> |
|
||||
<span |
|
||||
v-show="clientEditNameId !== client.id" |
|
||||
class="border-t-2 border-b-2 border-transparent" |
|
||||
>{{ client.name }}</span |
|
||||
> |
|
||||
|
|
||||
<!-- Edit --> |
|
||||
<span |
|
||||
v-show="clientEditNameId !== client.id" |
|
||||
class="cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity" |
|
||||
@click=" |
|
||||
clientEditName = client.name; |
|
||||
clientEditNameId = client.id; |
|
||||
nextTick(() => clientNameInput?.select()); |
|
||||
" |
|
||||
> |
|
||||
<IconsEdit |
|
||||
class="h-4 w-4 inline align-middle opacity-25 hover:opacity-100" |
|
||||
/> |
|
||||
</span> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script setup lang="ts"> |
|
||||
defineProps<{ |
|
||||
client: LocalClient; |
|
||||
}>(); |
|
||||
|
|
||||
const clientsStore = useClientsStore(); |
|
||||
|
|
||||
const clientNameInput = ref<HTMLInputElement | null>(null); |
|
||||
const clientEditName = ref<null | string>(null); |
|
||||
const clientEditNameId = ref<null | string>(null); |
|
||||
|
|
||||
function updateClientName(client: LocalClient, name: string | null) { |
|
||||
if (name === null) { |
|
||||
return; |
|
||||
} |
|
||||
api |
|
||||
.updateClientName({ clientId: client.id, name }) |
|
||||
.catch((err) => alert(err.message || err.toString())) |
|
||||
.finally(() => clientsStore.refresh().catch(console.error)); |
|
||||
} |
|
||||
</script> |
|
@ -0,0 +1,11 @@ |
|||||
|
<template> |
||||
|
<span class="inline-block"> |
||||
|
{{ client.address4 }} |
||||
|
</span> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
defineProps<{ |
||||
|
client: LocalClient; |
||||
|
}>(); |
||||
|
</script> |
@ -1,7 +1,7 @@ |
|||||
<template> |
<template> |
||||
<a |
<a |
||||
:disabled="!client.downloadableConfig" |
:disabled="!client.downloadableConfig" |
||||
:href="'./api/wireguard/client/' + client.id + '/configuration'" |
:href="'./api/client/' + client.id + '/configuration'" |
||||
:download="client.downloadableConfig ? 'configuration' : null" |
:download="client.downloadableConfig ? 'configuration' : null" |
||||
class="align-middle inline-block bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 p-2 rounded transition" |
class="align-middle inline-block bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 p-2 rounded transition" |
||||
:class="{ |
:class="{ |
@ -0,0 +1,14 @@ |
|||||
|
<template> |
||||
|
<NuxtLink |
||||
|
class="align-middle bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 p-2 rounded transition hover:bg-red-800 dark:hover:bg-red-800 hover:text-white dark:hover:text-white" |
||||
|
:to="`/clients/${client.id}`" |
||||
|
> |
||||
|
<IconsEdit class="w-5" /> |
||||
|
</NuxtLink> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
defineProps<{ |
||||
|
client: LocalClient; |
||||
|
}>(); |
||||
|
</script> |
@ -0,0 +1,25 @@ |
|||||
|
<template> |
||||
|
<div |
||||
|
v-show="globalStore.features.clientExpiration.enabled" |
||||
|
class="block md:inline-block pb-1 md:pb-0 text-gray-500 dark:text-neutral-400 text-xs" |
||||
|
> |
||||
|
<span class="inline-block">{{ expiredDateFormat(client.expiresAt) }}</span> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
defineProps<{ client: LocalClient }>(); |
||||
|
|
||||
|
const globalStore = useGlobalStore(); |
||||
|
const { t, locale } = useI18n(); |
||||
|
|
||||
|
function expiredDateFormat(value: string | null) { |
||||
|
if (value === null) return t('Permanent'); |
||||
|
const dateTime = new Date(value); |
||||
|
return dateTime.toLocaleDateString(locale.value, { |
||||
|
year: 'numeric', |
||||
|
month: 'long', |
||||
|
day: 'numeric', |
||||
|
}); |
||||
|
} |
||||
|
</script> |
@ -0,0 +1,16 @@ |
|||||
|
<template> |
||||
|
<div |
||||
|
class="text-gray-700 dark:text-neutral-200 group text-sm md:text-base" |
||||
|
:title="$t('createdOn') + dateTime(new Date(client.createdAt))" |
||||
|
> |
||||
|
<span class="border-t-2 border-b-2 border-transparent"> |
||||
|
{{ client.name }} |
||||
|
</span> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
defineProps<{ |
||||
|
client: LocalClient; |
||||
|
}>(); |
||||
|
</script> |
@ -0,0 +1,112 @@ |
|||||
|
<template> |
||||
|
<main v-if="data"> |
||||
|
<Panel> |
||||
|
<PanelHead> |
||||
|
<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"> |
||||
|
{{ $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 |
||||
|
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" |
||||
|
/> |
||||
|
</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 |
||||
|
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" |
||||
|
/> |
||||
|
</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> |
||||
|
</PanelBody> |
||||
|
</Panel> |
||||
|
</main> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
const route = useRoute(); |
||||
|
const id = route.params.id as string; |
||||
|
const { data } = await useFetch(`/api/client/${id}`, { method: 'get' }); |
||||
|
</script> |
@ -1,50 +0,0 @@ |
|||||
import { parseCidr } from 'cidr-tools'; |
|
||||
import { stringifyIp } from 'ip-bigint'; |
|
||||
import { z } from 'zod'; |
|
||||
|
|
||||
// TODO: check what are missing
|
|
||||
const clientSchema = z.object({ |
|
||||
id: z.string(), |
|
||||
name: z.string(), |
|
||||
address: z.string(), |
|
||||
privateKey: z.string(), |
|
||||
publicKey: z.string(), |
|
||||
preSharedKey: z.string(), |
|
||||
createdAt: z.string(), |
|
||||
updatedAt: z.string(), |
|
||||
enabled: z.boolean(), |
|
||||
}); |
|
||||
|
|
||||
const oldConfigSchema = z.object({ |
|
||||
server: z.object({ |
|
||||
privateKey: z.string(), |
|
||||
publicKey: z.string(), |
|
||||
address: z.string(), |
|
||||
}), |
|
||||
clients: z.record(z.string(), clientSchema), |
|
||||
}); |
|
||||
|
|
||||
export default defineEventHandler(async (event) => { |
|
||||
const { file } = await readValidatedBody(event, validateZod(fileType, event)); |
|
||||
const file_ = await oldConfigSchema.parseAsync(JSON.parse(file)); |
|
||||
|
|
||||
for (const [_, value] of Object.entries(file_.clients)) { |
|
||||
// remove the unused field
|
|
||||
const { address: _, ...filterValue } = value; |
|
||||
const cidr4 = parseCidr(value.address); |
|
||||
const address4 = stringifyIp({ number: cidr4.start + 0n, version: 4 }); |
|
||||
|
|
||||
await Database.client.create({ |
|
||||
...filterValue, |
|
||||
address4, |
|
||||
address6: '', |
|
||||
expiresAt: null, |
|
||||
allowedIPs: ['0.0.0.0/0', '::/0'], |
|
||||
oneTimeLink: null, |
|
||||
serverAllowedIPs: [], |
|
||||
persistentKeepalive: 0, |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
return { success: true }; |
|
||||
}); |
|
@ -0,0 +1,7 @@ |
|||||
|
export default defineEventHandler(async (event) => { |
||||
|
const { clientId } = await getValidatedRouterParams( |
||||
|
event, |
||||
|
validateZod(clientIdType) |
||||
|
); |
||||
|
return WireGuard.getClient({ clientId }); |
||||
|
}); |
@ -0,0 +1,83 @@ |
|||||
|
import { parseCidr } from 'cidr-tools'; |
||||
|
import { stringifyIp } from 'ip-bigint'; |
||||
|
import { z } from 'zod'; |
||||
|
import type { Database } from '~~/services/database/repositories/database'; |
||||
|
|
||||
|
export default defineEventHandler(async (event) => { |
||||
|
const { file } = await readValidatedBody(event, validateZod(fileType, event)); |
||||
|
const setupDone = await Database.setup.done(); |
||||
|
if (setupDone) { |
||||
|
throw createError({ |
||||
|
statusCode: 400, |
||||
|
statusMessage: 'Invalid state', |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const schema = z.object({ |
||||
|
server: z.object({ |
||||
|
privateKey: z.string(), |
||||
|
publicKey: z.string(), |
||||
|
address: z.string(), |
||||
|
}), |
||||
|
clients: z.record( |
||||
|
z.string(), |
||||
|
z.object({ |
||||
|
name: z.string(), |
||||
|
address: z.string(), |
||||
|
privateKey: z.string(), |
||||
|
publicKey: z.string(), |
||||
|
preSharedKey: z.string(), |
||||
|
createdAt: z.string(), |
||||
|
updatedAt: z.string(), |
||||
|
enabled: z.boolean(), |
||||
|
}) |
||||
|
), |
||||
|
}); |
||||
|
const res = await schema.safeParseAsync(JSON.parse(file)); |
||||
|
if (!res.success) { |
||||
|
throw new Error('Invalid Config'); |
||||
|
} |
||||
|
const system = await Database.system.get(); |
||||
|
const oldConfig = res.data; |
||||
|
const oldCidr = parseCidr(oldConfig.server.address + '/24'); |
||||
|
const db = { |
||||
|
system: { |
||||
|
...system, |
||||
|
// TODO: migrate to db calls
|
||||
|
interface: { |
||||
|
...system.interface, |
||||
|
address4: oldConfig.server.address, |
||||
|
privateKey: oldConfig.server.privateKey, |
||||
|
publicKey: oldConfig.server.publicKey, |
||||
|
}, |
||||
|
userConfig: { |
||||
|
...system.userConfig, |
||||
|
address4Range: |
||||
|
stringifyIp({ number: oldCidr.start, version: 4 }) + '/24', |
||||
|
}, |
||||
|
} satisfies Partial<Database['system']>, |
||||
|
clients: {} as Database['clients'], |
||||
|
}; |
||||
|
|
||||
|
for (const [oldId, oldClient] of Object.entries(oldConfig.clients)) { |
||||
|
const address6 = nextIPv6(db.system, db.clients); |
||||
|
|
||||
|
await Database.client.create({ |
||||
|
id: oldId, |
||||
|
address4: oldClient.address, |
||||
|
enabled: oldClient.enabled, |
||||
|
name: oldClient.name, |
||||
|
preSharedKey: oldClient.preSharedKey, |
||||
|
privateKey: oldClient.privateKey, |
||||
|
publicKey: oldClient.publicKey, |
||||
|
expiresAt: null, |
||||
|
oneTimeLink: null, |
||||
|
allowedIPs: db.system.userConfig.allowedIps, |
||||
|
serverAllowedIPs: [], |
||||
|
persistentKeepalive: 0, |
||||
|
address6: address6, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return { success: true }; |
||||
|
}); |
Loading…
Reference in new issue