mirror of
https://github.com/lush2020/edgetunnel.git
synced 2026-03-21 17:12:33 +08:00
add cf-page-vless
This commit is contained in:
17
apps/cf-page-vless/index.html
Normal file
17
apps/cf-page-vless/index.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Edge Tunnel VLESS CF</title>
|
||||
<base href="/" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" href="/src/styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cf-page",
|
||||
"name": "cf-page-vless",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "apps/cf-page/src",
|
||||
"sourceRoot": "apps/cf-page-vless/src",
|
||||
"projectType": "application",
|
||||
"targets": {
|
||||
"build": {
|
||||
@@ -9,7 +9,7 @@
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"defaultConfiguration": "production",
|
||||
"options": {
|
||||
"outputPath": "dist/apps/cf-page"
|
||||
"outputPath": "dist/apps/cf-page-vless"
|
||||
},
|
||||
"configurations": {
|
||||
"development": {
|
||||
@@ -24,15 +24,15 @@
|
||||
"executor": "@nx/vite:dev-server",
|
||||
"defaultConfiguration": "development",
|
||||
"options": {
|
||||
"buildTarget": "cf-page:build"
|
||||
"buildTarget": "cf-page-vless:build"
|
||||
},
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildTarget": "cf-page:build:development",
|
||||
"buildTarget": "cf-page-vless:build:development",
|
||||
"hmr": true
|
||||
},
|
||||
"production": {
|
||||
"buildTarget": "cf-page:build:production",
|
||||
"buildTarget": "cf-page-vless:build:production",
|
||||
"hmr": false
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@
|
||||
"serve-cf-page": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"command": "wrangler pages dev dist/apps/cf-page"
|
||||
"command": "wrangler pages dev dist/apps/cf-page-vless"
|
||||
},
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
@@ -55,7 +55,7 @@
|
||||
"executor": "@nx/linter:eslint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["apps/cf-page/**/*.{ts,tsx,js,jsx}"]
|
||||
"lintFilePatterns": ["apps/cf-page-vless/**/*.{ts,tsx,js,jsx}"]
|
||||
}
|
||||
}
|
||||
},
|
||||
46
apps/cf-page-vless/public/401.html
Normal file
46
apps/cf-page-vless/public/401.html
Normal file
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>401 - UUID Not Valid</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1 style="color: red">Not set valid UUID in Environment Variables.</h1>
|
||||
<h2>
|
||||
Please use tool to generate and
|
||||
<span style="color: red">remember</span> UUID or use this one
|
||||
<span style="color: blue" id="uuidSpan"></span>
|
||||
</h2>
|
||||
<h3>
|
||||
You must use same UUID for login this page after config valid UUID
|
||||
Environment Variables
|
||||
</h3>
|
||||
<h2>
|
||||
Please refer to
|
||||
<a
|
||||
href="https://github.com/zizifn/edgetunnel/blob/main/doc/edge-tunnel-deno.md#%E6%B5%81%E7%A8%8B%E6%BC%94%E7%A4%BA"
|
||||
>deno deploy guide</a
|
||||
>
|
||||
</h2>
|
||||
|
||||
<h3>
|
||||
Or maybe check below
|
||||
<a
|
||||
href="https://raw.githubusercontent.com/zizifn/edgetunnel/main/doc/deno-deploy2.gif"
|
||||
>GIF</a
|
||||
>
|
||||
</h3>
|
||||
<img
|
||||
src="https://raw.githubusercontent.com/zizifn/edgetunnel/main/doc/deno-deploy2.gif"
|
||||
alt="guide"
|
||||
srcset=""
|
||||
/>
|
||||
<script>
|
||||
let uuid = URL.createObjectURL(new Blob([])).substr(-36);
|
||||
document.getElementById('uuidSpan').textContent = uuid;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -1,19 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Edge Tunnel VLESS CF</title>
|
||||
<base href="/" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" href="/src/styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,33 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>401 - UUID Not Valid</title>
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
<h1 style="color: red;">Not set valid UUID in Environment Variables.</h1>
|
||||
<h2>Please use tool to generate and <span style="color: red;">remember</span> UUID or use this one <span
|
||||
style="color: blue;" id="uuidSpan"></span>
|
||||
</h2>
|
||||
<h3> You must use same UUID for login this page after config valid UUID Environment Variables
|
||||
</h3>
|
||||
<h2>Please refer to <a
|
||||
href="https://github.com/zizifn/edgetunnel/blob/main/doc/edge-tunnel-deno.md#%E6%B5%81%E7%A8%8B%E6%BC%94%E7%A4%BA">deno
|
||||
deploy guide</a>
|
||||
</h2>
|
||||
|
||||
<h3>Or maybe check below <a
|
||||
href="https://raw.githubusercontent.com/zizifn/edgetunnel/main/doc/deno-deploy2.gif">GIF</a> </h3>
|
||||
<img src="https://raw.githubusercontent.com/zizifn/edgetunnel/main/doc/deno-deploy2.gif" alt="guide" srcset="">
|
||||
<script>
|
||||
let uuid = URL.createObjectURL(new Blob([])).substr(-36);
|
||||
document.getElementById('uuidSpan').textContent = uuid
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -42,6 +42,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"implicitDependencies": ["cf-page"],
|
||||
"implicitDependencies": ["cf-page-vless"],
|
||||
"tags": []
|
||||
}
|
||||
|
||||
@@ -60,6 +60,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"implicitDependencies": ["cf-page"],
|
||||
"implicitDependencies": ["cf-page-vless"],
|
||||
"tags": []
|
||||
}
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
"vnext": [
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"port": 4200,
|
||||
"port": 8788,
|
||||
"users": [
|
||||
{
|
||||
"id": "e2839021-2313-427f-977c-a1b1dec79ace",
|
||||
"id": "1a403b79-039b-4dc2-9b45-1ad13197b99a",
|
||||
"encryption": "none",
|
||||
"level": 0
|
||||
}
|
||||
@@ -41,10 +41,10 @@
|
||||
]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "ws"
|
||||
// "wsSettings": {
|
||||
// "path": "/node-vless"
|
||||
// }
|
||||
"network": "ws",
|
||||
"wsSettings": {
|
||||
"path": "/vless"
|
||||
}
|
||||
// "security": "tls"
|
||||
},
|
||||
"tag": "zizi-ws"
|
||||
@@ -57,11 +57,11 @@
|
||||
"routing": {
|
||||
"domainStrategy": "IPIfNonMatch",
|
||||
"rules": [
|
||||
// {
|
||||
// "type": "field",
|
||||
// "ip": ["8.8.8.8"],
|
||||
// "outboundTag": "zizi-ws"
|
||||
// },
|
||||
{
|
||||
"type": "field",
|
||||
"ip": ["8.8.8.8"],
|
||||
"outboundTag": "zizi-ws"
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"ip": ["geoip:private"],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { index401 } from './util';
|
||||
import { parse, stringify, validate } from 'uuid';
|
||||
const skipUrls = ['ws', 'assets', 'http2', 'connect'];
|
||||
const skipUrls = ['ws', 'assets', 'http2', 'connect', 'vless'];
|
||||
|
||||
async function errorHandling(context: EventContext<any, any, any>) {
|
||||
try {
|
||||
|
||||
177
functions/vless.ts
Normal file
177
functions/vless.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
import {
|
||||
makeReadableWebSocketStream,
|
||||
processVlessHeader,
|
||||
vlessJs,
|
||||
} from 'vless-js';
|
||||
import { connect } from 'cloudflare:sockets';
|
||||
|
||||
interface Env {
|
||||
KV: KVNamespace;
|
||||
UUID: string;
|
||||
}
|
||||
|
||||
export const onRequest: PagesFunction<Env> = async (context) => {
|
||||
let address = '';
|
||||
let portWithRandomLog = '';
|
||||
const userID = context.env['UUID'];
|
||||
|
||||
const log = (info: string, event?: any) => {
|
||||
console.log(`[${address}:${portWithRandomLog}] ${info}`, event || '');
|
||||
};
|
||||
|
||||
const upgradeHeader = context.request.headers.get('Upgrade');
|
||||
if (!upgradeHeader || upgradeHeader !== 'websocket') {
|
||||
return new Response(`Expected Upgrade: websocket--uuid--${userID}`, {
|
||||
status: 426,
|
||||
});
|
||||
}
|
||||
|
||||
const webSocketPair = new WebSocketPair();
|
||||
const [client, webSocket] = Object.values(webSocketPair);
|
||||
|
||||
const earlyDataHeader =
|
||||
context.request.headers.get('sec-websocket-protocol') || '';
|
||||
let remoteSocket: TransformStream = null;
|
||||
webSocket.accept();
|
||||
|
||||
const readableWebSocketStream = makeReadableWebSocketStream(
|
||||
webSocket,
|
||||
earlyDataHeader,
|
||||
log
|
||||
);
|
||||
let vlessResponseHeader = new Uint8Array([0, 0]);
|
||||
let remoteConnectionReadyResolve: Function;
|
||||
|
||||
// ws-->remote
|
||||
|
||||
readableWebSocketStream.pipeTo(
|
||||
new WritableStream({
|
||||
async write(chunk, controller) {
|
||||
if (remoteSocket) {
|
||||
const writer = remoteSocket.writable.getWriter();
|
||||
await writer.write(chunk);
|
||||
writer.releaseLock();
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
hasError,
|
||||
message,
|
||||
portRemote,
|
||||
addressRemote,
|
||||
rawDataIndex,
|
||||
vlessVersion,
|
||||
isUDP,
|
||||
} = processVlessHeader(chunk, userID);
|
||||
address = addressRemote || '';
|
||||
portWithRandomLog = `${portRemote}--${Math.random()} ${
|
||||
isUDP ? 'udp ' : 'tcp '
|
||||
} `;
|
||||
// if UDP but port not DNS port, close it
|
||||
if (isUDP && portRemote != 53) {
|
||||
controller.error('UDP proxy only enable for DNS which is port 53');
|
||||
webSocket.close(); // server close will not casuse worker throw error
|
||||
return;
|
||||
}
|
||||
if (hasError) {
|
||||
controller.error(message);
|
||||
webSocket.close(); // server close will not casuse worker throw error
|
||||
return;
|
||||
}
|
||||
vlessResponseHeader = new Uint8Array([vlessVersion![0], 0]);
|
||||
const rawClientData = chunk.slice(rawDataIndex!);
|
||||
remoteSocket = connect({
|
||||
hostname: addressRemote,
|
||||
port: portRemote,
|
||||
});
|
||||
log(`connected`);
|
||||
|
||||
const writer = remoteSocket.writable.getWriter();
|
||||
await writer.write(rawClientData); // first write, nomal is tls client hello
|
||||
writer.releaseLock();
|
||||
|
||||
// remoteSocket ready
|
||||
remoteConnectionReadyResolve(remoteSocket);
|
||||
},
|
||||
close() {
|
||||
console.log(
|
||||
`[${address}:${portWithRandomLog}] readableWebSocketStream is close`
|
||||
);
|
||||
},
|
||||
abort(reason) {
|
||||
console.log(
|
||||
`[${address}:${portWithRandomLog}] readableWebSocketStream is abort`,
|
||||
JSON.stringify(reason)
|
||||
);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
(async () => {
|
||||
await new Promise((resolve) => (remoteConnectionReadyResolve = resolve));
|
||||
|
||||
// remote--> ws
|
||||
let count = 0;
|
||||
remoteSocket.readable
|
||||
.pipeTo(
|
||||
new WritableStream({
|
||||
start() {
|
||||
if (webSocket.readyState === WebSocket.READY_STATE_OPEN) {
|
||||
webSocket.send(vlessResponseHeader!);
|
||||
}
|
||||
},
|
||||
async write(chunk: Uint8Array, controller) {
|
||||
if (webSocket.readyState === WebSocket.READY_STATE_OPEN) {
|
||||
if (count++ > 20000) {
|
||||
// cf one package is 4096 byte(4kb), 4096 * 20000 = 80M
|
||||
await delay(1);
|
||||
}
|
||||
webSocket.send(chunk);
|
||||
// console.log(chunk.byteLength);
|
||||
} else {
|
||||
controller.error('webSocket.readyState is not open, maybe close');
|
||||
}
|
||||
},
|
||||
close() {
|
||||
console.log(
|
||||
`[${address}:${portWithRandomLog}] remoteConnection!.readable is close`
|
||||
);
|
||||
},
|
||||
abort(reason) {
|
||||
console.error(
|
||||
`[${address}:${portWithRandomLog}] remoteConnection!.readable abort`,
|
||||
reason
|
||||
);
|
||||
},
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
`[${address}:${portWithRandomLog}] processWebSocket has exception `,
|
||||
error.stack || error
|
||||
);
|
||||
safeCloseWebSocket(webSocket);
|
||||
});
|
||||
})();
|
||||
|
||||
return new Response(null, {
|
||||
status: 101,
|
||||
webSocket: client,
|
||||
});
|
||||
};
|
||||
|
||||
function safeCloseWebSocket(ws: WebSocket) {
|
||||
try {
|
||||
if (ws.readyState !== WebSocket.READY_STATE_CLOSED) {
|
||||
ws.close();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('safeCloseWebSocket error', error);
|
||||
}
|
||||
}
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise((resolve, rej) => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
28
node-ws.mjs
Normal file
28
node-ws.mjs
Normal file
@@ -0,0 +1,28 @@
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('error', console.error);
|
||||
|
||||
ws.on('message', function message(data) {
|
||||
console.log('received: %s', data);
|
||||
if (data.toString() === 'close') {
|
||||
console.log('---------close--------');
|
||||
ws.close()
|
||||
}
|
||||
});
|
||||
ws.on('close', () => {
|
||||
console.log('-----------in close-------------close');
|
||||
console.log(ws.readyState);
|
||||
ws.send("xxxxxx")
|
||||
ws.close()
|
||||
|
||||
setTimeout(() => {
|
||||
console.log(ws.readyState);
|
||||
ws.close()
|
||||
}, 10000)
|
||||
})
|
||||
|
||||
ws.send('something');
|
||||
});
|
||||
2
nx.json
2
nx.json
@@ -50,5 +50,5 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "cf-page"
|
||||
"defaultProject": "cf-page-vless"
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
"scripts": {
|
||||
"start": "node dist/apps/node-vless/main.js",
|
||||
"build": "nx build",
|
||||
"cf-page": "nx build cf-page",
|
||||
"node-vless:build": "nx build cf-page --configuration=production && nx build node-vless --configurations=production",
|
||||
"node-vless:bunled": "nx build cf-page --configuration=production && nx build node-vless --skip-nx-cache --configurations=production -- --externalDependencies=none",
|
||||
"cf-page-vless": "wrangler pages dev dist/apps/cf-page-vless",
|
||||
"node-vless:build": "nx build cf-page-vless --configuration=production && nx build node-vless --configurations=production",
|
||||
"node-vless:bunled": "nx build cf-page-vless --configuration=production && nx build node-vless --skip-nx-cache --configurations=production -- --externalDependencies=none",
|
||||
"node-vless:start": "node dist/apps/node-vless/main.js",
|
||||
"deno-vless:bunled": "nx deno-bunled deno-vless",
|
||||
"test": "nx test",
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
// Follow this setup guide to integrate the Deno language server with your editor:
|
||||
// https://deno.land/manual/getting_started/setup_your_environment
|
||||
// This enables autocomplete, go to definition, etc.
|
||||
|
||||
import { serve } from "https://deno.land/std@0.168.0/http/server.ts"
|
||||
|
||||
console.log("Hello from Functions!")
|
||||
|
||||
serve(async (req) => {
|
||||
const socket = await Deno.connect({
|
||||
port: 443,
|
||||
hostname: 'google.com',
|
||||
})
|
||||
console.log(socket);
|
||||
return new Response(
|
||||
JSON.stringify( {
|
||||
message: `Hello from Functions!`,
|
||||
}),
|
||||
{ headers: { "Content-Type": "application/json" } },
|
||||
)
|
||||
})
|
||||
|
||||
// To invoke:
|
||||
// curl -i --location --request POST 'http://localhost:54321/functions/v1/' \
|
||||
// --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0' \
|
||||
// --header 'Content-Type: application/json' \
|
||||
// --data '{"name":"Functions"}'
|
||||
Reference in New Issue
Block a user