Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions e2e/dashboard-widgets.spec.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { expect, test } from "@playwright/test";
import { encode } from "next-auth/jwt";

Expand Down Expand Up @@ -65,6 +65,13 @@
});
});

await page.route("**/api/goals/sync", async (route) => {
await route.fulfill({
contentType: "application/json",
body: JSON.stringify({ updated: 0, commitCount: 0 }),
});
});

await page.route("**/api/goals", async (route) => {
if (route.request().method() === "POST") {
await route.fulfill({
Expand All @@ -87,6 +94,7 @@
unit: "commits",
recurrence: "weekly",
period_start: "2026-05-18",
last_synced_at: new Date().toISOString(),
},
],
}),
Expand Down Expand Up @@ -124,7 +132,7 @@
await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible({ timeout: 30000 });
await expect(page.getByRole("heading", { name: "Your Commits" })).toBeVisible({ timeout: 10000 });
await expect(page.getByRole("heading", { name: "PR Analytics" })).toBeVisible({ timeout: 10000 });
await expect(page.getByRole("heading", { name: "Goals" })).toBeVisible({ timeout: 10000 });

Check failure on line 135 in e2e/dashboard-widgets.spec.js

View workflow job for this annotation

GitHub Actions / Playwright smoke tests

[chromium] › dashboard-widgets.spec.js:130:5 › dashboard widgets render with mocked metrics

1) [chromium] › dashboard-widgets.spec.js:130:5 › dashboard widgets render with mocked metrics ─── Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── Error: Timed out 10000ms waiting for expect(locator).toBeVisible() Locator: getByRole('heading', { name: 'Goals' }) Expected: visible Received: <element(s) not found> Call log: - expect.toBeVisible with timeout 10000ms - waiting for getByRole('heading', { name: 'Goals' }) 133 | await expect(page.getByRole("heading", { name: "Your Commits" })).toBeVisible({ timeout: 10000 }); 134 | await expect(page.getByRole("heading", { name: "PR Analytics" })).toBeVisible({ timeout: 10000 }); > 135 | await expect(page.getByRole("heading", { name: "Goals" })).toBeVisible({ timeout: 10000 }); | ^ 136 | await expect(page.getByText("Make 10 commits")).toBeVisible({ timeout: 10000 }); 137 | }); 138 | at /home/runner/work/devtrack/devtrack/e2e/dashboard-widgets.spec.js:135:62

Check failure on line 135 in e2e/dashboard-widgets.spec.js

View workflow job for this annotation

GitHub Actions / Playwright smoke tests

[chromium] › dashboard-widgets.spec.js:130:5 › dashboard widgets render with mocked metrics

1) [chromium] › dashboard-widgets.spec.js:130:5 › dashboard widgets render with mocked metrics ─── Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: Timed out 10000ms waiting for expect(locator).toBeVisible() Locator: getByRole('heading', { name: 'Goals' }) Expected: visible Received: <element(s) not found> Call log: - expect.toBeVisible with timeout 10000ms - waiting for getByRole('heading', { name: 'Goals' }) 133 | await expect(page.getByRole("heading", { name: "Your Commits" })).toBeVisible({ timeout: 10000 }); 134 | await expect(page.getByRole("heading", { name: "PR Analytics" })).toBeVisible({ timeout: 10000 }); > 135 | await expect(page.getByRole("heading", { name: "Goals" })).toBeVisible({ timeout: 10000 }); | ^ 136 | await expect(page.getByText("Make 10 commits")).toBeVisible({ timeout: 10000 }); 137 | }); 138 | at /home/runner/work/devtrack/devtrack/e2e/dashboard-widgets.spec.js:135:62

Check failure on line 135 in e2e/dashboard-widgets.spec.js

View workflow job for this annotation

GitHub Actions / Playwright smoke tests

[chromium] › dashboard-widgets.spec.js:130:5 › dashboard widgets render with mocked metrics

