@@ -69,24 +69,15 @@ export default {
6969 userID = UUID || userID ;
7070 socks5Address = SOCKS5 || socks5Address ;
7171 socks5Relay = SOCKS5_RELAY || socks5Relay ;
72- if ( PROXYIP ) {
73- // Split PROXYIP into an array of proxy addresses
74- const proxyAddresses = PROXYIP . split ( ',' ) . map ( addr => addr . trim ( ) ) ;
75- // Randomly select one proxy address
76- const selectedProxy = proxyAddresses [ Math . floor ( Math . random ( ) * proxyAddresses . length ) ] ;
77- [ proxyIP , proxyPort = '443' ] = selectedProxy . split ( ':' ) ;
78- } else {
79- proxyPort = proxyIP . includes ( ':' ) ? proxyIP . split ( ':' ) [ 1 ] : '443' ;
80- proxyIP = proxyIP . split ( ':' ) [ 0 ] ;
81- }
82- console . log ( 'ProxyIP:' , proxyIP ) ;
83- console . log ( 'ProxyPort:' , proxyPort ) ;
72+
73+ // Handle proxy configuration
74+ const proxyConfig = handleProxyConfig ( PROXYIP ) ;
75+ proxyIP = proxyConfig . ip ;
76+ proxyPort = proxyConfig . port ;
77+
8478 if ( socks5Address ) {
8579 try {
86- // Split SOCKS5 into an array of addresses
87- const socks5Addresses = socks5Address . split ( ',' ) . map ( addr => addr . trim ( ) ) ;
88- // Randomly select one SOCKS5 address
89- const selectedSocks5 = socks5Addresses [ Math . floor ( Math . random ( ) * socks5Addresses . length ) ] ;
80+ const selectedSocks5 = selectRandomAddress ( socks5Address ) ;
9081 parsedSocks5Address = socks5AddressParser ( selectedSocks5 ) ;
9182 enableSocks = true ;
9283 } catch ( err ) {
@@ -95,32 +86,49 @@ export default {
9586 }
9687 }
9788
98- const userID_Path = userID . includes ( ',' ) ? userID . split ( ',' ) [ 0 ] : userID ;
89+ const userIDs = userID . split ( ',' ) . map ( id => id . trim ( ) ) ;
9990 const url = new URL ( request . url ) ;
10091 const host = request . headers . get ( 'Host' ) ;
92+ const requestedPath = url . pathname . substring ( 1 ) ; // Remove leading slash
93+ const matchingUserID = userIDs . find ( id => {
94+ // Check if the path starts with the UUID or sub/UUID or bestip/UUID
95+ const patterns = [
96+ id ,
97+ `sub/${ id } ` ,
98+ `bestip/${ id } `
99+ ] ;
100+ return patterns . some ( pattern => requestedPath . startsWith ( pattern ) ) ;
101+ } ) ;
101102
102103 if ( request . headers . get ( 'Upgrade' ) !== 'websocket' ) {
103- switch ( url . pathname ) {
104- case '/cf' :
105- return new Response ( JSON . stringify ( request . cf , null , 4 ) , {
106- status : 200 ,
107- headers : { "Content-Type" : "application/json;charset=utf-8" } ,
108- } ) ;
109- case `/${ userID_Path } ` :
110- return new Response ( getConfig ( userID , host ) , {
111- status : 200 ,
112- headers : { "Content-Type" : "text/html; charset=utf-8" } ,
113- } ) ;
114- case `/sub/${ userID_Path } ` :
115- return new Response ( btoa ( GenSub ( userID , host ) ) , {
104+ if ( url . pathname === '/cf' ) {
105+ return new Response ( JSON . stringify ( request . cf , null , 4 ) , {
106+ status : 200 ,
107+ headers : { "Content-Type" : "application/json;charset=utf-8" } ,
108+ } ) ;
109+ }
110+
111+ if ( matchingUserID ) {
112+ if ( url . pathname === `/${ matchingUserID } ` || url . pathname === `/sub/${ matchingUserID } ` ) {
113+ const isSubscription = url . pathname . startsWith ( '/sub/' ) ;
114+ const proxyAddresses = PROXYIP ? PROXYIP . split ( ',' ) . map ( addr => addr . trim ( ) ) : proxyIP ;
115+ const content = isSubscription ?
116+ GenSub ( matchingUserID , host , proxyAddresses ) :
117+ getConfig ( matchingUserID , host , proxyAddresses ) ;
118+
119+ return new Response ( content , {
116120 status : 200 ,
117- headers : { "Content-Type" : "text/plain;charset=utf-8" } ,
121+ headers : {
122+ "Content-Type" : isSubscription ?
123+ "text/plain;charset=utf-8" :
124+ "text/html; charset=utf-8"
125+ } ,
118126 } ) ;
119- case `/bestip/${ userID_Path } ` :
120- return fetch ( `https://sub.xf.free.hr/auto?host=${ host } &uuid=${ userID } &path=/` , { headers : request . headers } ) ;
121- default :
122- return handleDefaultPath ( url , request ) ;
127+ } else if ( url . pathname === `/bestip/${ matchingUserID } ` ) {
128+ return fetch ( `https://sub.xf.free.hr/auto?host=${ host } &uuid=${ matchingUserID } &path=/` , { headers : request . headers } ) ;
129+ }
123130 }
131+ return handleDefaultPath ( url , request ) ;
124132 } else {
125133 return await ProtocolOverWSHandler ( request ) ;
126134 }
@@ -482,16 +490,16 @@ async function ProtocolOverWSHandler(request) {
482490/**
483491 * Handles outbound TCP connections for the proxy.
484492 * Establishes connection to remote server and manages data flow.
485- * @param {Socket } remoteSocket - Socket for remote connection
493+ * @param {Socket } remoteSocket - Remote socket connection
486494 * @param {string } addressType - Type of address (IPv4/IPv6)
487495 * @param {string } addressRemote - Remote server address
488496 * @param {number } portRemote - Remote server port
489497 * @param {Uint8Array } rawClientData - Raw data from client
490498 * @param {WebSocket } webSocket - WebSocket connection
491- * @param {Uint8Array } ProtocolResponseHeader - Protocol response header
499+ * @param {Uint8Array } protocolResponseHeader - Protocol response header
492500 * @param {Function } log - Logging function
493501 */
494- async function HandleTCPOutBound ( remoteSocket , addressType , addressRemote , portRemote , rawClientData , webSocket , ProtocolResponseHeader , log , ) {
502+ async function HandleTCPOutBound ( remoteSocket , addressType , addressRemote , portRemote , rawClientData , webSocket , protocolResponseHeader , log , ) {
495503 async function connectAndWrite ( address , port , socks = false ) {
496504 /** @type {import("@cloudflare/workers-types").Socket } */
497505 let tcpSocket ;
@@ -525,14 +533,14 @@ async function HandleTCPOutBound(remoteSocket, addressType, addressRemote, portR
525533 } ) . finally ( ( ) => {
526534 safeCloseWebSocket ( webSocket ) ;
527535 } )
528- RemoteSocketToWS ( tcpSocket , webSocket , ProtocolResponseHeader , null , log ) ;
536+ RemoteSocketToWS ( tcpSocket , webSocket , protocolResponseHeader , null , log ) ;
529537 }
530538
531539 let tcpSocket = await connectAndWrite ( addressRemote , portRemote ) ;
532540
533541 // when remoteSocket is ready, pass to websocket
534542 // remote--> ws
535- RemoteSocketToWS ( tcpSocket , webSocket , ProtocolResponseHeader , retry , log ) ;
543+ RemoteSocketToWS ( tcpSocket , webSocket , protocolResponseHeader , retry , log ) ;
536544}
537545
538546/**
@@ -1039,9 +1047,10 @@ const ed = 'RUR0dW5uZWw=';
10391047 * Generates configuration for VLESS client.
10401048 * @param {string } userIDs - Single or comma-separated user IDs
10411049 * @param {string } hostName - Host name for configuration
1050+ * @param {string|string[] } proxyIP - Proxy IP address or array of addresses
10421051 * @returns {string } Configuration HTML
10431052 */
1044- function getConfig ( userIDs , hostName ) {
1053+ function getConfig ( userIDs , hostName , proxyIP ) {
10451054 const commonUrlPart = `?encryption=none&security=tls&sni=${ hostName } &fp=randomized&type=ws&host=${ hostName } &path=%2F%3Fed%3D2048#${ hostName } ` ;
10461055
10471056 // Split the userIDs into an array
@@ -1050,7 +1059,7 @@ function getConfig(userIDs, hostName) {
10501059 // Prepare output string for each userID
10511060 const sublink = `https://${ hostName } /sub/${ userIDArray [ 0 ] } ?format=clash`
10521061 const subbestip = `https://${ hostName } /bestip/${ userIDArray [ 0 ] } ` ;
1053- const clash_link = `https://url.v1.mk/sub?target=clash&url=${ encodeURIComponent ( sublink ) } &insert=false&emoji=true&list=false&tfo=false&scv=true&fdn=false&sort=false&new_name=true` ;
1062+ const clash_link = `https://url.v1.mk/sub?target=clash&url=${ encodeURIComponent ( `https:// ${ hostName } /sub/ ${ userIDArray [ 0 ] } ?format=clash` ) } &insert=false&emoji=true&list=false&tfo=false&scv=true&fdn=false&sort=false&new_name=true` ;
10541063 // HTML Head with CSS and FontAwesome library
10551064 const htmlHead = `
10561065 <head>
@@ -1061,12 +1070,12 @@ function getConfig(userIDs, hostName) {
10611070 <meta property='og:title' content='EDtunnel - Protocol Configuration and Subscribe Output' />
10621071 <meta property='og:description' content='Use Cloudflare Pages and Worker serverless to implement protocol' />
10631072 <meta property='og:url' content='https://${ hostName } /' />
1064- <meta property='og:image' content='https://ipfs.io/ipfs/bafybeigd6i5aavwpr6wvnwuyayklq3omonggta4x2q7kpmgafj357nkcky ' />
1073+ <meta property='og:image' content='https://cdn.jsdelivr.net/gh/6Kmfi6HP/EDtunnel@refs/heads/main/image/logo.png ' />
10651074 <meta name='twitter:card' content='summary_large_image' />
10661075 <meta name='twitter:title' content='EDtunnel - Protocol Configuration and Subscribe Output' />
10671076 <meta name='twitter:description' content='Use Cloudflare Pages and Worker serverless to implement protocol' />
10681077 <meta name='twitter:url' content='https://${ hostName } /' />
1069- <meta name='twitter:image' content='https://ipfs.io/ipfs/bafybeigd6i5aavwpr6wvnwuyayklq3omonggta4x2q7kpmgafj357nkcky ' />
1078+ <meta name='twitter:image' content='https://cdn.jsdelivr.net/gh/6Kmfi6HP/EDtunnel@refs/heads/main/image/logo.png ' />
10701079 <meta property='og:image:width' content='1500' />
10711080 <meta property='og:image:height' content='1500' />
10721081
@@ -1191,7 +1200,7 @@ function getConfig(userIDs, hostName) {
11911200 const header = `
11921201 <div class="container">
11931202 <h1>EDtunnel: Protocol Configuration</h1>
1194- <img src="https://ipfs.io/ipfs/bafybeigd6i5aavwpr6wvnwuyayklq3omonggta4x2q7kpmgafj357nkcky " alt="EDtunnel Logo" class="logo">
1203+ <img src="https://cdn.jsdelivr.net/gh/6Kmfi6HP/EDtunnel@refs/heads/main/image/logo.png " alt="EDtunnel Logo" class="logo">
11951204 <p>Welcome! This function generates configuration for the vless protocol. If you found this useful, please check our GitHub project:</p>
11961205 <p><a href="https://github.com/6Kmfi6HP/EDtunnel" target="_blank" style="color: #00ff00;">EDtunnel - https://github.com/6Kmfi6HP/EDtunnel</a></p>
11971206 <div style="clear: both;"></div>
@@ -1216,7 +1225,7 @@ function getConfig(userIDs, hostName) {
12161225
12171226 const configOutput = userIDArray . map ( ( userID ) => {
12181227 const protocolMain = atob ( pt ) + '://' + userID + atob ( at ) + hostName + ":443" + commonUrlPart ;
1219- const protocolSec = atob ( pt ) + '://' + userID + atob ( at ) + proxyIP + ":" + proxyPort + commonUrlPart ;
1228+ const protocolSec = atob ( pt ) + '://' + userID + atob ( at ) + proxyIP [ 0 ] . split ( ':' ) [ 0 ] + ":" + proxyPort + commonUrlPart ;
12201229 return `
12211230 <div class="container config-item">
12221231 <h2>UUID: ${ userID } </h2>
@@ -1227,9 +1236,15 @@ function getConfig(userIDs, hostName) {
12271236 </div>
12281237
12291238 <h3>Best IP Configuration</h3>
1239+ <div class="input-group mb-3">
1240+ <select class="form-select" id="proxySelect" onchange="updateProxyConfig()">
1241+ ${ Array . from ( proxyIP ) . map ( proxy => `<option value="${ proxy } ">${ proxy } </option>` ) . join ( '' ) }
1242+ </select>
1243+ </div>
1244+ <br>
12301245 <div class="code-container">
1231- <pre><code>${ protocolSec } </code></pre>
1232- <button class="btn copy-btn" onclick='copyToClipboard(" ${ protocolSec } " )'><i class="fas fa-copy"></i> Copy</button>
1246+ <pre><code id="proxyConfig" >${ protocolSec } </code></pre>
1247+ <button class="btn copy-btn" onclick='copyToClipboard(document.getElementById("proxyConfig").textContent )'><i class="fas fa-copy"></i> Copy</button>
12331248 </div>
12341249 </div>
12351250 ` ;
@@ -1241,18 +1256,31 @@ function getConfig(userIDs, hostName) {
12411256 <body>
12421257 ${ header }
12431258 ${ configOutput }
1259+ <script>
1260+ const userIDArray = ${ JSON . stringify ( userIDArray ) } ;
1261+ const pt = "${ pt } ";
1262+ const at = "${ at } ";
1263+ const commonUrlPart = "?encryption=none&security=tls&sni=${ hostName } &fp=randomized&type=ws&host=${ hostName } &path=%2F%3Fed%3D2048#${ hostName } ";
1264+
1265+ function copyToClipboard(text) {
1266+ navigator.clipboard.writeText(text)
1267+ .then(() => {
1268+ alert("Copied to clipboard");
1269+ })
1270+ .catch((err) => {
1271+ console.error("Failed to copy to clipboard:", err);
1272+ });
1273+ }
1274+
1275+ function updateProxyConfig() {
1276+ const select = document.getElementById('proxySelect');
1277+ const proxyValue = select.value;
1278+ const [host, port] = proxyValue.split(':');
1279+ const protocolSec = atob(pt) + '://' + userIDArray[0] + atob(at) + host + ":" + port + commonUrlPart;
1280+ document.getElementById("proxyConfig").textContent = protocolSec;
1281+ }
1282+ </script>
12441283 </body>
1245- <script>
1246- function copyToClipboard(text) {
1247- navigator.clipboard.writeText(text)
1248- .then(() => {
1249- alert("Copied to clipboard");
1250- })
1251- .catch((err) => {
1252- console.error("Failed to copy to clipboard:", err);
1253- });
1254- }
1255- </script>
12561284 </html>` ;
12571285}
12581286
@@ -1263,38 +1291,119 @@ const HttpsPort = new Set([443, 8443, 2053, 2096, 2087, 2083]);
12631291 * Generates subscription content.
12641292 * @param {string } userID_path - User ID path
12651293 * @param {string } hostname - Host name
1294+ * @param {string|string[] } proxyIP - Proxy IP address or array of addresses
12661295 * @returns {string } Subscription content
12671296 */
1268- function GenSub ( userID_path , hostname ) {
1269- const userIDArray = userID_path . includes ( ',' ) ? userID_path . split ( ',' ) : [ userID_path ] ;
1297+ function GenSub ( userID_path , hostname , proxyIP ) {
1298+ // Add all CloudFlare public CNAME domains
1299+ const mainDomains = new Set ( [
1300+ hostname ,
1301+ // public domains
1302+ 'icook.hk' ,
1303+ 'japan.com' ,
1304+ 'malaysia.com' ,
1305+ 'russia.com' ,
1306+ 'singapore.com' ,
1307+ 'www.visa.com' ,
1308+ 'www.csgo.com' ,
1309+ 'www.shopify.com' ,
1310+ 'www.whatismyip.com' ,
1311+ 'www.ipget.net' ,
1312+ // 高频率更新
1313+ 'speed.marisalnc.com' , // 1000ip/3min
1314+ 'freeyx.cloudflare88.eu.org' , // 1000ip/3min
1315+ 'cloudflare.182682.xyz' , // 15ip/15min
1316+ '115155.xyz' , // 18ip/1小时
1317+ 'cdn.2020111.xyz' , // 15ip/10min
1318+ 'cfip.cfcdn.vip' , // 6ip/1天
1319+ proxyIPs ,
1320+ // 手动更新和未知频率
1321+ 'cf.0sm.com' , // 手动更新
1322+ 'cloudflare-ip.mofashi.ltd' , // 未知频率
1323+ 'cf.090227.xyz' , // 未知频率
1324+ 'cname.xirancdn.us' , // 未知频率
1325+ 'f3058171cad.002404.xyz' , // 未知频率
1326+ 'cf.zhetengsha.eu.org' , // 未知频率
1327+ 'cloudflare.9jy.cc' , // 未知频率
1328+ '8.889288.xyz' , // 未知频率
1329+ 'cf.zerone-cdn.pp.ua' , // 未知频率
1330+ 'cfip.1323123.xyz' , // 未知频率
1331+ 'cdn.tzpro.xyz' , // 未知频率
1332+ 'cf.877771.xyz' , // 未知频率
1333+ 'cnamefuckxxs.yuchen.icu' , // 未知频率
1334+ 'cfip.xxxxxxxx.tk' , // OTC大佬提供维护
1335+ ] ) ;
1336+
1337+ const userIDArray = userID_path . includes ( ',' ) ? userID_path . split ( "," ) : [ userID_path ] ;
1338+ const proxyIPArray = Array . isArray ( proxyIP ) ? proxyIP : ( proxyIP ? ( proxyIP . includes ( ',' ) ? proxyIP . split ( ',' ) : [ proxyIP ] ) : proxyIPs ) ;
12701339 const randomPath = ( ) => '/' + Math . random ( ) . toString ( 36 ) . substring ( 2 , 15 ) + '?ed=2048' ;
12711340 const commonUrlPartHttp = `?encryption=none&security=none&fp=random&type=ws&host=${ hostname } &path=${ encodeURIComponent ( randomPath ( ) ) } #` ;
1272- const commonUrlPartHttps = `?encryption=none&security=tls&sni=${ hostname } &fp=random&type=ws&host=${ hostname } &path=%2F%3Fed%3D2048#` ;
1341+ const commonUrlPartHttps = `?encryption=none&security=tls&sni=${ hostname } &fp=random&type=ws&host=${ hostname } &path=%2F%3Fed%3D2048#${ hostname } ` ;
12731342
12741343 const result = userIDArray . flatMap ( ( userID ) => {
1275- const PartHttp = Array . from ( HttpPort ) . flatMap ( ( port ) => {
1276- if ( ! hostname . includes ( 'pages.dev' ) ) {
1277- const urlPart = `${ hostname } -HTTP-${ port } ` ;
1278- const mainProtocolHttp = atob ( pt ) + '://' + userID + atob ( at ) + hostname + ':' + port + commonUrlPartHttp + urlPart ;
1279- return proxyIPs . flatMap ( ( proxyIP ) => {
1280- const secondaryProtocolHttp = atob ( pt ) + '://' + userID + atob ( at ) + proxyIP . split ( ':' ) [ 0 ] + ':' + proxyPort + commonUrlPartHttp + urlPart + '-' + proxyIP + '-' + atob ( ed ) ;
1281- return [ mainProtocolHttp , secondaryProtocolHttp ] ;
1344+ let allUrls = [ ] ;
1345+ // Generate main HTTP URLs first for all domains
1346+ if ( ! hostname . includes ( 'pages.dev' ) ) {
1347+ mainDomains . forEach ( domain => {
1348+ Array . from ( HttpPort ) . forEach ( ( port ) => {
1349+ const urlPart = `${ hostname . split ( '.' ) [ 0 ] } -HTTP-${ port } ` ;
1350+ const mainProtocolHttp = atob ( pt ) + '://' + userID + atob ( at ) + domain + ':' + port + commonUrlPartHttp + urlPart ;
1351+ allUrls . push ( mainProtocolHttp ) ;
12821352 } ) ;
1283- }
1284- return [ ] ;
1353+ } ) ;
1354+ }
1355+
1356+ // Generate main HTTPS URLs for all domains
1357+ mainDomains . forEach ( domain => {
1358+ Array . from ( HttpsPort ) . forEach ( ( port ) => {
1359+ const urlPart = `${ hostname . split ( '.' ) [ 0 ] } -HTTPS-${ port } ` ;
1360+ const mainProtocolHttps = atob ( pt ) + '://' + userID + atob ( at ) + domain + ':' + port + commonUrlPartHttps + urlPart ;
1361+ allUrls . push ( mainProtocolHttps ) ;
1362+ } ) ;
12851363 } ) ;
12861364
1287- const PartHttps = Array . from ( HttpsPort ) . flatMap ( ( port ) => {
1288- const urlPart = `${ hostname } -HTTPS-${ port } ` ;
1289- const mainProtocolHttps = atob ( pt ) + '://' + userID + atob ( at ) + hostname + ':' + port + commonUrlPartHttps + urlPart ;
1290- return proxyIPs . flatMap ( ( proxyIP ) => {
1291- const secondaryProtocolHttps = atob ( pt ) + '://' + userID + atob ( at ) + proxyIP . split ( ':' ) [ 0 ] + ':' + proxyPort + commonUrlPartHttps + urlPart + '-' + proxyIP + '-' + atob ( ed ) ;
1292- return [ mainProtocolHttps , secondaryProtocolHttps ] ;
1365+ // Generate proxy HTTPS URLs
1366+ proxyIPArray . forEach ( ( proxyAddr ) => {
1367+ const [ proxyHost , proxyPort ] = proxyAddr . split ( ':' ) ;
1368+ Array . from ( HttpsPort ) . forEach ( ( port ) => {
1369+ const urlPart = `${ hostname . split ( '.' ) [ 0 ] } -HTTPS-${ port } ` ;
1370+ const secondaryProtocolHttps = atob ( pt ) + '://' + userID + atob ( at ) + proxyHost + ':' + proxyPort + commonUrlPartHttps + urlPart + '-' + proxyAddr + '-' + atob ( ed ) ;
1371+ allUrls . push ( secondaryProtocolHttps ) ;
12931372 } ) ;
12941373 } ) ;
12951374
1296- return [ ... PartHttp , ... PartHttps ] ;
1375+ return allUrls ;
12971376 } ) ;
12981377
1299- return result . join ( '\n' ) ;
1378+ return btoa ( result . join ( '\n' ) ) ;
1379+ }
1380+
1381+ /**
1382+ * Handles proxy configuration and returns standardized proxy settings
1383+ * @param {string } PROXYIP - Proxy IP configuration from environment
1384+ * @returns {{ip: string, port: string} } Standardized proxy configuration
1385+ */
1386+ function handleProxyConfig ( PROXYIP ) {
1387+ if ( PROXYIP ) {
1388+ const proxyAddresses = PROXYIP . split ( ',' ) . map ( addr => addr . trim ( ) ) ;
1389+ const selectedProxy = selectRandomAddress ( proxyAddresses ) ;
1390+ const [ ip , port = '443' ] = selectedProxy . split ( ':' ) ;
1391+ return { ip, port } ;
1392+ } else {
1393+ const port = proxyIP . includes ( ':' ) ? proxyIP . split ( ':' ) [ 1 ] : '443' ;
1394+ const ip = proxyIP . split ( ':' ) [ 0 ] ;
1395+ return { ip, port } ;
1396+ }
1397+ }
1398+
1399+ /**
1400+ * Selects a random address from a comma-separated string or array of addresses
1401+ * @param {string|string[] } addresses - Comma-separated string or array of addresses
1402+ * @returns {string } Selected address
1403+ */
1404+ function selectRandomAddress ( addresses ) {
1405+ const addressArray = typeof addresses === 'string' ?
1406+ addresses . split ( ',' ) . map ( addr => addr . trim ( ) ) :
1407+ addresses ;
1408+ return addressArray [ Math . floor ( Math . random ( ) * addressArray . length ) ] ;
13001409}
0 commit comments