Add search / filter box (#2170)
* feat: Add search client based on #1978 * moved the filtering to the DB level using zod and tidied up some imports. * minor fix * minor fix * fix typo --------- Co-authored-by: Bernd Storath <999999bst@gmail.com>
This commit is contained in:
@@ -1,6 +1,17 @@
|
||||
export default definePermissionEventHandler('clients', 'custom', ({ user }) => {
|
||||
if (user.role === roles.ADMIN) {
|
||||
return WireGuard.getAllClients();
|
||||
import { ClientQuerySchema } from '#db/repositories/client/types';
|
||||
|
||||
export default definePermissionEventHandler(
|
||||
'clients',
|
||||
'custom',
|
||||
async ({ event, user }) => {
|
||||
const { filter } = await getValidatedQuery(
|
||||
event,
|
||||
validateZod(ClientQuerySchema, event)
|
||||
);
|
||||
|
||||
if (user.role === roles.ADMIN) {
|
||||
return WireGuard.getAllClients(filter);
|
||||
}
|
||||
return WireGuard.getClientsForUser(user.id, filter);
|
||||
}
|
||||
return WireGuard.getClientsForUser(user.id);
|
||||
});
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { eq, sql } from 'drizzle-orm';
|
||||
import { eq, sql, or, like, and } from 'drizzle-orm';
|
||||
import { containsCidr, parseCidr } from 'cidr-tools';
|
||||
import { client } from './schema';
|
||||
import type {
|
||||
@@ -42,6 +42,39 @@ function createPreparedStatement(db: DBType) {
|
||||
},
|
||||
})
|
||||
.prepare(),
|
||||
findAllPublicFiltered: db.query.client
|
||||
.findMany({
|
||||
where: or(
|
||||
like(client.name, sql.placeholder('filter')),
|
||||
like(client.ipv4Address, sql.placeholder('filter')),
|
||||
like(client.ipv6Address, sql.placeholder('filter'))
|
||||
),
|
||||
with: {
|
||||
oneTimeLink: true,
|
||||
},
|
||||
columns: {
|
||||
privateKey: false,
|
||||
preSharedKey: false,
|
||||
},
|
||||
})
|
||||
.prepare(),
|
||||
findByUserIdFiltered: db.query.client
|
||||
.findMany({
|
||||
where: and(
|
||||
eq(client.userId, sql.placeholder('userId')),
|
||||
or(
|
||||
like(client.name, sql.placeholder('filter')),
|
||||
like(client.ipv4Address, sql.placeholder('filter')),
|
||||
like(client.ipv6Address, sql.placeholder('filter'))
|
||||
)
|
||||
),
|
||||
with: { oneTimeLink: true },
|
||||
columns: {
|
||||
privateKey: false,
|
||||
preSharedKey: false,
|
||||
},
|
||||
})
|
||||
.prepare(),
|
||||
toggle: db
|
||||
.update(client)
|
||||
.set({ enabled: sql.placeholder('enabled') as never as boolean })
|
||||
@@ -96,6 +129,41 @@ export class ClientService {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get clients based on user ID and filter conditions
|
||||
*/
|
||||
async getForUserFiltered(userId: ID, filter: string) {
|
||||
const filterPattern = `%${filter.toLowerCase()}%`;
|
||||
|
||||
const result = await this.#statements.findByUserIdFiltered.execute({
|
||||
userId,
|
||||
filter: filterPattern,
|
||||
});
|
||||
|
||||
return result.map((row) => ({
|
||||
...row,
|
||||
createdAt: new Date(row.createdAt),
|
||||
updatedAt: new Date(row.updatedAt),
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all clients based on filter conditions without sensitive data
|
||||
*/
|
||||
async getAllPublicFiltered(filter: string) {
|
||||
const filterPattern = `%${filter.toLowerCase()}%`;
|
||||
|
||||
const result = await this.#statements.findAllPublicFiltered.execute({
|
||||
filter: filterPattern,
|
||||
});
|
||||
|
||||
return result.map((row) => ({
|
||||
...row,
|
||||
createdAt: new Date(row.createdAt),
|
||||
updatedAt: new Date(row.updatedAt),
|
||||
}));
|
||||
}
|
||||
|
||||
get(id: ID) {
|
||||
return this.#statements.findById.execute({ id });
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ const address6 = z
|
||||
.min(1, { message: t('zod.client.address6') })
|
||||
.pipe(safeStringRefine);
|
||||
|
||||
const filter = z.string().optional();
|
||||
|
||||
const serverAllowedIps = z.array(AddressSchema, {
|
||||
message: t('zod.client.serverAllowedIps'),
|
||||
});
|
||||
@@ -50,6 +52,12 @@ export const ClientCreateSchema = z.object({
|
||||
|
||||
export type ClientCreateType = z.infer<typeof ClientCreateSchema>;
|
||||
|
||||
export const ClientQuerySchema = z.object({
|
||||
filter: filter,
|
||||
});
|
||||
|
||||
export type ClientQueryType = z.infer<typeof ClientQuerySchema>;
|
||||
|
||||
export const ClientUpdateSchema = schemaForType<UpdateClientType>()(
|
||||
z.object({
|
||||
name: name,
|
||||
|
||||
@@ -61,10 +61,15 @@ class WireGuard {
|
||||
WG_DEBUG('Config synced successfully.');
|
||||
}
|
||||
|
||||
async getClientsForUser(userId: ID) {
|
||||
async getClientsForUser(userId: ID, filter?: string) {
|
||||
const wgInterface = await Database.interfaces.get();
|
||||
|
||||
const dbClients = await Database.clients.getForUser(userId);
|
||||
let dbClients;
|
||||
if (filter?.trim()) {
|
||||
dbClients = await Database.clients.getForUserFiltered(userId, filter);
|
||||
} else {
|
||||
dbClients = await Database.clients.getForUser(userId);
|
||||
}
|
||||
|
||||
const clients = dbClients.map((client) => ({
|
||||
...client,
|
||||
@@ -104,9 +109,16 @@ class WireGuard {
|
||||
return clientDump;
|
||||
}
|
||||
|
||||
async getAllClients() {
|
||||
async getAllClients(filter?: string) {
|
||||
const wgInterface = await Database.interfaces.get();
|
||||
const dbClients = await Database.clients.getAllPublic();
|
||||
|
||||
let dbClients;
|
||||
if (filter?.trim()) {
|
||||
dbClients = await Database.clients.getAllPublicFiltered(filter);
|
||||
} else {
|
||||
dbClients = await Database.clients.getAllPublic();
|
||||
}
|
||||
|
||||
const clients = dbClients.map((client) => ({
|
||||
...client,
|
||||
latestHandshakeAt: null as Date | null,
|
||||
|
||||
Reference in New Issue
Block a user