Merge branch 'main' into feat/issue-1932-edge-functions#2383
Closed
knoxiboy wants to merge 7 commits into
Closed
Conversation
GSSoC Label Checklist 🏷️@Priyanshu-byte-coder — please apply the appropriate labels before merging: Difficulty (pick one):
Quality (optional):
Validation (required to score):
|
Contributor
Author
|
Closing as not planned — this PR was created by mistake (automated script ran unintentionally). The issue is already addressed upstream. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implementing changes for branch feat/issue-1932-edge-functions:
Merge branch 'main' into feat/issue-1932-edge-functions
fix: add fflate to package.json dependencies
chore: normalize package-lock.json after dnd-kit install
Co-Authored-By: Priyanshu Doshi priyanshu@devtrack.dev
fix: improve GitHub integration error handling and loading states (#2061)
feat: add streak freeze feature ([FEAT] Streak freeze — allow users to protect a streak day #37)
fix: revert package-lock.json
fix: improve GitHub integration error handling and loading states
fix: resolve merge conflict in streak freeze route
ci: trigger checks
fix: add input sanitization to prevent XSS (fix: add input sanitization to prevent XSS vulnerabilities #2054)
feat(dashboard): add customizable widget layout (feat(dashboard): add customizable widget layout #2006)
feat: add shareable links for individual goals (feat: add shareable links for individual goals #2037)
feat(export): add portable user data export (feat(export): add portable user data export #2036)
Closes #240
Adds GET /api/user/export returning a ZIP archive with all user-owned
data in portable formats. The existing /api/user/data-export endpoint
(JSON only) is preserved; this new endpoint supplements it.
Architecture discovered
with rate limiting via data_export_audit table (1 export/hour),
audit logging (ip, user-agent, timestamp), and field redaction.
endpoint; updated to use the new ZIP endpoint.
directly for in-memory ZIP generation via zipSync/strToU8.
goals.json export.
Export format (ZIP structure)
devtrack-export-YYYY-MM-DD.zip
├── metadata.json version, exportedAt, userId, githubLogin
├── profile.json account row, linked GitHub accounts, webhooks
├── goals.json goals + goalHistory (graceful if table absent)
├── streaks.json streak_freezes, streak_milestones, snapshots
└── contributions.csv metric_snapshots (date + counts) as CSV
Security review
password, api_key, access_key, refresh, credential, private, auth,
iv, hash before the value reaches the ZIP.
are never selected. Linked accounts omit token columns entirely.
Audit logging
Reuses the existing data_export_audit table. Every successful export
writes action='export' with IP and User-Agent. Logging failure is
non-fatal (swallowed) to not block the export.
Rate limiting
Shares the existing data_export_audit rate-limit check (1 export/hour).
Both the JSON and ZIP endpoints count against the same user budget.
429 response includes Retry-After header and retryAfterSeconds in body.
UI (PrivacySettings.tsx)
Tests (test/user-export.test.ts) — 25 tests
404 (unknown user), 429 (rate limited), Retry-After header,
retryAfterSeconds body, 200 on first export, application/zip
Content-Type, non-empty body, Content-Disposition with .zip,
Cache-Control no-store, audit log written on success,
audit log NOT written when rate-limited.
double-quote escaping, null as empty cell.
Verification
npm run lint warnings only (all pre-existing)
npm run type-check clean
npm run test test/user-export.test.ts 25/25 passed
feat(github): add organization contribution support (#2035)
Closes #1039
Architecture discovered
DevTrack uses GitHub OAuth with scopes read:user user:email repo
read:discussion (no read:org previously). The contributions metrics
route already uses author:{login} in GitHub Search queries, which
inherently includes commits in org repos — organization contributions
were already counted but there was no way to:
OAuth scope changes
Added read:org to the GitHub provider scope in src/lib/auth.ts.
Without this scope the /user/orgs endpoint returns only public
memberships; with it all memberships are visible. Existing sessions
continue working — they show public org memberships only until the
user reauthorizes.
Organization sync strategy
GET /api/user/github-orgs fetches the live org list from GitHub,
upserts discovered orgs into user_github_orgs (preserving existing
include_in_metrics preferences), then returns the merged list.
No token storage is required: the primary access token is reused for
all org API calls.
Org filtering in contributions
The contributions route now handles accountId values prefixed with
org: (e.g. accountId=org:acme-corp). When this pattern is detected
the primary token is used and org:acme-corp is appended to the GitHub
commit search query, scoping results to that organization. Existing
personal and combined-account paths are unchanged.
Privacy controls
PATCH /api/user/github-orgs lets users toggle include_in_metrics
per org. AccountToggle shows only orgs where include_in_metrics=true
so users can exclude specific orgs from dashboard filtering without
deleting the discovery record.
Dashboard integration
AccountToggle now renders an Organizations section below the account
buttons. Clicking an org button sets selectedAccount = org:{orgLogin},
which ContributionGraph (and other components that already read
selectedAccount) pass as accountId to the contributions API. No
changes to AccountContext or any metric widget were required.
Migration
supabase/migrations/20260604000000_add_user_github_orgs.sql
user_github_orgs table: id, user_id (FK + cascade), org_login,
org_id, avatar_url, include_in_metrics (default true), timestamps.
unique(user_id, org_id), RLS policies matching project conventions.
Files changed
src/lib/auth.ts — add read:org scope
src/lib/github-orgs.ts — fetchUserOrgs(), orgSearchSegment()
src/app/api/user/github-orgs/route.ts — GET + PATCH handlers
src/app/api/metrics/contributions/route.ts — org: prefix support
src/components/AccountToggle.tsx — Organizations section
supabase/migrations/20260604000000_add_user_github_orgs.sql
Tests added (21 tests)
test/github-orgs.test.ts:
fetchUserOrgs — success, 403 (missing scope), network error, empty
orgSearchSegment — with org, null, undefined, empty string
GET /api/user/github-orgs — 401, merged prefs, no orgs, db error
PATCH /api/user/github-orgs — 401, success, missing orgId,
non-bool includeInMetrics, invalid JSON, db error
contributions org: prefix — query contains org filter, 400 for
empty org name, no filter for personal view
Verification
tsc --noEmit clean
next lint warnings only (all pre-existing)
vitest run 21/21 passed
fix: store only API key prefix instead of hash to prevent credential exposure (#2038)
fix: show native share button on desktop browsers with Web Share API support
feat: add downloadable profile share card
fix: store api_key prefix only and remove OR filter in auth query
fix: resolve ShareProfileSection JSX issues
fix: remove duplicate className props in share profile section
perf: optimize dashboard component rerender behavior (perf: optimize dashboard component rerender behavior #2053)
fix: isolate metrics comparison cache by requester
perf: reduce unnecessary dashboard rerenders
fix: isolate metrics comparison cache by requester (fix: isolate metrics comparison cache by requester context #2050)
fix: reduce race condition window in local coding API limits (fix: reduce race condition window in local coding API limits #2044)
fix(security): prevent reverse tabnabbing in ResumePreview (fix(security): prevent reverse tabnabbing in ResumePreview #2032)
fix(security): prevent reverse tabnabbing in ContributionAnalysisPanel (fix(security): prevent reverse tabnabbing in ContributionAnalysisPanel #2031)
security: add X-XSS-Protection, X-DNS-Prefetch-Control, and CSP headers
fix(security): override serialize-javascript to mitigate RCE (#2028)
fix(security): add Strict-Transport-Security header (#2023)
fix(security): disable X-Powered-By header (#2027)
fix: surface github rate limit errors (#2012)
fix: handle Resend API failures in weekly digest cron job (#2042)
fix: resolve pagination state reset bug in RecentActivity (#2043)
fix: resolve pagination state reset bug in RecentActivity
fix: address Copilot AI review feedback for pagination
feat: persist last synced timestamp to localStorage (feat: persist last synced timestamp in localStorage #2049)
fix(date-utils): add dateDiff alias to resolve import error ([Bug]: Typo in exported function name causes build/import error in src/lib/date-utils.ts #1879) (fix(date-utils): add dateDiff alias to resolve import error (#1879) #2009)
fix: add loading spinner to ExportButton during export ([BUG] ExportButton does not indicate progress for large data exports #1912) (fix: add loading spinner to ExportButton during export (#1912) #2008)
fix(ci): replace broken actions/first-interaction with github-script in welcome workflow ([BUG]
welcome.ymlfails for non-first-time contributors on every PR #2039) (fix(ci): replace broken actions/first-interaction with github-script in welcome workflow #2041)docs(coc): add GSSoC contributor procedures section (docs(coc): add GSSoC contributor procedures section #2064)
Adds a clearly-labelled 'GSSoC Contributor Procedures' section to
CODE_OF_CONDUCT.md covering:
The Contributor Covenant content is preserved verbatim above; this
adds supplementary procedures specific to GSSoC 2026 contributors.
Closes #1945
docs: add GSSoC pooling guidelines reference manual (#1955) (#2055)
Co-authored-by: unknown Naresh@VisweswaraRaoM15.vanaralu.com
docs: add GSSoC semantic versioning reference manual (#1951) (#2056)
Co-authored-by: unknown Naresh@VisweswaraRaoM15.vanaralu.com
docs: add GSSoC reporting escalation reference manual (#1959) (#2057)
Co-authored-by: unknown Naresh@VisweswaraRaoM15.vanaralu.com
fix: add documentation for SSE security safeguards and confirm connection cap fix (#1687) (#2059)
Co-authored-by: unknown Naresh@VisweswaraRaoM15.vanaralu.com
docs: enhance PR template with accessibility checklist and reviewer guidance (Closes #2011) (#2062)
leaderboard: add persistent Supabase cache, dedupe builds, and scheduled rebuild endpoint
feat(profile): add responsive ProfileCard component and demo
feat(landing): replace hardcoded stats with live GitHub repo metrics
Stats section now shows real data fetched from GitHub API:
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
fix(cv): pass analysis from client to generate endpoint
The Supabase cv_analyses table upsert silently fails (table may not
exist), so the generate step couldn't find the cached analysis. Now
the client sends the analysis object it already has from step 1
directly in the generate request body, with Supabase lookup as
fallback.
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
fix(cv): reduce GraphQL query size and retry on transient 502/503
Query was fetching 50 repos × 20 PRs × 30 commits in one call,
exceeding GitHub's GraphQL complexity budget and causing 502.
Reduced to 20 repos × 10 PRs × 15 commits — still plenty for
resume generation. Added retry with backoff for transient 502/503.
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
fix(cv): handle GraphQL errors and surface real error messages
messages instead of silently returning null data
generic "Internal server error" to aid debugging
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
fix(cv): pass githubLogin instead of githubId to GitHub GraphQL API
fetchContributionData calls user(login: ...) in GraphQL which expects
the string login (e.g. "octocat"), not the numeric GitHub ID. Passing
githubId caused the query to return null for every user, making the
entire analyze step throw "GitHub user not found."
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
fix: settings page caching, landing page updates, and settings UI cleanup
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
perf: extend weekly-summary cache TTL from 5min to 30min
Weekly summary makes 3+ GitHub Search API calls per miss. The previous
5-minute TTL with an invalid cache key type (
as any) bypassed theMETRICS_CACHE_TTL_SECONDS lookup entirely. Now uses the proper typed key
so Redis and in-memory cache both apply correctly.
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
perf: reduce Supabase egress and clean up dashboard UI
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
fix(sync): move decryptTokenEdge to separate file to resolve Edge webpack build error
feat: resolve all Copilot PR suggestions
Merge branch 'main' into feat/issue-1932-edge-functions
feat: implement actual Edge-compatible WakaTime and GitHub sync
feat(csrf): add Origin/Referer CSRF protection for state-changing API routes
feat: persist daily focus goals in database with GET/POST/DELETE API route
fix: persist daily focus goals in database
fix: resolve app user for daily focus
Co-authored-by: nishupr <nishupundir073.com>
feat: add confetti celebration for personal records and unit tests for PersonalRecords component
feat: add confetti animation when user hits a new personal record ([FEAT] Add confetti animation when user hits a new personal record #1902)
fix(e2e): resolve playwright smoke test failures by mocking unhandled API endpoints and landing page fetch rates
fix(e2e): update theme tests to match new multi-theme palette selector UI
test: add comprehensive unit tests and E2E mock routes for date-utils and new API endpoints
test: add comprehensive unit tests for date-utils.ts and fix timezone sensitivity in existing dateUtils tests
fix(e2e): resolve playwright smoke test failures by mocking unhandled API endpoints and landing page fetch rates
fix(e2e): update theme tests to match new multi-theme palette selector UI
feat: add secure POST /api/leaderboard/rebuild endpoint with persistent Supabase cache
leaderboard: add persistent Supabase cache, dedupe builds, and scheduled rebuild endpoint
tsconfig: target ES2022 and include vitest types
perf: debounce repo search input and fix GoalTracker percentage
perf: debounce repo search input in RepoAnalyticsExplorer
fix: show minimum 1% progress for non-zero goal completion
Update GoalTracker.tsx
feat: add rich tooltip to TopRepos showing name, description, and language
chore(deps): bump actions/github-script from 7 to 9 (chore(deps): bump actions/github-script from 7 to 9 #2000)
Bumps actions/github-script from 7 to 9.
updated-dependencies:
dependency-version: '9'
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
chore(deps): bump actions/first-interaction from 1 to 3 (#1999)
Bumps actions/first-interaction from 1 to 3.
updated-dependencies:
dependency-version: '3'
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
chore(deps): bump actions/setup-node from 4 to 6 (#1998)
Bumps actions/setup-node from 4 to 6.
updated-dependencies:
dependency-version: '6'
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
chore(deps): bump actions/checkout from 4 to 6 (#1997)
Bumps actions/checkout from 4 to 6.
updated-dependencies:
dependency-version: '6'
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
chore(deps): bump actions/stale from 9 to 10 (#1996)
Bumps actions/stale from 9 to 10.
updated-dependencies:
dependency-version: '10'
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
fix: extend streak lookback from 90 to 365 days to prevent silent breaks
[Test] : Base schema.sql is out of sync with recent migrations, causing 500 Errors for local setups
[BUG] Streak tracker silently breaks
docs: document schema.sql sync requirement and add leaderboard index
feat(public-profile): add public_since audit column and weekly goal progress
feat(ui): add multiple dashboard themes with CSS custom properties
feat: add multiple dashboard themes
fix: add sharp for standalone production build
fix: align theme system with latest main
fix: migrate ci workflow to pnpm
fix: migrate github workflows to pnpm
revert: restore npm workflows and lockfile
feat: add empty states for dashboard widgets with no data
feat: add empty states for dashboard widgets
fix: resolve CIAnalytics JSX issues
feat: add empty state for CI analytics widget
feat: add pagination to RecentActivity widget
feat: add pagination to RecentActivity widget ([FEAT] Add pagination to RecentActivity widget #1901)
fix: address PR review feedback on pagination
chore: enhance Dependabot config with groups, reviewer, and proper quoting
fix: remove redundant Sign In button from navbar for unauthenticated users
Drop the duplicate SIGN IN CTA from the global header; sign-in remains on the landing hero and auth routes.
fix: hide StreakAtRiskBanner when streak freeze is active or loading
The StreakAtRiskBanner was incorrectly showing even when a user had an
active streak freeze applied. This was caused by a race condition:
streak data loaded before freeze status, so the banner briefly rendered
with hasStreakFreezeState=undefined (falsy), and could persist if the
freeze API call failed silently.
Fix: Add a render-level guard that checks hasStreakFreezeState !== false,
ensuring the banner is hidden both when freeze is active (true) and while
freeze status is still loading (undefined). The banner now only renders
when we have confirmed the user has no freeze (explicit false).
Fixes #1900
Co-authored-by: Claude Opus 4.8 noreply@anthropic.com
fix: replace redundant tabIndex with focus-visible ring on UserAvatar
fix(auth): add rate limiting for authentication endpoints and harden cron auth
Closes #1657
Three cron/sync endpoints bypassed authentication when NODE_ENV was
development. The condition used was:
if (authHeader !== Bearer-secret AND process.env.NODE_ENV !== development)
This made the authorization check a no-op in dev mode, allowing any
unauthenticated caller to trigger bulk operations: sponsors sync,
WakaTime stats sync, and Discord notifications.
Root cause: a developer-convenience shortcut never removed before deploy.
Fix: created src/lib/cron-auth.ts with validateCronRequest() that
enforces a consistent, environment-independent fail-closed check:
No NODE_ENV bypass in any environment.
Affected routes (all fixed):
src/app/api/wakatime/sync/route.ts
src/app/api/sponsors/sync/route.ts
src/app/api/notifications/discord-sync/route.ts
Already-correct (no change needed):
src/app/api/cron/weekly-digest/route.ts
Local development: set CRON_SECRET in .env.local and supply the
matching Authorization header when calling endpoints manually.
Documented in .env.example.
Tests added:
test/cron-auth.test.ts (17 tests) - unit tests for validateCronRequest
and integration regression tests for discord-sync covering all cases
including development environment enforcement
test/wakatime-sync-auth.test.ts (12 tests) - replaced bypass test
with enforcement tests for dev environment
test/sponsors-sync.test.ts (12 tests) - auth and dev enforcement
Closes #1303
Security analysis
DevTrack authenticates exclusively via GitHub OAuth; there is no
password, email, or OTP flow. Despite this, the NextAuth endpoints
responsible for initiating and completing the OAuth handshake were
entirely unprotected:
POST /api/auth/signin/github - initiates the OAuth redirect
GET /api/auth/callback/github - exchanges the code for a token
GET /api/auth/link-github - secondary account link initiation
GET /api/auth/link-github/callback - secondary account link callback
Flooding these routes can exhaust GitHub token-exchange quota for the
application, probe for valid OAuth codes, or generate high server load.
Endpoints deliberately excluded from rate limiting:
GET /api/auth/session - polled on every page render
GET /api/auth/csrf - CSRF token fetch, not an attack surface
GET /api/auth/signout - termination, not initiation
Authentication flows discovered
GitHub OAuth only. No password, email/OTP, or magic-link flows exist.
Rate limiting strategy
5 requests per IP per 15-minute fixed window (issue #1303).
Allows two complete sign-in flows (initiation + callback) plus one
spare before throttling. In development the limit is raised to 1000.
Uses the shared createMemoryFixedWindowRateLimiter factory from
@/lib/rate-limit. Key namespace auth: is isolated from the
metrics-rate-limit:* namespace to prevent interference.
429 responses carry X-RateLimit-* and Retry-After headers. Body:
{ error: 'Too many authentication attempts. Please try again later.' }
Protected endpoints
Four auth-sensitive paths added to the Next.js middleware matcher and
handled by the auth rate limiter before the general metrics gate.
Files changed
src/lib/auth-rate-limit.ts (new)
Exports checkAuthRateLimit(), isAuthSensitivePath(), AUTH_LIMIT,
AUTH_WINDOW_MS, AUTH_SENSITIVE_PREFIXES. No new dependencies.
src/middleware.ts (modified)
Imports auth limiter; inserts auth rate-limit block after the
protected-route check; adds auth paths to the matcher config.
Tests added
test/auth-rate-limit.test.ts - 22 tests:
isAuthSensitivePath (9) - protected vs excluded path classification
basic behaviour (5) - allow/block, remaining, 429-compatible status
window expiry (2) - counter resets after window, not before
IP isolation (2) - independent counters per IP
custom limit override (2) - dev/test relaxation
reset timestamp (2) - window-boundary reset, shared within window
Verification
tsc --noEmit clean (no errors in changed files)
next lint warnings only (all pre-existing)
vitest run 22/22 tests passed
Upstream rewrote the in-memory rate-limiter: replaced the external
createMemoryFixedWindowRateLimiter factory with inline memoryBuckets,
added pruneMemoryBuckets, and inlined getIp header extraction. This
PR added per-IP rate limiting on OAuth auth endpoints using
checkAuthRateLimit / isAuthSensitivePath / AUTH_LIMIT. Both edits
touched the same lines in src/middleware.ts.
Resolution keeps all upstream improvements:
And preserves all PR auth rate-limiting functionality:
fix: resolve E2E 500 error from incomplete weekly-summary mock
Align auth patterns across e2e spec files; add missing issues and
productivityScore fields to weekly-summary mock; add defensive
guard in WeeklySummaryCard for optional issues section.
fix(api): propagate database errors correctly with 500 status codes
feat: add search filter to TopRepos widget with accessible clear button
test: add goals CRUD API coverage
Closes #1907
Signed-off-by: Abhisumat Kashyap abhisumatkashyap@gmail.com
fix: close SSE stream on abort and add max-duration timeout
fix: address copilot review suggestions for edge sync route
feat: add End-to-End Type Safety using Supabase Typegen (#1968)
feat: add End-to-End Type Safety using Supabase Typegen (Issue [FEATURE] Add End-to-End Type Safety between Supabase Database and Next.js Frontend using Supabase Typegen #1931)
fix: pin supabase cli version and add header to generated types
fix: wrap dashboard widget with error boundary (fix: wrap dashboard widget with error boundary #1965)
fix: wrap dashboard widget with error boundary
fix: install sharp for standalone build
fix: prevent LanguageBreakdown legend overflow on mobile (fix: prevent LanguageBreakdown legend overflow on mobile #1964)
test: add edge case coverage for dateDiffDays (test: add edge case coverage for dateDiffDays #1963)
fix: persist recurring goal history on reset (fix: persist recurring goal history on reset #1969)
Co-authored-by: nishupr <nishupundir073.com>
feat: move WakaTime and GitHub Sync to Edge Functions (Issue #1932)
feat(leaderboard): async cache refresh via cron endpoint (#1962)
Adds refreshLeaderboardCache() to src/lib/leaderboard.ts, a new GET /api/leaderboard/refresh cron endpoint with CRON_SECRET Bearer auth, and rewrites the GET handler to cache-only flow. Closes #1799.
[Fix] #1312 Streak logic duplication resolved - core logic abstracted to src/lib/streak.ts. (#1876)
feat(activity): add GitHub Discussions to RecentActivity (#1939)
Root cause (confirmed)
The initial migration created local_coding_api_keys with a single column
api_key to store SHA-256 hashes. A later migration added api_key_hash as
a nullable column. At the time issue #1748 was filed, the code was split:
Creation → insert({ api_key: hash }) # only api_key written
Auth → .eq("api_key_hash", hash) # only api_key_hash read
Every key generated through the UI was therefore permanently invalid;
authentication always returned "Invalid API key".
Applied fix (in place on main branch)
Key creation now writes the same SHA-256 hash to BOTH columns:
insert({ api_key: hash, api_key_hash: hash })
Authentication queries BOTH columns with an OR filter so that pre-existing
rows (with only api_key populated) and newly created rows (with both
populated) authenticate identically:
.or("api_key_hash.eq.,api_key.eq.")
This preserves backward compatibility with any deployment that had keys
created before the api_key_hash column existed.
Regression test suite — test/local-coding-auth-regression.test.ts (9 tests)
Tests added in this commit provide explicit coverage that was absent:
would satisfy the filter used during authentication.
The pre-existing tests in test/local-coding-keys.test.ts and
test/local-coding-sync.test.ts continue to pass unchanged.
Closes #1748
Add parallel GraphQL fetch alongside the REST events endpoint to
reliably surface discussion-comment activity in RecentActivity.
formatGraphQLDiscussionComment() normalizer, mergeActivityItems()
dedup+sort helper
viewer.repositoryDiscussionComments(first:20); fetchDiscussionItems
ViaGraphQL() silently returns [] on error; both fetchFormatted
Activity() and the public-events fallback path run REST + GraphQL
in parallel via Promise.all
formatGraphQLDiscussionComment and mergeActivityItems