sync: add AI review pipeline, parallel skill-files fetch, Redis cache improvements
- feat(review): add skill_reviews table, review API endpoints (pending/submit/stats), admin auth - perf: parallel file fetching in skill-files API (was sequential → timeout) - fix: handle Date serialization from Redis cache - fix: align curation batch scripts with current browseReadyFilter Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
46
apps/web/lib/admin-auth.ts
Normal file
46
apps/web/lib/admin-auth.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server';
|
||||
import { auth } from '@/lib/auth';
|
||||
import { createDb, userQueries } from '@skillhub/db';
|
||||
|
||||
/**
|
||||
* Check if the current request is from an admin user.
|
||||
* Supports two auth methods:
|
||||
* 1. API key via Authorization: Bearer <REVIEW_API_KEY> header (for automation/scripts)
|
||||
* 2. Session-based admin check (for dashboard)
|
||||
*
|
||||
* Pass the request object to enable API key auth.
|
||||
* Returns the user on success, or a NextResponse error to return early.
|
||||
*/
|
||||
export async function requireAdmin(request?: NextRequest): Promise<
|
||||
| { authorized: true; username: string }
|
||||
| { authorized: false; response: NextResponse }
|
||||
> {
|
||||
// Check API key auth first (for automation/scripting)
|
||||
if (request) {
|
||||
const authHeader = request.headers.get('authorization');
|
||||
const apiKey = process.env.REVIEW_API_KEY;
|
||||
if (apiKey && authHeader === `Bearer ${apiKey}`) {
|
||||
return { authorized: true, username: 'api-key' };
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to session auth
|
||||
const session = await auth();
|
||||
if (!session?.user?.githubId) {
|
||||
return {
|
||||
authorized: false,
|
||||
response: NextResponse.json({ error: 'Unauthorized' }, { status: 401 }),
|
||||
};
|
||||
}
|
||||
|
||||
const db = createDb();
|
||||
const user = await userQueries.getByGithubId(db, session.user.githubId);
|
||||
if (!user?.isAdmin) {
|
||||
return {
|
||||
authorized: false,
|
||||
response: NextResponse.json({ error: 'Admin access required' }, { status: 403 }),
|
||||
};
|
||||
}
|
||||
|
||||
return { authorized: true, username: user.username };
|
||||
}
|
||||
Reference in New Issue
Block a user