1) [chromium] › dashboard-widgets.spec.js:130:5 › dashboard widgets render with mocked metrics ─── Error: Timed out 10000ms waiting for expect(locator).toBeVisible() Locator: getByRole('heading', { name: 'Goals' }) Expected: visible Received: <element(s) not found> Call log: - expect.toBeVisible with timeout 10000ms - waiting for getByRole('heading', { name: 'Goals' }) 133 | await expect(page.getByRole("heading", { name: "Your Commits" })).toBeVisible({ timeout: 10000 }); 134 | await expect(page.getByRole("heading", { name: "PR Analytics" })).toBeVisible({ timeout: 10000 }); > 135 | await expect(page.getByRole("heading", { name: "Goals" })).toBeVisible({ timeout: 10000 }); | ^ 136 | await expect(page.getByText("Make 10 commits")).toBeVisible({ timeout: 10000 }); 137 | }); 138 | at /home/runner/work/devtrack/devtrack/e2e/dashboard-widgets.spec.js:135:62
await expect(page.getByText("Make 10 commits")).toBeVisible({ timeout: 10000 });
});

Expand All @@ -140,7 +148,7 @@
await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible({ timeout: 30000 });
await page.getByRole("button", { name: "Show 90-day range" }).click();

await expect.poll(() => contributionRequests.some((url) => url.includes("days=90")), { timeout: 15000 }).toBe(true);

Check failure on line 151 in e2e/dashboard-widgets.spec.js

View workflow job for this annotation

GitHub Actions / Playwright smoke tests

[chromium] › dashboard-widgets.spec.js:139:5 › contribution graph range buttons request a new range

2) [chromium] › dashboard-widgets.spec.js:139:5 › contribution graph range buttons request a new range Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── Error: expect(received).toBe(expected) // Object.is equality Expected: true Received: false Call Log: - Timeout 15000ms exceeded while waiting on the predicate 149 | await page.getByRole("button", { name: "Show 90-day range" }).click(); 150 | > 151 | await expect.poll(() => contributionRequests.some((url) => url.includes("days=90")), { timeout: 15000 }).toBe(true); | ^ 152 | }); 153 | 154 | test("goal form posts a new goal", async ({ page }) => { at /home/runner/work/devtrack/devtrack/e2e/dashboard-widgets.spec.js:151:3

Check failure on line 151 in e2e/dashboard-widgets.spec.js

View workflow job for this annotation

GitHub Actions / Playwright smoke tests

[chromium] › dashboard-widgets.spec.js:139:5 › contribution graph range buttons request a new range

2) [chromium] › dashboard-widgets.spec.js:139:5 › contribution graph range buttons request a new range Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: expect(received).toBe(expected) // Object.is equality Expected: true Received: false Call Log: - Timeout 15000ms exceeded while waiting on the predicate 149 | await page.getByRole("button", { name: "Show 90-day range" }).click(); 150 | > 151 | await expect.poll(() => contributionRequests.some((url) => url.includes("days=90")), { timeout: 15000 }).toBe(true); | ^ 152 | }); 153 | 154 | test("goal form posts a new goal", async ({ page }) => { at /home/runner/work/devtrack/devtrack/e2e/dashboard-widgets.spec.js:151:3

Check failure on line 151 in e2e/dashboard-widgets.spec.js

View workflow job for this annotation

GitHub Actions / Playwright smoke tests

[chromium] › dashboard-widgets.spec.js:139:5 › contribution graph range buttons request a new range

2) [chromium] › dashboard-widgets.spec.js:139:5 › contribution graph range buttons request a new range Error: expect(received).toBe(expected) // Object.is equality Expected: true Received: false Call Log: - Timeout 15000ms exceeded while waiting on the predicate 149 | await page.getByRole("button", { name: "Show 90-day range" }).click(); 150 | > 151 | await expect.poll(() => contributionRequests.some((url) => url.includes("days=90")), { timeout: 15000 }).toBe(true); | ^ 152 | }); 153 | 154 | test("goal form posts a new goal", async ({ page }) => { at /home/runner/work/devtrack/devtrack/e2e/dashboard-widgets.spec.js:151:3
});

