Files

99 lines
4.0 KiB
TypeScript

import { NextResponse } from 'next/server';
import jwt from 'jsonwebtoken';
// Konfigurasi CORS agar JUMPA.ID VC (atau subdomain lain) bisa mengakses endpoint ini
function applyCorsHeaders(res: NextResponse, req: Request) {
const origin = req.headers.get('origin');
// Di sistem produksi, pastikan hanya mengizinkan origin yang valid (misal: *.ultramodul.xyz)
if (origin && (origin.includes('ultramodul.xyz') || origin.includes('localhost'))) {
res.headers.set('Access-Control-Allow-Origin', origin);
}
res.headers.set('Access-Control-Allow-Credentials', 'true');
res.headers.set('Access-Control-Allow-Methods', 'GET, OPTIONS');
res.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
return res;
}
export async function OPTIONS(req: Request) {
const res = new NextResponse(null, { status: 204 });
return applyCorsHeaders(res, req);
}
export async function GET(req: Request) {
try {
// 1. Ekstrak Session Token JUMPA.ID (Authentication)
const cookieHeader = req.headers.get('cookie') || '';
const cookies = Object.fromEntries(cookieHeader.split('; ').map(c => c.split('=')));
const sessionToken = cookies['jumpa_token'];
if (!sessionToken) {
const res = NextResponse.json({ error: 'Unauthorized: No session token found' }, { status: 401 });
return applyCorsHeaders(res, req);
}
// 2. Dekripsi Session Token
const jwtSecret = process.env.JWT_SECRET;
if (!jwtSecret) {
throw new Error('JWT_SECRET is not configured');
}
let decodedSession: { licenses?: Record<string, string>; tenantName?: string };
try {
decodedSession = jwt.verify(sessionToken, jwtSecret) as { licenses?: Record<string, string>; tenantName?: string };
} catch (_e) {
const res = NextResponse.json({ error: 'Unauthorized: Invalid session token' }, { status: 401 });
return applyCorsHeaders(res, req);
}
// 3. Ekstrak hak akses (licenses) dari Token Session
// Format licenses: { "chat": "GRANTED", "vc": "GRANTED", "recording": "GRANTED", "pulsar_codec": "GRANTED" }
const licenses = decodedSession.licenses || {};
const tenantName = decodedSession.tenantName || 'UNKNOWN_TENANT';
// 4. Translasi fitur IAM ke Module ID XCU Core (Ala Carte Modules)
const allowedModules: number[] = [];
// Pengecualian Khusus VIP TELAH DIHAPUS (No more hardcoded bypass)
// Auto-Pilot & JVC Package Logic
if (licenses['pulsar_codec'] === 'GRANTED' || licenses['recording'] === 'GRANTED' || licenses['JVC'] === 'GRANTED') {
allowedModules.push(43); // Modul 43: PulsarCodec / Recording / Hot-Swap HD
}
if (licenses['crdt_chat'] === 'GRANTED' || licenses['x_ray_log'] === 'GRANTED' || licenses['JC'] === 'GRANTED' || licenses['JVC'] === 'GRANTED') {
allowedModules.push(72); // Modul 72: Neural CRDT Chat / X-Ray Diagnostic
}
if (licenses['ebpf_shield'] === 'GRANTED' || licenses['JVC'] === 'GRANTED') {
allowedModules.push(88); // Modul 88: The eBPF Shield (Ring-0 DDoS)
}
if (licenses['ouroboros_sla'] === 'GRANTED' || licenses['JVC'] === 'GRANTED') {
allowedModules.push(99); // Modul 99: Ouroboros Automation (Self-Healing SLA)
}
// 5. Generate The Quantum Entitlement Token (JWS murni)
// Ditandatangani menggunakan kunci simetris yang hanya diketahui oleh IAM dan XCU Core
const quantumSecret = process.env.XCU_TOKEN_SECRET || "UltR4S3cr3T_XCU_Key_2026!"; // Sinkron dengan Rust xcu-core
const quantumToken = jwt.sign(
{
tenant: tenantName,
allowed_modules: allowedModules,
},
quantumSecret,
{ expiresIn: '8h' }
);
const res = NextResponse.json({
success: true,
token: quantumToken,
modules: allowedModules,
}, { status: 200 });
return applyCorsHeaders(res, req);
} catch (error: unknown) {
console.error('[QUANTUM TOKEN ERROR]', error);
const res = NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
return applyCorsHeaders(res, req);
}
}