diff --git a/bun.lockb b/bun.lockb
index 42f632d..67d87b7 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/lefthook.yml b/lefthook.yml
index c2616a3..026c11e 100644
--- a/lefthook.yml
+++ b/lefthook.yml
@@ -13,3 +13,5 @@ pre-commit:
glob: "**/wrangler.toml"
run: cd view && bun generate && cd ../worker && bun generate
stage_fixed: true
+ check:
+ run: bun check
diff --git a/package.json b/package.json
index ae78c92..bb02a0a 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,7 @@
"prettier-plugin-tailwindcss": "^0.6.9"
},
"scripts": {
- "prepare": "bunx lefthook install && cd worker; bun i --frozen-lockfile && cd ../view && bun i --frozen-lockfile",
+ "prepare": "bunx lefthook install",
"generate": "cd view && bun generate && cd ../worker && bun generate",
"check": "cd view && bun check && cd ../worker && bun check"
},
diff --git a/view/bun.lockb b/view/bun.lockb
index 3b62128..61fbfe3 100755
Binary files a/view/bun.lockb and b/view/bun.lockb differ
diff --git a/view/package.json b/view/package.json
index b25b553..e00632e 100644
--- a/view/package.json
+++ b/view/package.json
@@ -6,7 +6,6 @@
"dev": "vite dev",
"build": "vite build",
"generate": "bun run cf-typegen && svelte-kit sync",
- "prepare": "bun generate",
"preview": "bun run build && wrangler pages dev",
"check": "bun generate && svelte-check --tsconfig ./tsconfig.json",
"db:push": "drizzle-kit push",
@@ -39,7 +38,6 @@
"apexcharts": "^4.0.0",
"daisyui": "^4.12.14",
"drizzle-orm": "^0.33.0",
- "svelte-apexcharts": "^1.0.2",
"valibot": "^1.0.0-beta.7"
}
}
diff --git a/view/src/components/TotalVisits.svelte b/view/src/components/TotalVisits.svelte
index 2bffff2..5081d0e 100644
--- a/view/src/components/TotalVisits.svelte
+++ b/view/src/components/TotalVisits.svelte
@@ -1,5 +1,8 @@
@@ -8,7 +11,7 @@
{total}
- {perDay.toFixed(1)} / day
+ {perHour.toFixed(1)} / Hour, {perDay.toFixed(1)} / Day
diff --git a/view/src/components/charts/Line.svelte b/view/src/components/charts/Line.svelte
index 9c2f60e..0169a87 100644
--- a/view/src/components/charts/Line.svelte
+++ b/view/src/components/charts/Line.svelte
@@ -3,7 +3,10 @@
type Props = { dataset: { name: string; data: number[] }[]; titles: Date[] };
const { dataset, titles }: Props = $props();
- $inspect(titles);
+ onMount(() => {
+ console.log(titles);
+ console.log(dataset);
+ });
const options = {
series: dataset,
chart: {
@@ -18,15 +21,17 @@
},
xaxis: {
type: 'datetime',
- categories: titles.map((title) => title.toLocaleDateString())
+ // it's written in UTC time for some reason. converting to JST.
+ categories: titles.map((title) =>
+ new Date(title.getTime() + 9 * 60 * 60 * 1000).toISOString()
+ )
},
tooltip: {
x: {
- format: 'dd/MM/yy HH:mm'
+ format: 'MM/dd HH:mm'
}
}
};
- const id = Math.random().toFixed(6).toString();
onMount(async () => {
const { default: ApexCharts } = await import('apexcharts');
const chart = new ApexCharts(document.querySelector('#chart-line'), options);
diff --git a/view/src/lib/consts.ts b/view/src/lib/consts.ts
new file mode 100644
index 0000000..306b9f8
--- /dev/null
+++ b/view/src/lib/consts.ts
@@ -0,0 +1,2 @@
+export const DAY = 24 * 60 * 60 * 1000;
+export const HOUR = 60 * 60 * 1000;
diff --git a/view/src/lib/utils.test.ts b/view/src/lib/utils.test.ts
new file mode 100644
index 0000000..f334910
--- /dev/null
+++ b/view/src/lib/utils.test.ts
@@ -0,0 +1,47 @@
+import { test, expect } from 'bun:test';
+import { groupByTime } from './utils';
+
+const now = new Date();
+const then = new Date(now.getTime() - 3 * 24 * 60 * 60 * 1000);
+
+test('group by time: 1', () => {
+ const got = groupByTime(now, then, [new Date(now.getTime() - 1 * 24 * 60 * 60 * 1000 + 50)], 3);
+ expect(got).toEqual([0, 0, 1]);
+});
+test('group by time: 2', () => {
+ const got = groupByTime(now, then, [new Date(now.getTime() - 1 * 24 * 60 * 60 * 1000 - 50)], 3);
+ expect(got).toEqual([0, 1, 0]);
+});
+test('group by time: 3', () => {
+ const got = groupByTime(now, then, [new Date(now.getTime() - 3 * 24 * 60 * 60 * 1000 + 50)], 3);
+ expect(got).toEqual([1, 0, 0]);
+});
+test('group by time: 4', () => {
+ const got = groupByTime(
+ now,
+ then,
+ [
+ new Date(now.getTime() - 1 * 24 * 60 * 60 * 1000 + 50),
+ new Date(now.getTime() - 1 * 24 * 60 * 60 * 1000 + 50),
+ new Date(now.getTime() - 1 * 24 * 60 * 60 * 1000 + 50),
+ new Date(now.getTime() - 1 * 24 * 60 * 60 * 1000 + 50),
+ new Date(now.getTime() - 1 * 24 * 60 * 60 * 1000 - 50)
+ ],
+ 3
+ );
+ expect(got).toEqual([0, 1, 4]);
+});
+test('group by time: 5', () => {
+ const got = groupByTime(
+ now,
+ then,
+ [
+ new Date(now.getTime() - 3 * 24 * 60 * 60 * 1000 + 50),
+ new Date(now.getTime() - 3 * 24 * 60 * 60 * 1000 + 50),
+ new Date(now.getTime() - 3 * 24 * 60 * 60 * 1000 + 50),
+ new Date(now.getTime() - 3 * 24 * 60 * 60 * 1000 + 50)
+ ],
+ 3
+ );
+ expect(got).toEqual([4, 0, 0]);
+});
diff --git a/view/src/lib/utils.ts b/view/src/lib/utils.ts
index 55450b0..e77d2fa 100644
--- a/view/src/lib/utils.ts
+++ b/view/src/lib/utils.ts
@@ -8,6 +8,13 @@ export function unwrap(val: T | null | undefined): T {
export function panic(message: string): never {
throw new Error(message);
}
+export function stairs(len: number, top: number): number[] {
+ const ret: number[] = [];
+ for (let i = 0; i < len; i++) {
+ ret.push((top / len) * i);
+ }
+ return ret;
+}
export function groupBy(list: T[], groupFn: (t: T) => U): { key: U; val: T[] }[] {
const map = new Map();
@@ -25,34 +32,19 @@ export function groupBy(list: T[], groupFn: (t: T) => U): { key: U; val: T
return ret;
}
-export function groupSteps(
- current: Date,
- list: Visit[],
- start: Date,
- steps: number
-): [number, Date][] {
+export function groupByTime(current: Date, start: Date, list: Date[], steps: number): number[] {
const currentTime = current.getTime();
const totalDuration = currentTime - start.getTime();
const stepWidth = totalDuration / steps;
- const values = list.map((i) => currentTime - i.at.getTime()); // all should be pos
- const result: [number, Date][] = groupInSteps(values, stepWidth).map(
- (count, index) => [count, new Date(currentTime - index * stepWidth)] as const
- );
- return result;
+ const values = list.map((i) => currentTime - i.getTime()); // all should be pos
+ const result: number[] = groupInSteps(values, totalDuration, steps);
+ return result.reverse();
}
-export function stairs(len: number, top: number): number[] {
- const ret: number[] = [];
- for (let i = 0; i < len; i++) {
- ret.push((top / len) * i);
- }
- return ret;
-}
-function groupInSteps(list: number[], stepWidth: number): number[] {
- const maxVal = list.reduce((a, b) => Math.max(a, b));
- const steps = Math.ceil(maxVal / stepWidth);
- const arr: number[] = new Array(steps + 1).fill(0);
- for (const datapoint of list) {
+export function groupInSteps(input: number[], maxVal: number, length: number): number[] {
+ const stepWidth = Math.ceil(maxVal / length);
+ const arr: number[] = new Array(length).fill(0);
+ for (const datapoint of input) {
const idx = Math.floor(datapoint / stepWidth);
const val = arr[idx];
if (val === undefined)
diff --git a/view/src/pages/Dashboard.svelte b/view/src/pages/Dashboard.svelte
index fef21a8..f005480 100644
--- a/view/src/pages/Dashboard.svelte
+++ b/view/src/pages/Dashboard.svelte
@@ -3,7 +3,8 @@
import Line from '~/components/charts/Line.svelte';
import PieChart from '~/components/charts/PieChart.svelte';
import type { Visit } from '~/db/schema';
- import { groupBy, groupSteps, stairs } from '~/lib/utils';
+ import { HOUR } from '~/lib/consts';
+ import { groupBy, groupByTime, stairs } from '~/lib/utils';
import type { Kind } from '~/share/schema';
const URL_LABELS = new Map([
@@ -15,17 +16,19 @@
['security.utcode.net', 'セキュリティ']
]);
- const SAMPLING_COUNT = 20;
-
type Props = {
data: Visit[];
duration: number;
lastFetch: Date;
kind: Kind | 'all';
};
- const MILLISECS_PER_DAY = 24 * 60 * 60 * 1000;
const { data, duration, lastFetch }: Props = $props();
- const start = $derived(new Date(lastFetch.getTime() - duration * MILLISECS_PER_DAY));
+ function clamp(target: number, min: number, max: number) {
+ return Math.min(Math.max(target, min), max);
+ }
+
+ const SAMPLING_COUNT = $derived(clamp(duration / HOUR, 6, 20)); // limit sampling count to 20 if it's too big
+ const start = $derived(new Date(lastFetch.getTime() - duration));
const sanitizedData = $derived(
data.map((item) => {
const sanitized = item.url.split('://')[1]?.split('/')[0];
@@ -55,16 +58,22 @@
(v) => new Date(v + start.getTime())
)
);
+ $inspect(titles);
const linedata = $derived(
grouped.map((e) => ({
name: URL_LABELS.get(e.key) ?? e.key,
- data: groupSteps(lastFetch, e.val, start, SAMPLING_COUNT).map((row) => row[0])
+ data: groupByTime(
+ lastFetch,
+ start,
+ e.val.map((i) => i.at),
+ SAMPLING_COUNT
+ )
}))
);
-
+
diff --git a/view/src/routes/+page.svelte b/view/src/routes/+page.svelte
index 73c64db..d922edc 100644
--- a/view/src/routes/+page.svelte
+++ b/view/src/routes/+page.svelte
@@ -1,4 +1,5 @@