builder / app /api /admin /workspaces /route.ts
Leon4gr45's picture
Upload folder using huggingface_hub
eeb9404 verified
Raw
History Blame Contribute Delete
4.14 kB
/**
* Admin Workspaces API
* GET — list all workspaces with stats
* POST — create a new workspace
*/
import { NextRequest, NextResponse } from 'next/server';
import { requireAuth, verifyInstanceApiKey } from '@/lib/auth/session';
import {
listWorkspaces,
createWorkspace,
grantWorkspaceAccess,
getUserByEmail,
getSystemDatabase,
getWorkspaceProjectCount,
} from '@/lib/auth/system-database';
function getWorkspaceDeploymentCount(workspaceId: string): number {
try {
const sysDb = getSystemDatabase();
const row = sysDb.prepare('SELECT COUNT(*) as count FROM deployment_routing WHERE workspace_id = ?').get(workspaceId) as { count: number };
return row.count;
} catch { return 0; }
}
function getWorkspaceMemberCount(workspaceId: string): number {
try {
const sysDb = getSystemDatabase();
const row = sysDb.prepare('SELECT COUNT(*) as count FROM workspace_access WHERE workspace_id = ?').get(workspaceId) as { count: number };
return row.count;
} catch { return 0; }
}
function getWorkspaceOwnerEmail(ownerId: string): string | null {
try {
const sysDb = getSystemDatabase();
const row = sysDb.prepare('SELECT email FROM users WHERE id = ?').get(ownerId) as { email: string } | undefined;
return row?.email ?? null;
} catch { return null; }
}
export async function GET(request: NextRequest) {
try {
const apiSession = verifyInstanceApiKey(request);
const session = apiSession || await requireAuth();
if (!session.isAdmin) {
return NextResponse.json({ error: 'Admin access required' }, { status: 403 });
}
const workspaces = listWorkspaces();
const enriched = workspaces.map(ws => ({
id: ws.id,
name: ws.name,
ownerId: ws.owner_id,
ownerEmail: getWorkspaceOwnerEmail(ws.owner_id),
maxProjects: ws.max_projects,
maxDeployments: ws.max_deployments,
maxStorageMb: ws.max_storage_mb,
memberCount: getWorkspaceMemberCount(ws.id),
projectCount: getWorkspaceProjectCount(ws.id),
deploymentCount: getWorkspaceDeploymentCount(ws.id),
createdAt: ws.created_at,
updatedAt: ws.updated_at,
}));
return NextResponse.json({ workspaces: enriched });
} catch (error) {
if (error instanceof Error && error.message === 'Unauthorized') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
return NextResponse.json({ error: 'Failed to list workspaces' }, { status: 500 });
}
}
export async function POST(request: NextRequest) {
try {
const apiSession = verifyInstanceApiKey(request);
const session = apiSession || await requireAuth();
if (!session.isAdmin) {
return NextResponse.json({ error: 'Admin access required' }, { status: 403 });
}
const body = await request.json();
const { name, ownerId, ownerEmail } = body;
if (!name) {
return NextResponse.json({ error: 'Workspace name is required' }, { status: 400 });
}
// Resolve owner: either by ownerId directly or by email lookup
let resolvedOwnerId = ownerId;
if (!resolvedOwnerId && ownerEmail) {
const user = getUserByEmail(ownerEmail);
if (!user) {
return NextResponse.json({ error: `No user found with email: ${ownerEmail}` }, { status: 404 });
}
resolvedOwnerId = user.id;
}
if (!resolvedOwnerId) {
return NextResponse.json({ error: 'Owner is required (provide ownerId or ownerEmail)' }, { status: 400 });
}
const workspaceId = createWorkspace(name, resolvedOwnerId);
// createWorkspace already inserts owner into workspace_access, but grantWorkspaceAccess
// uses INSERT OR REPLACE so calling it again with owner role is idempotent and ensures role.
grantWorkspaceAccess(resolvedOwnerId, workspaceId, 'owner');
return NextResponse.json({ id: workspaceId }, { status: 201 });
} catch (error) {
if (error instanceof Error && error.message === 'Unauthorized') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
return NextResponse.json({ error: 'Failed to create workspace' }, { status: 500 });
}
}