Browse Source

group even more

pull/1397/head
Bernd Storath 7 months ago
parent
commit
4b3f5b838e
  1. 1
      src/app/app.vue
  2. 16
      src/app/components/Client/Charts.vue
  3. 4
      src/app/components/Client/Client.vue
  4. 2
      src/app/components/Client/LastSeen.vue
  5. 2
      src/app/layouts/Header.vue
  6. 33
      src/app/stores/global.ts
  7. 18
      src/app/utils/api.ts
  8. 2
      src/server/api/cnf/[oneTimeLink].ts
  9. 7
      src/server/api/features.get.ts
  10. 4
      src/server/api/statistics.get.ts
  11. 2
      src/server/api/wireguard/client/[clientId]/generateOneTimeLink.post.ts
  12. 4
      src/server/utils/WireGuard.ts
  13. 8
      src/services/database/lowdb.ts
  14. 30
      src/services/database/migrations/1.ts
  15. 37
      src/services/database/repositories/system.ts

1
src/app/app.vue

@ -16,6 +16,7 @@ const globalStore = useGlobalStore();
globalStore.fetchFeatures();
globalStore.fetchRelease();
globalStore.setLanguage();
globalStore.fetchStatistics();
useHead({
bodyAttrs: {
class: 'bg-gray-50 dark:bg-neutral-800',

16
src/app/components/Client/Charts.vue

@ -1,13 +1,13 @@
<template>
<div
v-if="globalStore.features.trafficStats.type"
:class="`absolute z-0 bottom-0 left-0 right-0 h-6 ${globalStore.features.trafficStats.type === 1 && 'line-chart'}`"
v-if="globalStore.statistics.chartType"
:class="`absolute z-0 bottom-0 left-0 right-0 h-6 ${globalStore.statistics.chartType === 1 && 'line-chart'}`"
>
<UiChart :options="chartOptionsTX" :series="client.transferTxSeries" />
</div>
<div
v-if="globalStore.features.trafficStats.type"
:class="`absolute z-0 top-0 left-0 right-0 h-6 ${globalStore.features.trafficStats.type === 1 && 'line-chart'}`"
v-if="globalStore.statistics.chartType"
:class="`absolute z-0 top-0 left-0 right-0 h-6 ${globalStore.statistics.chartType === 1 && 'line-chart'}`"
>
<UiChart
:options="chartOptionsRX"
@ -33,9 +33,9 @@ const chartOptionsTX = computed(() => {
colors: [CHART_COLORS.tx[theme.value]],
};
opts.chart.type =
UI_CHART_TYPES[globalStore.features.trafficStats.type]?.type || undefined;
UI_CHART_TYPES[globalStore.statistics.chartType]?.type || undefined;
opts.stroke.width =
UI_CHART_TYPES[globalStore.features.trafficStats.type]?.strokeWidth ?? 0;
UI_CHART_TYPES[globalStore.statistics.chartType]?.strokeWidth ?? 0;
return opts;
});
@ -45,9 +45,9 @@ const chartOptionsRX = computed(() => {
colors: [CHART_COLORS.rx[theme.value]],
};
opts.chart.type =
UI_CHART_TYPES[globalStore.features.trafficStats.type]?.type || undefined;
UI_CHART_TYPES[globalStore.statistics.chartType]?.type || undefined;
opts.stroke.width =
UI_CHART_TYPES[globalStore.features.trafficStats.type]?.strokeWidth ?? 0;
UI_CHART_TYPES[globalStore.statistics.chartType]?.strokeWidth ?? 0;
return opts;
});

4
src/app/components/Client/Client.vue

@ -14,7 +14,7 @@
>
<ClientAddress4 :client="client" />
<ClientInlineTransfer
v-if="!globalStore.features.trafficStats.enabled"
v-if="!globalStore.statistics.enabled"
:client="client"
/>
<ClientLastSeen :client="client" />
@ -25,7 +25,7 @@
<!-- Info -->
<div
v-if="globalStore.features.trafficStats.enabled"
v-if="globalStore.statistics.enabled"
class="flex gap-2 items-center shrink-0 text-gray-400 dark:text-neutral-400 text-xs mt-px justify-end"
>
<ClientTransfer :client="client" />

2
src/app/components/Client/LastSeen.vue

@ -4,7 +4,7 @@
class="text-gray-400 dark:text-neutral-500 whitespace-nowrap"
:title="$t('lastSeen') + dateTime(new Date(client.latestHandshakeAt))"
>
{{ !globalStore.features.trafficStats.enabled ? ' · ' : ''
{{ !globalStore.statistics.enabled ? ' · ' : ''
}}{{ timeago(new Date(client.latestHandshakeAt)) }}
</span>
</template>

2
src/app/layouts/Header.vue

@ -38,7 +38,7 @@
</button>
<!-- Show / hide charts -->
<label
v-if="globalStore.features.trafficStats.type > 0"
v-if="globalStore.statistics.chartType > 0"
class="inline-flex items-center justify-center cursor-pointer w-8 h-8 rounded-full bg-gray-200 hover:bg-gray-300 dark:bg-neutral-700 dark:hover:bg-neutral-600 whitespace-nowrap transition group"
:title="$t('toggleCharts')"
>

33
src/app/stores/global.ts

@ -8,10 +8,6 @@ export const useGlobalStore = defineStore('Global', () => {
);
const updateAvailable = ref(false);
const features = ref({
trafficStats: {
enabled: false,
type: 0,
},
sortClients: {
enabled: false,
},
@ -22,12 +18,18 @@ export const useGlobalStore = defineStore('Global', () => {
enabled: false,
},
});
const statistics = ref({
enabled: false,
chartType: 0,
});
const sortClient = ref(true); // Sort clients by name, true = asc, false = desc
const { availableLocales, locale } = useI18n();
async function setLanguage() {
const { data: lang } = await api.getLang();
const { data: lang } = await useFetch('/api/lang', {
method: 'get',
});
if (
lang.value !== getItem('lang') &&
availableLocales.includes(lang.value!)
@ -38,7 +40,9 @@ export const useGlobalStore = defineStore('Global', () => {
}
async function fetchRelease() {
const { data: release } = await api.getRelease();
const { data: release } = await useFetch('/api/release', {
method: 'get',
});
if (!release.value) {
return;
@ -50,14 +54,25 @@ export const useGlobalStore = defineStore('Global', () => {
}
async function fetchFeatures() {
const { data: apiFeatures } = await api.getFeatures();
const { data: apiFeatures } = await useFetch('/api/features', {
method: 'get',
});
if (apiFeatures.value) {
features.value = apiFeatures.value;
}
}
async function fetchStatistics() {
const { data: apiStatistics } = await useFetch('/api/statistics', {
method: 'get',
});
if (apiStatistics.value) {
statistics.value = apiStatistics.value;
}
}
const updateCharts = computed(() => {
return features.value.trafficStats.type > 0 && uiShowCharts.value;
return statistics.value.chartType > 0 && uiShowCharts.value;
});
return {
@ -68,8 +83,10 @@ export const useGlobalStore = defineStore('Global', () => {
currentRelease,
latestRelease,
updateAvailable,
statistics,
fetchRelease,
fetchFeatures,
setLanguage,
fetchStatistics,
};
});

18
src/app/utils/api.ts

@ -1,16 +1,4 @@
class API {
async getRelease() {
return useFetch('/api/release', {
method: 'get',
});
}
async getLang() {
return useFetch('/api/lang', {
method: 'get',
});
}
async getSession() {
return useFetch('/api/session', {
method: 'get',
@ -140,12 +128,6 @@ class API {
});
}
async getFeatures() {
return useFetch('/api/features', {
method: 'get',
});
}
async updateFeatures(features: Record<string, { enabled: boolean }>) {
return $fetch('/api/admin/features', {
method: 'post',

2
src/server/api/cnf/[oneTimeLink].ts

@ -1,6 +1,6 @@
export default defineEventHandler(async (event) => {
const system = await Database.system.get();
if (!system.oneTimeLinks.enabled) {
if (!system.features.oneTimeLinks.enabled) {
throw createError({
statusCode: 404,
statusMessage: 'Invalid state',

7
src/server/api/features.get.ts

@ -1,9 +1,4 @@
export default defineEventHandler(async () => {
const system = await Database.system.get();
return {
trafficStats: system.trafficStats,
sortClients: system.sortClients,
clientExpiration: system.clientExpiration,
oneTimeLinks: system.oneTimeLinks,
};
return system.features;
});

4
src/server/api/statistics.get.ts

@ -0,0 +1,4 @@
export default defineEventHandler(async () => {
const system = await Database.system.get();
return system.statistics;
});

2
src/server/api/wireguard/client/[clientId]/generateOneTimeLink.post.ts

@ -1,6 +1,6 @@
export default defineEventHandler(async (event) => {
const system = await Database.system.get();
if (!system.oneTimeLinks.enabled) {
if (!system.features.oneTimeLinks.enabled) {
throw createError({
status: 404,
message: 'Invalid state',

4
src/server/utils/WireGuard.ts

@ -318,7 +318,7 @@ class WireGuard {
const clients = await Database.client.findAll();
const system = await Database.system.get();
// Expires Feature
if (system.clientExpiration.enabled) {
if (system.features.clientExpiration.enabled) {
for (const client of Object.values(clients)) {
if (client.enabled !== true) continue;
if (
@ -331,7 +331,7 @@ class WireGuard {
}
}
// One Time Link Feature
if (system.oneTimeLinks.enabled) {
if (system.features.oneTimeLinks.enabled) {
for (const client of Object.values(clients)) {
if (
client.oneTimeLink !== null &&

8
src/services/database/lowdb.ts

@ -19,9 +19,10 @@ import {
type OneTimeLink,
} from './repositories/client';
import {
Features,
AvailableFeatures,
SystemRepository,
type Feature,
type Features,
} from './repositories/system';
const DEBUG = debug('LowDB');
@ -46,8 +47,9 @@ export class LowDBSystem extends SystemRepository {
DEBUG('Update Features');
this.#db.update((v) => {
for (const key in features) {
if (Features.includes(key as Features)) {
v.system[key as Features].enabled = features[key]!.enabled;
if (AvailableFeatures.includes(key as keyof Features)) {
v.system.features[key as keyof Features].enabled =
features[key]!.enabled;
}
}
});

30
src/services/database/migrations/1.ts

@ -16,6 +16,10 @@ export async function run1(db: Low<Database>) {
const database: Database = {
migrations: [],
system: {
general: {
sessionTimeout: 3600, // 1 hour
lang: 'en',
},
// Config to configure Server
interface: {
privateKey: privateKey,
@ -26,10 +30,6 @@ export async function run1(db: Low<Database>) {
port: 51820,
device: 'eth0',
},
general: {
sessionTimeout: 3600, // 1 hour
lang: 'en',
},
// Config to configure Peer & Client Config
userConfig: {
mtu: 1420,
@ -49,18 +49,20 @@ export async function run1(db: Low<Database>) {
PreDown: '',
PostDown: '',
},
trafficStats: {
enabled: false,
type: ChartType.None,
},
clientExpiration: {
enabled: false,
},
oneTimeLinks: {
enabled: false,
features: {
clientExpiration: {
enabled: false,
},
oneTimeLinks: {
enabled: false,
},
sortClients: {
enabled: false,
},
},
sortClients: {
statistics: {
enabled: false,
chartType: ChartType.None,
},
metrics: {
prometheus: {

37
src/services/database/repositories/system.ts

@ -37,9 +37,9 @@ export enum ChartType {
Bar = 3,
}
export type TrafficStats = {
export type Statistics = {
enabled: boolean;
type: ChartType;
chartType: ChartType;
};
export type Prometheus = {
@ -60,34 +60,39 @@ export type General = {
lang: Lang;
};
export type Features = {
clientExpiration: Feature;
oneTimeLinks: Feature;
sortClients: Feature;
};
export const AvailableFeatures: (keyof Features)[] = [
'clientExpiration',
'oneTimeLinks',
'sortClients',
] as const;
/**
* Representing the WireGuard network configuration data structure of a computer interface system.
*/
export type System = {
interface: WGInterface;
general: General;
interface: WGInterface;
userConfig: WGConfig;
iptables: IpTables;
trafficStats: TrafficStats;
metrics: Metrics;
features: Features;
clientExpiration: Feature;
oneTimeLinks: Feature;
sortClients: Feature;
statistics: Statistics;
metrics: Metrics;
sessionConfig: SessionConfig;
};
export const Features = [
'clientExpiration',
'oneTimeLinks',
'sortClients',
] as const;
export type Features = (typeof Features)[number];
/**
* Interface for system-related database operations.
* This interface provides methods for retrieving system configuration data

Loading…
Cancel
Save