Skip to content

Commit ba6d1f7

Browse files
committed
updating
1 parent 70cb3d9 commit ba6d1f7

File tree

4 files changed

+159
-53
lines changed

4 files changed

+159
-53
lines changed

app/api/analytics/collect/route.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { NextRequest, NextResponse } from 'next/server';
2+
import { analyticsDB } from '@/lib/analytics-db';
3+
4+
export async function POST(request: NextRequest) {
5+
try {
6+
// This endpoint runs in Node.js runtime and can use Prisma
7+
const analyticsData = await request.json();
8+
9+
// Validate the data structure
10+
if (!analyticsData.timestamp || !analyticsData.endpoint || !analyticsData.method) {
11+
return NextResponse.json({ error: 'Invalid analytics data' }, { status: 400 });
12+
}
13+
14+
// Convert ISO string back to Date object
15+
const requestData = {
16+
...analyticsData,
17+
timestamp: new Date(analyticsData.timestamp)
18+
};
19+
20+
// Log the request using our optimized analytics system
21+
await analyticsDB.logRequest(requestData);
22+
23+
return NextResponse.json({ success: true });
24+
} catch (error) {
25+
console.error('Analytics collection error:', error);
26+
// Return success anyway - analytics shouldn't break the app
27+
return NextResponse.json({ success: true });
28+
}
29+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { NextRequest, NextResponse } from 'next/server';
2+
import { analyticsDB } from '@/lib/analytics-db';
3+
4+
export async function POST(request: NextRequest) {
5+
try {
6+
// This endpoint runs in Node.js runtime and can use Prisma
7+
const securityData = await request.json();
8+
9+
// Validate the data structure
10+
if (!securityData.timestamp || !securityData.eventType || !securityData.ipAddress) {
11+
return NextResponse.json({ error: 'Invalid security data' }, { status: 400 });
12+
}
13+
14+
// Convert ISO string back to Date object
15+
const eventData = {
16+
...securityData,
17+
timestamp: new Date(securityData.timestamp)
18+
};
19+
20+
// Log the security event using our optimized analytics system
21+
await analyticsDB.logSecurityEvent(eventData);
22+
23+
return NextResponse.json({ success: true });
24+
} catch (error) {
25+
console.error('Security event logging error:', error);
26+
// Return success anyway - security logging shouldn't break the app
27+
return NextResponse.json({ success: true });
28+
}
29+
}

app/mcp/[transport]/route.ts

Lines changed: 59 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,68 @@ import { createMcpHandler } from "@vercel/mcp-adapter";
22
import { z } from "zod";
33
import { prisma } from '@/app/prisma';
44
import { NextRequest } from 'next/server';
5-
import { analyticsDB, getClientIP } from '@/lib/analytics-db';
5+
6+
// Utility function for getting client IP
7+
function getClientIP(request: NextRequest): string {
8+
const forwarded = request.headers.get('x-forwarded-for');
9+
if (forwarded) {
10+
return forwarded.split(',')[0].trim();
11+
}
12+
13+
const realIP = request.headers.get('x-real-ip');
14+
if (realIP) {
15+
return realIP;
16+
}
17+
18+
const cfConnectingIP = request.headers.get('cf-connecting-ip');
19+
if (cfConnectingIP) {
20+
return cfConnectingIP;
21+
}
22+
23+
return '127.0.0.1';
24+
}
25+
26+
// Helper function to log security events (Edge Runtime compatible)
27+
async function logSecurityEvent(request: NextRequest, eventType: string, details: string, clientId?: string) {
28+
try {
29+
const host = request.headers.get('host');
30+
const protocol = process.env.NODE_ENV === 'production' ? 'https' : 'http';
31+
const baseUrl = `${protocol}://${host}`;
32+
33+
const securityData = {
34+
timestamp: new Date().toISOString(),
35+
eventType,
36+
ipAddress: getClientIP(request),
37+
userAgent: request.headers.get('user-agent') || '',
38+
clientId,
39+
details
40+
};
41+
42+
await fetch(`${baseUrl}/api/analytics/security`, {
43+
method: 'POST',
44+
headers: {
45+
'Content-Type': 'application/json',
46+
},
47+
body: JSON.stringify(securityData)
48+
}).catch(() => {
49+
// Silent fail - security logging shouldn't break auth
50+
});
51+
} catch (error) {
52+
console.warn('Security event logging failed:', error);
53+
}
54+
}
655

756
// Authentication helper
857
async function authenticateRequest(request: NextRequest) {
958
const authHeader = request.headers.get('authorization');
10-
const ip = getClientIP(request);
11-
const userAgent = request.headers.get('user-agent') || '';
1259

1360
console.log('[MCP] Auth header present:', !!authHeader);
1461

1562
if (!authHeader) {
1663
console.log('[MCP] No auth header, returning 401');
1764

1865
// Log security event for missing auth
19-
analyticsDB.logSecurityEvent({
20-
timestamp: new Date(),
21-
eventType: 'auth_failure',
22-
ipAddress: ip,
23-
userAgent,
24-
details: 'Missing authorization header'
25-
});
66+
await logSecurityEvent(request, 'auth_failure', 'Missing authorization header');
2667

2768
return null;
2869
}
@@ -34,13 +75,7 @@ async function authenticateRequest(request: NextRequest) {
3475
console.log('[MCP] No token, returning 401');
3576

3677
// Log security event for malformed auth header
37-
analyticsDB.logSecurityEvent({
38-
timestamp: new Date(),
39-
eventType: 'auth_failure',
40-
ipAddress: ip,
41-
userAgent,
42-
details: 'Malformed authorization header'
43-
});
78+
await logSecurityEvent(request, 'auth_failure', 'Malformed authorization header');
4479

4580
return null;
4681
}
@@ -61,13 +96,7 @@ async function authenticateRequest(request: NextRequest) {
6196
console.log('[MCP] No access token found, returning 401');
6297

6398
// Log security event for invalid token
64-
analyticsDB.logSecurityEvent({
65-
timestamp: new Date(),
66-
eventType: 'invalid_token',
67-
ipAddress: ip,
68-
userAgent,
69-
details: 'Invalid access token provided'
70-
});
99+
await logSecurityEvent(request, 'invalid_token', 'Invalid access token provided');
71100

72101
return null;
73102
}
@@ -79,14 +108,7 @@ async function authenticateRequest(request: NextRequest) {
79108
console.log('[MCP] Token expired, returning 401');
80109

81110
// Log security event for expired token
82-
analyticsDB.logSecurityEvent({
83-
timestamp: new Date(),
84-
eventType: 'invalid_token',
85-
ipAddress: ip,
86-
userAgent,
87-
clientId: accessToken.clientId,
88-
details: 'Expired access token used'
89-
});
111+
await logSecurityEvent(request, 'invalid_token', 'Expired access token used', accessToken.clientId);
90112

91113
return null;
92114
}
@@ -100,14 +122,9 @@ async function authenticateRequest(request: NextRequest) {
100122
console.log('[MCP] Token audience mismatch. Expected:', currentResource, 'Got:', accessToken.resource);
101123

102124
// Log security event for audience mismatch
103-
analyticsDB.logSecurityEvent({
104-
timestamp: new Date(),
105-
eventType: 'suspicious_activity',
106-
ipAddress: ip,
107-
userAgent,
108-
clientId: accessToken.clientId,
109-
details: `Token audience mismatch. Expected: ${currentResource}, Got: ${accessToken.resource}`
110-
});
125+
await logSecurityEvent(request, 'suspicious_activity',
126+
`Token audience mismatch. Expected: ${currentResource}, Got: ${accessToken.resource}`,
127+
accessToken.clientId);
111128

112129
return null;
113130
}
@@ -118,13 +135,8 @@ async function authenticateRequest(request: NextRequest) {
118135
console.error('[MCP] Error validating token:', e);
119136

120137
// Log security event for authentication error
121-
analyticsDB.logSecurityEvent({
122-
timestamp: new Date(),
123-
eventType: 'auth_failure',
124-
ipAddress: ip,
125-
userAgent,
126-
details: `Authentication error: ${e instanceof Error ? e.message : 'Unknown error'}`
127-
});
138+
await logSecurityEvent(request, 'auth_failure',
139+
`Authentication error: ${e instanceof Error ? e.message : 'Unknown error'}`);
128140

129141
return null;
130142
}

middleware.ts

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,34 @@
11
import { NextResponse } from 'next/server';
22
import type { NextRequest } from 'next/server';
3-
import { analyticsDB, getClientIP } from './lib/analytics-db';
3+
4+
// Utility function for Edge Runtime
5+
function getClientIP(request: NextRequest): string {
6+
const forwarded = request.headers.get('x-forwarded-for');
7+
if (forwarded) {
8+
return forwarded.split(',')[0].trim();
9+
}
10+
11+
const realIP = request.headers.get('x-real-ip');
12+
if (realIP) {
13+
return realIP;
14+
}
15+
16+
const cfConnectingIP = request.headers.get('cf-connecting-ip');
17+
if (cfConnectingIP) {
18+
return cfConnectingIP;
19+
}
20+
21+
return '127.0.0.1';
22+
}
423

524
export async function middleware(request: NextRequest) {
625
const startTime = Date.now();
726

827
// Let the request proceed
928
const response = NextResponse.next();
1029

11-
// Collect analytics data after response (optimized, non-blocking)
30+
// For Edge Runtime, we'll send analytics data to our API endpoint
31+
// This avoids Prisma issues in middleware
1232
Promise.resolve().then(async () => {
1333
try {
1434
const endTime = Date.now();
@@ -21,9 +41,9 @@ export async function middleware(request: NextRequest) {
2141
let clientId: string | undefined;
2242
let userId: string | undefined;
2343

24-
// Quick, lightweight analytics logging with batching
25-
await analyticsDB.logRequest({
26-
timestamp: new Date(startTime),
44+
// Send analytics data to our API endpoint (Edge Runtime compatible)
45+
const analyticsData = {
46+
timestamp: new Date(startTime).toISOString(),
2747
endpoint: request.nextUrl.pathname,
2848
method: request.method,
2949
statusCode: response.status,
@@ -32,8 +52,24 @@ export async function middleware(request: NextRequest) {
3252
userId,
3353
ipAddress: ip,
3454
userAgent
35-
// Geographic data will be enriched asynchronously
55+
};
56+
57+
// Make a fetch call to our analytics API endpoint
58+
// This runs in Edge Runtime and sends data to Node.js runtime
59+
const host = request.headers.get('host');
60+
const protocol = process.env.NODE_ENV === 'production' ? 'https' : 'http';
61+
const baseUrl = `${protocol}://${host}`;
62+
63+
await fetch(`${baseUrl}/api/analytics/collect`, {
64+
method: 'POST',
65+
headers: {
66+
'Content-Type': 'application/json',
67+
},
68+
body: JSON.stringify(analyticsData)
69+
}).catch(() => {
70+
// Silent fail - analytics shouldn't break the main request
3671
});
72+
3773
} catch (error) {
3874
console.warn('Analytics collection failed:', error);
3975
}

0 commit comments

Comments
 (0)