// Developed by Surfboardv2ray
// https://github.com/Surfboardv2ray/v2ray-refiner
// Version 1.2.1
export default {
async fetch(request) {
return handleRequest(request);
}
};
async function handleRequest(request) {
const headers = new Headers();
headers.set('Access-Control-Allow-Origin', '*');
headers.set('Access-Control-Allow-Methods', 'GET, POST');
headers.set('Access-Control-Allow-Headers', 'Content-Type');
if (request.headers.get('Upgrade') === 'websocket') {
return handleWebSocket(request);
}
if (request.method === 'OPTIONS') {
return new Response(null, {
headers,
});
}
if (request.method === 'GET') {
return new Response(renderHTML(), {
headers: { 'content-type': 'text/html;charset=UTF-8' },
});
} else if (request.method === 'POST') {
const formData = await request.formData();
const config = formData.get('config');
const cleanIp = formData.get('cleanIp');
const workerHost = new URL(request.url).hostname;
try {
const refinedConfig = await refineConfig(config, cleanIp, workerHost);
return new Response(JSON.stringify({ refinedConfig }), {
headers: { 'content-type': 'application/json', ...headers },
});
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
headers: { 'content-type': 'application/json', ...headers },
status: 400,
});
}
}
return new Response('Method not allowed', { status: 405 });
}
async function handleWebSocket(request) {
const url = new URL(request.url);
const newUrl = new URL("https://" + url.pathname.replace(/^\/|\/$/g, ""));
return fetch(new Request(newUrl, request));
}
function renderHTML() {
return `
TLS Config Refiner
`;
}
async function refineConfig(config, cleanIp, workerHost) {
const allowedPorts = ['443', '8443', '2053', '2083', '2087', '2096'];
if (config.startsWith('vmess://')) {
return refineVmess(config, cleanIp, workerHost, allowedPorts);
} else if (config.startsWith('vless://')) {
return refineVless(config, cleanIp, workerHost, allowedPorts);
} else if (config.startsWith('trojan://')) {
return refineTrojan(config, cleanIp, workerHost, allowedPorts);
} else {
throw new Error('Invalid config format. Please enter a valid vmess, vless or trojan config with WS+TLS.');
}
}
function refineVmess(config, cleanIp, workerHost, allowedPorts) {
const base64Data = config.slice(8); // Remove 'vmess://'
const decodedString = atob(base64Data); // Decode base64 string
const decoded = JSON.parse(decodeURIComponent(escape(decodedString))); // Handle non-Latin1 characters
if (decoded.net !== 'ws') throw new Error('Network must be WS');
if (decoded.tls !== 'tls') throw new Error('Security must be TLS');
// Check if the input port is allowed
if (!allowedPorts.includes(String(decoded.port))) {
throw new Error('Config must use a Cloudflare TLS Port (443, 8443, 2053, 2083, 2087, or 2096) to proceed');
}
// Preserve the original `host` and `sni` values before overwriting
const originalHost = decoded.host || ''; // Original host
const originalSni = decoded.sni || decoded.host || ''; // Original SNI or fallback to host
// Set the port to 443 regardless of input config
decoded.port = 443;
decoded.add = cleanIp; // Set clean IP for "address"
decoded.host = workerHost; // New worker host
decoded.sni = workerHost; // New worker SNI
const originalPath = decoded.path || ''; // Original path
decoded.path = `/${originalSni}${originalPath}`; // Concatenated path with original SNI
const newConfig = 'vmess://' + btoa(unescape(encodeURIComponent(JSON.stringify(decoded))));
return newConfig;
}
function refineVless(config, cleanIp, workerHost, allowedPorts) {
const url = new URL(config);
if (url.searchParams.get('type') !== 'ws') throw new Error('Network must be WS');
if (url.searchParams.get('security') !== 'tls') throw new Error('Security must be TLS');
// Check if the input port is allowed
if (!allowedPorts.includes(url.port)) {
throw new Error('Config must use a Cloudflare TLS Port (443, 8443, 2053, 2083, 2087, or 2096) to proceed');
}
// Set the port to 443 regardless of input config
url.port = 443;
url.host = cleanIp; // Set clean IP for "address"
const originalHost = url.searchParams.get('host') || ''; // Original host
const originalPath = url.searchParams.get('path') || ''; // Original path
url.searchParams.set('host', workerHost);
url.searchParams.set('sni', workerHost);
url.searchParams.set('path', `/${originalHost}${originalPath}`); // Concatenated path
return url.toString();
}
function refineTrojan(config, cleanIp, workerHost, allowedPorts) {
const url = new URL(config);
if (url.searchParams.get('type') !== 'ws') throw new Error('Network must be WS');
if (url.searchParams.get('security') !== 'tls') throw new Error('Security must be TLS');
// Check if the input port is allowed
if (!allowedPorts.includes(url.port)) {
throw new Error('Config must use a Cloudflare TLS Port (443, 8443, 2053, 2083, 2087, or 2096) to proceed');
}
// Set the port to 443 regardless of input config
url.port = 443;
url.host = cleanIp; // Set clean IP for "address"
const originalHost = url.searchParams.get('host') || ''; // Original host
const originalPath = url.searchParams.get('path') || ''; // Original path
url.searchParams.set('host', workerHost);
url.searchParams.set('sni', workerHost);
url.searchParams.set('path', `/${originalHost}${originalPath}`); // Concatenated path
return url.toString();
}