Skip to content

Merge branch 'main' into feat/issue-1932-edge-functions#2383

Closed
knoxiboy wants to merge 7 commits into
Priyanshu-byte-coder:mainfrom
knoxiboy:feat/issue-1932-edge-functions
Closed

Merge branch 'main' into feat/issue-1932-edge-functions#2383
knoxiboy wants to merge 7 commits into
Priyanshu-byte-coder:mainfrom
knoxiboy:feat/issue-1932-edge-functions

Conversation

@knoxiboy

Copy link
Copy Markdown
Contributor

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)

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

  • /api/user/data-export already exists (JSON export + account delete),
    with rate limiting via data_export_audit table (1 export/hour),
    audit logging (ip, user-agent, timestamp), and field redaction.
  • PrivacySettings component had an export button wired to the JSON
    endpoint; updated to use the new ZIP endpoint.
  • fflate 0.8.3 is present as a jspdf transitive dependency; used
    directly for in-memory ZIP generation via zipSync/strToU8.
  • goal_history table exists (migration 20260603000001); included in
    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

  • Auth: getServerSession + resolveAppUser; no user-supplied ID.
  • Field redaction: recursive scan strips any key matching token, secret,
    password, api_key, access_key, refresh, credential, private, auth,
    iv, hash before the value reaches the ZIP.
  • Column-level: webhook url is included (user-supplied); signing secrets
    are never selected. Linked accounts omit token columns entirely.
  • IDOR: all queries scoped to user.id resolved from authenticated session.
  • Cache-Control: no-store on the response.

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)

  • Replaced plain JSON export button with Download ZIP Archive button.
  • Loading spinner while export is in-flight.
  • Rate-limit error displays remaining minutes to user.
  • Generic error fallback message.

Tests (test/user-export.test.ts) — 25 tests

  • GET /api/user/export: 401 (no session), 401 (missing githubId),
    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.
  • toCsv: empty array, header row, multi-row, comma quoting,
    double-quote escaping, null as empty cell.
  • csvCell: null, undefined, plain string, comma, newline, number.

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:

  • discover which organizations a user belongs to
  • filter metrics by a specific organization
  • persist per-org inclusion preferences

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

  • keys/route.ts: api_key column mein hash ki jagah 8-char prefix store karo
  • sync/route.ts: authenticateApiKey mein sirf api_key_hash column check karo
  • Removes dual-write vulnerability and insecure OR fallback in auth

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)

Adds a clearly-labelled 'GSSoC Contributor Procedures' section to
CODE_OF_CONDUCT.md covering:

  • Scope (who and what these procedures apply to)
  • Reporting procedure (private advisory form, email fallback, required info)
  • Response timeline (48h ack, 5 business day review, resolution comm)
  • Confidentiality and anonymity guarantees
  • Appeals process (14 day window, uninvolved reviewer)
  • After-a-report workflow (acknowledge, investigate, decide, communicate)
  • Out-of-scope items (technical disputes, GSSoC platform issues)

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:

  • COMMITS IN REPO: sum of all contributor commit counts
  • PRS MERGED: total from GitHub search API
  • CONTRIBUTORS: actual contributor count (up from per_page=30 to 100)
  • GITHUB STARS: live stargazer count

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

  • githubGraphQL now checks json.errors and throws with actual error
    messages instead of silently returning null data
  • CV analyze endpoint returns the real error message instead of
    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

  • Add 5-min cache to settings GET route, invalidate on PATCH
  • Remove duplicate bio section and fake save button from settings page
  • Add retry UI when settings fail to load (Supabase throttling)
  • Update landing page features list (8 items, add AI insights/resume/leaderboard)
  • Fix hardcoded colors in landing page to use CSS variables

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 the
METRICS_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

  • Increase all metrics cache TTLs from 2-10min to 30-60min (6-10x fewer DB reads)
  • Bump SSE poll interval from 15s to 60s (4x fewer notification/goal queries)
  • Simplify AppNavbar authenticated nav to 3 real pages (remove scroll-anchor fakes)
  • Hide navbar Settings/SignOut on dashboard routes — DashboardHeader already provides them
  • Add ThrottleBanner: shows dismissible alert after 3+ 429/502/503 errors in 30s
  • Remove duplicate Settings+ExportButton that merged in from upstream
  • Install swagger-ui-react and canvas-confetti types to fix upstream TS errors

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-name: actions/github-script
    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-name: actions/first-interaction
    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-name: actions/setup-node
    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-name: actions/checkout
    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-name: actions/stale
    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

  • Add public_since timestamptz column to users table with migration
  • Add show_weekly_goals boolean column with migration
  • Set/clear public_since in settings PATCH when toggling is_public
  • Add show_weekly_goals toggle to settings page UI
  • Extend PublicProfileData with weeklyGoalProgress field
  • Add fetchPublicWeeklyGoalProgress query to public-profile-data
  • Render weekly goal completion ring on /u/[username] page
  • Update User interface and supabase queries for new columns
    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

  • fix(security): remove development auth bypass from cron routes

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:

  • CRON_SECRET not configured -> 500
  • Authorization header missing or wrong -> 401
  • Correct secret -> null (proceed)
    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

  • fix(auth): add rate limiting for authentication endpoints

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

  • merge: resolve middleware conflict for auth rate limiting

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:

  • inline memoryBuckets / pruneMemoryBuckets / checkMemoryLimit
  • getIp reads headers directly (no external getClientIp)
  • removes createMemoryFixedWindowRateLimiter dependency from middleware

