Edit: customaio
Nama Worker
Kode Sumber
addEventListener('fetch', event => { // Perubahan di sini: kita tidak perlu meneruskan 'env' lagi event.respondWith(handleRequest(event.request)) }); // Perubahan di sini: hapus parameter 'env' async function handleRequest(request) { // Otentikasi sudah dihapus sesuai permintaan sebelumnya if (request.method === 'POST') { try { const data = await request.json(); let response; if (data.action === 'create_dns') { if (!data.subdomain || !data.ip || !data.domain) throw new Error('Subdomain, IP, and domain are required for creating DNS record.'); // Perubahan di sini: hapus parameter 'env' saat memanggil fungsi response = await addDnsRecord(data.subdomain, data.ip, data.domain, data.proxied); } else if (data.action === 'assign_route') { if (!data.routePattern || !data.scriptName || !data.domain) throw new Error('Route pattern, script name, and domain are required for assigning a route.'); // Perubahan di sini: hapus parameter 'env' saat memanggil fungsi response = await addWorkerRoute(data.routePattern, data.scriptName, data.domain); } else { throw new Error('Invalid action specified.'); } return new Response(JSON.stringify(response), { headers: { 'Content-Type': 'application/json' } }); } catch (error) { return new Response(JSON.stringify({ success: false, errors: [{ message: error.message }] }), { status: 400, headers: { 'Content-Type': 'application/json' } }); } } return new Response(getHtmlPage(), { headers: { 'Content-Type': 'text/html;charset=UTF-8' } }); } // Perubahan di sini: hapus parameter 'env' dan panggil variabel secara langsung async function addWorkerRoute(pattern, scriptName, domain) { const zoneId = domain === 'kudacuki.ggff.net' ? ZONE_ID_KUDACUKI : ZONE_ID_GRATISAN; if (!zoneId) throw new Error('Invalid domain selected.'); const url = `https://api.cloudflare.com/client/v4/zones/${zoneId}/workers/routes`; const apiToken = CLOUDFLARE_API_TOKEN; const response = await fetch(url, { method: 'POST', headers: { 'Authorization': `Bearer ${apiToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ pattern: `${pattern}`, script: scriptName }), }); const result = await response.json(); if (!result.success) throw new Error(`Cloudflare API Error: ${result.errors.map(err => err.message).join(', ')}`); return result; } // Perubahan di sini: hapus parameter 'env' dan panggil variabel secara langsung async function addDnsRecord(subdomain, ipAddress, domain, proxied) { const zoneId = domain === 'kudacuki.ggff.net' ? ZONE_ID_KUDACUKI : ZONE_ID_GRATISAN; if (!zoneId) throw new Error('Invalid domain selected.'); const url = `https://api.cloudflare.com/client/v4/zones/${zoneId}/dns_records`; const apiToken = CLOUDFLARE_API_TOKEN; const recordName = subdomain; const response = await fetch(url, { method: 'POST', headers: { 'Authorization': `Bearer ${apiToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 'A', name: recordName, content: ipAddress, ttl: 1, proxied: proxied }), }); const result = await response.json(); if (!result.success) throw new Error(`Cloudflare API Error: ${result.errors.map(err => err.message).join(', ')}`); return result; } // TIDAK ADA PERUBAHAN DI FUNGSI HTML INI function getHtmlPage() { return `<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>AIO Control Panel</title> <link rel='icon' type='image/png' href='https://i.imgur.com/O4p13lg.png'> <style> :root { --bg-color: #1a1b26; --surface-color: #24283b; --text-color: #c0caf5; --accent-color: #7aa2f7; --success-color: #9ece6a; --error-color: #f7768e; --border-color: #414868; --muted-text-color: #565f89; --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; } body { background-color: var(--bg-color); color: var(--text-color); font-family: var(--font-family); margin: 0; padding: 40px 20px; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; } .container { width: 100%; max-width: 600px; background: var(--surface-color); border-radius: 12px; padding: 30px; box-shadow: 0 10px 30px rgba(0,0,0,0.2); } h1 { text-align: center; margin-top: 0; margin-bottom: 30px; font-weight: 600; } .tabs { display: flex; background: #16161e; border-radius: 8px; padding: 5px; margin-bottom: 25px; } .tab-button { flex: 1; padding: 12px 10px; background: transparent; border: none; border-radius: 6px; color: var(--text-color); font-family: var(--font-family); font-size: 14px; font-weight: 500; cursor: pointer; transition: background-color 0.2s, color 0.2s; display: flex; align-items: center; justify-content: center; gap: 8px; } .tab-button:hover { background: rgba(255,255,255,0.05); } .tab-button.active { background: var(--accent-color); color: var(--bg-color); } .tab-button svg { width: 18px; height: 18px; stroke-width: 2; } .form-section { display: none; } .form-section.active { display: block; } .form-group { margin-bottom: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; font-size: 14px; } input[type="text"], select { width: 100%; background: var(--bg-color); border: 1px solid var(--border-color); border-radius: 6px; padding: 12px; color: var(--text-color); font-size: 16px; box-sizing: border-box; transition: border-color 0.2s, box-shadow 0.2s; } input[type="text"]:focus, select:focus { outline: none; border-color: var(--accent-color); box-shadow: 0 0 0 3px rgba(122, 162, 247, 0.3); } button[type="submit"] { width: 100%; background: var(--accent-color); border: none; border-radius: 6px; padding: 14px; color: var(--bg-color); font-size: 16px; font-weight: bold; cursor: pointer; transition: background-color 0.2s; } button[type="submit"]:hover { background-color: #9abdf5; } .result { margin-top: 25px; } .alert { padding: 15px; border-radius: 6px; font-size: 14px; } .alert-success { background-color: rgba(158, 206, 106, 0.1); color: var(--success-color); border: 1px solid var(--success-color); } .alert-danger { background-color: rgba(247, 118, 142, 0.1); color: var(--error-color); border: 1px solid var(--error-color); } pre { background-color: var(--bg-color); padding: 15px; border-radius: 6px; border: 1px solid var(--border-color); color: #9aa5ce; white-space: pre-wrap; word-wrap: break-word; font-size: 13px; } .switch-container { display: flex; align-items: center; gap: 12px; } .switch { position: relative; display: inline-block; width: 50px; height: 28px; } .switch input { opacity: 0; width: 0; height: 0; } .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #3b4261; transition: .4s; border-radius: 28px; } .slider:before { position: absolute; content: ""; height: 20px; width: 20px; left: 4px; bottom: 4px; background-color: white; transition: .4s; border-radius: 50%; } input:checked + .slider { background-color: var(--accent-color); } input:checked + .slider:before { transform: translateX(22px); } /* CSS BARU UNTUK FOOTER */ footer { text-align: center; margin-top: 30px; padding-top: 20px; border-top: 1px solid var(--border-color); font-size: 14px; color: var(--muted-text-color); } </style> </head> <body> <div class="container"> <h1>Custom AIO</h1> <div class="tabs"> <button class="tab-button active" onclick="showForm(event, 'dns-form')"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"></path></svg> DNS Record </button> <button class="tab-button" onclick="showForm(event, 'route-form')"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72"></path></svg> Assign Route </button> </div> <div class="content"> <div id="dns-form" class="form-section active"> <form id="form-dns"> <input type="hidden" name="action" value="create_dns"> <div class="form-group"> <label for="dns-subdomain">Masukan Subdomain (atau '*' untuk wildcard)</label> <input type="text" id="dns-subdomain" class="form-control" name="subdomain" required> </div> <div class="form-group"> <label for="dns-ip">IP Address</label> <input type="text" id="dns-ip" class="form-control" name="ip" required> </div> <div class="form-group"> <label for="dns-domain">Pilih Domain</label> <select id="dns-domain" class="form-control" name="domain" required> <option value="kudacuki.ggff.net">kudacuki.ggff.net</option> <option value="gratisan.filegear-sg.me">gratisan.filegear-sg.me</option> </select> </div> <div class="form-group"> <div class="switch-container"> <label class="switch"> <input type="checkbox" name="proxied" checked> <span class="slider"></span> </label> <span>Proxied (YTTA)</span> </div> </div> <button type="submit" class="btn btn-primary">Buat Record</button> </form> </div> <div id="route-form" class="form-section"> <form id="form-route"> <input type="hidden" name="action" value="assign_route"> <div class="form-group"> <label for="route-pattern">Route (contoh, asu.domainmu.com/*)</label> <input type="text" id="route-pattern" class="form-control" name="routePattern" required> </div> <div class="form-group"> <label for="route-script">Nama Worker</label> <input type="text" id="route-script" class="form-control" name="scriptName" required> </div> <div class="form-group"> <label for="route-domain">Pilih Domain</label> <select id="route-domain" class="form-control" name="domain" required> <option value="kudacuki.ggff.net">kudacuki.ggff.net</option> <option value="gratisan.filegear-sg.me">gratisan.filegear-sg.me</option> </select> </div> <button type="submit" class="btn btn-primary">Buat Route</button> </form> </div> </div> <div class="result" id="result"></div> <footer>Copyright 2025 - By Ariv</footer> </div> <script> function showForm(event, formId) { document.querySelectorAll('.form-section').forEach(form => form.classList.remove('active')); document.querySelectorAll('.tab-button').forEach(button => button.classList.remove('active')); document.getElementById(formId).classList.add('active'); event.currentTarget.classList.add('active'); } async function handleFormSubmit(event) { event.preventDefault(); const form = event.currentTarget; const formData = new FormData(form); const data = Object.fromEntries(formData.entries()); if (form.id === 'form-dns') { data.proxied = form.querySelector('input[name=proxied]').checked; } const resultDiv = document.getElementById('result'); resultDiv.innerHTML = '<p>Processing...</p>'; try { const response = await fetch(window.location.href, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }); const result = await response.json(); if (result.success) { resultDiv.innerHTML = \`<div class="alert alert-success"><strong>Berhasil!</strong> Operation completed.</div><pre>\${JSON.stringify(result, null, 2)}</pre>\`; } else { resultDiv.innerHTML = \`<div class="alert alert-danger"><strong>Error:</strong> \${result.errors.map(e => e.message).join(', ')}</div>\`; } } catch (error) { resultDiv.innerHTML = \`<div class="alert alert-danger"><strong>Fatal Error:</strong> \${error.message}</div>\`; } } document.getElementById('form-dns').addEventListener('submit', handleFormSubmit); document.getElementById('form-route').addEventListener('submit', handleFormSubmit); </script> </body> </html>`; }
Bindings
Tambah KV
Tambah R2
Tambah D1
← Kembali
Simpan & Deploy
Hapus Script Worker Ini