Spaces:
Runtime error
Runtime error
File size: 25,082 Bytes
f7a4106 0df87ee f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b 0df87ee f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 9d9e25b 0df87ee f7a4106 9d9e25b f7a4106 9d9e25b f7a4106 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 | const express = require('express');
const multer = require('multer');
const { Storage, File } = require('megajs');
const fs = require('fs');
require('dotenv').config();
const app = express();
const port = process.env.PORT || 7860;
// Setup penyimpanan sementara untuk upload transit
const upload = multer({ dest: '/tmp/' });
app.use(express.json());
let megaStorage;
// Variabel tracker statis sederhana (akan ter-reset jika space restart/tidur)
let totalFiles = 0;
let totalBytes = 0;
let totalDownloads = 0;
// Fungsi format ukuran biner
function formatBytes(bytes, decimals = 2) {
if (!bytes) return '0 B';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
async function connectStorage() {
return new Promise((resolve, reject) => {
const email = process.env.MEGA_EMAIL;
const password = process.env.MEGA_PASSWORD;
if (!email || !password) {
console.error("ERROR: Variabel lingkungan kredensial belum diatur!");
return reject("Kredensial kosong");
}
const storage = new Storage({
email: email,
password: password,
keepalive: true
}, (err) => {
if (err) return reject(err);
console.log('✓ Sistem backend CDN siap dan terhubung!');
resolve(storage);
});
});
}
connectStorage().then(storage => {
megaStorage = storage;
}).catch(err => {
console.error('⚠️ Gagal menginisialisasi storage:', err);
});
// Endpoint 1: Frontend Dashboard Utama (Tema Putih Bersih Premium)
app.get('/', (req, res) => {
const hostUrl = `${req.protocol}://${req.get('host')}`;
res.send(`
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CDN FAREL - Secure CDN Gateway</title>
<style>
:root {
--bg-main: #f8fafc;
--bg-card: #ffffff;
--text-main: #0f172a;
--text-muted: #64748b;
--border-color: #e2e8f0;
--accent: #ff4757;
--accent-hover: #ff6b81;
--indigo: #4f46e5;
--success: #10b981;
}
* { box-sizing: border-box; margin: 0; padding: 0; font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; }
body { background: var(--bg-main); color: var(--text-main); padding: 40px 20px; line-height: 1.5; }
.container { max-width: 1000px; margin: 0 auto; }
/* Header */
header { text-align: center; margin-bottom: 40px; }
header h1 { font-size: 2.2rem; font-weight: 800; tracking-spacing: -0.05em; color: var(--text-main); }
header h1 span { color: var(--accent); }
header p.tagline { font-size: 1.1rem; color: var(--text-muted); font-weight: 500; margin-top: 5px; }
header p.desc { max-width: 600px; margin: 10px auto 0; color: var(--text-muted); font-size: 0.95rem; }
/* Grid Statistik */
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 20px; margin-bottom: 40px; }
.stat-card { background: var(--bg-card); padding: 24px; border-radius: 12px; border: 1px solid var(--border-color); box-shadow: 0 1px 3px rgba(0,0,0,0.02); }
.stat-card h3 { font-size: 2rem; font-weight: 700; color: var(--text-main); margin-bottom: 4px; }
.stat-card p.label { font-size: 0.9rem; font-weight: 600; color: var(--text-main); }
.stat-card p.sub { font-size: 0.8rem; color: var(--text-muted); }
/* Card Utama */
.card { background: var(--bg-card); padding: 40px; border-radius: 16px; border: 1px solid var(--border-color); box-shadow: 0 4px 6px -1px rgba(0,0,0,0.05), 0 2px 4px -1px rgba(0,0,0,0.03); margin-bottom: 40px; }
.card-title { font-size: 1.4rem; font-weight: 700; margin-bottom: 6px; }
.card-subtitle { font-size: 0.95rem; color: var(--text-muted); margin-bottom: 25px; }
/* Dropzone */
.upload-box { border: 2px dashed #cbd5e1; padding: 50px 20px; text-align: center; border-radius: 12px; cursor: pointer; transition: all 0.2s ease; background: #f8fafc; }
.upload-box:hover { border-color: var(--accent); background: #fff5f5; }
.upload-box p.primary-text { font-size: 1.05rem; font-weight: 600; color: var(--text-main); margin-bottom: 4px; }
.upload-box p.secondary-text { font-size: 0.85rem; color: var(--text-muted); margin-bottom: 15px; }
.upload-box p.info-text { font-size: 0.85rem; color: var(--text-muted); line-height: 1.4; background: #fff; padding: 8px 12px; border-radius: 6px; display: inline-block; border: 1px solid var(--border-color); }
input[type="file"] { display: none; }
button.btn-upload { background: var(--accent); color: white; border: none; padding: 14px 30px; border-radius: 8px; font-weight: 700; cursor: pointer; width: 100%; margin-top: 20px; font-size: 16px; transition: background 0.2s; }
button.btn-upload:hover { background: var(--accent-hover); }
/* Hasil Upload */
.result { margin-top: 30px; display: none; background: #f0fdf4; padding: 25px; border-radius: 10px; border: 1px solid #bbf7d0; }
.result h3 { margin-bottom: 12px; color: #166534; font-size: 1.15rem; display: flex; align-items: center; gap: 8px; }
.result p { font-size: 0.95rem; margin-bottom: 10px; color: var(--text-main); }
.result-link-box { background: #ffffff; padding: 12px; border-radius: 6px; border: 1px solid #dcfce7; display: flex; align-items: center; justify-content: space-between; margin-top: 10px; gap: 10px; }
.result-link-box a { color: var(--indigo); word-break: break-all; font-weight: 600; text-decoration: none; font-size: 0.9rem; overflow: hidden; text-overflow: ellipsis; }
.result-link-box a:hover { text-decoration: underline; }
.btn-copy-link { background: var(--text-main); color: #fff; border: none; padding: 6px 12px; border-radius: 4px; font-size: 0.8rem; font-weight: 600; cursor: pointer; white-space: nowrap; }
.status { text-align: center; margin-top: 15px; color: var(--indigo); font-weight: 600; font-size: 0.95rem; display: none; }
/* Fitur & Manfaat Kelebihan */
.section-title { font-size: 1.6rem; font-weight: 800; text-align: center; margin-bottom: 6px; }
.section-subtitle { text-align: center; color: var(--text-muted); font-size: 0.95rem; margin-bottom: 35px; }
.features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 25px; margin-bottom: 5px; }
.feature-item { background: var(--bg-card); padding: 30px; border-radius: 12px; border: 1px solid var(--border-color); }
.feature-item h4 { font-size: 1.1rem; font-weight: 700; margin-bottom: 8px; color: var(--text-main); }
.feature-item p { font-size: 0.9rem; color: var(--text-muted); line-height: 1.5; }
/* Dokumentasi API Section */
.api-card { background: var(--bg-card); border-radius: 14px; border: 1px solid var(--border-color); margin-top: 40px; overflow: hidden; }
.api-header { padding: 25px; border-bottom: 1px solid var(--border-color); background: #fafafa; }
.api-badge-row { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
.badge-method { background: var(--success); color: white; padding: 4px 10px; border-radius: 4px; font-weight: 700; font-size: 0.8rem; }
.badge-type { background: #e2e8f0; color: var(--text-main); padding: 4px 10px; border-radius: 4px; font-weight: 600; font-size: 0.8rem; }
.api-endpoint { font-family: monospace; font-size: 1.1rem; font-weight: 700; color: var(--text-main); }
.api-desc { padding: 0 25px 20px; font-size: 0.9rem; color: var(--text-muted); border-bottom: 1px solid var(--border-color); background: #fafafa; }
/* Code tabs */
.api-body { padding: 25px; background: #1e293b; color: #f8fafc; }
.tab-header { display: flex; gap: 8px; margin-bottom: 15px; border-bottom: 1px solid #334155; padding-bottom: 10px; }
.tab-btn { background: transparent; color: #94a3b8; border: none; padding: 6px 14px; font-size: 0.85rem; font-weight: 600; cursor: pointer; border-radius: 4px; }
.tab-btn.active { background: #334155; color: #fff; }
.code-container { position: relative; }
pre { font-family: 'Courier New', Courier, monospace; font-size: 0.85rem; overflow-x: auto; white-space: pre-wrap; word-break: break-all; color: #e2e8f0; line-height: 1.5; }
.btn-copy-code { position: absolute; top: 0; right: 0; background: #334155; color: #f8fafc; border: none; padding: 5px 10px; border-radius: 4px; font-size: 0.75rem; cursor: pointer; font-weight: 600; }
.btn-copy-code:hover { background: #475569; }
.cors-text { font-size: 0.75rem; font-weight: 700; color: #38bdf8; letter-spacing: 0.05em; margin-top: 15px; display: inline-block; }
/* Footer */
footer { border-top: 1px solid var(--border-color); margin-top: 60px; padding-top: 30px; color: var(--text-muted); font-size: 0.85rem; }
.footer-top { display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; gap: 20px; margin-bottom: 20px; }
.footer-brand h5 { font-size: 1.1rem; font-weight: 800; color: var(--text-main); margin-bottom: 4px; }
.footer-meta { display: flex; gap: 15px; font-weight: 600; color: var(--text-main); font-size: 0.85rem; }
.footer-bottom { display: flex; justify-content: space-between; align-items: center; border-top: 1px solid var(--border-color); padding-top: 20px; color: var(--text-muted); font-size: 0.8rem; }
</style>
</head>
<body>
<div class="container">
<!-- Header -->
<header>
<h1>CDN <span>FAREL</span></h1>
<p class="tagline">Secure CDN Gateway</p>
<p class="desc">Unggah Berkas, Dapatkan Direct Link</p>
<p class="desc" style="font-size: 0.85rem; margin-top: 5px; max-width: 500px;">Sistem direct link CDN mandiri berkecepatan tinggi dengan enkripsi awan ganda yang aman dan global.</p>
</header>
<!-- Grid Statistik -->
<div class="stats-grid">
<div class="stat-card">
<h3 id="statFiles">${totalFiles}</h3>
<p class="label">Total File Aktif</p>
<p class="sub">Seluruh file yang tersimpan di CDN</p>
</div>
<div class="stat-card">
<h3 id="statBytes">${formatBytes(totalBytes)}</h3>
<p class="label">Kapasitas Terpakai</p>
<p class="sub">Total kapasitas cloud terpakai</p>
</div>
<div class="stat-card">
<h3 id="statDownloads">${totalDownloads}</h3>
<p class="label">Total Pengunduhan</p>
<p class="sub">File dikonsumsi via link CDN</p>
</div>
<div class="stat-card">
<h3>1</h3>
<p class="label">Node Storage Cluster</p>
<p class="sub">Penyimpanan cloud node terenkripsi aktif</p>
</div>
</div>
<!-- Card Upload Dropzone -->
<div class="card">
<h2 class="card-title">Unggah Berkas ke CDN Farel</h2>
<p class="card-subtitle">Dapatkan direct link, embedding, dan direct download berkecepatan tinggi instan dengan mengunggah berkas ke gateway kami.</p>
<form id="uploadForm">
<div class="upload-box" onclick="document.getElementById('fileInput').click()" id="dropzone">
<p class="primary-text" id="boxText">Seret & taruh berkas di sini, atau pilih manual</p>
<p class="secondary-text">Semua format file didukung (Maks 100 MB)</p>
<p class="info-text">Pilih atau seret berkas ke dropzone untuk mempersiapkan pratinjau dan mengaktifkan tautan CDN.</p>
<input type="file" id="fileInput" name="file" required onchange="updateBoxText()">
</div>
<button type="submit" class="btn-upload">UNGGAH KE CDN</button>
</form>
<div class="status" id="statusText">Sedang memproses berkas... Harap tunggu...</div>
<!-- Box Hasil Output -->
<div class="result" id="resultBox">
<h3>🎉 Berkas Berhasil Diproses!</h3>
<p><strong>Nama Berkas:</strong> <span id="resName"></span></p>
<p style="font-weight: 600; margin-top: 15px; font-size: 0.9rem; color: #15803d;">Link CDN Anda:</p>
<div class="result-link-box">
<a id="resCdn" href="#" target="_blank"></a>
<button class="btn-copy-link" onclick="copyLinkOutput()">Copy Link</button>
</div>
</div>
</div>
<!-- Section Kelebihan -->
<h2 class="section-title">Manfaat & Kelebihan CDN FAREL</h2>
<p class="section-subtitle">Infrastruktur andal yang dioptimalkan untuk performa pengiriman aset digital terbaik.</p>
<div class="features-grid">
<div class="feature-item">
<h4>Direct Streaming Link</h4>
<p>Dapatkan link bersih langsung tanpa halaman pengalihan, iklan, atau timer. Sempurna untuk disematkan langsung pada HTML, Markdown, stylesheet, atau tag media web.</p>
</div>
<div class="feature-item">
<h4>Bebas Hambatan CORS</h4>
<p>Mendukung kebijakan penuh Cross-Origin Resource Sharing (CORS). Integrasikan aset seperti gambar kustom, font kustom, atau file JSON dari domain manapun secara mulus.</p>
</div>
<div class="feature-item">
<h4>Penyimpanan MEGA Aman</h4>
<p>Semua aset diunggah langsung dan disimpan ke dalam penyimpanan awan MEGA terenkripsi yang aman untuk menjamin ketersediaan data Anda tetap utuh, aman, dan dapat diakses terus.</p>
</div>
<div class="feature-item">
<h4>MIME Type yang Tepat</h4>
<p>Deteksi tipe konten otomatis secara cerdas. Server menyajikan berkas dengan header <code>Content-Type</code> yang akurat agar dapat di-render atau diputar langsung di browser.</p>
</div>
<div class="feature-item">
<h4>Kapasitas Besar 100MB</h4>
<p>Kirim berkas media berukuran besar seperti video klip, track musik berkualitas tinggi, dokumen PDF tebal, hingga kompresi zip tanpa takut batasan ukuran yang kecil.</p>
</div>
<div class="feature-item">
<h4>Arsip Selamanya (Persistent)</h4>
<p>Berkas tidak memiliki batas kedaluwarsa waktu. File Anda tetap tersimpan utuh di gateway awan tanpa kekhawatiran akan terhapus otomatis setelah beberapa hari.</p>
</div>
</div>
<!-- Section API Dokumentasi -->
<div class="api-card">
<div class="api-header">
<div class="api-badge-row">
<span class="badge-method">POST</span>
<span class="badge-type">multipart/form-data</span>
</div>
<div class="api-endpoint">/api/upload</div>
</div>
<div class="api-desc">
Gunakan endpoint pengunggahan file dari CDN FAREL untuk mengotomatiskan proses upload file langsung dari aplikasi, skrip backend, website, atau bot Anda. Kirim berkas Anda pada parameter body bernama <code>file</code>. Respon akan mengembalikan URL streaming langsung secara instan.
</div>
<div class="api-body">
<div class="tab-header">
<button class="tab-btn active" onclick="switchTab('curl')">cURL</button>
<button class="tab-btn" onclick="switchTab('js')">javascript</button>
<button class="tab-btn" onclick="switchTab('py')">python</button>
</div>
<div class="code-container">
<button class="btn-copy-code" onclick="copyCodeSnippet()">Copy Snippet</button>
<pre id="codeBlock">curl -X POST "${hostUrl}/api/upload" \\\n -F "file=@/path/to/your/file.png"</pre>
</div>
<span class="cors-text">CORS ENABLED</span>
</div>
</div>
<!-- Footer -->
<footer>
<div class="footer-top">
<div class="footer-brand">
<h5>CDN FAREL</h5>
<p>Penyimpanan awan performa tinggi dan gerbang distribusi direct link aman.</p>
</div>
<div class="footer-meta">
<span>Node Kluster Utama Aktif</span>
<span>|</span>
<span>SSL Terenkripsi</span>
<span>|</span>
<span>CORS Terbuka</span>
</div>
</div>
<div class="footer-bottom">
<p>© 2026 CDN FAREL. Hak Cipta Dilindungi.</p>
<p>v1.4.0 Production • SLA 99.9%</p>
</div>
</footer>
</div>
<script>
const hostBase = "${hostUrl}";
let currentTab = 'curl';
// Snippets generator dinamis sesuai host saat ini
const snippets = {
curl: \`curl -X POST "\${hostBase}/api/upload" \\\n -F "file=@/path/to/your/file.png"\`,
js: \`const data = new FormData();\ndata.append('file', fileInput.files[0]);\n\nfetch('\${hostBase}/api/upload', {\n method: 'POST',\n body: data\n})\n.then(res => res.json())\n.then(json => console.log(json.cdnLink));\`,
py: \`import requests\n\nurl = "\${hostBase}/api/upload"\nfiles = {'file': open('file.png', 'rb')}\nr = requests.post(url, files=files)\nprint(r.json()['cdnLink'])\`
};
function switchTab(lang) {
currentTab = lang;
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
event.target.classList.add('active');
document.getElementById('codeBlock').innerText = snippets[lang];
}
function copyCodeSnippet() {
navigator.clipboard.writeText(snippets[currentTab]);
alert('Snippet kode berhasil disalin!');
}
function copyLinkOutput() {
const linkText = document.getElementById('resCdn').innerText;
navigator.clipboard.writeText(linkText);
alert('Tautan CDN berhasil disalin!');
}
function updateBoxText() {
const input = document.getElementById('fileInput');
if(input.files.length > 0) {
document.getElementById('boxText').innerText = "Berkas terpilih: " + input.files[0].name;
}
}
// Setup Drag and Drop
const dropzone = document.getElementById('dropzone');
const fileInput = document.getElementById('fileInput');
['dragenter', 'dragover'].forEach(eventName => {
dropzone.addEventListener(eventName, (e) => { e.preventDefault(); dropzone.style.borderColor = 'var(--accent)'; }, false);
});
['dragleave', 'drop'].forEach(eventName => {
dropzone.addEventListener(eventName, (e) => { e.preventDefault(); dropzone.style.borderColor = '#cbd5e1'; }, false);
});
dropzone.addEventListener('drop', (e) => {
fileInput.files = e.dataTransfer.files;
updateBoxText();
});
// Submit form lewat AJAX
document.getElementById('uploadForm').addEventListener('submit', async (e) => {
e.preventDefault();
if (fileInput.files.length === 0) return;
const formData = new FormData();
formData.append('file', fileInput.files[0]);
const statusText = document.getElementById('statusText');
const resultBox = document.getElementById('resultBox');
statusText.style.display = "block";
resultBox.style.display = "none";
try {
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
const data = await response.json();
if (data.success) {
statusText.style.display = "none";
document.getElementById('resName').innerText = data.fileName;
document.getElementById('resCdn').innerText = data.cdnLink;
document.getElementById('resCdn').href = data.cdnLink;
resultBox.style.display = "block";
// Update UI statistik secara real-time di halaman
if(data.stats) {
document.getElementById('statFiles').innerText = data.stats.files;
document.getElementById('statBytes').innerText = data.stats.formattedBytes;
}
} else {
statusText.innerText = "Gagal memproses: " + data.error;
}
} catch (err) {
statusText.innerText = "Terjadi kesalahan sistem saat memproses berkas.";
console.error(err);
}
});
</script>
</body>
</html>
`);
});
// Endpoint 2: API Proses Upload File (Update data stat secara real-time)
app.post('/api/upload', upload.single('file'), async (req, res) => {
if (!megaStorage) return res.status(503).json({ error: 'Storage backend belum siap' });
if (!req.file) return res.status(400).json({ error: 'Tidak ada file yang dikirim' });
try {
const filePath = req.file.path;
const fileName = req.file.originalname;
const fileSize = req.file.size;
const fileStream = fs.createReadStream(filePath);
// Upload berkas ke cloud storage MEGA
const megaFile = await megaStorage.upload({
name: fileName,
size: fileSize
}, fileStream).complete;
// Dapatkan tautan cloud publik internal
const downloadLink = await megaFile.link();
// Hapus file transit di lokal (/tmp/)
fs.unlinkSync(filePath);
// Ubah '#' menjadi '-' agar aman dilewati via URL Express Gateway
const fileId = downloadLink.split('/file/')[1].replace('#', '-');
// Tambah counter statistik internal server
totalFiles += 1;
totalBytes += fileSize;
res.json({
success: true,
fileName: fileName,
cdnLink: `${req.protocol}://${req.get('host')}/cdn/${fileId}`,
stats: {
files: totalFiles,
formattedBytes: formatBytes(totalBytes)
}
});
} catch (error) {
console.error(error);
res.status(500).json({ error: 'Gagal memproses penyimpanan: ' + error.message });
}
});
// Endpoint 3: Proxy CDN Gateway (Streaming Data Langsung + Dukungan CORS)
app.get('/cdn/:fileIdAndKey', async (req, res) => {
try {
const fullId = req.params.fileIdAndKey;
// Kembalikan karakter '-' menjadi '#' untuk menyusun URL asli MEGA
const correctedId = fullId.replace('-', '#');
const targetUrl = `https://mega.nz/file/${correctedId}`;
const file = File.fromURL(targetUrl);
await file.loadAttributes();
// Dukungan Penuh Kebijakan CORS
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
// Atur header disposisi agar browser langsung memutar/merender inline aset
res.setHeader('Content-Disposition', `inline; filename="${file.name}"`);
// Tambah hit counter unduhan
totalDownloads += 1;
// Alirkan data secara streaming waktu nyata ke pengguna luar
const stream = file.download();
stream.pipe(res);
} catch (error) {
console.error(error);
res.status(404).send('Berkas tidak ditemukan atau tautan CDN salah.');
}
});
app.listen(port, () => {
console.log(`Server CDN Farel berjalan aktif pada port ${port}`);
}); |