And preserves all PR auth rate-limiting functionality:

  • checkAuthRateLimit / isAuthSensitivePath / AUTH_LIMIT imports
  • runtime = nodejs
  • isAuthSensitivePath block intercepting OAuth endpoints
  • /api/:path* matcher so auth paths are covered
    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)

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:

  • Key creation writes hash to api_key AND api_key_hash.
  • POST /local-coding/sync uses OR filter across both columns.
  • Legacy row (api_key only, api_key_hash NULL) still authenticates.
  • Invalid key is rejected by both POST and GET sync handlers.
  • GET /local-coding/sync was previously untested; 4 tests now cover:
    • missing Authorization header → 401
    • invalid key → 401
    • valid key → 200 with session data
    • same OR filter used as POST
  • Hash function consistency: verifies the hash written during creation
    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

  • feat(activity): add GitHub Discussions to RecentActivity feed

Add parallel GraphQL fetch alongside the REST events endpoint to
reliably surface discussion-comment activity in RecentActivity.

  • activity-formatter.ts: GraphQLDiscussionCommentNode interface,
    formatGraphQLDiscussionComment() normalizer, mergeActivityItems()
    dedup+sort helper
  • metrics/activity/route.ts: DISCUSSION_COMMENTS_QUERY via
    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
  • test/activity-formatter.test.ts: 15 new tests for
    formatGraphQLDiscussionComment and mergeActivityItems

@github-actions github-actions Bot added type:bug GSSoC type bonus: bug fix type:feature GSSoC type bonus: new feature type:performance GSSoC type bonus: performance (+15 pts) gssoc26 GSSoC 2026 contribution labels Jun 12, 2026
@github-actions

Copy link
Copy Markdown

GSSoC Label Checklist 🏷️

@Priyanshu-byte-coder — please apply the appropriate labels before merging:

Difficulty (pick one):

  • level:beginner — 20 pts
  • level:intermediate — 35 pts
  • level:advanced — 55 pts
  • level:critical — 80 pts

Quality (optional):

  • quality:clean — ×1.2 multiplier
  • quality:exceptional — ×1.5 multiplier

Validation (required to score):

  • gssoc:approved — counts for points
  • gssoc:invalid / gssoc:spam / gssoc:ai-slop — does not score

Type labels (type:*) are auto-detected from files and title. Review and adjust if needed.
Points formula: (difficulty × quality_multiplier) + type_bonus

@knoxiboy

Copy link
Copy Markdown
Contributor Author

Closing as not planned — this PR was created by mistake (automated script ran unintentionally). The issue is already addressed upstream.

@knoxiboy knoxiboy closed this Jun 12, 2026
@knoxiboy knoxiboy deleted the feat/issue-1932-edge-functions branch June 12, 2026 17:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment