From 189c52dd4d5aad93fdb903387c03299646e57f97 Mon Sep 17 00:00:00 2001 From: ccbikai Date: Sun, 11 May 2025 17:49:27 +0800 Subject: [PATCH 1/2] chore: remove realtime dashboard tab Temporarily disables the realtime dashboard navigation option by commenting out its TabsTrigger component in the navigation menu. This indicates a planned feature or work in progress. --- app/components/dashboard/Nav.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/components/dashboard/Nav.vue b/app/components/dashboard/Nav.vue index 1f74333..87871a0 100644 --- a/app/components/dashboard/Nav.vue +++ b/app/components/dashboard/Nav.vue @@ -18,9 +18,9 @@ const route = useRoute() {{ $t('nav.analysis') }} - + From dc23ff02a2ee7f1ca06061455b583c479c4ce58c Mon Sep 17 00:00:00 2001 From: ccbikai Date: Sun, 11 May 2025 18:33:47 +0800 Subject: [PATCH 2/2] feat: add latitude/longitude to access logs Extends access logging system to support geographic coordinates: - Adds `doublesMap` for latitude/longitude data - Updates type definitions to handle numeric values - Implements conversion functions for double values - Captures coordinates from Cloudflare headers Enhances analytics capabilities for real-time globe visualization --- server/utils/access-log.ts | 59 +++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/server/utils/access-log.ts b/server/utils/access-log.ts index c79de32..2db2a55 100644 --- a/server/utils/access-log.ts +++ b/server/utils/access-log.ts @@ -36,27 +36,59 @@ export const blobsMap = { blob15: 'deviceType', } as const +export const doublesMap = { + double1: 'latitude', + double2: 'longitude', +} as const + export type BlobsMap = typeof blobsMap export type BlobsKey = keyof BlobsMap -export type LogsKey = BlobsMap[BlobsKey] -export type LogsMap = { [key in LogsKey]: string | undefined } -export const logsMap: LogsMap = Object.entries(blobsMap).reduce((acc, [k, v]) => ({ ...acc, [v]: k }), {}) as LogsMap +export type DoublesMap = typeof doublesMap +export type DoublesKey = keyof DoublesMap + +export type LogsKey = BlobsMap[BlobsKey] | DoublesMap[DoublesKey] +export type LogsMap = { + [key in BlobsMap[BlobsKey]]: string | undefined +} & { + [key in DoublesMap[DoublesKey]]?: number | undefined +} + +export const logsMap = { + ...Object.entries(blobsMap).reduce((acc, [k, v]) => ({ ...acc, [v]: k }), {}), + ...Object.entries(doublesMap).reduce((acc, [k, v]) => ({ ...acc, [v]: k }), {}), +} as LogsMap function logs2blobs(logs: LogsMap) { - // @ts-expect-error todo - return Object.keys(blobsMap).sort((a, b) => toBlobNumber(a) - toBlobNumber(b)).map(key => logs[blobsMap[key]] || '') + return (Object.keys(blobsMap) as BlobsKey[]) + .sort((a, b) => toBlobNumber(a) - toBlobNumber(b)) + .map(key => String(logs[blobsMap[key] as LogsKey] || '')) } function blobs2logs(blobs: string[]) { const logsList = Object.keys(blobsMap) - // @ts-expect-error todo - return blobs.reduce((logs: LogsMap, blob, i) => { - // @ts-expect-error todo - logs[blobsMap[logsList[i]]] = blob + return blobs.reduce((logs, blob, i) => { + const key = blobsMap[logsList[i] as BlobsKey] + logs[key] = blob return logs - }, {}) + }, {} as Partial) +} + +function logs2doubles(logs: LogsMap) { + return (Object.keys(doublesMap) as DoublesKey[]) + .sort((a, b) => toBlobNumber(a) - toBlobNumber(b)) + .map(key => Number(logs[doublesMap[key] as LogsKey] || 0)) +} + +function doubles2logs(doubles: number[]) { + const logsList = Object.keys(doublesMap) + + return doubles.reduce((logs, double, i) => { + const key = doublesMap[logsList[i] as DoublesKey] + logs[key] = double + return logs + }, {} as Partial) } export function useAccessLog(event: H3Event) { @@ -108,16 +140,21 @@ export function useAccessLog(event: H3Event) { browserType: uaInfo?.browser?.type, device: uaInfo?.device?.model, deviceType: uaInfo?.device?.type, + + // For RealTime Globe + latitude: Number(cf?.latitude || getHeader(event, 'cf-iplatitude') || 0), + longitude: Number(cf?.longitude || getHeader(event, 'cf-iplongitude') || 0), } if (process.env.NODE_ENV === 'production') { return hubAnalytics().put({ indexes: [link.id], // only one index blobs: logs2blobs(accessLogs), + doubles: logs2doubles(accessLogs), }) } else { - console.log('access logs:', logs2blobs(accessLogs), blobs2logs(logs2blobs(accessLogs))) + console.log('access logs:', accessLogs, logs2blobs(accessLogs), logs2doubles(accessLogs), { ...blobs2logs(logs2blobs(accessLogs)), ...doubles2logs(logs2doubles(accessLogs)) }) return Promise.resolve() } }