Skip to content
Merged
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
4 changes: 2 additions & 2 deletions src/__tests__/commission-monthly-reset.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ describe('Commission Tier Monthly Reset Flow', () => {
it('should identify when monthly reset is needed', () => {
// Current month's reset should not need reset
const now = new Date()
const currentMonthStart = new Date(now.getFullYear(), now.getMonth(), 1)
const currentMonthStart = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1, 0, 0, 0, 0))
expect(shouldResetMonthlyVolume(currentMonthStart.toISOString())).toBe(false)

// Last month's reset should need reset
const lastMonthStart = new Date(now.getFullYear(), now.getMonth() - 1, 1)
const lastMonthStart = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - 1, 1, 0, 0, 0, 0))
expect(shouldResetMonthlyVolume(lastMonthStart.toISOString())).toBe(true)

// Null should need reset
Expand Down
4 changes: 3 additions & 1 deletion src/app/api/auth/login/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,14 @@ export async function POST(request: NextRequest) {
},
});

// Set httpOnly cookies for secure session persistence
// Set httpOnly cookies for secure session persistence with explicit path
response.cookies.set({
name: 'sb-access-token',
value: data.session.access_token,
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: data.session.expires_in,
});

Expand All @@ -67,6 +68,7 @@ export async function POST(request: NextRequest) {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 7, // 7 days
});

Expand Down
30 changes: 23 additions & 7 deletions src/app/api/auth/logout/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,40 @@ import { NextRequest, NextResponse } from 'next/server';
import { getSupabase } from '@/lib/db';
import * as Sentry from '@sentry/nextjs';

export async function POST(_request: NextRequest) {
export async function POST(request: NextRequest) {
try {
const supabase = getSupabase();
const { error } = await supabase.auth.signOut();

// Retrieve cookies to populate session state
const accessToken = request.cookies.get('sb-access-token')?.value;
const refreshToken = request.cookies.get('sb-refresh-token')?.value;

if (accessToken && refreshToken) {
await supabase.auth.setSession({
access_token: accessToken,
refresh_token: refreshToken,
});
}

// Call signOut with global scope to invalidate session in Supabase DB
const { error } = await supabase.auth.signOut({ scope: 'global' });

if (error) {
Sentry.captureException(error);
return NextResponse.json(
{ error: error.message },
const errorResponse = NextResponse.json(
{ error: 'Logout failed' },
{ status: 400 }
);
errorResponse.cookies.set('sb-access-token', '', { path: '/', maxAge: 0 });
errorResponse.cookies.set('sb-refresh-token', '', { path: '/', maxAge: 0 });
return errorResponse;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const response = NextResponse.json({ message: 'Logout successful' });

// Clear session cookies
response.cookies.delete('sb-access-token');
response.cookies.delete('sb-refresh-token');
// Clear session cookies using explicit path
response.cookies.set('sb-access-token', '', { path: '/', maxAge: 0 });
response.cookies.set('sb-refresh-token', '', { path: '/', maxAge: 0 });

return response;
} catch (error) {
Expand Down
23 changes: 19 additions & 4 deletions src/app/api/auth/refresh/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,16 @@ import * as Sentry from '@sentry/nextjs';

export async function POST(request: NextRequest) {
try {
const { refreshToken } = await request.json();
let refreshToken = request.cookies.get('sb-refresh-token')?.value;

if (!refreshToken) {
try {
const body = await request.json();
refreshToken = body.refreshToken;
} catch {
// Ignore JSON parsing errors if cookie was expected
}
}

if (!refreshToken) {
return NextResponse.json(
Expand All @@ -24,40 +33,46 @@ export async function POST(request: NextRequest) {
if (!error && !data.session) {
Sentry.captureException(new Error('Token refresh: no error but no session'));
}
return NextResponse.json(
const errorResponse = NextResponse.json(
{ error: 'Token refresh failed' },
{ status: 401 }
);
errorResponse.cookies.set('sb-access-token', '', { path: '/', maxAge: 0 });
errorResponse.cookies.set('sb-refresh-token', '', { path: '/', maxAge: 0 });
return errorResponse;
}

const response = NextResponse.json({
message: 'Token refreshed successfully',
session: {
access_token: data.session.access_token,
refresh_token: data.session.refresh_token,
expires_in: data.session.expires_in,
expires_at: data.session.expires_at,
token_type: 'Bearer',
},
});

// Update access token cookie
// Update access token cookie with explicit path
response.cookies.set({
name: 'sb-access-token',
value: data.session.access_token,
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: data.session.expires_in,
});

// Update refresh token cookie if provided
// Update refresh token cookie if provided with explicit path
if (data.session.refresh_token) {
response.cookies.set({
name: 'sb-refresh-token',
value: data.session.refresh_token,
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 7, // 7 days
});
}
Expand Down
4 changes: 2 additions & 2 deletions src/lib/commissions/monthlyResetScheduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ export function shouldResetMonthlyVolume(resetAt: string | null): boolean {
const lastReset = new Date(resetAt);
const now = new Date();

// Get start of current month (00:00:00 on the 1st)
const startOfCurrentMonth = new Date(now.getFullYear(), now.getMonth(), 1);
// Get start of current month in UTC (00:00:00 on the 1st) to align timezone logic
const startOfCurrentMonth = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1, 0, 0, 0, 0));
Comment thread
coderabbitai[bot] marked this conversation as resolved.

return lastReset < startOfCurrentMonth;
}
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.tsbuildinfo

Large diffs are not rendered by default.

Loading