feat(backfill): support ops-secret bootstrap auth for backfill-isolation
Made-with: Cursor
This commit is contained in:
@@ -14,9 +14,9 @@
|
||||
*/
|
||||
|
||||
import { NextResponse } from 'next/server';
|
||||
import { authSession } from '@/lib/auth/session-server';
|
||||
import { query } from '@/lib/db-postgres';
|
||||
import { getOrCreateProvisionedWorkspace } from '@/lib/workspaces';
|
||||
import { requireWorkspacePrincipal } from '@/lib/auth/workspace-auth';
|
||||
import { getOrCreateProvisionedWorkspace, type VibnWorkspace } from '@/lib/workspaces';
|
||||
import {
|
||||
ensureProjectCoolifyProject,
|
||||
ensureProjectResourcesTable,
|
||||
@@ -39,28 +39,42 @@ interface BackfillReport {
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
export async function POST() {
|
||||
const session = await authSession();
|
||||
if (!session?.user?.email) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
const email = session.user.email;
|
||||
export async function POST(request: Request) {
|
||||
// Three accepted auth modes:
|
||||
// 1. NextAuth session (browser)
|
||||
// 2. Bearer vibn_sk_... workspace API key (matches /api/mcp)
|
||||
// 3. Bearer <NEXTAUTH_SECRET> + ?email=<owner> (ops bootstrap so the
|
||||
// maintainer can curl the backfill from a workstation without
|
||||
// needing a session cookie or pre-minted API key)
|
||||
let ws: VibnWorkspace | null = null;
|
||||
|
||||
// Resolve workspace + load all of the user's projects.
|
||||
const users = await query<{ id: string }>(
|
||||
`SELECT id FROM fs_users WHERE data->>'email' = $1 LIMIT 1`,
|
||||
[email],
|
||||
);
|
||||
if (users.length === 0) {
|
||||
return NextResponse.json({ error: 'User not found' }, { status: 404 });
|
||||
}
|
||||
const firebaseUserId = users[0].id;
|
||||
const authHeader = request.headers.get('authorization') ?? '';
|
||||
const bearer = authHeader.toLowerCase().startsWith('bearer ')
|
||||
? authHeader.slice(7).trim()
|
||||
: '';
|
||||
const opsSecret = process.env.NEXTAUTH_SECRET;
|
||||
const url = new URL(request.url);
|
||||
const opsEmail = url.searchParams.get('email');
|
||||
|
||||
if (bearer && opsSecret && bearer === opsSecret && opsEmail) {
|
||||
const users = await query<{ id: string }>(
|
||||
`SELECT id FROM fs_users WHERE data->>'email' = $1 LIMIT 1`,
|
||||
[opsEmail],
|
||||
);
|
||||
if (users.length === 0) {
|
||||
return NextResponse.json({ error: `No fs_users row for ${opsEmail}` }, { status: 404 });
|
||||
}
|
||||
ws = await getOrCreateProvisionedWorkspace({
|
||||
userId: users[0].id,
|
||||
email: opsEmail,
|
||||
displayName: opsEmail,
|
||||
});
|
||||
} else {
|
||||
const principal = await requireWorkspacePrincipal(request);
|
||||
if (principal instanceof NextResponse) return principal;
|
||||
ws = principal.workspace;
|
||||
}
|
||||
|
||||
const ws = await getOrCreateProvisionedWorkspace({
|
||||
userId: firebaseUserId,
|
||||
email,
|
||||
displayName: session.user.name ?? email,
|
||||
});
|
||||
if (!ws) {
|
||||
return NextResponse.json({ error: 'Workspace not provisioned' }, { status: 503 });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user