From 814624bc65f147bebffaad87501db8d0fdd80429 Mon Sep 17 00:00:00 2001 From: Bernd Storath <999999bst@gmail.com> Date: Fri, 13 Sep 2024 14:48:49 +0200 Subject: [PATCH] basic statistics page --- src/app/pages/admin/features.vue | 7 +- src/app/pages/admin/statistics.vue | 115 +++++++++++++++++++ src/app/utils/api.ts | 7 -- src/server/api/admin/statistics.post.ts | 8 ++ src/server/utils/types.ts | 17 ++- src/services/database/lowdb.ts | 15 +++ src/services/database/repositories/system.ts | 1 + 7 files changed, 160 insertions(+), 10 deletions(-) create mode 100644 src/app/pages/admin/statistics.vue create mode 100644 src/server/api/admin/statistics.post.ts diff --git a/src/app/pages/admin/features.vue b/src/app/pages/admin/features.vue index 1593356c..88cac238 100644 --- a/src/app/pages/admin/features.vue +++ b/src/app/pages/admin/features.vue @@ -37,7 +37,7 @@ const globalStore = useGlobalStore(); const open = ref(false); type ExtendedFeatures = { - [K in keyof Omit<(typeof globalStore)['features'], 'trafficStats'>]: { + [K in keyof (typeof globalStore)['features']]: { name: string; description: string; enabled: boolean; @@ -71,7 +71,10 @@ function toggleFeature(key: keyof ExtendedFeatures) { } async function submit() { - const response = await api.updateFeatures(featuresData.value); + const response = await $fetch('/api/admin/features', { + method: 'post', + body: { features: featuresData.value }, + }); if (response.success) { open.value = true; } diff --git a/src/app/pages/admin/statistics.vue b/src/app/pages/admin/statistics.vue new file mode 100644 index 00000000..0f4a828e --- /dev/null +++ b/src/app/pages/admin/statistics.vue @@ -0,0 +1,115 @@ + + + diff --git a/src/app/utils/api.ts b/src/app/utils/api.ts index 72350bea..7ef85a89 100644 --- a/src/app/utils/api.ts +++ b/src/app/utils/api.ts @@ -127,13 +127,6 @@ class API { body: { username, password }, }); } - - async updateFeatures(features: Record) { - return $fetch('/api/admin/features', { - method: 'post', - body: { features }, - }); - } } type WGClientReturn = Awaited< diff --git a/src/server/api/admin/statistics.post.ts b/src/server/api/admin/statistics.post.ts new file mode 100644 index 00000000..5865580e --- /dev/null +++ b/src/server/api/admin/statistics.post.ts @@ -0,0 +1,8 @@ +export default defineEventHandler(async (event) => { + const { statistics } = await readValidatedBody( + event, + validateZod(statisticsType) + ); + await Database.system.updateStatistics(statistics); + return { success: true }; +}); diff --git a/src/server/utils/types.ts b/src/server/utils/types.ts index 2d3568e6..3e1697de 100644 --- a/src/server/utils/types.ts +++ b/src/server/utils/types.ts @@ -59,7 +59,7 @@ const oneTimeLink = z .pipe(safeStringRefine); const features = z.record( - z.string({ message: 'key must be a valid object' }), + z.string({ message: 'key must be a valid string' }), z.object( { enabled: z.boolean({ message: 'enabled must be a valid boolean' }), @@ -69,6 +69,14 @@ const features = z.record( { message: 'features must be a valid record' } ); +const statistics = z.object( + { + enabled: z.boolean({ message: 'enabled must be a valid boolean' }), + chartType: z.number({ message: 'chartType must be a valid number' }), + }, + { message: 'statistics must be a valid object' } +); + const objectMessage = 'Body must be a valid object'; export const clientIdType = z.object( @@ -145,6 +153,13 @@ export const featuresType = z.object( { message: objectMessage } ); +export const statisticsType = z.object( + { + statistics: statistics, + }, + { message: objectMessage } +); + export function validateZod(schema: ZodSchema) { return async (data: unknown) => { try { diff --git a/src/services/database/lowdb.ts b/src/services/database/lowdb.ts index d4018bd6..762dcff2 100644 --- a/src/services/database/lowdb.ts +++ b/src/services/database/lowdb.ts @@ -20,9 +20,11 @@ import { } from './repositories/client'; import { AvailableFeatures, + ChartType, SystemRepository, type Feature, type Features, + type Statistics, } from './repositories/system'; const DEBUG = debug('LowDB'); @@ -54,6 +56,19 @@ export class LowDBSystem extends SystemRepository { } }); } + + async updateStatistics(statistics: Statistics) { + DEBUG('Update Statistics'); + this.#db.update((v) => { + v.system.statistics.enabled = statistics.enabled; + if ( + statistics.chartType >= ChartType.None && + statistics.chartType <= ChartType.Bar + ) { + v.system.statistics.chartType = statistics.chartType; + } + }); + } } export class LowDBUser extends UserRepository { diff --git a/src/services/database/repositories/system.ts b/src/services/database/repositories/system.ts index 474808a2..2a82d0eb 100644 --- a/src/services/database/repositories/system.ts +++ b/src/services/database/repositories/system.ts @@ -105,4 +105,5 @@ export abstract class SystemRepository { abstract get(): Promise; abstract updateFeatures(features: Record): Promise; + abstract updateStatistics(statistics: Statistics): Promise; }