mirror of
https://github.com/lush2020/edgetunnel.git
synced 2026-03-24 09:08:16 +08:00
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -4,6 +4,7 @@
|
|||||||
"deno.enablePaths": [
|
"deno.enablePaths": [
|
||||||
"apps/deno-bypass",
|
"apps/deno-bypass",
|
||||||
"apps/deno-vless/src/deno",
|
"apps/deno-vless/src/deno",
|
||||||
"apps/deno-vless/src/main.ts"
|
"apps/deno-vless/src/main.ts",
|
||||||
|
"apps/deno-vless/src/deno-test.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,15 +42,14 @@ https://blog.cloudflare.com/introducing-socket-workers/
|
|||||||
|
|
||||||
## V2ray Edge server --- Node.js
|
## V2ray Edge server --- Node.js
|
||||||
|
|
||||||
很多 Node.js 的平台都是支持 docker 的,所以可以直接部署原版。但是既然很多人要,我就写一个,但是我不承若一定回答关于 Node.js 平台的所有问题。因为太多了。
|
很多 Node.js 的平台都是支持 docker 的,所以可以直接部署原版。但是既然很多人要,我就写一个。我目前仅仅 render 平台。
|
||||||
|
|
||||||
### railway.app
|
|
||||||
|
|
||||||
### render.com
|
### render.com
|
||||||
|
|
||||||
## 客户端 v2rayN 配置
|
## 客户端 v2rayN 配置
|
||||||
|
|
||||||
> ⚠️ 由于 edge 平台限制,无法转发 UDP 包。请在配置时候,把 DNS 的策略改成 "Asis", 否则会影响速度。
|
> ⚠️ 由于 edge 平台限制,无法转发 UDP 包。请在配置时候,把 DNS 的策略改成 "Asis", 否则会影响速度。
|
||||||
|
> 请不要开启 ipv6 优先。
|
||||||
|
|
||||||
> [ DNS 科普文章](https://tachyondevel.medium.com/%E6%BC%AB%E8%B0%88%E5%90%84%E7%A7%8D%E9%BB%91%E7%A7%91%E6%8A%80%E5%BC%8F-dns-%E6%8A%80%E6%9C%AF%E5%9C%A8%E4%BB%A3%E7%90%86%E7%8E%AF%E5%A2%83%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8-62c50e58cbd0)
|
> [ DNS 科普文章](https://tachyondevel.medium.com/%E6%BC%AB%E8%B0%88%E5%90%84%E7%A7%8D%E9%BB%91%E7%A7%91%E6%8A%80%E5%BC%8F-dns-%E6%8A%80%E6%9C%AF%E5%9C%A8%E4%BB%A3%E7%90%86%E7%8E%AF%E5%A2%83%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8-62c50e58cbd0)
|
||||||
|
|
||||||
|
|||||||
16
apps/deno-vless/src/deno-test.ts
Normal file
16
apps/deno-vless/src/deno-test.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { serve } from 'https://deno.land/std@0.170.0/http/server.ts';
|
||||||
|
|
||||||
|
const handler = async (req: Request) => {
|
||||||
|
console.log('start');
|
||||||
|
|
||||||
|
const connect = await Deno.connect({
|
||||||
|
port: 443,
|
||||||
|
hostname: '2606:4700:0000:0000:0000:0000:6810:7c60',
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(connect.remoteAddr);
|
||||||
|
return new Response('hello', {
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
serve(handler, { port: 8081, hostname: '0.0.0.0' });
|
||||||
@@ -29,7 +29,7 @@ const handler = async (req: Request): Promise<Response> => {
|
|||||||
const { socket, response } = Deno.upgradeWebSocket(req);
|
const { socket, response } = Deno.upgradeWebSocket(req);
|
||||||
socket.addEventListener('open', () => {});
|
socket.addEventListener('open', () => {});
|
||||||
|
|
||||||
let test: Deno.TcpConn | null = null;
|
// let test: Deno.TcpConn | null = null;
|
||||||
// test!.writable.abort();
|
// test!.writable.abort();
|
||||||
//
|
//
|
||||||
processWebSocket({
|
processWebSocket({
|
||||||
|
|||||||
18
apps/node-vless/.eslintrc.json
Normal file
18
apps/node-vless/.eslintrc.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"extends": ["../../.eslintrc.json"],
|
||||||
|
"ignorePatterns": ["!**/*"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx"],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.js", "*.jsx"],
|
||||||
|
"rules": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
16
apps/node-vless/jest.config.ts
Normal file
16
apps/node-vless/jest.config.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
export default {
|
||||||
|
displayName: 'node-vless',
|
||||||
|
preset: '../../jest.preset.js',
|
||||||
|
globals: {
|
||||||
|
'ts-jest': {
|
||||||
|
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testEnvironment: 'node',
|
||||||
|
transform: {
|
||||||
|
'^.+\\.[tj]s$': 'ts-jest',
|
||||||
|
},
|
||||||
|
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||||
|
coverageDirectory: '../../coverage/apps/node-vless',
|
||||||
|
};
|
||||||
61
apps/node-vless/project.json
Normal file
61
apps/node-vless/project.json
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"name": "node-vless",
|
||||||
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
|
"sourceRoot": "apps/node-vless/src",
|
||||||
|
"projectType": "application",
|
||||||
|
"targets": {
|
||||||
|
"build": {
|
||||||
|
"executor": "@nrwl/webpack:webpack",
|
||||||
|
"outputs": ["{options.outputPath}"],
|
||||||
|
"options": {
|
||||||
|
"target": "node",
|
||||||
|
"compiler": "tsc",
|
||||||
|
"outputPath": "dist/apps/node-vless",
|
||||||
|
"main": "apps/node-vless/src/main.ts",
|
||||||
|
"tsConfig": "apps/node-vless/tsconfig.app.json",
|
||||||
|
"assets": ["apps/node-vless/src/assets"]
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"optimization": true,
|
||||||
|
"extractLicenses": true,
|
||||||
|
"inspect": false,
|
||||||
|
"fileReplacements": [
|
||||||
|
{
|
||||||
|
"replace": "apps/node-vless/src/environments/environment.ts",
|
||||||
|
"with": "apps/node-vless/src/environments/environment.prod.ts"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"executor": "@nrwl/js:node",
|
||||||
|
"options": {
|
||||||
|
"buildTarget": "node-vless:build"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"buildTarget": "node-vless:build:production"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"executor": "@nrwl/linter:eslint",
|
||||||
|
"outputs": ["{options.outputFile}"],
|
||||||
|
"options": {
|
||||||
|
"lintFilePatterns": ["apps/node-vless/**/*.ts"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"executor": "@nrwl/jest:jest",
|
||||||
|
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||||
|
"options": {
|
||||||
|
"jestConfig": "apps/node-vless/jest.config.ts",
|
||||||
|
"passWithNoTests": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"implicitDependencies": ["cf-page"],
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
0
apps/node-vless/src/app/.gitkeep
Normal file
0
apps/node-vless/src/app/.gitkeep
Normal file
61
apps/node-vless/src/app/utils.ts
Normal file
61
apps/node-vless/src/app/utils.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { createReadStream, existsSync } from 'node:fs';
|
||||||
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
||||||
|
import { resolve, join, extname } from 'node:path';
|
||||||
|
import { cacheHeader } from 'pretty-cache-header';
|
||||||
|
|
||||||
|
const mimeLookup = {
|
||||||
|
'.js': 'application/javascript,charset=UTF-8',
|
||||||
|
'.html': 'text/html,charset=UTF-8',
|
||||||
|
'.css': 'text/css; charset=UTF-8',
|
||||||
|
};
|
||||||
|
const staticPath = 'dist/apps/cf-page/';
|
||||||
|
const file401 = 'dist/apps/node-vless/assets/401.html';
|
||||||
|
let filepath = null;
|
||||||
|
export function serverStaticFile(req: IncomingMessage, resp: ServerResponse) {
|
||||||
|
const url = new URL(req.url, `http://${req.headers['host']}`);
|
||||||
|
let fileurl = url.pathname;
|
||||||
|
fileurl = join(staticPath, fileurl);
|
||||||
|
console.log('....', fileurl);
|
||||||
|
filepath = resolve(fileurl);
|
||||||
|
console.log(filepath);
|
||||||
|
|
||||||
|
if (existsSync(filepath)) {
|
||||||
|
let fileExt = extname(filepath);
|
||||||
|
console.log('fileExt', fileExt);
|
||||||
|
let mimeType = mimeLookup[fileExt];
|
||||||
|
|
||||||
|
resp.writeHead(200, {
|
||||||
|
'Content-Type': mimeType,
|
||||||
|
'Cache-Control': cacheHeader({
|
||||||
|
public: true,
|
||||||
|
maxAge: '1year',
|
||||||
|
staleWhileRevalidate: '1year',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
return createReadStream(filepath).pipe(resp);
|
||||||
|
} else {
|
||||||
|
resp.writeHead(404);
|
||||||
|
resp.write('not found');
|
||||||
|
resp.end();
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function index401(req: IncomingMessage, resp: ServerResponse) {
|
||||||
|
const file401Path = resolve(file401);
|
||||||
|
if (existsSync(file401Path)) {
|
||||||
|
createReadStream(file401Path).pipe(resp);
|
||||||
|
} else {
|
||||||
|
resp.writeHead(401);
|
||||||
|
resp.write('UUID env not set');
|
||||||
|
resp.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serverIndexPage(
|
||||||
|
req: IncomingMessage,
|
||||||
|
resp: ServerResponse,
|
||||||
|
uuid
|
||||||
|
) {
|
||||||
|
// if()
|
||||||
|
}
|
||||||
0
apps/node-vless/src/assets/.gitkeep
Normal file
0
apps/node-vless/src/assets/.gitkeep
Normal file
33
apps/node-vless/src/assets/401.html
Normal file
33
apps/node-vless/src/assets/401.html
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<!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>
|
||||||
3
apps/node-vless/src/environments/environment.prod.ts
Normal file
3
apps/node-vless/src/environments/environment.prod.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export const environment = {
|
||||||
|
production: true,
|
||||||
|
};
|
||||||
3
apps/node-vless/src/environments/environment.ts
Normal file
3
apps/node-vless/src/environments/environment.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export const environment = {
|
||||||
|
production: false,
|
||||||
|
};
|
||||||
219
apps/node-vless/src/main.ts
Normal file
219
apps/node-vless/src/main.ts
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
import { createServer } from 'http';
|
||||||
|
import { parse } from 'url';
|
||||||
|
import { WebSocketServer, WebSocket } from 'ws';
|
||||||
|
import { index401, serverStaticFile } from './app/utils';
|
||||||
|
import * as uuid from 'uuid';
|
||||||
|
import * as lodash from 'lodash';
|
||||||
|
import { createReadStream } from 'node:fs';
|
||||||
|
import {
|
||||||
|
makeReadableWebSocketStream,
|
||||||
|
processVlessHeader,
|
||||||
|
delay,
|
||||||
|
closeWebSocket,
|
||||||
|
} from 'vless-js';
|
||||||
|
import { connect, Socket } from 'node:net';
|
||||||
|
import { Duplex, Readable } from 'stream';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
|
||||||
|
const port = process.env.PORT;
|
||||||
|
const userID = process.env.UUID || '';
|
||||||
|
let isVaildUser = uuid.validate(userID);
|
||||||
|
if (!isVaildUser) {
|
||||||
|
console.log('not set valid UUID');
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = createServer((req, resp) => {
|
||||||
|
if (!isVaildUser) {
|
||||||
|
return index401(req, resp);
|
||||||
|
}
|
||||||
|
const url = new URL(req.url, `http://${req.headers['host']}`);
|
||||||
|
// health check
|
||||||
|
if (req.method === 'GET' && url.pathname.startsWith('/health')) {
|
||||||
|
resp.writeHead(200);
|
||||||
|
resp.write('health 200');
|
||||||
|
resp.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// index page
|
||||||
|
if (url.pathname.includes(userID)) {
|
||||||
|
const index = 'dist/apps/cf-page/index.html';
|
||||||
|
return createReadStream(index).pipe(resp);
|
||||||
|
}
|
||||||
|
if (req.method === 'GET' && url.pathname.startsWith('/assets')) {
|
||||||
|
return serverStaticFile(req, resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
const basicAuth = req.headers.authorization || '';
|
||||||
|
const authStringBase64 = basicAuth.split(' ')?.[1] || '';
|
||||||
|
const authString = Buffer.from(authStringBase64, 'base64').toString('ascii');
|
||||||
|
console.log('-----authString--', authString);
|
||||||
|
if (authString && authString.includes(userID)) {
|
||||||
|
resp.writeHead(302, {
|
||||||
|
'content-type': 'text/html; charset=utf-8',
|
||||||
|
Location: `./${userID}`,
|
||||||
|
});
|
||||||
|
resp.end();
|
||||||
|
} else {
|
||||||
|
resp.writeHead(401, {
|
||||||
|
'content-type': 'text/html; charset=utf-8',
|
||||||
|
'WWW-Authenticate': 'Basic',
|
||||||
|
});
|
||||||
|
resp.end();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const vlessWServer = new WebSocketServer({ noServer: true });
|
||||||
|
|
||||||
|
vlessWServer.on('connection', async function connection(ws) {
|
||||||
|
let address = '';
|
||||||
|
let portWithRandomLog = '';
|
||||||
|
try {
|
||||||
|
const log = (info: string, event?: any) => {
|
||||||
|
console.log(`[${address}:${portWithRandomLog}] ${info}`, event || '');
|
||||||
|
};
|
||||||
|
let remoteConnection: Socket = null;
|
||||||
|
let remoteConnectionReadyResolve: Function;
|
||||||
|
|
||||||
|
const readableWebSocketStream = makeReadableWebSocketStream(ws, log);
|
||||||
|
let vlessResponseHeader: Uint8Array | null = null;
|
||||||
|
|
||||||
|
// ws --> remote
|
||||||
|
readableWebSocketStream
|
||||||
|
.pipeTo(
|
||||||
|
new WritableStream({
|
||||||
|
async write(chunk: Buffer, controller) {
|
||||||
|
const vlessBuffer = chunk.buffer.slice(chunk.byteOffset);
|
||||||
|
if (remoteConnection) {
|
||||||
|
await wsAsyncWrite(remoteConnection, vlessBuffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
hasError,
|
||||||
|
message,
|
||||||
|
portRemote,
|
||||||
|
addressRemote,
|
||||||
|
rawDataIndex,
|
||||||
|
vlessVersion,
|
||||||
|
} = processVlessHeader(vlessBuffer, userID, uuid, lodash);
|
||||||
|
address = addressRemote || '';
|
||||||
|
portWithRandomLog = `${portRemote}--${Math.random()}`;
|
||||||
|
if (hasError) {
|
||||||
|
controller.error(`[${address}:${portWithRandomLog}] ${message} `);
|
||||||
|
}
|
||||||
|
// const addressType = requestAddr >> 42
|
||||||
|
// const addressLength = requestAddr & 0x0f;
|
||||||
|
console.log(`[${address}:${portWithRandomLog}] connecting`);
|
||||||
|
remoteConnection = await connect2Remote(portRemote, address, log);
|
||||||
|
vlessResponseHeader = new Uint8Array([vlessVersion![0], 0]);
|
||||||
|
|
||||||
|
const rawClientData = vlessBuffer.slice(rawDataIndex!);
|
||||||
|
remoteConnection.write(new Uint8Array(rawClientData));
|
||||||
|
remoteConnectionReadyResolve(remoteConnection);
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
console.log(
|
||||||
|
`[${address}:${portWithRandomLog}] readableWebSocketStream is close`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
abort(reason) {
|
||||||
|
console.log(
|
||||||
|
`[${address}:${portWithRandomLog}] readableWebSocketStream is abort`,
|
||||||
|
JSON.stringify(reason)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(
|
||||||
|
`[${address}:${portWithRandomLog}] readableWebSocketStream pipeto has exception`,
|
||||||
|
error.stack || error
|
||||||
|
);
|
||||||
|
// error is cancel readable stream anyway, no need close websocket in here
|
||||||
|
// closeWebSocket(webSocket);
|
||||||
|
// close remote conn
|
||||||
|
// remoteConnection?.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
await new Promise((resolve) => (remoteConnectionReadyResolve = resolve));
|
||||||
|
// remote --> ws
|
||||||
|
let remoteChunkCount = 0;
|
||||||
|
let totoal = 0;
|
||||||
|
await Readable.toWeb(remoteConnection).pipeTo(
|
||||||
|
new WritableStream({
|
||||||
|
start() {
|
||||||
|
if (ws.readyState === ws.OPEN) {
|
||||||
|
ws.send(vlessResponseHeader!);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async write(chunk: Uint8Array, controller) {
|
||||||
|
ws.send(chunk);
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
console.log(
|
||||||
|
`[${address}:${portWithRandomLog}] remoteConnection!.readable is close`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
abort(reason) {
|
||||||
|
closeWebSocket(ws);
|
||||||
|
console.error(
|
||||||
|
`[${address}:${portWithRandomLog}] remoteConnection!.readable abort`,
|
||||||
|
reason
|
||||||
|
);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`[${address}:${portWithRandomLog}] processWebSocket has exception `,
|
||||||
|
error.stack || error
|
||||||
|
);
|
||||||
|
closeWebSocket(ws);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('upgrade', function upgrade(request, socket, head) {
|
||||||
|
console.log('upgrade');
|
||||||
|
const { pathname } = parse(request.url);
|
||||||
|
|
||||||
|
if (pathname === '/foo') {
|
||||||
|
vlessWServer.handleUpgrade(request, socket, head, function done(ws) {
|
||||||
|
vlessWServer.emit('connection', ws, request);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
socket.destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(port, () => {
|
||||||
|
console.log(`server listen in http://127.0.0.1:${port}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function connect2Remote(port, host, log: Function): Promise<Socket> {
|
||||||
|
return new Promise((resole, reject) => {
|
||||||
|
const remoteSocket = connect(
|
||||||
|
{
|
||||||
|
port: port,
|
||||||
|
host: host,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
log(`connected`);
|
||||||
|
resole(remoteSocket);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
remoteSocket.addListener('error', () => {
|
||||||
|
reject('remoteSocket has error');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function wsAsyncWrite(ws: Socket, chunk: ArrayBuffer) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
ws.write(Buffer.from(chunk), (error) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
10
apps/node-vless/tsconfig.app.json
Normal file
10
apps/node-vless/tsconfig.app.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"module": "commonjs",
|
||||||
|
"types": ["node"]
|
||||||
|
},
|
||||||
|
"exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts"],
|
||||||
|
"include": ["**/*.ts"]
|
||||||
|
}
|
||||||
13
apps/node-vless/tsconfig.json
Normal file
13
apps/node-vless/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"files": [],
|
||||||
|
"include": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.spec.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
9
apps/node-vless/tsconfig.spec.json
Normal file
9
apps/node-vless/tsconfig.spec.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"module": "commonjs",
|
||||||
|
"types": ["jest", "node"]
|
||||||
|
},
|
||||||
|
"include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"]
|
||||||
|
}
|
||||||
29
doc/render.md
Normal file
29
doc/render.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Render
|
||||||
|
|
||||||
|
## 登录 render 账户
|
||||||
|
|
||||||
|
https://render.com/
|
||||||
|
|
||||||
|
## 访问 https://dashboard.render.com/
|
||||||
|
|
||||||
|
## New Project
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 关联 github 账户
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 部署新项目
|
||||||
|
|
||||||
|
需要填写如下信息,具体请参考下图.
|
||||||
|
|
||||||
|
| 选项 | 值 |
|
||||||
|
| ------------- | --- |
|
||||||
|
| Build Command | 3 |
|
||||||
|
| Start Command | 3 |
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**⚠️ 添加环境变量 UUID**
|
||||||
|

|
||||||
BIN
doc/render1.jpg
Normal file
BIN
doc/render1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
BIN
doc/render2.jpg
Normal file
BIN
doc/render2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 54 KiB |
BIN
doc/render3.jpg
Normal file
BIN
doc/render3.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 207 KiB |
BIN
doc/render4.jpg
Normal file
BIN
doc/render4.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 136 KiB |
@@ -1 +1 @@
|
|||||||
export { vlessJs, processWebSocket as processSocket } from './lib/vless-js';
|
export * from './lib/vless-js';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ export function vlessJs(): string {
|
|||||||
return 'vless-js';
|
return 'vless-js';
|
||||||
}
|
}
|
||||||
|
|
||||||
function delay(ms: number) {
|
export function delay(ms: number) {
|
||||||
return new Promise((resolve, rej) => {
|
return new Promise((resolve, rej) => {
|
||||||
setTimeout(resolve, ms);
|
setTimeout(resolve, ms);
|
||||||
});
|
});
|
||||||
@@ -163,16 +163,22 @@ export async function processWebSocket({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeReadableWebSocketStream(ws: WebSocket, log: Function) {
|
export function makeReadableWebSocketStream(
|
||||||
|
ws: WebSocket | any,
|
||||||
|
log: Function
|
||||||
|
) {
|
||||||
let readableStreamCancel = false;
|
let readableStreamCancel = false;
|
||||||
return new ReadableStream<ArrayBuffer>({
|
return new ReadableStream<ArrayBuffer>({
|
||||||
start(controller) {
|
start(controller) {
|
||||||
ws.addEventListener('message', async (e) => {
|
ws.addEventListener('message', async (e: { data: ArrayBuffer }) => {
|
||||||
|
// console.log('MESSAGE');
|
||||||
const vlessBuffer: ArrayBuffer = e.data;
|
const vlessBuffer: ArrayBuffer = e.data;
|
||||||
|
// console.log('MESSAGE', vlessBuffer);
|
||||||
|
|
||||||
// console.log(`message is ${vlessBuffer.byteLength}`);
|
// console.log(`message is ${vlessBuffer.byteLength}`);
|
||||||
controller.enqueue(vlessBuffer);
|
controller.enqueue(vlessBuffer);
|
||||||
});
|
});
|
||||||
ws.addEventListener('error', (e) => {
|
ws.addEventListener('error', (e: any) => {
|
||||||
log('socket has error');
|
log('socket has error');
|
||||||
readableStreamCancel = true;
|
readableStreamCancel = true;
|
||||||
controller.error(e);
|
controller.error(e);
|
||||||
@@ -202,13 +208,19 @@ function makeReadableWebSocketStream(ws: WebSocket, log: Function) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeWebSocket(socket: WebSocket) {
|
export function closeWebSocket(socket: WebSocket | any) {
|
||||||
if (socket.readyState === socket.OPEN) {
|
if (socket.readyState === socket.OPEN) {
|
||||||
socket.close();
|
socket.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processVlessHeader(
|
//https://github.com/v2ray/v2ray-core/issues/2636
|
||||||
|
// 1 字节 16 字节 1 字节 M 字节 1 字节 2 字节 1 字节 S 字节 X 字节
|
||||||
|
// 协议版本 等价 UUID 附加信息长度 M 附加信息 ProtoBuf 指令 端口 地址类型 地址 请求数据
|
||||||
|
|
||||||
|
// 1 字节 1 字节 N 字节 Y 字节
|
||||||
|
// 协议版本,与请求的一致 附加信息长度 N 附加信息 ProtoBuf 响应数据
|
||||||
|
export function processVlessHeader(
|
||||||
vlessBuffer: ArrayBuffer,
|
vlessBuffer: ArrayBuffer,
|
||||||
userID: string,
|
userID: string,
|
||||||
uuidLib: any,
|
uuidLib: any,
|
||||||
|
|||||||
19
node-test.mjs
Normal file
19
node-test.mjs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { connect, Socket } from 'node:net';
|
||||||
|
import { Duplex } from 'node:stream';
|
||||||
|
import { WritableStream } from 'node:stream/web';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const socket = connect(
|
||||||
|
{
|
||||||
|
port: '443',
|
||||||
|
host: 'www.google.com',
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
console.log('connect ', socket.readyState);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.log('----', err);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('end');
|
||||||
72
package-lock.json
generated
72
package-lock.json
generated
@@ -13,6 +13,8 @@
|
|||||||
"@heroicons/react": "^2.0.13",
|
"@heroicons/react": "^2.0.13",
|
||||||
"commander": "^9.4.1",
|
"commander": "^9.4.1",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"pretty-cache-header": "^1.0.0",
|
||||||
"qrcode": "^1.5.1",
|
"qrcode": "^1.5.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
@@ -20,7 +22,8 @@
|
|||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"undici": "^5.13.0",
|
"undici": "^5.13.0",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"wrangler": "^2.6.2"
|
"wrangler": "^2.6.2",
|
||||||
|
"ws": "^8.12.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/preset-react": "^7.14.5",
|
"@babel/preset-react": "^7.14.5",
|
||||||
@@ -39,6 +42,7 @@
|
|||||||
"@nrwl/workspace": "15.2.4",
|
"@nrwl/workspace": "15.2.4",
|
||||||
"@testing-library/react": "13.4.0",
|
"@testing-library/react": "13.4.0",
|
||||||
"@types/jest": "28.1.1",
|
"@types/jest": "28.1.1",
|
||||||
|
"@types/lodash": "^4.14.191",
|
||||||
"@types/node": "18.11.9",
|
"@types/node": "18.11.9",
|
||||||
"@types/qrcode": "^1.5.0",
|
"@types/qrcode": "^1.5.0",
|
||||||
"@types/react": "18.0.25",
|
"@types/react": "18.0.25",
|
||||||
@@ -5857,6 +5861,12 @@
|
|||||||
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
|
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/lodash": {
|
||||||
|
"version": "4.14.191",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.191.tgz",
|
||||||
|
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/mime": {
|
"node_modules/@types/mime": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/@types/mime/-/mime-3.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/@types/mime/-/mime-3.0.1.tgz",
|
||||||
@@ -13487,8 +13497,7 @@
|
|||||||
"node_modules/lodash": {
|
"node_modules/lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/lodash.camelcase": {
|
"node_modules/lodash.camelcase": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
@@ -15896,6 +15905,17 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pretty-cache-header": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/pretty-cache-header/-/pretty-cache-header-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-xtXazslu25CdnGnUkByU1RoOjK55TqwatJkjjJLg5ZAdz2Lngko/mmaUgeET36P2GMlNwh3fdM7FWBO717pNcw==",
|
||||||
|
"dependencies": {
|
||||||
|
"timestring": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pretty-format": {
|
"node_modules/pretty-format": {
|
||||||
"version": "28.1.3",
|
"version": "28.1.3",
|
||||||
"resolved": "https://registry.npmmirror.com/pretty-format/-/pretty-format-28.1.3.tgz",
|
"resolved": "https://registry.npmmirror.com/pretty-format/-/pretty-format-28.1.3.tgz",
|
||||||
@@ -18099,6 +18119,14 @@
|
|||||||
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/timestring": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/timestring/-/timestring-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-wMctrWD2HZZLuIlchlkE2dfXJh7J2KDI9Dwl+2abPYg0mswQHfOAyQW3jJg1pY5VfttSINZuKcXoB3FGypVklA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tinybench": {
|
"node_modules/tinybench": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmmirror.com/tinybench/-/tinybench-2.3.1.tgz",
|
"resolved": "https://registry.npmmirror.com/tinybench/-/tinybench-2.3.1.tgz",
|
||||||
@@ -19759,16 +19787,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ws": {
|
"node_modules/ws": {
|
||||||
"version": "8.11.0",
|
"version": "8.12.0",
|
||||||
"resolved": "https://registry.npmmirror.com/ws/-/ws-8.11.0.tgz",
|
"resolved": "https://registry.npmmirror.com/ws/-/ws-8.12.0.tgz",
|
||||||
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
"integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"bufferutil": "^4.0.1",
|
"bufferutil": "^4.0.1",
|
||||||
"utf-8-validate": "^5.0.2"
|
"utf-8-validate": ">=5.0.2"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"bufferutil": {
|
"bufferutil": {
|
||||||
@@ -24444,6 +24471,12 @@
|
|||||||
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
|
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/lodash": {
|
||||||
|
"version": "4.14.191",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.191.tgz",
|
||||||
|
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/mime": {
|
"@types/mime": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/@types/mime/-/mime-3.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/@types/mime/-/mime-3.0.1.tgz",
|
||||||
@@ -30522,8 +30555,7 @@
|
|||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"lodash.camelcase": {
|
"lodash.camelcase": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
@@ -32325,6 +32357,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"pretty-cache-header": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/pretty-cache-header/-/pretty-cache-header-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-xtXazslu25CdnGnUkByU1RoOjK55TqwatJkjjJLg5ZAdz2Lngko/mmaUgeET36P2GMlNwh3fdM7FWBO717pNcw==",
|
||||||
|
"requires": {
|
||||||
|
"timestring": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"pretty-format": {
|
"pretty-format": {
|
||||||
"version": "28.1.3",
|
"version": "28.1.3",
|
||||||
"resolved": "https://registry.npmmirror.com/pretty-format/-/pretty-format-28.1.3.tgz",
|
"resolved": "https://registry.npmmirror.com/pretty-format/-/pretty-format-28.1.3.tgz",
|
||||||
@@ -34134,6 +34174,11 @@
|
|||||||
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"timestring": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/timestring/-/timestring-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-wMctrWD2HZZLuIlchlkE2dfXJh7J2KDI9Dwl+2abPYg0mswQHfOAyQW3jJg1pY5VfttSINZuKcXoB3FGypVklA=="
|
||||||
|
},
|
||||||
"tinybench": {
|
"tinybench": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmmirror.com/tinybench/-/tinybench-2.3.1.tgz",
|
"resolved": "https://registry.npmmirror.com/tinybench/-/tinybench-2.3.1.tgz",
|
||||||
@@ -35249,10 +35294,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "8.11.0",
|
"version": "8.12.0",
|
||||||
"resolved": "https://registry.npmmirror.com/ws/-/ws-8.11.0.tgz",
|
"resolved": "https://registry.npmmirror.com/ws/-/ws-8.12.0.tgz",
|
||||||
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
"integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==",
|
||||||
"dev": true,
|
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"xml-name-validator": {
|
"xml-name-validator": {
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -2,10 +2,15 @@
|
|||||||
"name": "edge-bypass",
|
"name": "edge-bypass",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^18"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "nx serve",
|
"start": "nx serve",
|
||||||
"build": "nx build",
|
"build": "nx build",
|
||||||
"cf-page": "nx build cf-page",
|
"cf-page": "nx build cf-page",
|
||||||
|
"node-vless:build": "nx build cf-page --configuration=production && nx build node-vless",
|
||||||
|
"node-vless:start": "node dist/apps/node-vless/main.js",
|
||||||
"test": "nx test"
|
"test": "nx test"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
@@ -14,6 +19,8 @@
|
|||||||
"@heroicons/react": "^2.0.13",
|
"@heroicons/react": "^2.0.13",
|
||||||
"commander": "^9.4.1",
|
"commander": "^9.4.1",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"pretty-cache-header": "^1.0.0",
|
||||||
"qrcode": "^1.5.1",
|
"qrcode": "^1.5.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
@@ -21,7 +28,8 @@
|
|||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"undici": "^5.13.0",
|
"undici": "^5.13.0",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"wrangler": "^2.6.2"
|
"wrangler": "^2.6.2",
|
||||||
|
"ws": "^8.12.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/preset-react": "^7.14.5",
|
"@babel/preset-react": "^7.14.5",
|
||||||
@@ -40,6 +48,7 @@
|
|||||||
"@nrwl/workspace": "15.2.4",
|
"@nrwl/workspace": "15.2.4",
|
||||||
"@testing-library/react": "13.4.0",
|
"@testing-library/react": "13.4.0",
|
||||||
"@types/jest": "28.1.1",
|
"@types/jest": "28.1.1",
|
||||||
|
"@types/lodash": "^4.14.191",
|
||||||
"@types/node": "18.11.9",
|
"@types/node": "18.11.9",
|
||||||
"@types/qrcode": "^1.5.0",
|
"@types/qrcode": "^1.5.0",
|
||||||
"@types/react": "18.0.25",
|
"@types/react": "18.0.25",
|
||||||
|
|||||||
Reference in New Issue
Block a user