From 76e82d9b98b0537d0cccf717602cbe619e59d9fe Mon Sep 17 00:00:00 2001 From: Bohdan Date: Wed, 3 Jun 2026 12:44:24 +0300 Subject: [PATCH 1/5] fix: migrate middleware to proxy (Next.js 16) --- CLAUDE.md | 2 +- dashboard/{middleware.ts => proxy.ts} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename dashboard/{middleware.ts => proxy.ts} (94%) diff --git a/CLAUDE.md b/CLAUDE.md index 89e596a..9712d69 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -36,7 +36,7 @@ and generating AI session reports. ## Auth Auth logic lives ONLY in: - `dashboard/app/api/auth/**/route.ts` — Google token verification, JWT issuance -- `dashboard/middleware.ts` — route guarding for `/dashboard/*` +- `dashboard/proxy.ts` — route guarding for `/dashboard/*` Do NOT duplicate auth checks in components, server modules, or other Route Handlers. diff --git a/dashboard/middleware.ts b/dashboard/proxy.ts similarity index 94% rename from dashboard/middleware.ts rename to dashboard/proxy.ts index b8db4aa..9b26a12 100644 --- a/dashboard/middleware.ts +++ b/dashboard/proxy.ts @@ -6,7 +6,7 @@ export const config = { matcher: ["/dashboard/:path*", "/login"], }; -export async function middleware(req: NextRequest) { +export async function proxy(req: NextRequest) { const token = req.cookies.get(SESSION_COOKIE)?.value; const isAuth = token ? await isValid(token) : false; From 684cdecbcb545595010c087cc1de7100d6f55941 Mon Sep 17 00:00:00 2001 From: Bohdan Date: Wed, 3 Jun 2026 13:00:13 +0300 Subject: [PATCH 2/5] docs: recommend sslmode=verify-full for Neon in .env.example --- dashboard/.env.example | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/dashboard/.env.example b/dashboard/.env.example index 7fa4ac7..28c672e 100644 --- a/dashboard/.env.example +++ b/dashboard/.env.example @@ -1,9 +1,14 @@ # ─── Database ────────────────────────────────────────────────────────────────── # Local dev: credentials from docker-compose.yml (port 5433). -# Neon (prod): https://console.neon.tech → your project → Connection string -# Use the "Pooled connection" string for DATABASE_URL. DATABASE_URL="postgresql://postgres:postgres@localhost:5433/worktrace" +# Neon (prod): https://console.neon.tech → your project → Connection string +# Use the "Pooled connection" string for DATABASE_URL and direct for DIRECT_URL. +# Use sslmode=verify-full (not require) to keep certificate verification and silence +# the pg-connection-string deprecation warning. +# DIRECT_URL="postgresql://:@.neon.tech/?sslmode=verify-full" +# DATABASE_URL="postgresql://:@-pooler.neon.tech/?sslmode=verify-full&channel_binding=require" + # ─── Google OAuth ────────────────────────────────────────────────────────────── # https://console.cloud.google.com → APIs & Services → Credentials # → "+ CREATE CREDENTIALS" → "OAuth 2.0 Client ID" From eceaa2bfe4fca384d48f6395f19f80d6bcaef232 Mon Sep 17 00:00:00 2001 From: Bohdan Date: Wed, 3 Jun 2026 13:04:11 +0300 Subject: [PATCH 3/5] feat(dashboard): auto-refetch every 30s to sync extension events --- dashboard/app/dashboard/charts.tsx | 5 +++-- dashboard/app/dashboard/event-feed.tsx | 1 + dashboard/app/dashboard/music/music-client.tsx | 5 +++-- dashboard/app/dashboard/top-sessions.tsx | 7 ++++--- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/dashboard/app/dashboard/charts.tsx b/dashboard/app/dashboard/charts.tsx index 8f26b6e..d8e46b8 100644 --- a/dashboard/app/dashboard/charts.tsx +++ b/dashboard/app/dashboard/charts.tsx @@ -36,8 +36,9 @@ export function Charts({ filters }: { filters: FeedFilters }) { const { data, isLoading, isError } = useQuery({ queryKey: statsQueryKey(filters), queryFn: () => fetchEventStats(filters), - placeholderData: keepPreviousData, - staleTime: 30_000, + placeholderData: keepPreviousData, + staleTime: 30_000, + refetchInterval: 30_000, }); if (isLoading) { diff --git a/dashboard/app/dashboard/event-feed.tsx b/dashboard/app/dashboard/event-feed.tsx index 6d9e576..ba256c5 100644 --- a/dashboard/app/dashboard/event-feed.tsx +++ b/dashboard/app/dashboard/event-feed.tsx @@ -33,6 +33,7 @@ export function EventFeed({ filters, onClearFilters }: Props) { getNextPageParam: (last) => last.nextCursor, placeholderData: keepPreviousData, staleTime: 30_000, + refetchInterval: 30_000, }); const events = useMemo( diff --git a/dashboard/app/dashboard/music/music-client.tsx b/dashboard/app/dashboard/music/music-client.tsx index 292bcc7..e68422a 100644 --- a/dashboard/app/dashboard/music/music-client.tsx +++ b/dashboard/app/dashboard/music/music-client.tsx @@ -56,8 +56,9 @@ export function MusicClient() { const { data, isLoading, isError, error } = useQuery({ queryKey: ["music-stats", range], queryFn: () => fetchMusicStats(range), - placeholderData: keepPreviousData, - staleTime: 60_000, + placeholderData: keepPreviousData, + staleTime: 60_000, + refetchInterval: 60_000, }); return ( diff --git a/dashboard/app/dashboard/top-sessions.tsx b/dashboard/app/dashboard/top-sessions.tsx index a323400..fc2cb56 100644 --- a/dashboard/app/dashboard/top-sessions.tsx +++ b/dashboard/app/dashboard/top-sessions.tsx @@ -28,9 +28,10 @@ function localDay(iso: string): string { export function TopSessions() { const { data, isLoading, isError } = useQuery({ - queryKey: topSessionsQueryKey, - queryFn: fetchTopSessions, - staleTime: 30_000, + queryKey: topSessionsQueryKey, + queryFn: fetchTopSessions, + staleTime: 30_000, + refetchInterval: 30_000, }); if (isLoading) { From a4ea1bc1efac02715a832df2af6c804eac55fd0a Mon Sep 17 00:00:00 2001 From: Bohdan Date: Wed, 3 Jun 2026 13:07:29 +0300 Subject: [PATCH 4/5] fix(ci): semver zip name + skip duplicate release; bump extension to v1.0.2 --- .github/workflows/release-extension.yml | 20 ++++++++++++++------ extension/package.json | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release-extension.yml b/.github/workflows/release-extension.yml index fd86a1f..de56600 100644 --- a/.github/workflows/release-extension.yml +++ b/.github/workflows/release-extension.yml @@ -18,25 +18,33 @@ jobs: cache: "npm" cache-dependency-path: extension/package-lock.json + - name: Get version + id: version + run: echo "version=$(node -p "require('./extension/package.json').version")" >> $GITHUB_OUTPUT + - name: Build extension working-directory: extension run: npm ci && npm run build - name: Package extension working-directory: extension - shell: pwsh - run: Compress-Archive -Path dist/* -DestinationPath worktrace-extension.zip -Force + run: zip -r "worktrace-extension-v${{ steps.version.outputs.version }}.zip" dist/ - name: Create GitHub Release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gh release create "extension-build-${{ github.run_number }}" \ - extension/worktrace-extension.zip \ - --title "WorkTrace Extension — build ${{ github.run_number }}" \ + VERSION="v${{ steps.version.outputs.version }}" + if gh release view "$VERSION" &>/dev/null; then + echo "Release $VERSION already exists — skipping." + exit 0 + fi + gh release create "$VERSION" \ + "extension/worktrace-extension-$VERSION.zip" \ + --title "WorkTrace Extension $VERSION" \ --notes "Автоматичний реліз при мерджі в main. Щоб завантажити розширення: - 1. Скачай \`worktrace-extension.zip\` + 1. Скачай \`worktrace-extension-$VERSION.zip\` 2. Розпакуй у будь-яку папку 3. chrome://extensions → Developer mode → Load unpacked → обери розпаковану папку" diff --git a/extension/package.json b/extension/package.json index b29f71e..2985b5c 100644 --- a/extension/package.json +++ b/extension/package.json @@ -1,6 +1,6 @@ { "name": "worktrace-extension", - "version": "0.1.0", + "version": "1.0.2", "private": true, "type": "module", "description": "WorkTrace Chrome extension — captures dev session context.", From cf5d4f210bd47e50cdf4a1f43c4f6fb9cf81c2f7 Mon Sep 17 00:00:00 2001 From: Bohdan Date: Wed, 3 Jun 2026 13:09:43 +0300 Subject: [PATCH 5/5] fix(dashboard): URL events as clickable links, note events as plain text --- dashboard/app/dashboard/event-card.tsx | 41 +++++++++++++++++++------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/dashboard/app/dashboard/event-card.tsx b/dashboard/app/dashboard/event-card.tsx index d7908f2..f5e8933 100644 --- a/dashboard/app/dashboard/event-card.tsx +++ b/dashboard/app/dashboard/event-card.tsx @@ -12,6 +12,10 @@ function hostnameOf(url: string): string { } } +function isHttpUrl(url: string): boolean { + return url.startsWith("http://") || url.startsWith("https://"); +} + function utcShort(iso: string): string { return iso.slice(0, 16).replace("T", " ") + " UTC"; } @@ -36,15 +40,21 @@ export function EventCard({ event }: { event: EventDTO }) { whileHover={{ scale: 1.012, transition: { duration: 0.15 } }} >
- - {event.title} - + {isHttpUrl(event.url) ? ( + + {event.title} + + ) : ( + + {event.title} + + )}
{event.content ? (