差别
这里会显示出您选择的修订版和当前版本之间的差别。
| 两侧同时换到之前的修订记录前一修订版后一修订版 | 前一修订版 | ||
| 工具:编辑:herokuv2ray [2023-07-22 07:23] – [CloudFlare 免费翻墙工具] goldentianya | 工具:编辑:herokuv2ray [2025-12-30 11:23] (当前版本) – [七合一一键脚本] goldentianya | ||
|---|---|---|---|
| 行 66: | 行 66: | ||
| ====== X-UI 搭建 ====== | ====== X-UI 搭建 ====== | ||
| [[https:// | [[https:// | ||
| + | |||
| + | [[https:// | ||
| x-ui 一键安装 | x-ui 一键安装 | ||
| 行 115: | 行 117: | ||
| [[https:// | [[https:// | ||
| + | |||
| + | 更新分流文件的命令,需要每年执行一次。 | ||
| + | wget https:// | ||
| ++++ 路由设置 | | ++++ 路由设置 | | ||
| 行 133: | 行 138: | ||
| ++++ | ++++ | ||
| - | ====== 七合一一键脚本 ====== | ||
| - | <code > | ||
| - | wget -P /root -N –no-check-certificate " | ||
| - | </ | ||
| - | |||
| - | - cloudflare 的小云朵要灰色 | ||
| - | - 证书放在 <color # | ||
| - | ====== CloudFlare 免费翻墙工具 ====== | ||
| - | 每秒的IP都变化。采用 simpinvest.eu.org 域名来实现这个机场。 | ||
| - | |||
| - | 1. 进入 CF => Worker and Pages => create Worker | ||
| - | |||
| - | 2. 打开v2ray 添加vlessf服务器,点击生成一个用户id,并复制到脚本的 userID 中去。选择下面其中一个域名,添加到 proxy 中去 | ||
| - | |||
| - | 五个加速地址 | ||
| - | <code | download> | ||
| - | | ||
| - | edgetunnel.anycast.eu.org | ||
| - | cdn-b100.xn--b6gac.eu.org | ||
| - | | ||
| - | | ||
| - | </ | ||
| - | |||
| - | 3. 保存并部署。然后返回到worker,选择查看,添加一个自定义域名 2207.simpinvest.eu.org (二级域名) | ||
| - | |||
| - | 4. 打开地址 <color # | ||
| - | |||
| - | 5. 选择网页上的订阅地址,打开v2ray, | ||
| - | |||
| - | |||
| - | ++++ worker.js | | ||
| - | |||
| - | <code | download> | ||
| - | // < | ||
| - | // @ts-ignore | ||
| - | import { connect } from ' | ||
| - | |||
| - | // How to generate your own UUID: | ||
| - | // [Windows] Press "Win + R", input cmd and run: Powershell -NoExit -Command " | ||
| - | let userID = ' | ||
| - | |||
| - | let proxyIP = ''; | ||
| - | |||
| - | |||
| - | if (!isValidUUID(userID)) { | ||
| - | throw new Error(' | ||
| - | } | ||
| - | |||
| - | export default { | ||
| - | /** | ||
| - | * @param {import(" | ||
| - | * @param {{UUID: string, PROXYIP: string}} env | ||
| - | * @param {import(" | ||
| - | * @returns {Promise< | ||
| - | */ | ||
| - | async fetch(request, | ||
| - | try { | ||
| - | userID = env.UUID || userID; | ||
| - | proxyIP = env.PROXYIP || proxyIP; | ||
| - | const upgradeHeader = request.headers.get(' | ||
| - | if (!upgradeHeader || upgradeHeader !== ' | ||
| - | const url = new URL(request.url); | ||
| - | switch (url.pathname) { | ||
| - | case '/': | ||
| - | return new Response(JSON.stringify(request.cf), | ||
| - | case `/ | ||
| - | const vlessConfig = getVLESSConfig(userID, | ||
| - | return new Response(`${vlessConfig}`, | ||
| - | status: | ||
| - | headers: | ||
| - | " | ||
| - | } | ||
| - | }); | ||
| - | } | ||
| - | default: | ||
| - | return new Response(' | ||
| - | } | ||
| - | } else { | ||
| - | return await vlessOverWSHandler(request); | ||
| - | } | ||
| - | } catch (err) { | ||
| - | /** @type {Error} */ let e = err; | ||
| - | return new Response(e.toString()); | ||
| - | } | ||
| - | }, | ||
| - | }; | ||
| - | |||
| - | |||
| - | |||
| - | |||
| - | /** | ||
| - | | ||
| - | * @param {import(" | ||
| - | */ | ||
| - | async function vlessOverWSHandler(request) { | ||
| - | |||
| - | /** @type {import(" | ||
| - | // @ts-ignore | ||
| - | const webSocketPair = new WebSocketPair(); | ||
| - | const [client, webSocket] = Object.values(webSocketPair); | ||
| - | |||
| - | webSocket.accept(); | ||
| - | |||
| - | let address = ''; | ||
| - | let portWithRandomLog = ''; | ||
| - | const log = (/** @type {string} */ info, /** @type {string | undefined} */ event) => { | ||
| - | console.log(`[${address}: | ||
| - | }; | ||
| - | const earlyDataHeader = request.headers.get(' | ||
| - | |||
| - | const readableWebSocketStream = makeReadableWebSocketStream(webSocket, | ||
| - | |||
| - | /** @type {{ value: import(" | ||
| - | let remoteSocketWapper = { | ||
| - | value: null, | ||
| - | }; | ||
| - | let udpStreamWrite = null; | ||
| - | let isDns = false; | ||
| - | |||
| - | // ws --> remote | ||
| - | readableWebSocketStream.pipeTo(new WritableStream({ | ||
| - | async write(chunk, | ||
| - | if (isDns && udpStreamWrite) { | ||
| - | return udpStreamWrite(chunk); | ||
| - | } | ||
| - | if (remoteSocketWapper.value) { | ||
| - | const writer = remoteSocketWapper.value.writable.getWriter() | ||
| - | await writer.write(chunk); | ||
| - | writer.releaseLock(); | ||
| - | return; | ||
| - | } | ||
| - | |||
| - | const { | ||
| - | hasError, | ||
| - | message, | ||
| - | portRemote = 443, | ||
| - | addressRemote = '', | ||
| - | rawDataIndex, | ||
| - | vlessVersion = new Uint8Array([0, | ||
| - | isUDP, | ||
| - | } = processVlessHeader(chunk, | ||
| - | address = addressRemote; | ||
| - | portWithRandomLog = `${portRemote}--${Math.random()} ${isUDP ? 'udp ' : 'tcp ' | ||
| - | } `; | ||
| - | if (hasError) { | ||
| - | // controller.error(message); | ||
| - | throw new Error(message); | ||
| - | // webSocket.close(1000, | ||
| - | return; | ||
| - | } | ||
| - | // if UDP but port not DNS port, close it | ||
| - | if (isUDP) { | ||
| - | if (portRemote === 53) { | ||
| - | isDns = true; | ||
| - | } else { | ||
| - | // controller.error(' | ||
| - | throw new Error(' | ||
| - | return; | ||
| - | } | ||
| - | } | ||
| - | // [" | ||
| - | const vlessResponseHeader = new Uint8Array([vlessVersion[0], | ||
| - | const rawClientData = chunk.slice(rawDataIndex); | ||
| - | |||
| - | // TODO: support udp here when cf runtime has udp support | ||
| - | if (isDns) { | ||
| - | const { write } = await handleUDPOutBound(webSocket, | ||
| - | udpStreamWrite = write; | ||
| - | udpStreamWrite(rawClientData); | ||
| - | return; | ||
| - | } | ||
| - | handleTCPOutBound(remoteSocketWapper, | ||
| - | }, | ||
| - | close() { | ||
| - | log(`readableWebSocketStream is close`); | ||
| - | }, | ||
| - | abort(reason) { | ||
| - | log(`readableWebSocketStream is abort`, JSON.stringify(reason)); | ||
| - | }, | ||
| - | })).catch((err) => { | ||
| - | log(' | ||
| - | }); | ||
| - | |||
| - | return new Response(null, | ||
| - | status: 101, | ||
| - | // @ts-ignore | ||
| - | webSocket: | ||
| - | }); | ||
| - | } | ||
| - | |||
| - | /** | ||
| - | * Handles outbound TCP connections. | ||
| - | * | ||
| - | * @param {any} remoteSocket | ||
| - | * @param {string} addressRemote The remote address to connect to. | ||
| - | * @param {number} portRemote The remote port to connect to. | ||
| - | * @param {Uint8Array} rawClientData The raw client data to write. | ||
| - | * @param {import(" | ||
| - | * @param {Uint8Array} vlessResponseHeader The VLESS response header. | ||
| - | * @param {function} log The logging function. | ||
| - | * @returns {Promise< | ||
| - | */ | ||
| - | async function handleTCPOutBound(remoteSocket, | ||
| - | async function connectAndWrite(address, | ||
| - | /** @type {import(" | ||
| - | const tcpSocket = connect({ | ||
| - | hostname: | ||
| - | port: port, | ||
| - | }); | ||
| - | remoteSocket.value = tcpSocket; | ||
| - | log(`connected to ${address}: | ||
| - | const writer = tcpSocket.writable.getWriter(); | ||
| - | await writer.write(rawClientData); | ||
| - | writer.releaseLock(); | ||
| - | return tcpSocket; | ||
| - | } | ||
| - | |||
| - | // if the cf connect tcp socket have no incoming data, we retry to redirect ip | ||
| - | async function retry() { | ||
| - | const tcpSocket = await connectAndWrite(proxyIP || addressRemote, | ||
| - | // no matter retry success or not, close websocket | ||
| - | tcpSocket.closed.catch(error => { | ||
| - | console.log(' | ||
| - | }).finally(() => { | ||
| - | safeCloseWebSocket(webSocket); | ||
| - | }) | ||
| - | remoteSocketToWS(tcpSocket, | ||
| - | } | ||
| - | |||
| - | const tcpSocket = await connectAndWrite(addressRemote, | ||
| - | |||
| - | // when remoteSocket is ready, pass to websocket | ||
| - | // remote--> | ||
| - | remoteSocketToWS(tcpSocket, | ||
| - | } | ||
| - | |||
| - | /** | ||
| - | | ||
| - | * @param {import(" | ||
| - | * @param {string} earlyDataHeader for ws 0rtt | ||
| - | * @param {(info: string)=> | ||
| - | */ | ||
| - | function makeReadableWebSocketStream(webSocketServer, | ||
| - | let readableStreamCancel = false; | ||
| - | const stream = new ReadableStream({ | ||
| - | start(controller) { | ||
| - | webSocketServer.addEventListener(' | ||
| - | if (readableStreamCancel) { | ||
| - | return; | ||
| - | } | ||
| - | const message = event.data; | ||
| - | controller.enqueue(message); | ||
| - | }); | ||
| - | |||
| - | // The event means that the client closed the client -> server stream. | ||
| - | // However, the server -> client stream is still open until you call close() on the server side. | ||
| - | // The WebSocket protocol says that a separate close message must be sent in each direction to fully close the socket. | ||
| - | webSocketServer.addEventListener(' | ||
| - | // client send close, need close server | ||
| - | // if stream is cancel, skip controller.close | ||
| - | safeCloseWebSocket(webSocketServer); | ||
| - | if (readableStreamCancel) { | ||
| - | return; | ||
| - | } | ||
| - | controller.close(); | ||
| - | } | ||
| - | ); | ||
| - | webSocketServer.addEventListener(' | ||
| - | log(' | ||
| - | controller.error(err); | ||
| - | } | ||
| - | ); | ||
| - | // for ws 0rtt | ||
| - | const { earlyData, error } = base64ToArrayBuffer(earlyDataHeader); | ||
| - | if (error) { | ||
| - | controller.error(error); | ||
| - | } else if (earlyData) { | ||
| - | controller.enqueue(earlyData); | ||
| - | } | ||
| - | }, | ||
| - | |||
| - | pull(controller) { | ||
| - | // if ws can stop read if stream is full, we can implement backpressure | ||
| - | // https:// | ||
| - | }, | ||
| - | cancel(reason) { | ||
| - | // 1. pipe WritableStream has error, this cancel will called, so ws handle server close into here | ||
| - | // 2. if readableStream is cancel, all controller.close/ | ||
| - | // 3. but from testing controller.error still work even if readableStream is cancel | ||
| - | if (readableStreamCancel) { | ||
| - | return; | ||
| - | } | ||
| - | log(`ReadableStream was canceled, due to ${reason}`) | ||
| - | readableStreamCancel = true; | ||
| - | safeCloseWebSocket(webSocketServer); | ||
| - | } | ||
| - | }); | ||
| - | |||
| - | return stream; | ||
| - | |||
| - | } | ||
| - | |||
| - | // https:// | ||
| - | // https:// | ||
| - | |||
| - | /** | ||
| - | | ||
| - | * @param { ArrayBuffer} vlessBuffer | ||
| - | * @param {string} userID | ||
| - | * @returns | ||
| - | */ | ||
| - | function processVlessHeader( | ||
| - | vlessBuffer, | ||
| - | userID | ||
| - | ) { | ||
| - | if (vlessBuffer.byteLength < 24) { | ||
| - | return { | ||
| - | hasError: | ||
| - | message: ' | ||
| - | }; | ||
| - | } | ||
| - | const version = new Uint8Array(vlessBuffer.slice(0, | ||
| - | let isValidUser = false; | ||
| - | let isUDP = false; | ||
| - | if (stringify(new Uint8Array(vlessBuffer.slice(1, | ||
| - | isValidUser = true; | ||
| - | } | ||
| - | if (!isValidUser) { | ||
| - | return { | ||
| - | hasError: | ||
| - | message: ' | ||
| - | }; | ||
| - | } | ||
| - | |||
| - | const optLength = new Uint8Array(vlessBuffer.slice(17, | ||
| - | //skip opt for now | ||
| - | |||
| - | const command = new Uint8Array( | ||
| - | vlessBuffer.slice(18 + optLength, 18 + optLength + 1) | ||
| - | )[0]; | ||
| - | |||
| - | // 0x01 TCP | ||
| - | // 0x02 UDP | ||
| - | // 0x03 MUX | ||
| - | if (command === 1) { | ||
| - | } else if (command === 2) { | ||
| - | isUDP = true; | ||
| - | } else { | ||
| - | return { | ||
| - | hasError: | ||
| - | message: `command ${command} is not support, command 01-tcp, | ||
| - | }; | ||
| - | } | ||
| - | const portIndex = 18 + optLength + 1; | ||
| - | const portBuffer = vlessBuffer.slice(portIndex, | ||
| - | // port is big-Endian in raw data etc 80 == 0x005d | ||
| - | const portRemote = new DataView(portBuffer).getUint16(0); | ||
| - | |||
| - | let addressIndex = portIndex + 2; | ||
| - | const addressBuffer = new Uint8Array( | ||
| - | vlessBuffer.slice(addressIndex, | ||
| - | ); | ||
| - | |||
| - | // 1--> ipv4 addressLength =4 | ||
| - | // 2--> domain name addressLength=addressBuffer[1] | ||
| - | // 3--> ipv6 addressLength =16 | ||
| - | const addressType = addressBuffer[0]; | ||
| - | let addressLength = 0; | ||
| - | let addressValueIndex = addressIndex + 1; | ||
| - | let addressValue = ''; | ||
| - | switch (addressType) { | ||
| - | case 1: | ||
| - | addressLength = 4; | ||
| - | addressValue = new Uint8Array( | ||
| - | vlessBuffer.slice(addressValueIndex, | ||
| - | ).join(' | ||
| - | break; | ||
| - | case 2: | ||
| - | addressLength = new Uint8Array( | ||
| - | vlessBuffer.slice(addressValueIndex, | ||
| - | )[0]; | ||
| - | addressValueIndex += 1; | ||
| - | addressValue = new TextDecoder().decode( | ||
| - | vlessBuffer.slice(addressValueIndex, | ||
| - | ); | ||
| - | break; | ||
| - | case 3: | ||
| - | addressLength = 16; | ||
| - | const dataView = new DataView( | ||
| - | vlessBuffer.slice(addressValueIndex, | ||
| - | ); | ||
| - | // 2001: | ||
| - | const ipv6 = []; | ||
| - | for (let i = 0; i < 8; i++) { | ||
| - | ipv6.push(dataView.getUint16(i * 2).toString(16)); | ||
| - | } | ||
| - | addressValue = ipv6.join(':' | ||
| - | // seems no need add [] for ipv6 | ||
| - | break; | ||
| - | default: | ||
| - | return { | ||
| - | hasError: | ||
| - | message: | ||
| - | }; | ||
| - | } | ||
| - | if (!addressValue) { | ||
| - | return { | ||
| - | hasError: | ||
| - | message: `addressValue is empty, addressType is ${addressType}`, | ||
| - | }; | ||
| - | } | ||
| - | |||
| - | return { | ||
| - | hasError: false, | ||
| - | addressRemote: | ||
| - | addressType, | ||
| - | portRemote, | ||
| - | rawDataIndex: | ||
| - | vlessVersion: | ||
| - | isUDP, | ||
| - | }; | ||
| - | } | ||
| - | |||
| - | |||
| - | /** | ||
| - | | ||
| - | * @param {import(" | ||
| - | * @param {import(" | ||
| - | * @param {ArrayBuffer} vlessResponseHeader | ||
| - | * @param {(() => Promise< | ||
| - | * @param {*} log | ||
| - | */ | ||
| - | async function remoteSocketToWS(remoteSocket, | ||
| - | // remote--> | ||
| - | let remoteChunkCount = 0; | ||
| - | let chunks = []; | ||
| - | /** @type {ArrayBuffer | null} */ | ||
| - | let vlessHeader = vlessResponseHeader; | ||
| - | let hasIncomingData = false; // check if remoteSocket has incoming data | ||
| - | await remoteSocket.readable | ||
| - | .pipeTo( | ||
| - | new WritableStream({ | ||
| - | start() { | ||
| - | }, | ||
| - | /** | ||
| - | * | ||
| - | * @param {Uint8Array} chunk | ||
| - | * @param {*} controller | ||
| - | */ | ||
| - | async write(chunk, | ||
| - | hasIncomingData = true; | ||
| - | // remoteChunkCount++; | ||
| - | if (webSocket.readyState !== WS_READY_STATE_OPEN) { | ||
| - | controller.error( | ||
| - | ' | ||
| - | ); | ||
| - | } | ||
| - | if (vlessHeader) { | ||
| - | webSocket.send(await new Blob([vlessHeader, | ||
| - | vlessHeader = null; | ||
| - | } else { | ||
| - | // seems no need rate limit this, CF seems fix this??.. | ||
| - | // if (remoteChunkCount > 20000) { | ||
| - | // // cf one package is 4096 byte(4kb), | ||
| - | // await delay(1); | ||
| - | // } | ||
| - | webSocket.send(chunk); | ||
| - | } | ||
| - | }, | ||
| - | close() { | ||
| - | log(`remoteConnection!.readable is close with hasIncomingData is ${hasIncomingData}`); | ||
| - | // safeCloseWebSocket(webSocket); | ||
| - | }, | ||
| - | abort(reason) { | ||
| - | console.error(`remoteConnection!.readable abort`, reason); | ||
| - | }, | ||
| - | }) | ||
| - | ) | ||
| - | .catch((error) => { | ||
| - | console.error( | ||
| - | `remoteSocketToWS has exception `, | ||
| - | error.stack || error | ||
| - | ); | ||
| - | safeCloseWebSocket(webSocket); | ||
| - | }); | ||
| - | |||
| - | // seems is cf connect socket have error, | ||
| - | // 1. Socket.closed will have error | ||
| - | // 2. Socket.readable will be close without any data coming | ||
| - | if (hasIncomingData === false && retry) { | ||
| - | log(`retry`) | ||
| - | retry(); | ||
| - | } | ||
| - | } | ||
| - | |||
| - | /** | ||
| - | | ||
| - | * @param {string} base64Str | ||
| - | * @returns | ||
| - | */ | ||
| - | function base64ToArrayBuffer(base64Str) { | ||
| - | if (!base64Str) { | ||
| - | return { error: null }; | ||
| - | } | ||
| - | try { | ||
| - | // go use modified Base64 for URL rfc4648 which js atob not support | ||
| - | base64Str = base64Str.replace(/ | ||
| - | const decode = atob(base64Str); | ||
| - | const arryBuffer = Uint8Array.from(decode, | ||
| - | return { earlyData: arryBuffer.buffer, | ||
| - | } catch (error) { | ||
| - | return { error }; | ||
| - | } | ||
| - | } | ||
| - | |||
| - | /** | ||
| - | * This is not real UUID validation | ||
| - | * @param {string} uuid | ||
| - | */ | ||
| - | function isValidUUID(uuid) { | ||
| - | const uuidRegex = / | ||
| - | return uuidRegex.test(uuid); | ||
| - | } | ||
| - | |||
| - | const WS_READY_STATE_OPEN = 1; | ||
| - | const WS_READY_STATE_CLOSING = 2; | ||
| - | /** | ||
| - | * Normally, WebSocket will not has exceptions when close. | ||
| - | * @param {import(" | ||
| - | */ | ||
| - | function safeCloseWebSocket(socket) { | ||
| - | try { | ||
| - | if (socket.readyState === WS_READY_STATE_OPEN || socket.readyState === WS_READY_STATE_CLOSING) { | ||
| - | socket.close(); | ||
| - | } | ||
| - | } catch (error) { | ||
| - | console.error(' | ||
| - | } | ||
| - | } | ||
| - | |||
| - | const byteToHex = []; | ||
| - | for (let i = 0; i < 256; ++i) { | ||
| - | byteToHex.push((i + 256).toString(16).slice(1)); | ||
| - | } | ||
| - | function unsafeStringify(arr, | ||
| - | return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + " | ||
| - | } | ||
| - | function stringify(arr, | ||
| - | const uuid = unsafeStringify(arr, | ||
| - | if (!isValidUUID(uuid)) { | ||
| - | throw TypeError(" | ||
| - | } | ||
| - | return uuid; | ||
| - | } | ||
| - | |||
| - | |||
| - | /** | ||
| - | | ||
| - | * @param {import(" | ||
| - | * @param {ArrayBuffer} vlessResponseHeader | ||
| - | * @param {(string)=> | ||
| - | */ | ||
| - | async function handleUDPOutBound(webSocket, | ||
| - | |||
| - | let isVlessHeaderSent = false; | ||
| - | const transformStream = new TransformStream({ | ||
| - | start(controller) { | ||
| - | |||
| - | }, | ||
| - | transform(chunk, | ||
| - | // udp message 2 byte is the the length of udp data | ||
| - | // TODO: this should have bug, beacsue maybe udp chunk can be in two websocket message | ||
| - | for (let index = 0; index < chunk.byteLength; | ||
| - | const lengthBuffer = chunk.slice(index, | ||
| - | const udpPakcetLength = new DataView(lengthBuffer).getUint16(0); | ||
| - | const udpData = new Uint8Array( | ||
| - | chunk.slice(index + 2, index + 2 + udpPakcetLength) | ||
| - | ); | ||
| - | index = index + 2 + udpPakcetLength; | ||
| - | controller.enqueue(udpData); | ||
| - | } | ||
| - | }, | ||
| - | flush(controller) { | ||
| - | } | ||
| - | }); | ||
| - | |||
| - | // only handle dns udp for now | ||
| - | transformStream.readable.pipeTo(new WritableStream({ | ||
| - | async write(chunk) { | ||
| - | const resp = await fetch(' | ||
| - | { | ||
| - | method: | ||
| - | headers: | ||
| - | ' | ||
| - | }, | ||
| - | body: chunk, | ||
| - | }) | ||
| - | const dnsQueryResult = await resp.arrayBuffer(); | ||
| - | const udpSize = dnsQueryResult.byteLength; | ||
| - | // console.log([...new Uint8Array(dnsQueryResult)].map((x) => x.toString(16))); | ||
| - | const udpSizeBuffer = new Uint8Array([(udpSize >> 8) & 0xff, udpSize & 0xff]); | ||
| - | if (webSocket.readyState === WS_READY_STATE_OPEN) { | ||
| - | log(`doh success and dns message length is ${udpSize}`); | ||
| - | if (isVlessHeaderSent) { | ||
| - | webSocket.send(await new Blob([udpSizeBuffer, | ||
| - | } else { | ||
| - | webSocket.send(await new Blob([vlessResponseHeader, | ||
| - | isVlessHeaderSent = true; | ||
| - | } | ||
| - | } | ||
| - | } | ||
| - | })).catch((error) => { | ||
| - | log(' | ||
| - | }); | ||
| - | |||
| - | const writer = transformStream.writable.getWriter(); | ||
| - | |||
| - | return { | ||
| - | /** | ||
| - | * | ||
| - | * @param {Uint8Array} chunk | ||
| - | */ | ||
| - | write(chunk) { | ||
| - | writer.write(chunk); | ||
| - | } | ||
| - | }; | ||
| - | } | ||
| - | |||
| - | /** | ||
| - | | ||
| - | * @param {string} userID | ||
| - | * @param {string | null} hostName | ||
| - | * @returns {string} | ||
| - | */ | ||
| - | function getVLESSConfig(userID, | ||
| - | const vlessMain = `vless:// | ||
| - | return ` | ||
| - | ################################################################ | ||
| - | v2ray | ||
| - | --------------------------------------------------------------- | ||
| - | ${vlessMain} | ||
| - | --------------------------------------------------------------- | ||
| - | ################################################################ | ||
| - | clash-meta | ||
| - | --------------------------------------------------------------- | ||
| - | - type: vless | ||
| - | name: ${hostName} | ||
| - | server: ${hostName} | ||
| - | port: 443 | ||
| - | uuid: ${userID} | ||
| - | network: ws | ||
| - | tls: true | ||
| - | udp: false | ||
| - | sni: ${hostName} | ||
| - | client-fingerprint: | ||
| - | ws-opts: | ||
| - | path: "/? | ||
| - | headers: | ||
| - | host: ${hostName} | ||
| - | --------------------------------------------------------------- | ||
| - | ################################################################ | ||
| - | `; | ||
| - | } | ||
| - | |||
| - | </ | ||
| - | |||
| - | ++++ | ||
| - | ====== 夏时 Accelerator ====== | ||
| - | 需要到淘宝买一个美区 APPLE ID | ||
| - | * [[https:// | + | {{tag>vpn}} |
| - | * [[https:// | + | |
| - | * [[https:// | + | |
| - | * [[https:// | + | |
| - | * [[https:// | + | |
