diff --git a/src/server/api/cnf/[clientOneTimeLink].ts b/src/server/api/cnf/[oneTimeLink].ts
similarity index 61%
rename from src/server/api/cnf/[clientOneTimeLink].ts
rename to src/server/api/cnf/[oneTimeLink].ts
index 5e2a058b..764923d1 100644
--- a/src/server/api/cnf/[clientOneTimeLink].ts
+++ b/src/server/api/cnf/[oneTimeLink].ts
@@ -1,30 +1,33 @@
export default defineEventHandler(async (event) => {
const system = await Database.getSystem();
- if (!system)
- throw createError({
- statusCode: 500,
- statusMessage: 'Invalid',
- });
if (!system.oneTimeLinks.enabled) {
throw createError({
- status: 404,
- message: 'Invalid state',
+ statusCode: 404,
+ statusMessage: 'Invalid state',
});
}
// TODO: validate with zod
- const clientOneTimeLink = getRouterParam(event, 'clientOneTimeLink');
+ const { oneTimeLink } = await getValidatedRouterParams(
+ event,
+ validateZod(oneTimeLinkType)
+ );
const clients = await WireGuard.getClients();
const client = clients.find(
- (client) => client.oneTimeLink === clientOneTimeLink
+ (client) => client.oneTimeLink?.oneTimeLink === oneTimeLink
);
- if (!client) return;
+ if (!client) {
+ throw createError({
+ statusCode: 404,
+ statusMessage: 'Invalid One Time Link',
+ });
+ }
const clientId = client.id;
const config = await WireGuard.getClientConfiguration({ clientId });
await WireGuard.eraseOneTimeLink({ clientId });
setHeader(
event,
'Content-Disposition',
- `attachment; filename="${clientOneTimeLink}.conf"`
+ `attachment; filename="${client.name}.conf"`
);
setHeader(event, 'Content-Type', 'text/plain');
return config;
diff --git a/src/server/utils/types.ts b/src/server/utils/types.ts
index 1b09c115..c0f54988 100644
--- a/src/server/utils/types.ts
+++ b/src/server/utils/types.ts
@@ -42,6 +42,11 @@ const expireDate = z
.pipe(safeStringRefine)
.nullable();
+const oneTimeLink = z
+ .string({ message: 'oneTimeLink must be a valid string' })
+ .min(1, 'oneTimeLink must be at least 1 Character')
+ .pipe(safeStringRefine);
+
export const clientIdType = z.object(
{
clientId: id,
@@ -70,6 +75,13 @@ export const expireDateType = z.object(
{ message: 'Body must be a valid object' }
);
+export const oneTimeLinkType = z.object(
+ {
+ oneTimeLink: oneTimeLink,
+ },
+ { message: 'Body must be a valid object' }
+);
+
export const createType = z.object(
{
name: name,
diff --git a/src/services/database/lowdb.ts b/src/services/database/lowdb.ts
index b155a3de..f0989d80 100644
--- a/src/services/database/lowdb.ts
+++ b/src/services/database/lowdb.ts
@@ -13,6 +13,7 @@ import type { Low } from 'lowdb';
import type { User } from './repositories/user';
import type { Database } from './repositories/database';
import { migrationRunner } from './migrations';
+import type { Client, NewClient, OneTimeLink } from './repositories/client';
const DEBUG = debug('LowDB');
@@ -99,15 +100,16 @@ export default class LowDB extends DatabaseProvider {
updatedAt: now,
};
- this.#db.update((data) => data.users.push(newUser));
+ await this.#db.update((data) => data.users.push(newUser));
}
async updateUser(user: User) {
+ // TODO: avoid mutation, prefer .update
let oldUser = await this.getUser(user.id);
if (oldUser) {
DEBUG('Update User');
oldUser = user;
- this.#db.write();
+ await this.#db.write();
}
}
@@ -115,7 +117,89 @@ export default class LowDB extends DatabaseProvider {
DEBUG('Delete User');
const idx = this.#db.data.users.findIndex((user) => user.id === id);
if (idx !== -1) {
- this.#db.update((data) => data.users.splice(idx, 1));
+ await this.#db.update((data) => data.users.splice(idx, 1));
}
}
+
+ async getClients() {
+ DEBUG('GET Clients');
+ return this.#db.data.clients;
+ }
+
+ async getClient(id: string) {
+ DEBUG('Get Client');
+ return this.#db.data.clients[id];
+ }
+
+ async createClient(client: NewClient) {
+ DEBUG('Create Client');
+ const now = new Date();
+ const newClient: Client = { ...client, createdAt: now, updatedAt: now };
+ await this.#db.update((data) => {
+ data.clients[client.id] = newClient;
+ });
+ }
+
+ async deleteClient(id: string) {
+ DEBUG('Delete Client');
+ await this.#db.update((data) => {
+ // TODO: find something better than delete
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
+ delete data.clients[id];
+ });
+ }
+
+ async toggleClient(id: string, enable: boolean) {
+ DEBUG('Toggle Client');
+ await this.#db.update((data) => {
+ if (data.clients[id]) {
+ data.clients[id].enabled = enable;
+ }
+ });
+ }
+
+ async updateClientName(id: string, name: string) {
+ DEBUG('Update Client Name');
+ await this.#db.update((data) => {
+ if (data.clients[id]) {
+ data.clients[id].name = name;
+ }
+ });
+ }
+
+ async updateClientAddress(id: string, address: string) {
+ DEBUG('Update Client Address');
+ await this.#db.update((data) => {
+ if (data.clients[id]) {
+ data.clients[id].address = address;
+ }
+ });
+ }
+
+ async updateClientExpirationDate(id: string, expirationDate: Date | null) {
+ DEBUG('Update Client Address');
+ await this.#db.update((data) => {
+ if (data.clients[id]) {
+ data.clients[id].expiresAt = expirationDate;
+ }
+ });
+ }
+
+ async deleteOneTimeLink(id: string) {
+ DEBUG('Update Client Address');
+ await this.#db.update((data) => {
+ if (data.clients[id]) {
+ data.clients[id].oneTimeLink = null;
+ }
+ });
+ }
+
+ async createOneTimeLink(id: string, oneTimeLink: OneTimeLink) {
+ DEBUG('Update Client Address');
+ await this.#db.update((data) => {
+ if (data.clients[id]) {
+ data.clients[id].oneTimeLink = oneTimeLink;
+ }
+ });
+ }
}
diff --git a/src/services/database/migrations/1.ts b/src/services/database/migrations/1.ts
index b9fbbe1c..473c24bc 100644
--- a/src/services/database/migrations/1.ts
+++ b/src/services/database/migrations/1.ts
@@ -83,7 +83,7 @@ export async function run1(db: Low) {
},
},
users: [],
- clients: [],
+ clients: {},
};
db.data = database;