69 lines
2.7 KiB
TypeScript
69 lines
2.7 KiB
TypeScript
// [TSM.ID].[11031972] -- All Rights Reserved. Proprietary & Confidential.
|
|
import { NextRequest, NextResponse } from "next/server";
|
|
import crypto from "crypto";
|
|
import { promises as fs } from "fs";
|
|
import path from "path";
|
|
|
|
function decryptBuffer(vaultPayload: Buffer): Buffer {
|
|
const rawKey = process.env.QUANTUM_MASTER_KEY || "JUMPA-XCU-ABSOLUTE-SOVEREIGN-KEY";
|
|
const key = crypto.createHash("sha256").update(rawKey).digest();
|
|
|
|
// Ekstrak komponen dari paket vault: [IV (12 bytes)] + [Auth Tag (16 bytes)] + [Encrypted Data]
|
|
const iv = vaultPayload.subarray(0, 12);
|
|
const tag = vaultPayload.subarray(12, 28);
|
|
const encrypted = vaultPayload.subarray(28);
|
|
|
|
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
|
|
decipher.setAuthTag(tag);
|
|
|
|
return Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
}
|
|
|
|
export async function GET(
|
|
req: NextRequest,
|
|
{ params }: { params: Promise<{ filename: string }> }
|
|
) {
|
|
try {
|
|
const { filename } = await params;
|
|
|
|
// Mencegah eksploitasi Path Traversal
|
|
if (filename.includes("..") || filename.includes("/")) {
|
|
return NextResponse.json({ error: "Invalid filename" }, { status: 400 });
|
|
}
|
|
|
|
const localDir = process.env.OMNI_LOCAL_DIR || "/var/www/omni-storage";
|
|
const filePath = path.join(localDir, filename);
|
|
|
|
// 1. Baca data terenkripsi (Ciphertext) dari brankas baja lokal
|
|
const vaultPayload = await fs.readFile(filePath);
|
|
|
|
// 2. Dekripsi On-The-Fly (Cleartext) untuk disajikan ke pengguna yang berhak
|
|
const decryptedBuffer = decryptBuffer(vaultPayload);
|
|
|
|
// Deteksi tipe konten dari nama file asli (membuang .vault)
|
|
const originalExt = filename.replace(".vault", "").split(".").pop()?.toLowerCase();
|
|
|
|
let contentType = "application/octet-stream";
|
|
if (originalExt === "jpg" || originalExt === "jpeg") contentType = "image/jpeg";
|
|
else if (originalExt === "png") contentType = "image/png";
|
|
else if (originalExt === "gif") contentType = "image/gif";
|
|
else if (originalExt === "webp") contentType = "image/webp";
|
|
else if (originalExt === "pdf") contentType = "application/pdf";
|
|
else if (originalExt === "mp4") contentType = "video/mp4";
|
|
|
|
return new NextResponse(new Uint8Array(decryptedBuffer), {
|
|
status: 200,
|
|
headers: {
|
|
"Content-Type": contentType,
|
|
// Caching agresif karena file objek bersifat immutable
|
|
"Cache-Control": "public, max-age=31536000, immutable",
|
|
},
|
|
});
|
|
|
|
} catch (error: unknown) {
|
|
const msg = error instanceof Error ? error.message : "Unknown error";
|
|
console.error("[QUANTUM VAULT] Dekripsi gagal atau file hilang:", msg);
|
|
return NextResponse.json({ error: "Brankas tidak dapat diakses atau kunci enkripsi salah." }, { status: 404 });
|
|
}
|
|
}
|