Skrip Worker Netopen - Web.id
Skrip Worker Netopen - Web.id
// Variables
const rootDomain = "fandom.eu.org"; // Ganti dengan domain utama kalian
const serviceName = "proxy"; // Ganti dengan nama workers kalian
const apiKey = ""; // Ganti dengan Global API key kalian
(https://dash.cloudflare.com/profile/api-tokens)
const apiEmail = ""; // Ganti dengan email yang kalian gunakan
const accountID = ""; // Ganti dengan Account ID kalian
(https://dash.cloudflare.com -> Klik domain yang kalian gunakan)
const zoneID = ""; // Ganti dengan Zone ID kalian (https://dash.cloudflare.com ->
Klik domain yang kalian gunakan)
let isApiReady = false;
let proxyIP = "https://raw.githubusercontent.com/mrsyd-my/proxycf/refs/heads/main/
ProxyList.txt";
let cachedProxyList = [];
// Constant
const APP_DOMAIN = `${serviceName}.${rootDomain}`;
const PORTS = [443, 80];
const PROTOCOLS = [reverse("najort"), reverse("sselv"), reverse("ss")];
const KV_PROXY_URL =
"https://raw.githubusercontent.com/mrsyd-my/proxycf/refs/heads/main/
kvProxyList.json";
const PROXY_BANK_URL =
"https://raw.githubusercontent.com/mrsyd-my/proxycf/refs/heads/main/ProxyList.txt";
const DNS_SERVER_ADDRESS = "8.8.8.8";
const DNS_SERVER_PORT = 53;
const PROXY_HEALTH_CHECK_API = "https://id1.foolvpn.me/api/v1/check";
const CONVERTER_URL = "https://api.foolvpn.me/convert";
const DONATE_LINK = "https://suryd.biz.id";
const PROXY_PER_PAGE = 24;
const WS_READY_STATE_OPEN = 1;
const WS_READY_STATE_CLOSING = 2;
const CORS_HEADER_OPTIONS = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS",
"Access-Control-Max-Age": "86400",
};
return cachedProxyList;
}
targetUrl.hostname = targetChunk[0];
targetUrl.port = targetChunk[1]?.toString() || "443";
targetUrl.pathname = targetPath || targetUrl.pathname;
modifiedRequest.headers.set("X-Forwarded-Host", request.headers.get("Host"));
return newResponse;
}
try {
const uuid = crypto.randomUUID();
// Build URI
const uri = new URL(`${reverse("najort")}://${hostName}`);
uri.searchParams.set("encryption", "none");
uri.searchParams.set("type", "ws");
uri.searchParams.set("host", hostName);
// Build HTML
const document = new Document(request);
document.setTitle("Welcome to <span class='text-blue-500 font-
semibold'>netopen.web.id Proxy List</span>");
document.addInfo(`Total: ${proxyList.length}`);
document.addInfo(`Page: ${page}/${Math.floor(proxyList.length /
PROXY_PER_PAGE)}`);
uri.searchParams.set("path", `/${proxyIP}-${proxyPort}`);
uri.protocol = protocol;
uri.searchParams.set("security", port == 443 ? "tls" : "none");
uri.searchParams.set("sni", port == 80 && protocol == reverse("sselv") ?
"" : hostName);
return document.build();
} catch (error) {
return `An error occurred while generating the ${reverse("SSELV")}
configurations. ${error}`;
}
}
export default {
async fetch(request, env, ctx) {
try {
const url = new URL(request.url);
const upgradeHeader = request.headers.get("Upgrade");
// Gateway check
if (apiKey && apiEmail && accountID && zoneID) {
isApiReady = true;
}
if (url.pathname.length == 3 || url.pathname.match(",")) {
// Contoh: /ID, /SG, dll
const proxyKeys = url.pathname.replace("/", "").toUpperCase().split(",");
const proxyKey = proxyKeys[Math.floor(Math.random() * proxyKeys.length)];
const kvProxy = await getKVProxyList();
proxyIP = kvProxy[proxyKey][Math.floor(Math.random() *
kvProxy[proxyKey].length)];
if (url.pathname.startsWith("/sub")) {
const page = url.pathname.match(/^\/sub\/(\d+)$/);
const pageIndex = parseInt(page ? page[1] : "0");
const hostname = request.headers.get("Host");
// Queries
const countrySelect = url.searchParams.get("cc")?.split(",");
const proxyBankUrl = url.searchParams.get("proxy-list") ||
env.PROXY_BANK_URL;
let proxyList = (await getProxyList(proxyBankUrl)).filter((proxy) => {
// Filter proxies by Country
if (countrySelect) {
return countrySelect.includes(proxy.country);
}
return true;
});
if (apiPath.startsWith("/domains")) {
if (!isApiReady) {
return new Response("Api not ready", {
status: 500,
});
}
if (wildcardApiPath == "/get") {
const domains = await cloudflareApi.getDomainList();
return new Response(JSON.stringify(domains), {
headers: {
...CORS_HEADER_OPTIONS,
},
});
} else if (wildcardApiPath == "/put") {
const domain = url.searchParams.get("domain");
const register = await cloudflareApi.registerDomain(domain);
uri.protocol = protocol;
uri.port = port.toString();
if (protocol == "ss") {
uri.username = btoa(`none:${uuid}`);
uri.searchParams.set(
"plugin",
`v2ray-plugin${port == 80 ? "" :
";tls"};mux=0;mode=websocket;path=/${proxy.proxyIP}-${
proxy.proxyPort
};host=${APP_DOMAIN}`
);
} else {
uri.username = uuid;
}
webSocket.accept();
let remoteSocketWrapper = {
value: null,
};
let isDNS = false;
readableWebSocketStream
.pipeTo(
new WritableStream({
async write(chunk, controller) {
if (isDNS) {
return handleUDPOutbound(DNS_SERVER_ADDRESS, DNS_SERVER_PORT, chunk,
webSocket, null, log);
}
if (remoteSocketWrapper.value) {
const writer = remoteSocketWrapper.value.writable.getWriter();
await writer.write(chunk);
writer.releaseLock();
return;
}
addressLog = protocolHeader.addressRemote;
portLog = `${protocolHeader.portRemote} -> ${protocolHeader.isUDP ? "UDP"
: "TCP"}`;
if (protocolHeader.hasError) {
throw new Error(protocolHeader.message);
}
if (protocolHeader.isUDP) {
if (protocolHeader.portRemote === 53) {
isDNS = true;
} else {
// return handleUDPOutbound(protocolHeader.addressRemote,
protocolHeader.portRemote, chunk, webSocket, protocolHeader.version, log);
throw new Error("UDP only support for DNS port 53");
}
}
if (isDNS) {
return handleUDPOutbound(
DNS_SERVER_ADDRESS,
DNS_SERVER_PORT,
chunk,
webSocket,
protocolHeader.version,
log
);
}
handleTCPOutBound(
remoteSocketWrapper,
protocolHeader.addressRemote,
protocolHeader.portRemote,
protocolHeader.rawClientData,
webSocket,
protocolHeader.version,
log
);
},
close() {
log(`readableWebSocketStream is close`);
},
abort(reason) {
log(`readableWebSocketStream is abort`, JSON.stringify(reason));
},
})
)
.catch((err) => {
log("readableWebSocketStream pipeTo error", err);
});
return tcpSocket;
}
log(`Connected to ${targetAddress}:${targetPort}`);
await tcpSocket.readable.pipeTo(
new WritableStream({
async write(chunk) {
if (webSocket.readyState === WS_READY_STATE_OPEN) {
if (protocolHeader) {
webSocket.send(await new Blob([protocolHeader,
chunk]).arrayBuffer());
protocolHeader = null;
} else {
webSocket.send(chunk);
}
}
},
close() {
log(`UDP connection to ${targetAddress} closed`);
},
abort(reason) {
console.error(`UDP connection to ${targetPort} aborted due to $
{reason}`);
},
})
);
} catch (e) {
console.error(`Error while handling UDP outbound, error ${e.message}`);
}
}
pull(controller) {},
cancel(reason) {
if (readableStreamCancel) {
return;
}
log(`ReadableStream was canceled, due to ${reason}`);
readableStreamCancel = true;
safeCloseWebSocket(webSocketServer);
},
});
return stream;
}
function parseSsHeader(ssBuffer) {
const view = new DataView(ssBuffer);
switch (addressType) {
case 1:
addressLength = 4;
addressValue = new Uint8Array(ssBuffer.slice(addressValueIndex,
addressValueIndex + addressLength)).join(".");
break;
case 3:
addressLength = new Uint8Array(ssBuffer.slice(addressValueIndex,
addressValueIndex + 1))[0];
addressValueIndex += 1;
addressValue = new TextDecoder().decode(ssBuffer.slice(addressValueIndex,
addressValueIndex + addressLength));
break;
case 4:
addressLength = 16;
const dataView = new DataView(ssBuffer.slice(addressValueIndex,
addressValueIndex + addressLength));
const ipv6 = [];
for (let i = 0; i < 8; i++) {
ipv6.push(dataView.getUint16(i * 2).toString(16));
}
addressValue = ipv6.join(":");
break;
default:
return {
hasError: true,
message: `Invalid addressType for ${reverse("skcoswodahS")}: $
{addressType}`,
};
}
if (!addressValue) {
return {
hasError: true,
message: `Destination address empty, address type is: ${addressType}`,
};
}
function parseSselvHeader(buffer) {
const version = new Uint8Array(buffer.slice(0, 1));
let isUDP = false;
return {
hasError: false,
addressRemote: addressValue,
addressType: addressType,
portRemote: portRemote,
rawDataIndex: addressValueIndex + addressLength,
rawClientData: buffer.slice(addressValueIndex + addressLength),
version: new Uint8Array([version[0], 0]),
isUDP: isUDP,
};
}
function parseNajortHeader(buffer) {
const socks5DataBuffer = buffer.slice(58);
if (socks5DataBuffer.byteLength < 6) {
return {
hasError: true,
message: "invalid SOCKS5 request data",
};
}
let isUDP = false;
const view = new DataView(socks5DataBuffer);
const cmd = view.getUint8(0);
if (cmd == 3) {
isUDP = true;
} else if (cmd != 1) {
throw new Error("Unsupported command type!");
}
if (!addressValue) {
return {
hasError: true,
message: `address is empty, addressType is ${addressType}`,
};
}
function safeCloseWebSocket(socket) {
try {
if (socket.readyState === WS_READY_STATE_OPEN || socket.readyState ===
WS_READY_STATE_CLOSING) {
socket.close();
}
} catch (error) {
console.error("safeCloseWebSocket error", error);
}
}
// Helpers
function base64ToArrayBuffer(base64Str) {
if (!base64Str) {
return { error: null };
}
try {
base64Str = base64Str.replace(/-/g, "+").replace(/_/g, "/");
const decode = atob(base64Str);
const arryBuffer = Uint8Array.from(decode, (c) => c.charCodeAt(0));
return { earlyData: arryBuffer.buffer, error: null };
} catch (error) {
return { error };
}
}
function arrayBufferToHex(buffer) {
return [...new Uint8Array(buffer)].map((x) => x.toString(16).padStart(2,
"0")).join("");
}
function shuffleArray(array) {
let currentIndex = array.length;
return hashHex;
}
function reverse(s) {
return s.split("").reverse().join("");
}
function getFlagEmoji(isoCode) {
const codePoints = isoCode
.toUpperCase()
.split("")
.map((char) => 127397 + char.charCodeAt(0));
return String.fromCodePoint(...codePoints);
}
// CloudflareApi Class
class CloudflareApi {
constructor() {
this.bearer = `Bearer ${apiKey}`;
this.accountID = accountID;
this.zoneID = zoneID;
this.apiEmail = apiEmail;
this.apiKey = apiKey;
this.headers = {
Authorization: this.bearer,
"X-Auth-Email": this.apiEmail,
"X-Auth-Key": this.apiKey,
};
}
async getDomainList() {
const url =
`https://api.cloudflare.com/client/v4/accounts/${this.accountID}/workers/domains`;
const res = await fetch(url, {
headers: {
...this.headers,
},
});
if (res.status == 200) {
const respJson = await res.json();
return [];
}
async registerDomain(domain) {
domain = domain.toLowerCase();
const registeredDomains = await this.getDomainList();
try {
const domainTest = await fetch(`https://${domain.replaceAll("." + APP_DOMAIN,
"")}`);
if (domainTest.status == 530) return 530;
} catch (e) {
return 400;
}
const url =
`https://api.cloudflare.com/client/v4/accounts/${this.accountID}/workers/domains`;
const res = await fetch(url, {
method: "PUT",
body: JSON.stringify({
environment: "production",
hostname: domain,
service: serviceName,
zone_id: this.zoneID,
}),
headers: {
...this.headers,
},
});
return res.status;
}
}
<footer>
<div class="fixed bottom-3 right-3 flex flex-col gap-1 z-50">
<a href="${DONATE_LINK}" target="_blank">
<button class="bg-green-500 rounded-full border-2 border-neutral-800 p-1
block">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
fill="currentColor" class="size-6">
<path
d="M10.464 8.746c.227-.18.497-.311.786-.394v2.795a2.252 2.252 0 0
1-.786-.393c-.394-.313-.546-.681-.546-1.004 0-.323.152-.691.546-1.004ZM12.75
15.662v-2.824c.347.085.664.228.921.421.427.32.579.686.579.991
0 .305-.152.671-.579.991a2.534 2.534 0 0 1-.921.42Z"
/>
<path
fill-rule="evenodd"
d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75
9.75-4.365 9.75-9.75S17.385 2.25 12 2.25ZM12.75 6a.75.75 0 0 0-1.5 0v.816a3.836
3.836 0 0 0-1.72.756c-.712.566-1.112 1.35-1.112 2.178 0 .829.4 1.612 1.113
2.178.502.4 1.102.647 1.719.756v2.978a2.536 2.536 0 0 1-.921-.421l-.879-.66a.75.75
0 0 0-.9 1.2l.879.66c.533.4 1.169.645 1.821.75V18a.75.75 0 0 0 1.5 0v-.81a4.124
4.124 0 0 0 1.821-.749c.745-.559 1.179-1.344 1.179-2.191 0-.847-.434-1.632-1.179-
2.191a4.122 4.122 0 0 0-1.821-.75V8.354c.29.082.559.213.786.393l.415.33a.75.75 0 0
0 .933-1.175l-.415-.33a3.836 3.836 0 0 0-1.719-.755V6Z"
clip-rule="evenodd"
/>
</svg>
</button>
</a>
<button onclick="toggleWildcardsWindow()" class="bg-indigo-400 rounded-full
border-2 border-neutral-800 p-1 PLACEHOLDER_API_READY">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9 9V4.5M9 9H4.5M9 9 3.75 3.75M9 15v4.5M9 15H4.5M9 15l-5.25
5.25M15 9h4.5M15 9V4.5M15 9l5.25-5.25M15 15h4.5M15 15v4.5m0-4.5 5.25 5.25"
/>
</svg>
</button>
<button onclick="toggleDarkMode()" class="bg-amber-400 rounded-full border-
2 border-neutral-800 p-1">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 3v2.25m6.364.386-1.591 1.591M21 12h-2.25m-.386 6.364-1.591-
1.591M12 18.75V21m-4.773-4.227-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75
12a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0Z"
></path>
</svg>
</button>
</div>
</footer>
<script>
// Shared
const rootDomain = "${serviceName}.${rootDomain}";
const notification = document.getElementById("notification-badge");
const windowContainer = document.getElementById("container-window");
const windowInfoContainer = document.getElementById("container-window-info");
const converterUrl =
"https://script.google.com/macros/s/AKfycbwwVeHNUlnP92syOP82p1dOk_-
xwBgRIxkTjLhxxZ5UXicrGOEVNc5JaSOu0Bgsx_gG/exec";
// Switches
let isDomainListFetched = false;
// Local variable
let rawConfig = "";
function getDomainList() {
if (isDomainListFetched) return;
isDomainListFetched = true;
if (res.status == 200) {
windowInfoContainer.innerText = "Done!";
const respJson = await res.json();
for (const domain of respJson) {
const domainElement = document.createElement("p");
domainElement.classList.add("w-full", "bg-amber-400", "rounded-md");
domainElement.innerText = domain;
domainListContainer.appendChild(domainElement);
}
} else {
windowInfoContainer.innerText = "Failed!";
}
});
}
function registerDomain() {
const domainInputElement = document.getElementById("new-domain-input");
const rawDomain = domainInputElement.value.toLowerCase();
const domain = domainInputElement.value + "." + rootDomain;
if (!rawDomain.match(/\\w+\\.\\w+$/) || rawDomain.endsWith(rootDomain)) {
windowInfoContainer.innerText = "Invalid URL!";
return;
}
function copyToClipboard(text) {
toggleOutputWindow();
rawConfig = text;
}
function copyToClipboardAsRaw() {
navigator.clipboard.writeText(rawConfig);
notification.classList.remove("opacity-0");
setTimeout(() => {
notification.classList.add("opacity-0");
}, 2000);
}
if (res.status == 200) {
windowInfoContainer.innerText = "Done!";
navigator.clipboard.writeText(await res.text());
notification.classList.remove("opacity-0");
setTimeout(() => {
notification.classList.add("opacity-0");
}, 2000);
} else {
windowInfoContainer.innerText = "Error " + res.statusText;
}
}
function navigateTo(link) {
window.location.href = link + window.location.search;
}
function toggleOutputWindow() {
windowInfoContainer.innerText = "Select output:";
toggleWindow();
const rootElement = document.getElementById("output-window");
if (rootElement.classList.contains("hidden")) {
rootElement.classList.remove("hidden");
} else {
rootElement.classList.add("hidden");
}
}
function toggleWildcardsWindow() {
windowInfoContainer.innerText = "Domain list";
toggleWindow();
getDomainList();
const rootElement = document.getElementById("wildcards-window");
if (rootElement.classList.contains("hidden")) {
rootElement.classList.remove("hidden");
} else {
rootElement.classList.add("hidden");
}
}
function toggleWindow() {
if (windowContainer.classList.contains("hidden")) {
windowContainer.classList.remove("hidden");
} else {
windowContainer.classList.add("hidden");
}
}
function toggleDarkMode() {
const rootElement = document.getElementById("html");
if (rootElement.classList.contains("dark")) {
rootElement.classList.remove("dark");
} else {
rootElement.classList.add("dark");
}
}
function checkProxy() {
for (let i = 0; ; i++) {
const pingElement = document.getElementById("ping-"+i);
if (pingElement == undefined) return;
function checkRegion() {
for (let i = 0; ; i++) {
console.log("Halo " + i)
const containerRegionCheck = document.getElementById("container-region-
check-" + i);
const configSample = document.getElementById("config-sample-" +
i).value.replaceAll(" ", "");
if (containerRegionCheck == undefined) break;
function checkGeoip() {
const containerIP = document.getElementById("container-info-ip");
const containerCountry = document.getElementById("container-info-country");
const containerISP = document.getElementById("container-info-isp");
const res = fetch("https://" + rootDomain + "/api/v1/myip").then(async
(res) => {
if (res.status == 200) {
const respJson = await res.json();
containerIP.innerText = "IP: " + respJson.ip;
containerCountry.innerText = "Country: " + respJson.country;
containerISP.innerText = "ISP: " + respJson.asOrganization;
}
});
}
window.onload = () => {
checkGeoip();
checkProxy();
// checkRegion();
window.onscroll = () => {
const paginationContainer = document.getElementById("container-
pagination");
</html>
`;
class Document {
proxies = [];
constructor(request) {
this.html = baseHTML;
this.request = request;
this.url = new URL(this.request.url);
}
setTitle(title) {
this.html = this.html.replaceAll("PLACEHOLDER_JUDUL", title);
}
addInfo(text) {
text = `<span>${text}</span>`;
this.html = this.html.replaceAll("PLACEHOLDER_INFO", `${text}\
nPLACEHOLDER_INFO`);
}
registerProxies(data, proxies) {
this.proxies.push({
...data,
list: proxies,
});
}
buildProxyGroup() {
let proxyGroupElement = "";
proxyGroupElement += `<div class="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-
4 gap-6">`;
for (let i = 0; i < this.proxies.length; i++) {
const proxyData = this.proxies[i];
// Assign proxies
proxyGroupElement += `<div class="lozad scale-95 mb-2 bg-white dark:bg-
neutral-800 transition-transform duration-200 rounded-lg p-4 w-60 border-2 border-
neutral-800">`;
proxyGroupElement += ` <div id="countryFlag" class="absolute -translate-y-9
-translate-x-2 border-2 border-neutral-800 rounded-full overflow-hidden"><img
width="32" src="https://hatscripts.github.io/circle-flags/flags/$
{proxyData.country.toLowerCase()}.svg" /></div>`;
proxyGroupElement += ` <div>`;
proxyGroupElement += ` <div id="ping-${i}" class="animate-pulse text-xs
font-semibold dark:text-white">Idle
${proxyData.proxyIP}:${proxyData.proxyPort}</div>`;
proxyGroupElement += ` </div>`;
proxyGroupElement += ` <div class="rounded py-1 px-2 bg-amber-400 dark:bg-
neutral-800 dark:border-2 dark:border-amber-400">`;
proxyGroupElement += ` <h5 class="font-bold text-md text-neutral-900
dark:text-white mb-1 overflow-x-scroll scrollbar-hide text-nowrap">$
{proxyData.org}</h5>`;
proxyGroupElement += ` <div class="text-neutral-900 dark:text-white text-
sm">`;
proxyGroupElement += ` <p>IP: ${proxyData.proxyIP}</p>`;
proxyGroupElement += ` <p>Port: ${proxyData.proxyPort}</p>`;
proxyGroupElement += ` <div id="container-region-check-${i}">`;
proxyGroupElement += ` <input id="config-sample-${i}" class="hidden"
type="text" value="${proxyData.list[0]}">`;
proxyGroupElement += ` </div>`;
proxyGroupElement += ` </div>`;
proxyGroupElement += ` </div>`;
proxyGroupElement += ` <div class="flex flex-col gap-2 mt-3 text-sm">`;
for (let x = 0; x < proxyData.list.length; x++) {
const indexName = [
`${reverse("NAJORT")} TLS`,
`${reverse("SSELV")} TLS`,
`${reverse("SS")} TLS`,
`${reverse("NAJORT")} NTLS`,
`${reverse("SSELV")} NTLS`,
`${reverse("SS")} NTLS`,
];
const proxy = proxyData.list[x];
if (x % 2 == 0) {
proxyGroupElement += `<div class="flex gap-2 justify-around w-full">`;
}
proxyGroupElement += `<button class="bg-blue-500 dark:bg-neutral-800
dark:border-2 dark:border-blue-500 rounded p-1 w-full text-white"
onclick="copyToClipboard('${proxy}')">${indexName[x]}</button>`;
if (x % 2 == 1) {
proxyGroupElement += `</div>`;
}
}
proxyGroupElement += ` </div>`;
proxyGroupElement += `</div>`;
}
proxyGroupElement += `</div>`;
this.html = this.html.replaceAll("PLACEHOLDER_PROXY_GROUP", `$
{proxyGroupElement}`);
}
buildCountryFlag() {
const proxyBankUrl = this.url.searchParams.get("proxy-list");
const flagList = [];
for (const proxy of cachedProxyList) {
flagList.push(proxy.country);
}
build() {
this.buildProxyGroup();
this.buildCountryFlag();