╔══════════════════════════════════════════════════╗ ║ CODE REPOSITORY ARCHIVE ║ ╠══════════════════════════════════════════════════╣ ║ Repository: Wc-multi ║ ║ Generated: 8/31/2026, 11:26:06 AM ║ ║ Files: 1 ║ ║ Description: Wc ║ ╚══════════════════════════════════════════════════╝ FILE SUMMARY ──────────────────────────────────────────────────── Total files: 1 Total size: 155775 characters Folder structure: Wc-multi/ Languages: - javascript: 1 files FILE CONTENTS ════════════════════════════════════════════════════ ┌── FILE 01: Wc-multi/worker.js │ Size: 155775 characters │ Language: javascript │ Created: 7/5/2026 └──────────────────────────────────────────────────── const CF_BASE_URL = "https://api.cloudflare.com/client/v4"; function jsonResponse(payload, status = 200) { return new Response(JSON.stringify(payload), { status, headers: { ...corsHeaders, "Content-Type": "application/json" } }); } function sanitizeWorkerName(name) { const fallback = `wc-${Date.now().toString(36)}`; const clean = String(name || fallback) .toLowerCase() .replace(/[^a-z0-9-]/g, '-') .replace(/-+/g, '-') .replace(/^-|-$/g, '') .slice(0, 63); return clean || fallback; } function cleanHost(value) { return String(value || '') .trim() .toLowerCase() .replace(/^https?:\/\//, '') .replace(/\/.*$/, '') .replace(/\s+/g, '') .replace(/^\.+|\.+$/g, ''); } function validBaseHost(host) { host = cleanHost(host); return !!host && host.length <= 253 && !host.includes('..') && host.split('.').every(label => /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(label) ); } function validHost(host) { host = cleanHost(host); if (!host) return false; if (host.startsWith('*.')) return validBaseHost(host.slice(2)); return validBaseHost(host); } function autoNodeCount(totalHosts, targetMode = 'multi_failover') { const mode = String(targetMode || '').toLowerCase(); if (mode === 'single') return 1; const total = Math.max(1, Number.parseInt(String(totalHosts || '1'), 10) || 1); if (total <= 8) return 2; if (total <= 30) return 4; if (total <= 100) return 6; if (total <= 250) return 8; if (total <= 600) return 10; return 12; } function statusFromDomain(domain) { if (!domain) return 'not_found'; const values = [ domain.status, domain.state, domain.certificate_status, domain.ssl_status, domain?.certificate?.status, domain?.ssl?.status, domain?.ownership_verification?.status, domain?.verification?.status ].filter(v => v !== undefined && v !== null).map(v => String(v).toLowerCase()); if (values.some(v => v.includes('active') || v.includes('success') || v.includes('valid') || v.includes('issued') || v.includes('ready'))) return 'active'; if (values.some(v => v.includes('pending') || v.includes('initial') || v.includes('verify'))) return 'pending'; if (values.some(v => v.includes('fail') || v.includes('error') || v.includes('blocked'))) return 'error'; return values[0] || 'listed'; } class CfClient { constructor(email, apiKey) { this.email = String(email || '').trim(); this.apiKey = String(apiKey || '').trim(); } headers(contentType = 'application/json') { const h = { "X-Auth-Email": this.email, "X-Auth-Key": this.apiKey, "User-Agent": "Wildcard-Manager/1.6" }; if (contentType) h["Content-Type"] = contentType; return h; } async _fetch(path, options = {}) { if (!this.email || !this.apiKey) throw new Error('Email/API Key kosong. Import akun dulu.'); const url = path.startsWith('http') ? path : `${CF_BASE_URL}${path}`; const response = await fetch(url, { method: options.method || 'GET', headers: this.headers(options.contentType === undefined ? 'application/json' : options.contentType), body: options.body }); const text = await response.text(); let data = {}; if (text) { try { data = JSON.parse(text); } catch (_) { if (!response.ok) throw new Error(`Cloudflare API Error ${response.status}: ${text.slice(0, 180)}`); data = { success: true, result: text }; } } if (!response.ok || data?.success === false) { const msg = data?.errors?.map(e => e.message).join('; ') || data?.messages?.map(e => e.message).join('; ') || `Cloudflare API Error ${response.status}`; throw new Error(msg); } return data; } async getAccounts() { return this._fetch('/accounts?per_page=100'); } async listZones(status = 'active') { const all = []; for (let page = 1; page <= 10; page++) { let path = `/zones?per_page=100&page=${page}`; if (status) path += `&status=${encodeURIComponent(status)}`; const data = await this._fetch(path); const rows = Array.isArray(data.result) ? data.result : []; all.push(...rows); const info = data.result_info || {}; if (!rows.length || (info.total_pages && page >= info.total_pages)) break; } return { success: true, result: all }; } async listDnsRecords(zoneId, name = '') { let path = `/zones/${zoneId}/dns_records?per_page=100`; if (name) path += `&name=${encodeURIComponent(name)}`; return this._fetch(path); } async deleteDnsRecord(zoneId, recordId) { return this._fetch(`/zones/${zoneId}/dns_records/${recordId}`, { method: 'DELETE', contentType: null }); } async createZone(accountId, domainName) { return this._fetch('/zones', { method: 'POST', body: JSON.stringify({ account: { id: accountId }, name: domainName, jump_start: true, type: 'full' }) }); } async deleteZone(zoneId) { return this._fetch(`/zones/${zoneId}`, { method: 'DELETE', contentType: null }); } async createDnsRecord(zoneId, record) { return this._fetch(`/zones/${zoneId}/dns_records`, { method: 'POST', body: JSON.stringify(record) }); } async updateZoneSetting(zoneId, setting, value) { return this._fetch(`/zones/${zoneId}/settings/${setting}`, { method: 'PATCH', body: JSON.stringify({ value }) }); } async getUniversalSslSetting(zoneId) { return this._fetch(`/zones/${zoneId}/ssl/universal/settings`); } async updateUniversalSslSetting(zoneId, enabled = true) { return this._fetch(`/zones/${zoneId}/ssl/universal/settings`, { method: 'PATCH', body: JSON.stringify({ enabled: !!enabled }) }); } async getSslSettings(zoneId) { const settingNames = ['ssl', 'always_use_https', 'automatic_https_rewrites', 'min_tls_version']; const settings = {}; for (const name of settingNames) { try { const res = await this._fetch(`/zones/${zoneId}/settings/${name}`); settings[name] = res.result || res; } catch (e) { settings[name] = { value: null, error: e.message }; } } try { const universal = await this.getUniversalSslSetting(zoneId); settings.universal_ssl = universal.result || universal; } catch (e) { settings.universal_ssl = { enabled: null, error: e.message }; } return settings; } async activateSslRecommended(zoneId) { const steps = [ { label: 'Universal SSL', run: () => this.updateUniversalSslSetting(zoneId, true) }, { label: 'SSL/TLS mode Full', run: () => this.updateZoneSetting(zoneId, 'ssl', 'full') }, { label: 'Always Use HTTPS', run: () => this.updateZoneSetting(zoneId, 'always_use_https', 'on') }, { label: 'Automatic HTTPS Rewrites', run: () => this.updateZoneSetting(zoneId, 'automatic_https_rewrites', 'on') }, { label: 'Minimum TLS 1.2', run: () => this.updateZoneSetting(zoneId, 'min_tls_version', '1.2') } ]; const results = []; for (const step of steps) { try { const res = await step.run(); results.push({ label: step.label, success: true, result: res.result || res }); } catch (e) { results.push({ label: step.label, success: false, message: e.message }); } } return results; } async updateWorker(accountId, workerName, scriptContent) { const boundary = `----CFWildcardBoundary${Date.now().toString(16)}`; const metadata = { main_module: 'worker.js', compatibility_date: '2024-12-03', compatibility_flags: ['nodejs_compat'] }; const body = [ `--${boundary}`, 'Content-Disposition: form-data; name="worker.js"; filename="worker.js"', 'Content-Type: application/javascript+module', '', scriptContent, `--${boundary}`, 'Content-Disposition: form-data; name="metadata"', 'Content-Type: application/json', '', JSON.stringify(metadata), `--${boundary}--`, '' ].join('\r\n'); return this._fetch(`/accounts/${accountId}/workers/services/${workerName}/environments/production`, { method: 'PUT', contentType: `multipart/form-data; boundary=${boundary}`, body }); } async getOrCreateSubdomain(accountId) { try { const data = await this._fetch(`/accounts/${accountId}/workers/subdomain`); if (data?.result?.subdomain) return data.result.subdomain; } catch (_) {} const base = (this.email.split('@')[0] || 'worker') .toLowerCase() .replace(/[^a-z0-9-]/g, '') .slice(0, 28) || `wc${Date.now().toString(36)}`; const data = await this._fetch(`/accounts/${accountId}/workers/subdomain`, { method: 'PUT', body: JSON.stringify({ subdomain: base }) }); return data?.result?.subdomain || base; } async createWorker(accountId, workerName, scriptContent) { await this.updateWorker(accountId, workerName, scriptContent); try { await this._fetch(`/accounts/${accountId}/workers/services/${workerName}/environments/production/subdomain`, { method: 'POST', body: JSON.stringify({ enabled: true }) }); } catch (_) {} return { workerName, subdomain: await this.getOrCreateSubdomain(accountId) }; } async registerCustomDomain(accountId, workerName, hostname, zoneId) { return this._fetch(`/accounts/${accountId}/workers/domains`, { method: 'PUT', body: JSON.stringify({ environment: 'production', hostname, service: workerName, zone_id: zoneId }) }); } async findWorkerDomain(accountId, hostname, workerName) { const data = await this._fetch(`/accounts/${accountId}/workers/domains?hostname=${encodeURIComponent(hostname)}`); const rows = Array.isArray(data.result) ? data.result : []; const lowerHost = String(hostname || '').toLowerCase(); const lowerWorker = String(workerName || '').toLowerCase(); return rows.find(d => String(d.hostname || '').toLowerCase() === lowerHost && String(d.service || '').toLowerCase() === lowerWorker) || rows.find(d => String(d.hostname || '').toLowerCase() === lowerHost) || null; } async listPagesProjects(accountId) { const data = await this._fetch(`/accounts/${accountId}/pages/projects`); return { success: true, result: Array.isArray(data.result) ? data.result : [] }; } async createPagesProject(accountId, projectName) { return this._fetch(`/accounts/${accountId}/pages/projects`, { method: 'POST', body: JSON.stringify({ name: projectName, production_branch: 'main' }) }); } async deployPagesProject(accountId, projectName, scriptContent) { const boundary = '----Boundary' + Date.now().toString(36); let body = '--' + boundary + '\r\nContent-Disposition: form-data; name="manifest"\r\n\r\n{"name":"' + projectName + '"}\r\n'; body += '--' + boundary + '\r\nContent-Disposition: form-data; name="_worker.js"; filename="_worker.js"\r\nContent-Type: application/javascript\r\n\r\n' + scriptContent + '\r\n'; body += '--' + boundary + '--\r\n'; return this._fetch(`/accounts/${accountId}/pages/projects/${encodeURIComponent(projectName)}/deployments`, { method: 'POST', contentType: 'multipart/form-data; boundary=' + boundary, body: body }); } async listPagesDomains(accountId, projectName) { const data = await this._fetch(`/accounts/${accountId}/pages/projects/${encodeURIComponent(projectName)}/domains`); return { success: true, result: Array.isArray(data.result) ? data.result : [] }; } async addPagesDomain(accountId, projectName, domainName) { return this._fetch(`/accounts/${accountId}/pages/projects/${encodeURIComponent(projectName)}/domains`, { method: 'POST', body: JSON.stringify({ name: domainName }) }); } async deletePagesDomain(accountId, projectName, domainName) { return this._fetch(`/accounts/${accountId}/pages/projects/${encodeURIComponent(projectName)}/domains/${encodeURIComponent(domainName)}`, { method: 'DELETE', contentType: null }); } async triggerPagesVerification(accountId, projectName, domainName) { return this._fetch(`/accounts/${accountId}/pages/projects/${encodeURIComponent(projectName)}/domains/${encodeURIComponent(domainName)}`, { method: 'PATCH' }); } } const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type' }; async function handleApiRequest(request) { if (request.method === 'OPTIONS') return new Response(null, { headers: corsHeaders }); const url = new URL(request.url); const path = url.pathname; let body = {}; try { body = await request.json(); } catch (_) { body = {}; } try { const email = body.email; const apiKey = body.apiKey; const accountId = String(body.accountId || '').trim(); const client = new CfClient(email, apiKey); if (path === '/api/accounts') { return jsonResponse(await client.getAccounts()); } if (path === '/api/domainManager/list') { if (!accountId) throw new Error('Account ID kosong. Import akun format email|apiKey|accountId atau biarkan sistem mengambil account ID otomatis.'); const data = await client.listZones(''); const domains = (Array.isArray(data.result) ? data.result : []) .map(z => ({ id: z.id, name: z.name, status: z.status || '', paused: !!z.paused, type: z.type || '', name_servers: Array.isArray(z.name_servers) ? z.name_servers : [], original_name_servers: Array.isArray(z.original_name_servers) ? z.original_name_servers : [], plan: z.plan && z.plan.name ? z.plan.name : '' })) .filter(z => z.id && z.name) .sort((a, b) => a.name.localeCompare(b.name)); return jsonResponse({ success: true, domains }); } if (path === '/api/domainManager/add') { if (!accountId) throw new Error('Account ID kosong. Pilih account dulu.'); const domainName = cleanHost(body.domainName); if (!validBaseHost(domainName) || domainName.split('.length') < 2) throw new Error('Nama domain tidak valid. Contoh: domain.com'); const data = await client.createZone(accountId, domainName); return jsonResponse({ success: true, domain: data.result || data }); } if (path === '/api/domainManager/delete') { const domainId = String(body.domainId || '').trim(); if (!domainId) throw new Error('Domain ID kosong.'); const data = await client.deleteZone(domainId); return jsonResponse({ success: true, result: data.result || data }); } if (path === '/api/domainManager/dnsList') { const domainId = String(body.domainId || '').trim(); if (!domainId) throw new Error('Domain ID kosong.'); const data = await client.listDnsRecords(domainId); const records = (Array.isArray(data.result) ? data.result : []) .map(r => ({ id: r.id, type: r.type, name: r.name, content: r.content, proxied: !!r.proxied, ttl: r.ttl, priority: r.priority, created_on: r.created_on, modified_on: r.modified_on })) .sort((a, b) => String(a.name || '').localeCompare(String(b.name || '')) || String(a.type || '').localeCompare(String(b.type || ''))); return jsonResponse({ success: true, records }); } if (path === '/api/domainManager/fetchBulkText') { const rawUrl = String(body.url || body.rawUrl || '').trim(); if (!/^https?:\/\//i.test(rawUrl)) throw new Error('URL raw harus diawali http:// atau https://'); const res = await fetch(rawUrl, { method: 'GET', headers: { 'User-Agent': 'CF-Manager-Pro-BulkDNS/1.0' }, cf: { cacheTtl: 0, cacheEverything: false } }); if (!res.ok) throw new Error('Gagal mengambil URL raw: HTTP ' + res.status); const text = await res.text(); if (text.length > 300000) throw new Error('File raw terlalu besar. Maksimal sekitar 300KB.'); return jsonResponse({ success: true, text }); } if (path === '/api/domainManager/dnsAdd') { const domainId = String(body.domainId || '').trim(); const domainName = cleanHost(body.domainName); const type = String(body.type || '').trim().toUpperCase(); let name = cleanHost(body.name); const content = String(body.content || '').trim(); const proxied = body.proxied === true; const ttl = Number(body.ttl || 1); if (!domainId || !domainName) throw new Error('Domain wajib dipilih.'); if (!['A', 'AAAA', 'CNAME', 'TXT', 'MX', 'SRV', 'NS'].includes(type)) throw new Error('Tipe DNS tidak valid.'); if (!name || name === '@') name = domainName; else if (!(name === domainName || name.endsWith('.' + domainName))) name = `${name}.${domainName}`; if (!validHost(name)) throw new Error('Nama record tidak valid.'); if (!content) throw new Error('Target/content DNS kosong.'); const record = { type, name, content, ttl: ttl || 1 }; if (type === 'MX') record.priority = Number(body.priority || 10); if (['A', 'AAAA', 'CNAME'].includes(type)) record.proxied = proxied; const data = await client.createDnsRecord(domainId, record); return jsonResponse({ success: true, record: data.result || data }); } if (path === '/api/domainManager/dnsDelete') { const domainId = String(body.domainId || '').trim(); const recordId = String(body.recordId || '').trim(); if (!domainId || !recordId) throw new Error('Domain atau DNS record kosong.'); const data = await client.deleteDnsRecord(domainId, recordId); return jsonResponse({ success: true, result: data.result || data }); } if (path === '/api/domainManager/sslSettings') { const domainId = String(body.domainId || '').trim(); if (!domainId) throw new Error('Domain ID kosong.'); const settings = await client.getSslSettings(domainId); return jsonResponse({ success: true, settings }); } if (path === '/api/domainManager/sslUpdate') { const domainId = String(body.domainId || '').trim(); const setting = String(body.setting || '').trim(); const value = String(body.value || '').trim(); const allowedSettings = ['ssl', 'always_use_https', 'automatic_https_rewrites', 'min_tls_version']; if (!domainId) throw new Error('Domain ID kosong.'); if (!allowedSettings.includes(setting)) throw new Error('Setting SSL tidak didukung.'); if (!value) throw new Error('Nilai setting kosong.'); const data = await client.updateZoneSetting(domainId, setting, value); return jsonResponse({ success: true, result: data.result || data }); } if (path === '/api/domainManager/sslUniversal') { const domainId = String(body.domainId || '').trim(); if (!domainId) throw new Error('Domain ID kosong.'); const data = await client.updateUniversalSslSetting(domainId, body.enabled !== false); return jsonResponse({ success: true, result: data.result || data }); } if (path === '/api/domainManager/sslActivate') { const domainId = String(body.domainId || '').trim(); if (!domainId) throw new Error('Domain ID kosong.'); const results = await client.activateSslRecommended(domainId); return jsonResponse({ success: true, results }); } if (path === '/api/pagesManager/projects') { if (!accountId) throw new Error('Account ID kosong. Pilih/import akun dulu.'); const data = await client.listPagesProjects(accountId); const projects = (Array.isArray(data.result) ? data.result : []) .map(p => ({ name: p.name, subdomain: p.subdomain || '', domains: p.domains || [], production_branch: p.production_branch || '', created_on: p.created_on || '' })) .filter(p => p.name) .sort((a, b) => a.name.localeCompare(b.name)); return jsonResponse({ success: true, projects }); } if (path === '/api/pagesManager/domains') { if (!accountId) throw new Error('Account ID kosong.'); const projectName = String(body.projectName || '').trim(); if (!projectName) throw new Error('Pilih Pages Project dulu.'); const data = await client.listPagesDomains(accountId, projectName); const domains = (Array.isArray(data.result) ? data.result : []) .map(d => ({ name: d.name, status: d.status || d.validation_status || '', verification_data: d.verification_data || null, created_on: d.created_on || '', modified_on: d.modified_on || '' })) .filter(d => d.name) .sort((a, b) => a.name.localeCompare(b.name)); return jsonResponse({ success: true, domains }); } if (path === '/api/pagesManager/bulkAdd') { if (!accountId) throw new Error('Account ID kosong.'); const projectName = String(body.projectName || '').trim(); if (!projectName) throw new Error('Pilih Pages Project dulu.'); const raw = Array.isArray(body.domains) ? body.domains.join('\n') : String(body.domains || ''); const rows = []; const seen = new Set(); raw.split(/[\n,;]+/).map(v => v.replace(/#.*/, '').trim()).filter(Boolean).forEach(v => { const name = cleanHost(v); if (validBaseHost(name) && !seen.has(name)) { seen.add(name); rows.push(name); } }); if (!rows.length) throw new Error('Tidak ada domain Pages valid.'); const results = []; for (const name of rows.slice(0, 50)) { const row = { name, success: false, status: 'unknown' }; try { const data = await client.addPagesDomain(accountId, projectName, name); row.success = true; row.status = statusFromDomain(data.result || {}); row.result = data.result || null; } catch (e) { row.error = e.message || String(e); if (/already|exist|duplicate/i.test(row.error)) { row.success = true; row.status = 'already_exists_or_pending'; } } results.push(row); } return jsonResponse({ success: true, projectName, results }); } if (path === '/api/pagesManager/delete') { if (!accountId) throw new Error('Account ID kosong.'); const projectName = String(body.projectName || '').trim(); const domainName = cleanHost(body.domainName); if (!projectName || !domainName) throw new Error('Project/domain kosong.'); const data = await client.deletePagesDomain(accountId, projectName, domainName); return jsonResponse({ success: true, result: data.result || data }); } if (path === '/api/wildcardSsl/resources') { if (!accountId) throw new Error('Account ID kosong. Import akun format email|apiKey|accountId atau biarkan sistem mengambil account ID otomatis.'); let workerSubdomain = ''; try { workerSubdomain = await client.getOrCreateSubdomain(accountId); } catch (_) { workerSubdomain = ''; } const zr = await client.listZones('active'); const zones = (Array.isArray(zr.result) ? zr.result : []) .map(z => ({ id: z.id, name: z.name })) .filter(z => z.id && z.name) .sort((a, b) => a.name.localeCompare(b.name)); return jsonResponse({ success: true, workerSubdomain, zones, warnings: {} }); } if (path === '/api/wildcardSsl/bulk') { const zoneName = cleanHost(body.zoneName); const zoneId = String(body.zoneId || '').trim(); const prefix = cleanHost(body.prefix); const mode = String(body.mode || 'attach'); const proxied = body.proxied !== false; const engine = String(body.engine || 'worker').toLowerCase(); const targetMode = String(body.targetMode || 'multi_failover').toLowerCase(); const offset = Math.max(0, Number.parseInt(String(body.offset || '0'), 10) || 0); const totalHostCount = Math.max(1, Number.parseInt(String(body.totalHostCount || body.totalHosts || '0'), 10) || 0); const requestedNodeCount = autoNodeCount(totalHostCount || (Array.isArray(body.hostnames) ? body.hostnames.length : 1), targetMode); if (!accountId) throw new Error('Account ID kosong. Pilih account dulu.'); if (!zoneId || !zoneName) throw new Error('Domain wajib dipilih.'); if (mode !== 'attach' && mode !== 'check') throw new Error('Mode wildcard hanya CONNECT dan CEK SSL. Tidak ada finalize/cleanup.'); const baseHost = (!prefix || prefix === '*' || prefix === '@') ? zoneName : (prefix === zoneName || prefix.endsWith('.' + zoneName) ? prefix : `${prefix}.${zoneName}`); const workerName = sanitizeWorkerName(`wc-${baseHost}`); const targetHost = baseHost; const seen = new Set(); const hostnames = []; const inSelectedZone = (h) => { h = cleanHost(h); const bare = h.startsWith('*.') ? h.slice(2) : h; return bare === zoneName || bare.endsWith('.' + zoneName); }; const pushHost = (h) => { h = cleanHost(h); if (validHost(h) && inSelectedZone(h) && !seen.has(h)) { seen.add(h); hostnames.push(h); } }; const suffixToHostname = (suffix) => { suffix = cleanHost(suffix); if (!suffix || suffix === '*') return baseHost; if (suffix.startsWith('*.')) { const bare = cleanHost(suffix.slice(2)); if (!bare) return baseHost; return (bare === zoneName || bare.endsWith('.' + zoneName)) ? bare : `${bare}.${baseHost}`; } return (suffix === zoneName || suffix.endsWith('.' + zoneName)) ? suffix : `${suffix}.${baseHost}`; }; pushHost(baseHost); const inputHosts = Array.isArray(body.hostnames) ? body.hostnames : []; if (inputHosts.length) { for (const h of inputHosts) pushHost(h); } else { String(body.suffixes || '').split(/[\n,;]+/).map(v => v.replace(/#.*/, '').trim()).filter(Boolean).forEach(s => pushHost(suffixToHostname(s))); } if (!hostnames.length) throw new Error('Tidak ada hostname valid.'); const removeConflictingDns = async (name) => { try { const records = await client.listDnsRecords(zoneId, name); const rows = Array.isArray(records.result) ? records.result : []; for (const r of rows) { if (['A', 'AAAA', 'CNAME'].includes(String(r.type || '').toUpperCase())) { await client.deleteDnsRecord(zoneId, r.id); } } } catch (_) {} }; const listDnsSummary = async (name) => { try { const records = await client.listDnsRecords(zoneId, name); return (Array.isArray(records.result) ? records.result : []) .filter(r => ['A', 'AAAA', 'CNAME'].includes(String(r.type || '').toUpperCase())) .map(r => ({ type: String(r.type || '').toUpperCase(), name: r.name || name, content: r.content || '', proxied: !!r.proxied })); } catch (_) { return []; } }; const parseHostList = (value) => { const out = []; const seenTargets = new Set(); const raw = Array.isArray(value) ? value.join('\n') : String(value || ''); raw.split(/[\n,;]+/).map(v => v.replace(/#.*/, '').trim()).filter(Boolean).forEach(v => { const h = cleanHost(v); if (validBaseHost(h) && !seenTargets.has(h)) { seenTargets.add(h); out.push(h); } }); return out; }; const failoverHosts = parseHostList(body.workerUrls || body.targets || body.targetHosts || body.upstreams); const multiEnabled = ['multi', 'balance', 'multi_balance', 'multi_failover', 'shard', 'sharding'].includes(targetMode); let workerNodes = []; if (!multiEnabled) workerNodes = [workerName]; if (multiEnabled) { for (let i = 1; i <= requestedNodeCount; i++) { workerNodes.push(sanitizeWorkerName(`${workerName}-n${String(i).padStart(2, '0')}`)); } } if (!workerNodes.length) workerNodes = [workerName]; const makeAutoWorkerScript = (nodeName, nodeIndex) => `// ============================================ // WORKER V5 - MULTI NODE + FAILOVER // ============================================ const NODE_NAME = ${JSON.stringify(nodeName)}; const WORKER_URLS = ${JSON.stringify(failoverHosts, null, 2)}; function shuffle(list) { return [...list].sort(() => Math.random() - 0.5); } function cleanHeaders(request) { const headers = new Headers(request.headers); headers.delete("host"); headers.delete("cf-connecting-ip"); headers.delete("cf-ipcountry"); headers.delete("cf-ray"); headers.delete("cf-visitor"); headers.delete("x-forwarded-proto"); headers.delete("x-real-ip"); headers.set("x-wc-node", NODE_NAME); return headers; } export default { async fetch(request, env, ctx) { if (!WORKER_URLS.length) { const u = new URL(request.url); return new Response("Wildcard Manager node aktif: " + NODE_NAME + "\\nHostname: " + u.hostname + "\\nIsi Target Failover / WORKER_URLS agar request diteruskan ke target.", { status: 200, headers: { "Content-Type": "text/plain; charset=utf-8", "Cache-Control": "no-store" } }); } const method = request.method.toUpperCase(); const bodyBuffer = method !== "GET" && method !== "HEAD" ? await request.arrayBuffer() : null; const shuffled = shuffle(WORKER_URLS); for (const host of shuffled) { try { const targetUrl = new URL(request.url); targetUrl.hostname = host; targetUrl.protocol = "https:"; const proxyRequest = new Request(targetUrl.toString(), { method: request.method, headers: cleanHeaders(request), body: bodyBuffer ? bodyBuffer.slice(0) : null, redirect: "manual" }); const response = await fetch(proxyRequest); if (response.status !== 429 && response.status < 500) return response; console.log(NODE_NAME + " -> " + host + " limit/error (" + response.status + "), skip ke cadangan..."); } catch (e) { console.log(NODE_NAME + " -> " + host + " timeout/koneksi putus: " + e.message); continue; } } return new Response("Semua target sedang tidak bisa diakses. Node: " + NODE_NAME, { status: 503, headers: { "Content-Type": "text/plain; charset=utf-8", "Retry-After": "30" } }); } };`; let workerSubdomain = ''; try { workerSubdomain = await client.getOrCreateSubdomain(accountId); } catch (_) { workerSubdomain = ''; } const workerDnsTargets = Object.fromEntries(workerNodes.map(n => [n, workerSubdomain ? `${n}.${workerSubdomain}.workers.dev` : `${n}.workers.dev`])); if (engine === 'worker' && mode === 'attach' && body.deployWorkers !== false) { for (let i = 0; i < workerNodes.length; i++) { const nodeName = workerNodes[i]; await client.createWorker(accountId, nodeName, makeAutoWorkerScript(nodeName, i)); } } const results = []; let pagesDomainsCache = null; for (let i = 0; i < hostnames.slice(0, 10).length; i++) { const hostname = hostnames[i]; const assignedIndex = (offset + i) % workerNodes.length; const assignedWorkerName = workerNodes[assignedIndex]; const row = { hostname, mode, targetMode, success: false, status: 'unknown', sslActive: false, workerName: assignedWorkerName, assignedWorker: assignedWorkerName, assignedWorkerUrl: workerDnsTargets[assignedWorkerName], targetHost, workerUrls: failoverHosts, workerNodes, proxied, actions: [] }; try { if (mode === 'attach') { await removeConflictingDns(hostname); if (engine === 'pages') { let pagesProjectName = body.pagesProjectName || sanitizeWorkerName(`ssl-${baseHost}`); if (offset === 0 && i === 0 && body.deployWorkers !== false) { try { await client.createPagesProject(accountId, pagesProjectName); row.actions.push('project_created'); } catch (e) {} try { let pagesScriptToDeploy = body.pagesCustomScript; if (!pagesScriptToDeploy) throw new Error("Script deploy tidak ditemukan."); await client.deployPagesProject(accountId, pagesProjectName, pagesScriptToDeploy); row.actions.push('proxy_deployed'); } catch (e) { row.actions.push('proxy_deploy_failed: ' + e.message); } } try { await client.addPagesDomain(accountId, pagesProjectName, hostname); row.actions.push('attached_to_pages'); } catch (e) { if (/already|exist|duplicate/i.test(e.message)) row.actions.push('pages_domain_exists'); else throw e; } try { await client.createDnsRecord(zoneId, { type: 'CNAME', name: hostname, content: `${pagesProjectName}.pages.dev`, ttl: 1, proxied }); row.actions.push('cname_created'); } catch (e) { if (/already|exist|conflict/i.test(e.message)) row.actions.push('cname_exists'); else throw e; } try { await client.triggerPagesVerification(accountId, pagesProjectName, hostname); row.actions.push('ssl_verification_triggered'); } catch (_) {} row.status = 'pending'; row.sslActive = false; row.success = true; row.projectName = pagesProjectName; } else { try { await client.registerCustomDomain(accountId, assignedWorkerName, hostname, zoneId); row.actions.push('attached_to_' + assignedWorkerName); } catch (e) { const msg = e.message || String(e); if (/already|exist|duplicate|conflict/i.test(msg)) row.actions.push('already_exists_or_conflict'); else throw e; } const d = await client.findWorkerDomain(accountId, hostname, assignedWorkerName); row.domainId = d?.id || null; row.status = statusFromDomain(d); row.sslActive = row.status === 'active'; row.dnsRecords = await listDnsSummary(hostname); row.dnsRecordCount = row.dnsRecords.length; row.actions.push('kept_worker_custom_domain'); row.actions.push('kept_dns_records'); if (multiEnabled) row.actions.push('balanced_suffix_to_worker_node'); if (failoverHosts.length) row.actions.push('failover_targets_ready'); else row.actions.push('status_worker_only_no_targets'); row.success = true; } } else { if (engine === 'pages') { const pagesProjectName = body.pagesProjectName || sanitizeWorkerName(`ssl-${baseHost}`); if (!pagesDomainsCache) { try { const pdReq = await client.listPagesDomains(accountId, pagesProjectName); pagesDomainsCache = Array.isArray(pdReq.result) ? pdReq.result : []; } catch (e) { pagesDomainsCache = []; } } const pDomain = pagesDomainsCache.find(d => String(d.name).toLowerCase() === hostname.toLowerCase()); row.status = pDomain ? statusFromDomain(pDomain) : 'not_found'; if (row.status !== 'active') { try { const probe = await fetch(`https://${hostname}/`, { method: 'HEAD', redirect: 'manual', cf: { cacheTtl: 0 } }); row.httpsProbe = { ok: true, status: probe.status }; if (probe.status > 0 && probe.status < 600) row.status = 'active'; } catch (e) { row.httpsProbe = { ok: false, message: e.message || String(e) }; } } row.sslActive = row.status === 'active'; row.success = true; } else { const d = await client.findWorkerDomain(accountId, hostname, assignedWorkerName); row.domainId = d?.id || null; row.status = statusFromDomain(d); if (row.status !== 'active') { try { const probe = await fetch(`https://${hostname}/`, { method: 'HEAD', redirect: 'manual', cf: { cacheTtl: 0 } }); row.httpsProbe = { ok: true, status: probe.status }; if (probe.status > 0 && probe.status < 600) row.status = 'active'; } catch (e) { row.httpsProbe = { ok: false, message: e.message || String(e) }; } } row.sslActive = row.status === 'active'; row.dnsRecords = await listDnsSummary(hostname); row.dnsRecordCount = row.dnsRecords.length; if (row.sslActive && row.dnsRecordCount > 0) row.actions.push('ssl_active_dns_record_present'); else if (row.sslActive) row.actions.push('ssl_active_waiting_dns_record_visible'); else if (row.dnsRecordCount > 0) row.actions.push('dns_record_present_ssl_pending'); else row.actions.push('waiting_ssl_and_dns_record'); row.actions.push('nothing_deleted'); row.success = true; } } } catch (e) { row.success = false; row.error = e.message || String(e); } results.push(row); } return jsonResponse({ success: true, baseHost, targetHost, workerName, workerNodes, workerDnsTargets, workerUrls: failoverHosts, targetMode, results }); } return jsonResponse({ success: false, message: 'Not found' }, 404); } catch (error) { return jsonResponse({ success: false, message: error.message || String(error) }, 400); } } function renderHTML() { return `
Pages + Domain + Multi Worker SSL Suite
Kelola root domain, name server, dan DNS record langsung dari akun yang dipilih.
List project dan daftar custom domain di Pages. Fitur edit script via menu Wildcard SSL.
Pilih root domain + prefix → pilih engine (Pages / Worker) → deploy custom proxy → cek SSL sampai active.