119 lines
3.8 KiB
TypeScript
119 lines
3.8 KiB
TypeScript
// [TSM.ID].[11031972] — All Rights Reserved. Proprietary & Confidential.
|
|
import { NextResponse } from 'next/server';
|
|
import { db } from "@/drizzle/db";
|
|
import { tenants, users } from "@/drizzle/schema";
|
|
import { eq } from 'drizzle-orm';
|
|
import { cookies } from 'next/headers';
|
|
import jwt from 'jsonwebtoken';
|
|
import { QuantumOrchestrator } from "@/lib/quantum-orchestrator";
|
|
|
|
export const dynamic = 'force-dynamic';
|
|
|
|
interface DecodedToken {
|
|
userId: string;
|
|
email: string;
|
|
role: string;
|
|
tenantId: string;
|
|
}
|
|
|
|
export async function GET() {
|
|
try {
|
|
const cookieStore = await cookies();
|
|
const token = cookieStore.get('jumpa_token')?.value;
|
|
|
|
if (!token) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
|
|
const decoded = jwt.verify(token, process.env.JWT_SECRET as string) as DecodedToken;
|
|
|
|
// Fetch tenant data to get BYOK settings and licenses
|
|
const tenantData = await db.select().from(tenants).where(eq(tenants.id, decoded.tenantId)).limit(1);
|
|
const userData = await db.select().from(users).where(eq(users.id, decoded.userId)).limit(1);
|
|
|
|
if (tenantData.length === 0) {
|
|
return NextResponse.json({ error: 'Tenant not found' }, { status: 404 });
|
|
}
|
|
|
|
const tenant = tenantData[0];
|
|
const user = userData[0];
|
|
|
|
// Parse licenses
|
|
const rawLicenses = tenant.licenses || '{}';
|
|
const tenantLicParsed = JSON.parse(rawLicenses);
|
|
let tenantLicNormalized: Record<string, string> = {};
|
|
if (Array.isArray(tenantLicParsed)) {
|
|
tenantLicParsed.forEach((k: string) => tenantLicNormalized[k] = 'GRANTED');
|
|
} else {
|
|
tenantLicNormalized = tenantLicParsed || {};
|
|
}
|
|
|
|
const userLicParsed = JSON.parse(user?.licenses || '{}');
|
|
let userLicNormalized: Record<string, string> = {};
|
|
if (Array.isArray(userLicParsed)) {
|
|
userLicParsed.forEach((k: string) => userLicNormalized[k] = 'GRANTED');
|
|
} else {
|
|
userLicNormalized = userLicParsed || {};
|
|
}
|
|
|
|
// Resolve 101-Module Matrix Capabilities
|
|
const capabilities = QuantumOrchestrator.resolve(
|
|
tenantLicNormalized,
|
|
userLicNormalized,
|
|
!!tenant.isActive
|
|
);
|
|
|
|
// Parse legacy modules string list
|
|
let modules: string[] = [];
|
|
try {
|
|
if (Array.isArray(tenantLicParsed)) {
|
|
modules = tenantLicParsed;
|
|
} else {
|
|
modules = Object.keys(tenantLicParsed).filter(k => (tenantLicParsed as Record<string, string>)[k] === 'GRANTED');
|
|
}
|
|
} catch (_e) {
|
|
modules = [];
|
|
}
|
|
|
|
// Determine the active BYOK Key
|
|
// Priority: User > Tenant
|
|
let activeByokKey = 'none';
|
|
let byokLevel = 'SYSTEM';
|
|
|
|
if (user?.byokEnabled && user?.byokKey) {
|
|
activeByokKey = user.byokKey;
|
|
byokLevel = 'USER';
|
|
} else if (tenant.byokEnabled && tenant.byokKey) {
|
|
activeByokKey = tenant.byokKey;
|
|
byokLevel = 'TENANT';
|
|
}
|
|
|
|
// Secret WAJIB sama dengan XCU_QCG_SECRET di XCU Core (PKX Rule #4)
|
|
const xcuSecret = process.env.XCU_QCG_SECRET || process.env.XCU_TOKEN_SECRET;
|
|
if (!xcuSecret) {
|
|
throw new Error('HUKUM MUTLAK: XCU_QCG_SECRET tidak ditemukan! Gerbang Anti-Jumping menolak akses.');
|
|
}
|
|
|
|
const quantumToken = jwt.sign({
|
|
sub: decoded.email,
|
|
tenantId: decoded.tenantId,
|
|
modules: modules,
|
|
capabilities: capabilities,
|
|
byok: activeByokKey,
|
|
byokLevel: byokLevel,
|
|
iat: Math.floor(Date.now() / 1000),
|
|
exp: Math.floor(Date.now() / 1000) + (60 * 60 * 12)
|
|
}, xcuSecret);
|
|
|
|
return NextResponse.json({
|
|
token: quantumToken,
|
|
modules: modules,
|
|
capabilities: capabilities,
|
|
byokActive: activeByokKey !== 'none',
|
|
byokLevel: byokLevel
|
|
});
|
|
|
|
} catch (error: unknown) {
|
|
console.error('[QUANTUM TOKEN ERROR]', error);
|
|
return NextResponse.json({ error: 'Quantum Sync Failed' }, { status: 500 });
|
|
}
|
|
}
|