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
51 changes: 51 additions & 0 deletions src/app/api/admin/cache-stats/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { NextRequest, NextResponse } from 'next/server';
import { searchCache } from '@/lib/cache';

export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const detailed = searchParams.get('detailed') === 'true';

const stats = detailed ? searchCache.getDetailedStats() : searchCache.getStats();

return NextResponse.json({
cache: stats,
timestamp: new Date().toISOString(),
});
} catch (error) {
console.error('Cache stats error:', error);
return NextResponse.json(
{ error: 'Failed to retrieve cache statistics' },
{ status: 500 }
);
}
}

export async function DELETE(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const key = searchParams.get('key');

if (key) {
// Delete specific cache entry
const deleted = searchCache.delete(key);
return NextResponse.json({
deleted,
key,
message: deleted ? 'Cache entry deleted' : 'Cache entry not found'
});
} else {
// Clear entire cache
searchCache.clear();
return NextResponse.json({
message: 'Cache cleared successfully'
});
}
} catch (error) {
console.error('Cache management error:', error);
return NextResponse.json(
{ error: 'Failed to manage cache' },
{ status: 500 }
);
}
}
70 changes: 70 additions & 0 deletions src/app/api/admin/rate-limit-stats/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { NextRequest, NextResponse } from 'next/server';
import { searchRateLimiter, getClientIdentifier } from '@/lib/rate-limit';

export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const detailed = searchParams.get('detailed') === 'true';
const clientId = searchParams.get('client');

if (clientId) {
// Get stats for specific client
const clientInfo = searchRateLimiter.getClientInfo(clientId);
return NextResponse.json({
client: clientInfo,
timestamp: new Date().toISOString(),
});
}

const stats = detailed ? searchRateLimiter.getDetailedStats() : searchRateLimiter.getStats();

return NextResponse.json({
rateLimiting: stats,
timestamp: new Date().toISOString(),
});
} catch (error) {
console.error('Rate limit stats error:', error);
return NextResponse.json(
{ error: 'Failed to retrieve rate limit statistics' },
{ status: 500 }
);
}
}

export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { action, clientId, additionalRequests, durationMs } = body;

if (action === 'reset' && clientId) {
const reset = searchRateLimiter.resetClient(clientId);
return NextResponse.json({
success: reset,
message: reset ? 'Client rate limit reset' : 'Client not found',
clientId,
});
}

if (action === 'increase' && clientId && additionalRequests) {
searchRateLimiter.increaseLimit(clientId, additionalRequests, durationMs);
return NextResponse.json({
success: true,
message: `Increased limit for client by ${additionalRequests} requests`,
clientId,
additionalRequests,
durationMs: durationMs || 'default',
});
}

return NextResponse.json(
{ error: 'Invalid action or missing parameters' },
{ status: 400 }
);
} catch (error) {
console.error('Rate limit management error:', error);
return NextResponse.json(
{ error: 'Failed to manage rate limits' },
{ status: 500 }
);
}
}
68 changes: 68 additions & 0 deletions src/app/api/admin/search-analytics/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { NextRequest, NextResponse } from 'next/server';
import { searchAnalytics } from '@/lib/analytics';

export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const timeRange = searchParams.get('timeRange');
const query = searchParams.get('query');
const action = searchParams.get('action');

// Handle different actions
if (action === 'recent') {
const limit = parseInt(searchParams.get('limit') || '50');
const recentSearches = searchAnalytics.getRecentSearches(limit);
return NextResponse.json({
recentSearches,
count: recentSearches.length,
timestamp: new Date().toISOString(),
});
}

if (action === 'history' && query) {
const limit = parseInt(searchParams.get('limit') || '10');
const searchHistory = searchAnalytics.getSearchHistory(query, limit);
return NextResponse.json({
query,
searchHistory,
count: searchHistory.length,
timestamp: new Date().toISOString(),
});
}

// Default: get analytics stats
let timeRangeMs: number | undefined;

switch (timeRange) {
case '1h':
timeRangeMs = 60 * 60 * 1000;
break;
case '24h':
timeRangeMs = 24 * 60 * 60 * 1000;
break;
case '7d':
timeRangeMs = 7 * 24 * 60 * 60 * 1000;
break;
case '30d':
timeRangeMs = 30 * 24 * 60 * 60 * 1000;
break;
default:
timeRangeMs = undefined; // All time
}

const stats = searchAnalytics.getStats(timeRangeMs);

return NextResponse.json({
analytics: stats,
timeRange: timeRange || 'all',
timeRangeMs,
timestamp: new Date().toISOString(),
});
} catch (error) {
console.error('Search analytics error:', error);
return NextResponse.json(
{ error: 'Failed to retrieve search analytics' },
{ status: 500 }
);
}
}
39 changes: 39 additions & 0 deletions src/app/api/dashboard/stats/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { NextResponse } from 'next/server';
import { createSupabaseServerClient } from '@/lib/supabase';
import { searchAnalytics } from '@/lib/analytics';
import { searchCache } from '@/lib/cache';
import { searchRateLimiter } from '@/lib/rate-limit';

export async function GET() {
try {
Expand Down Expand Up @@ -80,6 +83,12 @@ export async function GET() {
analyzed_at: repo.updated_at,
})) || [];

// Get search analytics (last 24 hours)
const searchStats = searchAnalytics.getStats(24 * 60 * 60 * 1000);
const cacheStats = searchCache.getStats();
const rateLimitStats = searchRateLimiter.getStats();
const recentSearches = searchAnalytics.getRecentSearches(5);

const dashboardStats = {
totalRepositories: totalRepositories || 0,
totalAnalyzed: totalAnalyzed || 0,
Expand All @@ -88,6 +97,36 @@ export async function GET() {
averageComplexity,
topLanguages,
recentAnalyses: formattedRecentAnalyses,
searchAnalytics: {
totalSearches24h: searchStats.totalSearches,
uniqueQueries24h: searchStats.uniqueQueries,
averageResponseTime: searchStats.averageResponseTime,
cacheHitRate: searchStats.cacheHitRate,
errorRate: searchStats.errorRate,
rateLimitRate: searchStats.rateLimitRate,
topQueries: searchStats.topQueries.slice(0, 5), // Top 5
topFilters: searchStats.topFilters.slice(0, 5), // Top 5
recentSearches: recentSearches.map(search => ({
query: search.query,
results_count: search.results_count,
response_time_ms: search.response_time_ms,
cached: search.cached,
timestamp: new Date(search.timestamp).toISOString(),
error: search.error,
})),
},
systemStats: {
cache: {
size: cacheStats.size,
hitRate: cacheStats.hitRate,
memoryUsage: cacheStats.totalMemoryUsage,
},
rateLimiting: {
activeClients: rateLimitStats.activeClients,
totalBlocked: rateLimitStats.totalBlocked,
blockRate: rateLimitStats.blockRate,
},
},
};

return NextResponse.json(dashboardStats);
Expand Down
Loading