test("goal form posts a new goal", async ({ page }) => {
Expand All @@ -153,7 +161,7 @@

await page.goto("/dashboard", { waitUntil: "load" });
await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible({ timeout: 30000 });
await page.getByLabel("Goal title").fill("Ship one PR");

Check failure on line 164 in e2e/dashboard-widgets.spec.js

View workflow job for this annotation

GitHub Actions / Playwright smoke tests

[chromium] › dashboard-widgets.spec.js:154:5 › goal form posts a new goal

3) [chromium] › dashboard-widgets.spec.js:154:5 › goal form posts a new goal ───────────────────── Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: locator.fill: Test timeout of 30000ms exceeded. Call log: - waiting for getByLabel('Goal title') 162 | await page.goto("/dashboard", { waitUntil: "load" }); 163 | await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible({ timeout: 30000 }); > 164 | await page.getByLabel("Goal title").fill("Ship one PR"); | ^ 165 | await page.getByLabel("Target").fill("1"); 166 | await page.getByLabel("Unit").selectOption("prs"); 167 | await page.getByRole("button", { name: "Add goal" }).click(); at /home/runner/work/devtrack/devtrack/e2e/dashboard-widgets.spec.js:164:39

Check failure on line 164 in e2e/dashboard-widgets.spec.js

View workflow job for this annotation

GitHub Actions / Playwright smoke tests

[chromium] › dashboard-widgets.spec.js:154:5 › goal form posts a new goal

3) [chromium] › dashboard-widgets.spec.js:154:5 › goal form posts a new goal ───────────────────── Error: locator.fill: Test timeout of 30000ms exceeded. Call log: - waiting for getByLabel('Goal title') 162 | await page.goto("/dashboard", { waitUntil: "load" }); 163 | await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible({ timeout: 30000 }); > 164 | await page.getByLabel("Goal title").fill("Ship one PR"); | ^ 165 | await page.getByLabel("Target").fill("1"); 166 | await page.getByLabel("Unit").selectOption("prs"); 167 | await page.getByRole("button", { name: "Add goal" }).click(); at /home/runner/work/devtrack/devtrack/e2e/dashboard-widgets.spec.js:164:39
await page.getByLabel("Target").fill("1");
await page.getByLabel("Unit").selectOption("prs");
await page.getByRole("button", { name: "Add goal" }).click();
Expand Down
46 changes: 37 additions & 9 deletions next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,40 @@
/** @type {import("next").NextConfig} */

const cspDirectives = [
"default-src 'self'",
"script-src 'self'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' https://avatars.githubusercontent.com data: blob:",
"font-src 'self' data:",
"connect-src 'self'",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'",
].join("; ");

const securityHeaders = [
{
key: "Content-Security-Policy",
value: cspDirectives,
},
{
key: "X-Frame-Options",
value: "DENY",
},
{
key: "X-Content-Type-Options",
value: "nosniff",
},
{
key: "Referrer-Policy",
value: "strict-origin-when-cross-origin",
},
{
key: "Permissions-Policy",
value: "camera=(), microphone=(), geolocation=()",
},
];

