Browse Source

feat: Add search client based on #1978

pull/2170/head
yuWorm 2 days ago
parent
commit
7c38792377
  1. 42
      src/app/components/Clients/Search.vue
  2. 2
      src/app/components/Panel/head/Boat.vue
  3. 1
      src/app/pages/index.vue
  4. 13
      src/app/stores/clients.ts
  5. 3
      src/i18n/locales/en.json
  6. 3
      src/i18n/locales/zh-CN.json
  7. 3
      src/i18n/locales/zh-HK.json
  8. 13
      src/server/api/client/index.get.ts
  9. 23
      src/server/utils/WireGuard.ts

42
src/app/components/Clients/Search.vue

@ -0,0 +1,42 @@
<template>
<div class="relative w-60 md:mr-2">
<div class="relative flex h-full items-center">
<MagnifyingGlassIcon
class="absolute left-2.5 h-4 w-4 text-gray-400 dark:text-neutral-500"
/>
<input
v-model="searchQuery"
type="text"
:placeholder="$t('client.search')"
class="w-full rounded bg-white py-2 pl-8 pr-8 text-sm text-gray-900 shadow-sm ring-1 ring-gray-300 transition-all placeholder:text-gray-400 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-red-600 dark:bg-neutral-800 dark:text-white dark:ring-neutral-700 dark:placeholder:text-neutral-500 dark:focus:ring-red-700"
@input="updateSearch"
/>
<button
v-if="searchQuery"
class="absolute right-2 flex h-5 w-5 items-center justify-center rounded-full bg-gray-200 text-gray-600 hover:bg-gray-300 hover:text-gray-800 dark:bg-neutral-700 dark:text-neutral-300 dark:hover:bg-neutral-600 dark:hover:text-neutral-100"
@click="clearSearch"
aria-label="Clear search"
>
<IconsClose class="h-3 w-3" />
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { MagnifyingGlassIcon } from '@heroicons/vue/24/outline';
import IconsClose from '../Icons/Close.vue';
const clientsStore = useClientsStore();
const searchQuery = ref('');
function updateSearch() {
clientsStore.setSearchQuery(searchQuery.value);
}
function clearSearch() {
searchQuery.value = '';
clientsStore.setSearchQuery('');
}
</script>

2
src/app/components/Panel/head/Boat.vue

@ -1,5 +1,5 @@
<template>
<div class="flex flex-shrink-0 space-x-1 md:block">
<div class="flex flex-shrink-0 items-center space-x-2">
<slot />
</div>
</template>

1
src/app/pages/index.vue

@ -4,6 +4,7 @@
<PanelHead>
<PanelHeadTitle :text="$t('pages.clients')" />
<PanelHeadBoat>
<ClientsSearch />
<ClientsSort />
<ClientsNew />
</PanelHeadBoat>

13
src/app/stores/clients.ts

@ -31,8 +31,13 @@ export const useClientsStore = defineStore('Clients', () => {
const clients = ref<null | LocalClient[]>(null);
const clientsPersist = ref<Record<string, ClientPersist>>({});
const searchParams = ref({
filter: '',
});
const { data: _clients, refresh: _refresh } = useFetch('/api/client', {
method: 'get',
params: searchParams,
});
// TODO: rewrite
@ -130,5 +135,11 @@ export const useClientsStore = defineStore('Clients', () => {
clients.value = transformedClients ?? null;
}
return { clients, clientsPersist, refresh, _clients };
function setSearchQuery(filter: string) {
clients.value = null;
searchParams.value.filter = filter;
}
return { clients, clientsPersist, refresh, _clients, setSearchQuery };
});

3
src/i18n/locales/en.json

@ -116,7 +116,8 @@
"dnsDesc": "DNS server clients will use (overrides global config)",
"notConnected": "Client not connected",
"endpoint": "Endpoint",
"endpointDesc": "IP of the client from which the WireGuard connection is established"
"endpointDesc": "IP of the client from which the WireGuard connection is established",
"search": "Search clients..."
},
"dialog": {
"change": "Change",

3
src/i18n/locales/zh-CN.json

@ -112,7 +112,8 @@
"persistentKeepaliveDesc": "设置保活数据包的发送间隔(秒)。0表示禁用",
"hooks": "钩子脚本",
"hooksDescription": "钩子脚本仅在使用wg-quick时有效",
"hooksLeaveEmpty": "如果不使用wg-quick,请留空此字段"
"hooksLeaveEmpty": "如果不使用wg-quick,请留空此字段",
"search": "搜索客户端..."
},
"dialog": {
"change": "确认修改",

3
src/i18n/locales/zh-HK.json

@ -113,7 +113,8 @@
"hooks": "掛鉤",
"hooksDescription": "掛鉤僅適用於wg-quick",
"hooksLeaveEmpty": "僅適用於wg-quick,否則請留空",
"dnsDesc": "客戶端使用的域名系統伺服器(取代全局配置)"
"dnsDesc": "客戶端使用的域名系統伺服器(取代全局配置)",
"search": "搜尋客戶端..."
},
"dialog": {
"change": "更改",

13
src/server/api/client/index.get.ts

@ -1,6 +1,11 @@
export default definePermissionEventHandler('clients', 'custom', ({ user }) => {
export default definePermissionEventHandler(
'clients',
'custom',
({ event, user }) => {
const { filter } = getQuery(event);
if (user.role === roles.ADMIN) {
return WireGuard.getAllClients();
return WireGuard.filterClients(null, filter as string);
}
return WireGuard.getClientsForUser(user.id);
});
return WireGuard.filterClients(user.id, filter as string);
}
);

23
src/server/utils/WireGuard.ts

@ -134,6 +134,29 @@ class WireGuard {
return clients;
}
async filterClients(userId: ID | null, filter: string) {
let clients;
if (userId != null) {
await this.getClientsForUser(userId);
} else {
clients = await this.getAllClients();
}
if (!clients) {
return [];
}
if (!filter.trim()) return clients;
const searchTerm = filter.toLowerCase().trim();
return clients?.filter((client) => {
const nameMatches = client.name.toLowerCase().includes(searchTerm);
const ipv4Matches = client.ipv4Address.toLowerCase().includes(searchTerm);
const ipv6Matches = client.ipv6Address.toLowerCase().includes(searchTerm);
return nameMatches || ipv4Matches || ipv6Matches;
});
}
async getClientConfiguration({ clientId }: { clientId: ID }) {
const wgInterface = await Database.interfaces.get();
const userConfig = await Database.userConfigs.get();

Loading…
Cancel
Save