diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index a20fe5b0..6a0e929e 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,3 @@ # These are supported funding model platforms -github: weejewel +github: [weejewel, kaaax0815] diff --git a/.github/workflows/deploy-development.yml b/.github/workflows/deploy-development.yml index 212ccff4..d1907ba1 100644 --- a/.github/workflows/deploy-development.yml +++ b/.github/workflows/deploy-development.yml @@ -32,7 +32,7 @@ jobs: with: context: . push: true - platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8 + platforms: linux/amd64,linux/arm64/v8 tags: ghcr.io/wg-easy/wg-easy:development cache-from: type=gha cache-to: type=gha,mode=min diff --git a/.github/workflows/deploy-nightly.yml b/.github/workflows/deploy-nightly.yml index 03a75bba..8fe8927c 100644 --- a/.github/workflows/deploy-nightly.yml +++ b/.github/workflows/deploy-nightly.yml @@ -36,7 +36,7 @@ jobs: with: context: . push: true - platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8 + platforms: linux/amd64,linux/arm64/v8 tags: ghcr.io/wg-easy/wg-easy:nightly cache-from: type=gha cache-to: type=gha,mode=min diff --git a/.github/workflows/deploy-pr.yml b/.github/workflows/deploy-pr.yml index d1c226c1..a0a0b442 100644 --- a/.github/workflows/deploy-pr.yml +++ b/.github/workflows/deploy-pr.yml @@ -37,7 +37,7 @@ jobs: with: context: . push: false - platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8 + platforms: linux/amd64,linux/arm64/v8 tags: ghcr.io/wg-easy/wg-easy:pr cache-from: type=gha cache-to: type=gha,mode=min diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 321c72e1..82d23344 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -48,7 +48,7 @@ jobs: with: context: . push: true - platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8 + platforms: linux/amd64,linux/arm64/v8 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha diff --git a/CHANGELOG.md b/CHANGELOG.md index d4b50efa..2266d660 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,13 +20,10 @@ This update is an entire rewrite to make it even easier to set up your own VPN. - CIDR Support - IPv6 Support - Changed API Structure -- Changed Database Structure +- SQLite Database - Deprecated Dockerless Installations -- Added Docker Volume Mount - -## Minor Changes - -- Renamed Chinese Locales (cht -> zh-cht, chs -> zh-chs) +- Added Docker Volume Mount (`/lib/modules`) +- Removed ARMv6 and ARMv7 support ## [14.0.0] - 2024-09-04 diff --git a/Dockerfile b/Dockerfile index 32591d9f..50d60b93 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,4 @@ -# nodejs 20 hangs on build with armv6/armv7 (https://github.com/nodejs/docker-node/issues/2077) -FROM docker.io/library/node:18-alpine AS build +FROM docker.io/library/node:lts-alpine AS build WORKDIR /app # update corepack @@ -7,20 +6,14 @@ RUN npm install --global corepack@latest # Install pnpm RUN corepack enable pnpm -# add build tools for argon2 -RUN apk add --no-cache make gcc g++ python3 - # Copy Web UI -COPY src ./ +COPY src/package.json src/pnpm-lock.yaml ./ RUN pnpm install # Build UI +COPY src ./ RUN pnpm build -# Remove unnecessary node modules -RUN find ./node_modules/.pnpm -mindepth 1 -maxdepth 1 -type d ! -name '@libsql+linux*' -exec rm -r {} + -RUN find ./node_modules/@libsql -mindepth 1 -maxdepth 1 -type l ! -name 'linux*' -exec rm -r {} + - # Copy build result to a new image. # This saves a lot of disk space. FROM docker.io/library/node:lts-alpine @@ -33,8 +26,7 @@ COPY --from=build /app/.output /app # Copy migrations COPY --from=build /app/server/database/migrations /app/server/database/migrations # libsql -COPY --from=build /app/node_modules/.pnpm/ /app/node_modules/.pnpm/ -COPY --from=build /app/node_modules/@libsql /app/node_modules/@libsql +RUN npm install --no-save libsql # Install Linux packages RUN apk add --no-cache \ diff --git a/src/app/app.vue b/src/app/app.vue index 9ce499e4..9991e288 100644 --- a/src/app/app.vue +++ b/src/app/app.vue @@ -16,6 +16,9 @@ const toast = useToast(); const toastRef = useTemplateRef('toastRef'); toast.setToast(toastRef); +// make sure to fetch release early +useGlobalStore(); + useHead({ bodyAttrs: { class: 'bg-gray-50 dark:bg-neutral-800', diff --git a/src/app/components/admin/CidrDialog.vue b/src/app/components/Admin/CidrDialog.vue similarity index 78% rename from src/app/components/admin/CidrDialog.vue rename to src/app/components/Admin/CidrDialog.vue index 4a6d7b15..d9635009 100644 --- a/src/app/components/admin/CidrDialog.vue +++ b/src/app/components/Admin/CidrDialog.vue @@ -1,7 +1,7 @@ diff --git a/src/app/components/base/Switch.vue b/src/app/components/Base/Switch.vue similarity index 100% rename from src/app/components/base/Switch.vue rename to src/app/components/Base/Switch.vue diff --git a/src/app/components/Base/Toast.vue b/src/app/components/Base/Toast.vue new file mode 100644 index 00000000..e1d84bb1 --- /dev/null +++ b/src/app/components/Base/Toast.vue @@ -0,0 +1,46 @@ + + + diff --git a/src/app/components/Base/Tooltip.vue b/src/app/components/Base/Tooltip.vue new file mode 100644 index 00000000..ed01b150 --- /dev/null +++ b/src/app/components/Base/Tooltip.vue @@ -0,0 +1,24 @@ + + + diff --git a/src/app/components/ClientCard/Config.vue b/src/app/components/ClientCard/Config.vue index 2ce4ec61..eddbe1d9 100644 --- a/src/app/components/ClientCard/Config.vue +++ b/src/app/components/ClientCard/Config.vue @@ -3,7 +3,7 @@ :href="'/api/client/' + client.id + '/configuration'" download class="inline-block rounded bg-gray-100 p-2 align-middle transition hover:bg-red-800 hover:text-white dark:bg-neutral-600 dark:text-neutral-300 dark:hover:bg-red-800 dark:hover:text-white" - :title="$t('downloadConfig')" + :title="$t('client.downloadConfig')" > diff --git a/src/app/components/ClientCard/ExpireDate.vue b/src/app/components/ClientCard/ExpireDate.vue index 98ed042d..3d8e1e04 100644 --- a/src/app/components/ClientCard/ExpireDate.vue +++ b/src/app/components/ClientCard/ExpireDate.vue @@ -12,7 +12,7 @@ defineProps<{ client: LocalClient }>(); const { t, locale } = useI18n(); function expiredDateFormat(value: string | null) { - if (value === null) return t('Permanent'); + if (value === null) return t('client.permanent'); const dateTime = new Date(value); return dateTime.toLocaleDateString(locale.value, { year: 'numeric', diff --git a/src/app/components/ClientCard/LastSeen.vue b/src/app/components/ClientCard/LastSeen.vue index 7836047b..fda45fb3 100644 --- a/src/app/components/ClientCard/LastSeen.vue +++ b/src/app/components/ClientCard/LastSeen.vue @@ -2,7 +2,7 @@ · {{ timeago(new Date(client.latestHandshakeAt)) }} diff --git a/src/app/components/ClientCard/Name.vue b/src/app/components/ClientCard/Name.vue index 7e7057a6..435db786 100644 --- a/src/app/components/ClientCard/Name.vue +++ b/src/app/components/ClientCard/Name.vue @@ -1,7 +1,7 @@ diff --git a/src/app/components/Clients/DeleteDialog.vue b/src/app/components/Clients/DeleteDialog.vue index 21fec0b6..974eb7c4 100644 --- a/src/app/components/Clients/DeleteDialog.vue +++ b/src/app/components/Clients/DeleteDialog.vue @@ -1,19 +1,19 @@ diff --git a/src/app/pages/setup/1.vue b/src/app/pages/setup/1.vue index ac67462e..a059e0a5 100644 --- a/src/app/pages/setup/1.vue +++ b/src/app/pages/setup/1.vue @@ -1,9 +1,11 @@ @@ -11,6 +13,7 @@ definePageMeta({ layout: 'setup', }); + const setupStore = useSetupStore(); setupStore.setStep(1); diff --git a/src/app/pages/setup/2.vue b/src/app/pages/setup/2.vue index 71e36e14..72cbffc1 100644 --- a/src/app/pages/setup/2.vue +++ b/src/app/pages/setup/2.vue @@ -1,83 +1,59 @@ diff --git a/src/app/pages/setup/3.vue b/src/app/pages/setup/3.vue index 620844be..54e3cae0 100644 --- a/src/app/pages/setup/3.vue +++ b/src/app/pages/setup/3.vue @@ -1,11 +1,15 @@ @@ -14,6 +18,7 @@ definePageMeta({ layout: 'setup', }); + const setupStore = useSetupStore(); setupStore.setStep(3); diff --git a/src/app/pages/setup/4.vue b/src/app/pages/setup/4.vue index 9cd88bd2..40e22eef 100644 --- a/src/app/pages/setup/4.vue +++ b/src/app/pages/setup/4.vue @@ -1,71 +1,60 @@ diff --git a/src/app/pages/setup/migrate.vue b/src/app/pages/setup/migrate.vue index 77622c81..a1185bbd 100644 --- a/src/app/pages/setup/migrate.vue +++ b/src/app/pages/setup/migrate.vue @@ -1,68 +1,57 @@ diff --git a/src/app/stores/auth.ts b/src/app/stores/auth.ts index b574945d..fab80ea0 100644 --- a/src/app/stores/auth.ts +++ b/src/app/stores/auth.ts @@ -14,26 +14,5 @@ export const useAuthStore = defineStore('Auth', () => { } } - /** - * @throws if unsuccessful - */ - async function login(username: string, password: string, remember: boolean) { - await $fetch('/api/session', { - method: 'post', - body: { username, password, remember }, - }); - return true as const; - } - - /** - * @throws if unsuccessful - */ - async function logout() { - const response = await $fetch('/api/session', { - method: 'delete', - }); - return response.success; - } - - return { userData, login, logout, update, getSession }; + return { userData, update, getSession }; }); diff --git a/src/app/stores/global.ts b/src/app/stores/global.ts index 7f515872..ddf83ffd 100644 --- a/src/app/stores/global.ts +++ b/src/app/stores/global.ts @@ -1,27 +1,9 @@ -import { defineStore } from 'pinia'; - export const useGlobalStore = defineStore('Global', () => { - const sortClient = ref(true); // Sort clients by name, true = asc, false = desc - - const currentRelease = ref(null); - const latestRelease = ref( - null - ); - const updateAvailable = ref(false); - - async function fetchRelease() { - const { data: release } = await useFetch('/api/release', { - method: 'get', - }); + const { data: release } = useFetch('/api/release', { + method: 'get', + }); - if (!release.value) { - return; - } - - currentRelease.value = release.value.currentRelease; - latestRelease.value = release.value.latestRelease; - updateAvailable.value = release.value.updateAvailable; - } + const sortClient = ref(true); // Sort clients by name, true = asc, false = desc const uiShowCharts = ref(getItem('uiShowCharts') === '1'); @@ -33,10 +15,7 @@ export const useGlobalStore = defineStore('Global', () => { return { sortClient, - currentRelease, - latestRelease, - updateAvailable, - fetchRelease, + release, uiShowCharts, toggleCharts, uiChartType, diff --git a/src/app/stores/setup.ts b/src/app/stores/setup.ts index 478c9e0a..e7bcc64e 100644 --- a/src/app/stores/setup.ts +++ b/src/app/stores/setup.ts @@ -1,39 +1,6 @@ import { defineStore } from 'pinia'; export const useSetupStore = defineStore('Setup', () => { - /** - * @throws if unsuccessful - */ - async function step2(username: string, password: string, accept: boolean) { - const response = await $fetch('/api/setup/2', { - method: 'post', - body: { username, password, accept }, - }); - return response.success; - } - - /** - * @throws if unsuccessful - */ - async function step4(host: string, port: number) { - const response = await $fetch('/api/setup/4', { - method: 'post', - body: { host, port }, - }); - return response.success; - } - - /** - * @throws if unsuccessful - */ - async function runMigration(file: string) { - const response = await $fetch('/api/setup/migrate', { - method: 'post', - body: { file }, - }); - return response.success; - } - const step = ref(1); const totalSteps = ref(5); function setStep(i: number) { @@ -41,9 +8,6 @@ export const useSetupStore = defineStore('Setup', () => { } return { - step2, - step4, - runMigration, step, totalSteps, setStep, diff --git a/src/app/stores/toast.ts b/src/app/stores/toast.ts index d56588db..9463154c 100644 --- a/src/app/stores/toast.ts +++ b/src/app/stores/toast.ts @@ -1,6 +1,8 @@ export const useToast = defineStore('Toast', () => { + const { t: $t } = useI18n(); + type ToastInterface = { - publish: (e: { title: string; message: string }) => void; + publish: (e: ToastParams) => void; }; type ToastRef = Ref; @@ -11,15 +13,36 @@ export const useToast = defineStore('Toast', () => { toast.value = toastInstance; } - function showToast({ - title, - message, - }: { - type: 'success' | 'error'; - title: string; - message: string; - }) { - toast.value?.value?.publish({ title, message }); + type ShowToast = + | { + type: 'success'; + title?: string; + message?: string; + } + | { + type: 'error'; + title?: string; + message: string; + }; + + function showToast({ type, title, message }: ShowToast) { + if (type === 'success') { + if (!title) { + title = $t('toast.success'); + } + if (!message) { + message = $t('toast.saved'); + } + } else if (type === 'error') { + if (!title) { + title = $t('toast.error'); + } + } + toast.value?.value?.publish({ + type, + title: title ?? '', + message: message ?? '', + }); } return { setToast, showToast }; diff --git a/src/app/utils/types.ts b/src/app/utils/types.ts new file mode 100644 index 00000000..0736ab97 --- /dev/null +++ b/src/app/utils/types.ts @@ -0,0 +1,6 @@ +export type ToastTypes = 'error' | 'success'; +export type ToastParams = { + type: ToastTypes; + title: string; + message: string; +}; diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 2edb235e..d623508d 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1,91 +1,155 @@ { "pages": { "me": "Account", - "clients": "Clients" + "clients": "Clients", + "admin": { + "panel": "Admin Panel", + "general": "General", + "config": "Config", + "interface": "Interface", + "hooks": "Hooks" + } + }, + "user": { + "email": "E-Mail" }, "me": { - "sectionGeneral": "General", - "sectionPassword": "Password" + "currentPassword": "Current Password", + "confirmPassword": "Confirm Password" }, - "email": "E-Mail", - "save": "Save", - "updatePassword": "Update Password", - "currentPassword": "Current Password", - "confirmPassword": "Confirm Password", - "setup": { - "welcome": "Welcome to your first setup of wg-easy !", - "messageWelcome": { - "whatIs": "You have found the easiest way to install and manage WireGuard on any Linux host!", - "warning": "First of all, make sure you have a backup of your data if you want to migrate your users to your new wg-easy.", - "next": "Click on the arrow button to proceed to the next step." - }, - "messageSetupLanguage": "Please choose a language for the setup.", - "messageSetupCreateAdminUser": "Please first enter an admin username and a strong secure password. This information will be used to log in to your administration panel.", - "messageSetupHostPort": "Please enter the host and port information. This will be used for the client configuration when setting up WireGuard on their devices.", - "messageSetupMigration": "Please provide the backup file if you want to migrate your data from your previous wg-easy version to your new setup.", - "messageSetupValidation": "Welcome to wg-easy ! The easiest way to run WireGuard VPN and Web-based Admin UI.", - "emptyFields": "The fields are required", - "chooseLang": "Select a language...", + "general": { + "name": "Name", + "username": "Username", + "password": "Password", "newPassword": "New Password", - "accept": "I accept the condition", - "submitBtn": "Create admin account", - "usernamePlaceholder": "Administrator", - "passwordPlaceholder": "Strong password", - "requirements": "Setup requirements", + "updatePassword": "Update Password", + "mtu": "MTU", + "allowedIps": "Allowed IPs", + "persistentKeepalive": "Persistent Keepalive", + "logout": "Logout", + "continue": "Continue", "host": "Host", - "hostPlaceholder": "wg-easy.example.com", "port": "Port", - "portPlaceholder": "443", - "migration": "Restore the backup" + "yes": "Yes", + "no": "No" + }, + "setup": { + "welcome": "Welcome to your first setup of wg-easy !", + "welcomeDesc": "You have found the easiest way to install and manage WireGuard on any Linux host!", + "existingSetup": "Do you have an existing setup?", + "createAdminDesc": "Please first enter an admin username and a strong secure password. This information will be used to log in to your administration panel.", + "setupConfigDesc": "Please enter the host and port information. This will be used for the client configuration when setting up WireGuard on their devices.", + "setupMigrationDesc": "Please provide the backup file if you want to migrate your data from your previous wg-easy version to your new setup.", + "upload": "Upload", + "migration": "Restore the backup", + "createAccount": "Create Account", + "successful": "Setup successful" + }, + "update": { + "updateAvailable": "There is an update available!", + "update": "Update" }, - "name": "Name", - "username": "Username", - "signIn": "Sign In", - "logout": "Logout", - "updateAvailable": "There is an update available!", - "update": "Update", - "new": "New", - "deleteClient": "Delete Client", - "deleteDialog1": "Are you sure you want to delete", - "deleteDialog2": "This action cannot be undone.", - "cancel": "Cancel", - "create": "Create", - "createdOn": "Created on ", - "lastSeen": "Last seen on ", - "totalDownload": "Total Download: ", - "totalUpload": "Total Upload: ", - "newClient": "New Client", - "disableClient": "Disable Client", - "enableClient": "Enable Client", - "noClients": "There are no clients yet.", - "noPrivKey": "This client has no known private key. Cannot create Configuration.", - "showQR": "Show QR Code", - "downloadConfig": "Download Configuration", - "madeBy": "Made by", - "donate": "Donate", - "toggleCharts": "Show/hide Charts", "theme": { "dark": "Dark theme", "light": "Light theme", "system": "System theme" }, - "rememberMe": "Remember me", - "titleRememberMe": "Stay logged after closing the browser", - "sort": "Sort", - "ExpireDate": "Expire Date", - "Permanent": "Permanent", - "OneTimeLink": "Generate short one time link", - "errorInit": "Initialization failed.", + "layout": { + "toggleCharts": "Show/hide Charts", + "donate": "Donate" + }, + "login": { + "signIn": "Sign In", + "rememberMe": "Remember me", + "rememberMeDesc": "Stay logged after closing the browser" + }, "error": { "clear": "Clear", "login": "Log in error" }, + "client": { + "empty": "There are no clients yet.", + "newShort": "New", + "sort": "Sort", + "create": "Create Client", + "created": "Client created", + "new": "New Client", + "name": "Name", + "expireDate": "Expire Date", + "expireDateDesc": "Date the client will be disabled. Blank for permanent", + "deleteClient": "Delete Client", + "deleteDialog1": "Are you sure you want to delete", + "deleteDialog2": "This action cannot be undone.", + "enabled": "Enabled", + "address": "Address", + "serverAllowedIps": "Server Allowed IPs", + "otlDesc": "Generate short one time link", + "permanent": "Permanent", + "createdOn": "Created on ", + "lastSeen": "Last seen on ", + "totalDownload": "Total Download: ", + "totalUpload": "Total Upload: ", + "newClient": "New Client", + "disableClient": "Disable Client", + "enableClient": "Enable Client", + "noPrivKey": "This client has no known private key. Cannot create Configuration.", + "showQR": "Show QR Code", + "downloadConfig": "Download Configuration", + "allowedIpsDesc": "Which IPs will be routed through the VPN", + "serverAllowedIpsDesc": "Which IPs the server will route to the client", + "mtuDesc": "Sets the maximum transmission unit (packet size) for the VPN tunnel", + "persistentKeepaliveDesc": "Sets the interval (in seconds) for keep-alive packets. 0 disables it" + }, + "dialog": { + "change": "Change", + "cancel": "Cancel", + "create": "Create" + }, + "toast": { + "success": "Success", + "saved": "Saved", + "error": "Error", + "errored": "Failed to save" + }, "form": { "actions": "Actions", "save": "Save", - "revert": "Revert" + "revert": "Revert", + "sectionGeneral": "General", + "sectionAdvanced": "Advanced" + }, + "admin": { + "general": { + "sessionTimeout": "Session Timeout", + "sessionTimeoutDesc": "Session duration for Remember Me (seconds)", + "metrics": "Metrics", + "metricsPassword": "Password", + "metricsPasswordDesc": "Bearer Password for the metrics endpoint (argon2 hash)", + "json": "JSON", + "jsonDesc": "Route for metrics in JSON format", + "prometheus": "Prometheus", + "prometheusDesc": "Route for Prometheus metrics" + }, + "config": { + "connection": "Connection", + "hostDesc": "Public hostname clients will connect to (invalidates config)", + "portDesc": "Public UDP port clients will connect to (invalidates config)", + "allowedIpsDesc": "Allowed IPs clients will use (invalidates config)", + "dns": "DNS", + "dnsDesc": "DNS server clients will use (invalidates config)", + "mtuDesc": "MTU clients will use (invalidates config)", + "persistentKeepaliveDesc": "Interval in seconds to send keepalives to the server. 0 = disabled (invalidates config)" + }, + "interface": { + "cidrSuccess": "Changed CIDR", + "cidrError": "Failed to change CIDR", + "device": "Device", + "deviceDesc": "Ethernet device the wireguard traffic should be forwarded through", + "mtuDesc": "MTU WireGuard will use", + "portDesc": "UDP Port WireGuard will listen on (could invalidate config)", + "changeCidr": "Change CIDR" + } }, - "password": "Password", "zod": { "generic": { "required": "{0} is required", @@ -112,8 +176,6 @@ "passwordNumber": "Password must have at least 1 number", "passwordSpecial": "Password must have at least 1 special character", "remember": "Remember", - "accept": "Accept", - "acceptTrue": "Accept Conditions to continue", "name": "Name", "email": "Email", "emailInvalid": "Email must be a valid email", diff --git a/src/server/database/repositories/user/types.ts b/src/server/database/repositories/user/types.ts index 1c071926..51eac4e5 100644 --- a/src/server/database/repositories/user/types.ts +++ b/src/server/database/repositories/user/types.ts @@ -29,17 +29,10 @@ export const UserLoginSchema = z.object( { message: objectMessage } ); -const accept = z - .boolean({ message: t('zod.user.accept') }) - .refine((val) => val === true, { - message: t('zod.user.acceptTrue'), - }); - export const UserSetupSchema = z.object( { username: username, password: password, - accept: accept, }, { message: objectMessage } ); diff --git a/src/server/utils/Database.ts b/src/server/utils/Database.ts index 297eba2c..76ae6797 100644 --- a/src/server/utils/Database.ts +++ b/src/server/utils/Database.ts @@ -21,5 +21,4 @@ connect().then((db) => { WireGuard.Startup(); }); -// TODO: check if old config exists and tell user about migration path export default provider; diff --git a/src/server/utils/WireGuard.ts b/src/server/utils/WireGuard.ts index 44928695..488ad7aa 100644 --- a/src/server/utils/WireGuard.ts +++ b/src/server/utils/WireGuard.ts @@ -180,12 +180,11 @@ class WireGuard { // TODO: handle as worker_thread async startCronJob() { - await this.cronJob().catch((err) => { - WG_DEBUG('Running Cron Job failed.'); - console.error(err); - }); - setTimeout(() => { - this.startCronJob(); + setIntervalImmediately(() => { + this.cronJob().catch((err) => { + WG_DEBUG('Running Cron Job failed.'); + console.error(err); + }); }, 60 * 1000); } @@ -208,7 +207,6 @@ class WireGuard { await Database.clients.toggle(client.id, false); } } - // One Time Link Feature for (const client of clients) { if ( @@ -216,7 +214,7 @@ class WireGuard { new Date() > new Date(client.oneTimeLink.expiresAt) ) { WG_DEBUG(`Client ${client.id} One Time Link expired.`); - await Database.oneTimeLinks.delete(client.id); + await Database.oneTimeLinks.delete(client.oneTimeLink.id); } } diff --git a/src/server/utils/types.ts b/src/server/utils/types.ts index 6fae6536..ca9db348 100644 --- a/src/server/utils/types.ts +++ b/src/server/utils/types.ts @@ -19,8 +19,6 @@ export const safeStringRefine = z { message: t('zod.stringMalformed') } ); -// TODO: create custom getValidatedRouterParams and readValidatedBody wrapper - export const EnabledSchema = z.boolean({ message: t('zod.enabled') }); export const MtuSchema = z diff --git a/src/shared/utils/time.ts b/src/shared/utils/time.ts index 3d4085e4..a7ac27c1 100644 --- a/src/shared/utils/time.ts +++ b/src/shared/utils/time.ts @@ -8,3 +8,8 @@ export function isPeerConnected(client: { latestHandshakeAt: Date | null }) { // connected if last handshake was less than 10 minutes ago return lastHandshakeMs < 1000 * 60 * 10; } + +export function setIntervalImmediately(func: () => void, interval: number) { + func(); + return setInterval(func, interval); +}