const nextConfig = {
images: {
remotePatterns: [
Expand All @@ -12,15 +48,7 @@ const nextConfig = {
return [
{
source: "/(.*)",
headers: [
{ key: "X-Frame-Options", value: "DENY" },
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
{
key: "Permissions-Policy",
value: "camera=(), microphone=(), geolocation=()",
},
],
headers: securityHeaders,
},
];
},
Expand Down
9 changes: 6 additions & 3 deletions src/app/api/goals/sync/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { getServerSession } from "next-auth";
import { NextRequest } from "next/server";
import { authOptions } from "@/lib/auth";
import { supabaseAdmin } from "@/lib/supabase";
import { getGitHubAccessToken } from "@/lib/server-github-token";

export const dynamic = "force-dynamic";

Expand Down Expand Up @@ -36,9 +38,10 @@ function currentWeekEnd(): string {

const GITHUB_API = "https://api.github.com";

export async function POST() {
export async function POST(req: NextRequest) {
const session = await getServerSession(authOptions);
if (!session?.accessToken || !session.githubId || !session.githubLogin) {
const accessToken = await getGitHubAccessToken(req);
if (!accessToken || !session?.githubId || !session?.githubLogin) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}

Expand Down Expand Up @@ -78,7 +81,7 @@ export async function POST() {
`${GITHUB_API}/search/commits?q=author:${session.githubLogin}+author-date:${weekStart}..${weekEnd}&per_page=100`,
{
headers: {
Authorization: `Bearer ${session.accessToken}`,
Authorization: `Bearer ${accessToken}`,
Accept: "application/vnd.github+json",
},
cache: "no-store",
Expand Down
5 changes: 3 additions & 2 deletions src/app/api/metrics/activity/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from "@/lib/metrics-cache";
import { supabaseAdmin } from "@/lib/supabase";
import { resolveAppUser } from "@/lib/resolve-user";
import { getGitHubAccessToken } from "@/lib/server-github-token";

export const dynamic = "force-dynamic";

Expand Down Expand Up @@ -221,11 +222,11 @@ async function fetchFormattedActivityWithFallback(
export async function GET(req: NextRequest) {
const session = await getServerSession(authOptions);

if (!session?.accessToken || !session.githubLogin) {
const accessToken = await getGitHubAccessToken(req);
if (!accessToken || !session?.githubLogin) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}

const accessToken: string = session.accessToken;
const githubLogin: string = session.githubLogin;
const accountId = req.nextUrl.searchParams.get("accountId");
const bypass = isMetricsCacheBypassed(req);
Expand Down
16 changes: 10 additions & 6 deletions src/app/api/metrics/ci/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { GITHUB_API } from "@/lib/github";
import { supabaseAdmin } from "@/lib/supabase";
import { resolveAppUser } from "@/lib/resolve-user";
import { isMetricsCacheBypassed, metricsCacheKey, withMetricsCache } from "@/lib/metrics-cache";
import { getGitHubAccessToken } from "@/lib/server-github-token";

export const dynamic = "force-dynamic";

Expand All @@ -27,7 +28,7 @@ async function fetchCIAnalyticsForAccount(token: string, githubLogin: string): P
const searchRes = await fetch(`${GITHUB_API}/search/commits?q=author:${githubLogin}+author-date:>=${toIsoDate(30)}&per_page=100&sort=author-date&order=desc`, { headers: { Authorization: `Bearer ${token}`, Accept: "application/vnd.github+json" }, cache: "no-store" });
if (!searchRes.ok) throw new Error("API error");
const data = await searchRes.json();

const repoMap = new Map<string, number>();
for (const item of data.items) { const n = item.repository.full_name; repoMap.set(n, (repoMap.get(n) ?? 0) + 1); }
const repos = Array.from(repoMap.entries()).map(([name, commits]) => ({ name, commits })).sort((a, b) => b.commits - a.commits).slice(0, 5);
Expand Down Expand Up @@ -58,28 +59,31 @@ async function fetchCIAnalyticsForAccount(token: string, githubLogin: string): P

export async function GET(req: NextRequest) {
const session = await getServerSession(authOptions);
if (!session?.accessToken || !session.githubLogin) return Response.json({ error: "Unauthorized" }, { status: 401 });
const accessToken = await getGitHubAccessToken(req);
if (!accessToken || !session?.githubLogin) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}

const accountId = req.nextUrl.searchParams.get("accountId");
const bypass = isMetricsCacheBypassed(req);
const key = metricsCacheKey(session.githubId ?? session.githubLogin, "ci" as any, { accountId: accountId || "default" });

try {
const data = await withMetricsCache({ bypass, key, ttlSeconds: 10 * 60 }, async () => {
if (!accountId) return await fetchCIAnalyticsForAccount(session.accessToken!, session.githubLogin!);
if (!accountId) return await fetchCIAnalyticsForAccount(accessToken, session.githubLogin!);

const userRow = await resolveAppUser(session.githubId!, session.githubLogin!);
if (!userRow) throw new Error("User not found");

if (accountId === "combined") {
const accounts = await getAllAccounts({ token: session.accessToken!, githubId: session.githubId!, githubLogin: session.githubLogin! }, userRow.id);
const accounts = await getAllAccounts({ token: accessToken, githubId: session.githubId!, githubLogin: session.githubLogin! }, userRow.id);
const results = await Promise.allSettled(accounts.map((a) => fetchCIAnalyticsForAccount(a.token, a.githubLogin)));
const merged = mergeMetrics(results, mergeCIAnalytics);
if (!merged) throw new Error("Merge failed");
return merged;
}

if (accountId === session.githubId) return await fetchCIAnalyticsForAccount(session.accessToken!, session.githubLogin!);
if (accountId === session.githubId) return await fetchCIAnalyticsForAccount(accessToken, session.githubLogin!);

const accountToken = await getAccountToken(userRow.id, accountId);
if (!accountToken) throw new Error("Token missing");
Expand Down
12 changes: 7 additions & 5 deletions src/app/api/metrics/coding-activity-insights/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from "@/lib/metrics-cache";
import { supabaseAdmin } from "@/lib/supabase";
import { resolveAppUser } from "@/lib/resolve-user";
import { getGitHubAccessToken } from "@/lib/server-github-token";
import {
summarizeCodingActivity,
type CodingActivityInsight,
Expand Down Expand Up @@ -138,8 +139,9 @@ async function buildInsightsForAccount(

export async function GET(req: NextRequest) {
const session = await getServerSession(authOptions);
const accessToken = await getGitHubAccessToken(req);

if (!session?.accessToken || !session.githubLogin) {
if (!accessToken || !session?.githubLogin) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}

Expand All @@ -151,7 +153,7 @@ export async function GET(req: NextRequest) {
if (!accountId) {
try {
const data = await buildInsightsForAccount(
session.accessToken,
accessToken,
session.githubLogin,
timeZone,
{ bypass, userId: cacheUserId }
Expand All @@ -176,8 +178,8 @@ export async function GET(req: NextRequest) {
if (accountId === "combined") {
const accounts = await getAllAccounts(
{
token: session.accessToken,
githubId: session.githubId,
token: accessToken,
githubId: session.githubId!,
githubLogin: session.githubLogin,
},
userRow.id
Expand Down Expand Up @@ -210,7 +212,7 @@ export async function GET(req: NextRequest) {

if (accountId === session.githubId) {
const data = await buildInsightsForAccount(
session.accessToken,
accessToken,
session.githubLogin,
timeZone,
{ bypass, userId: session.githubId }
Expand Down
30 changes: 15 additions & 15 deletions src/app/api/metrics/compare/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import { NextRequest } from "next/server";
import { authOptions } from "@/lib/auth";
import { dateDiffDays, toDateStr } from "@/lib/dateUtils";
import { normalizeGitHubUsername } from "@/lib/validate-github-username";
import { getGitHubAccessToken } from "@/lib/server-github-token";

export const dynamic = "force-dynamic";

const GITHUB_API = "https://api.github.com";

export async function GET(req: NextRequest) {
const session = await getServerSession(authOptions);
if (!session?.accessToken || !session.githubLogin) {
const accessToken = await getGitHubAccessToken(req);
if (!accessToken || !session?.githubLogin) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}

Expand All @@ -37,7 +39,7 @@ export async function GET(req: NextRequest) {

// 1. Verify user exists
const userRes = await fetch(`${GITHUB_API}/users/${encodedUsername}`, {
headers: { Authorization: `Bearer ${session.accessToken}` },
headers: { Authorization: `Bearer ${accessToken}` },
cache: "no-store",
});

Expand All @@ -50,7 +52,7 @@ export async function GET(req: NextRequest) {
const since90 = new Date();
since90.setDate(since90.getDate() - 90);
const since90Str = since90.toISOString().slice(0, 10);

const since30 = new Date();
since30.setDate(since30.getDate() - 30);
const since30Str = since30.toISOString().slice(0, 10);
Expand All @@ -66,7 +68,7 @@ export async function GET(req: NextRequest) {

const commitsRes = await fetch(commitsUrl.toString(), {
headers: {
Authorization: `Bearer ${session.accessToken}`,
Authorization: `Bearer ${accessToken}`,
Accept: "application/vnd.github+json",
},
cache: "no-store",
Expand All @@ -75,11 +77,11 @@ export async function GET(req: NextRequest) {
let streak = 0;
let commits30d = 0;
let topLanguage = "Unknown";

if (commitsRes.ok) {
const commitsData = await commitsRes.json();
const items = commitsData.items || [];

const daySet: Record<string, true> = {};
for (const item of items) {
const dateStr = item.commit.author.date.slice(0, 10);
Expand All @@ -89,22 +91,20 @@ export async function GET(req: NextRequest) {
}
}
const commitDays = Object.keys(daySet).sort();

if (commitDays.length > 0) {
let currentRun = 1;
let runs: { end: string; length: number }[] = [];
let runStart = commitDays[0];
const runs: { end: string; length: number }[] = [];
for (let i = 1; i < commitDays.length; i++) {
if (dateDiffDays(commitDays[i - 1], commitDays[i]) === 1) {
currentRun++;
} else {
runs.push({ end: commitDays[i - 1], length: currentRun });
runStart = commitDays[i];
currentRun = 1;
}
}
runs.push({ end: commitDays[commitDays.length - 1], length: currentRun });

const today = toDateStr(new Date());
const yesterday = toDateStr(new Date(Date.now() - 86400000));
const lastRun = runs[runs.length - 1];
Expand All @@ -118,10 +118,10 @@ export async function GET(req: NextRequest) {
reposUrl.searchParams.set("sort", "pushed");

const reposRes = await fetch(reposUrl.toString(), {
headers: { Authorization: `Bearer ${session.accessToken}` },
headers: { Authorization: `Bearer ${accessToken}` },
cache: "no-store",
});

if (reposRes.ok) {
const reposData = await reposRes.json();
const langCounts: Record<string, number> = {};
Expand All @@ -140,7 +140,7 @@ export async function GET(req: NextRequest) {
prsUrl.searchParams.set("per_page", "1");

const prsRes = await fetch(prsUrl.toString(), {
headers: { Authorization: `Bearer ${session.accessToken}` },
headers: { Authorization: `Bearer ${accessToken}` },
cache: "no-store",
});
let prs = 0;
Expand All @@ -154,6 +154,6 @@ export async function GET(req: NextRequest) {
streak,
commits30d,
topLanguage,
prs
prs,
});
}
6 changes: 4 additions & 2 deletions src/app/api/metrics/contributions/daily/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
metricsCacheKey,
withMetricsCache,
} from "@/lib/metrics-cache";
import { getGitHubAccessToken } from "@/lib/server-github-token";

export const dynamic = "force-dynamic";

Expand All @@ -19,7 +20,8 @@ interface RepoCommit {

export async function GET(req: NextRequest) {
const session = await getServerSession(authOptions);
if (!session?.accessToken || !session.githubLogin) {
const accessToken = await getGitHubAccessToken(req);
if (!accessToken || !session?.githubLogin) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}

Expand Down Expand Up @@ -47,7 +49,7 @@ export async function GET(req: NextRequest) {
`${GITHUB_API}/search/commits?q=author:${session.githubLogin}+author-date:${date}&per_page=100`,
{
headers: {
Authorization: `Bearer ${session.accessToken}`,
Authorization: `Bearer ${accessToken}`,
Accept: "application/vnd.github+json",
},
cache: "no-store",
Expand Down
Loading
Loading