diff --git a/.vscode/settings.json b/.vscode/settings.json index daae9f3..1af5e57 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,7 @@ "deno.enablePaths": [ "apps/deno-bypass", "apps/deno-vless/src/deno", - "apps/deno-vless/src/main.ts" + "apps/deno-vless/src/main.ts", + "apps/deno-vless/src/deno-test.ts" ] } diff --git a/README.md b/README.md index 0a67dc7..d09fad8 100644 --- a/README.md +++ b/README.md @@ -42,15 +42,14 @@ https://blog.cloudflare.com/introducing-socket-workers/ ## V2ray Edge server --- Node.js -很多 Node.js 的平台都是支持 docker 的,所以可以直接部署原版。但是既然很多人要,我就写一个,但是我不承若一定回答关于 Node.js 平台的所有问题。因为太多了。 - -### railway.app +很多 Node.js 的平台都是支持 docker 的,所以可以直接部署原版。但是既然很多人要,我就写一个。我目前仅仅 render 平台。 ### render.com ## 客户端 v2rayN 配置 > ⚠️ 由于 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) diff --git a/apps/deno-vless/src/deno-test.ts b/apps/deno-vless/src/deno-test.ts new file mode 100644 index 0000000..de854dc --- /dev/null +++ b/apps/deno-vless/src/deno-test.ts @@ -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' }); diff --git a/apps/deno-vless/src/main.ts b/apps/deno-vless/src/main.ts index 5e1b814..4d4f543 100644 --- a/apps/deno-vless/src/main.ts +++ b/apps/deno-vless/src/main.ts @@ -29,7 +29,7 @@ const handler = async (req: Request): Promise => { const { socket, response } = Deno.upgradeWebSocket(req); socket.addEventListener('open', () => {}); - let test: Deno.TcpConn | null = null; + // let test: Deno.TcpConn | null = null; // test!.writable.abort(); // processWebSocket({ diff --git a/apps/node-vless/.eslintrc.json b/apps/node-vless/.eslintrc.json new file mode 100644 index 0000000..9d9c0db --- /dev/null +++ b/apps/node-vless/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/node-vless/jest.config.ts b/apps/node-vless/jest.config.ts new file mode 100644 index 0000000..839dfe9 --- /dev/null +++ b/apps/node-vless/jest.config.ts @@ -0,0 +1,16 @@ +/* eslint-disable */ +export default { + displayName: 'node-vless', + preset: '../../jest.preset.js', + globals: { + 'ts-jest': { + tsconfig: '/tsconfig.spec.json', + }, + }, + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': 'ts-jest', + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/apps/node-vless', +}; diff --git a/apps/node-vless/project.json b/apps/node-vless/project.json new file mode 100644 index 0000000..6e5cbb6 --- /dev/null +++ b/apps/node-vless/project.json @@ -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": [] +} diff --git a/apps/node-vless/src/app/.gitkeep b/apps/node-vless/src/app/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/apps/node-vless/src/app/utils.ts b/apps/node-vless/src/app/utils.ts new file mode 100644 index 0000000..c1b74b2 --- /dev/null +++ b/apps/node-vless/src/app/utils.ts @@ -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() +} diff --git a/apps/node-vless/src/assets/.gitkeep b/apps/node-vless/src/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/apps/node-vless/src/assets/401.html b/apps/node-vless/src/assets/401.html new file mode 100644 index 0000000..38839d5 --- /dev/null +++ b/apps/node-vless/src/assets/401.html @@ -0,0 +1,33 @@ + + + + + + + + 401 - UUID Not Valid + + + + +

Not set valid UUID in Environment Variables.

+

Please use tool to generate and remember UUID or use this one +

+

You must use same UUID for login this page after config valid UUID Environment Variables +

+

Please refer to deno + deploy guide +

+ +

Or maybe check below GIF

+ guide + + + + \ No newline at end of file diff --git a/apps/node-vless/src/environments/environment.prod.ts b/apps/node-vless/src/environments/environment.prod.ts new file mode 100644 index 0000000..c966979 --- /dev/null +++ b/apps/node-vless/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true, +}; diff --git a/apps/node-vless/src/environments/environment.ts b/apps/node-vless/src/environments/environment.ts new file mode 100644 index 0000000..a20cfe5 --- /dev/null +++ b/apps/node-vless/src/environments/environment.ts @@ -0,0 +1,3 @@ +export const environment = { + production: false, +}; diff --git a/apps/node-vless/src/main.ts b/apps/node-vless/src/main.ts new file mode 100644 index 0000000..dbf320b --- /dev/null +++ b/apps/node-vless/src/main.ts @@ -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 { + 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(''); + } + }); + }); +} diff --git a/apps/node-vless/tsconfig.app.json b/apps/node-vless/tsconfig.app.json new file mode 100644 index 0000000..0d16dce --- /dev/null +++ b/apps/node-vless/tsconfig.app.json @@ -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"] +} diff --git a/apps/node-vless/tsconfig.json b/apps/node-vless/tsconfig.json new file mode 100644 index 0000000..63dbe35 --- /dev/null +++ b/apps/node-vless/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/apps/node-vless/tsconfig.spec.json b/apps/node-vless/tsconfig.spec.json new file mode 100644 index 0000000..546f128 --- /dev/null +++ b/apps/node-vless/tsconfig.spec.json @@ -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"] +} diff --git a/doc/render.md b/doc/render.md new file mode 100644 index 0000000..a15413c --- /dev/null +++ b/doc/render.md @@ -0,0 +1,29 @@ +# Render + +## 登录 render 账户 + +https://render.com/ + +## 访问 https://dashboard.render.com/ + +## New Project + +![render1](./render1.jpg) + +## 关联 github 账户 + +![render2](./render2.jpg) + +## 部署新项目 + +需要填写如下信息,具体请参考下图. + +| 选项 | 值 | +| ------------- | --- | +| Build Command | 3 | +| Start Command | 3 | + +![render3](./render3.jpg) + +**⚠️ 添加环境变量 UUID** +![render4](./render4.jpg) diff --git a/doc/render1.jpg b/doc/render1.jpg new file mode 100644 index 0000000..aa46ad3 Binary files /dev/null and b/doc/render1.jpg differ diff --git a/doc/render2.jpg b/doc/render2.jpg new file mode 100644 index 0000000..143719d Binary files /dev/null and b/doc/render2.jpg differ diff --git a/doc/render3.jpg b/doc/render3.jpg new file mode 100644 index 0000000..3368eef Binary files /dev/null and b/doc/render3.jpg differ diff --git a/doc/render4.jpg b/doc/render4.jpg new file mode 100644 index 0000000..fc38637 Binary files /dev/null and b/doc/render4.jpg differ diff --git a/libs/vless-js/src/index.ts b/libs/vless-js/src/index.ts index 6808abf..d1be224 100644 --- a/libs/vless-js/src/index.ts +++ b/libs/vless-js/src/index.ts @@ -1 +1 @@ -export { vlessJs, processWebSocket as processSocket } from './lib/vless-js'; +export * from './lib/vless-js'; diff --git a/libs/vless-js/src/lib/vless-js.ts b/libs/vless-js/src/lib/vless-js.ts index 101e889..ba5e051 100644 --- a/libs/vless-js/src/lib/vless-js.ts +++ b/libs/vless-js/src/lib/vless-js.ts @@ -2,7 +2,7 @@ export function vlessJs(): string { return 'vless-js'; } -function delay(ms: number) { +export function delay(ms: number) { return new Promise((resolve, rej) => { setTimeout(resolve, ms); }); @@ -163,16 +163,22 @@ export async function processWebSocket({ return; } -function makeReadableWebSocketStream(ws: WebSocket, log: Function) { +export function makeReadableWebSocketStream( + ws: WebSocket | any, + log: Function +) { let readableStreamCancel = false; return new ReadableStream({ start(controller) { - ws.addEventListener('message', async (e) => { + ws.addEventListener('message', async (e: { data: ArrayBuffer }) => { + // console.log('MESSAGE'); const vlessBuffer: ArrayBuffer = e.data; + // console.log('MESSAGE', vlessBuffer); + // console.log(`message is ${vlessBuffer.byteLength}`); controller.enqueue(vlessBuffer); }); - ws.addEventListener('error', (e) => { + ws.addEventListener('error', (e: any) => { log('socket has error'); readableStreamCancel = true; 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) { 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, userID: string, uuidLib: any, diff --git a/node-test.mjs b/node-test.mjs new file mode 100644 index 0000000..7497d44 --- /dev/null +++ b/node-test.mjs @@ -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'); diff --git a/package-lock.json b/package-lock.json index 7ae85c9..1948ec2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,8 @@ "@heroicons/react": "^2.0.13", "commander": "^9.4.1", "core-js": "^3.6.5", + "lodash": "^4.17.21", + "pretty-cache-header": "^1.0.0", "qrcode": "^1.5.1", "react": "18.2.0", "react-dom": "18.2.0", @@ -20,7 +22,8 @@ "tslib": "^2.3.0", "undici": "^5.13.0", "uuid": "^9.0.0", - "wrangler": "^2.6.2" + "wrangler": "^2.6.2", + "ws": "^8.12.0" }, "devDependencies": { "@babel/preset-react": "^7.14.5", @@ -39,6 +42,7 @@ "@nrwl/workspace": "15.2.4", "@testing-library/react": "13.4.0", "@types/jest": "28.1.1", + "@types/lodash": "^4.14.191", "@types/node": "18.11.9", "@types/qrcode": "^1.5.0", "@types/react": "18.0.25", @@ -5857,6 +5861,12 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "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": { "version": "3.0.1", "resolved": "https://registry.npmmirror.com/@types/mime/-/mime-3.0.1.tgz", @@ -13487,8 +13497,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.camelcase": { "version": "4.3.0", @@ -15896,6 +15905,17 @@ "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": { "version": "28.1.3", "resolved": "https://registry.npmmirror.com/pretty-format/-/pretty-format-28.1.3.tgz", @@ -18099,6 +18119,14 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "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": { "version": "2.3.1", "resolved": "https://registry.npmmirror.com/tinybench/-/tinybench-2.3.1.tgz", @@ -19759,16 +19787,15 @@ } }, "node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmmirror.com/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, + "version": "8.12.0", + "resolved": "https://registry.npmmirror.com/ws/-/ws-8.12.0.tgz", + "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -24444,6 +24471,12 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "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": { "version": "3.0.1", "resolved": "https://registry.npmmirror.com/@types/mime/-/mime-3.0.1.tgz", @@ -30522,8 +30555,7 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.camelcase": { "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": { "version": "28.1.3", "resolved": "https://registry.npmmirror.com/pretty-format/-/pretty-format-28.1.3.tgz", @@ -34134,6 +34174,11 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, + "timestring": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/timestring/-/timestring-6.0.0.tgz", + "integrity": "sha512-wMctrWD2HZZLuIlchlkE2dfXJh7J2KDI9Dwl+2abPYg0mswQHfOAyQW3jJg1pY5VfttSINZuKcXoB3FGypVklA==" + }, "tinybench": { "version": "2.3.1", "resolved": "https://registry.npmmirror.com/tinybench/-/tinybench-2.3.1.tgz", @@ -35249,10 +35294,9 @@ } }, "ws": { - "version": "8.11.0", - "resolved": "https://registry.npmmirror.com/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, + "version": "8.12.0", + "resolved": "https://registry.npmmirror.com/ws/-/ws-8.12.0.tgz", + "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", "requires": {} }, "xml-name-validator": { diff --git a/package.json b/package.json index f518417..eea55b5 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,15 @@ "name": "edge-bypass", "version": "0.0.0", "license": "MIT", + "engines": { + "node": "^18" + }, "scripts": { "start": "nx serve", "build": "nx build", "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" }, "private": true, @@ -14,6 +19,8 @@ "@heroicons/react": "^2.0.13", "commander": "^9.4.1", "core-js": "^3.6.5", + "lodash": "^4.17.21", + "pretty-cache-header": "^1.0.0", "qrcode": "^1.5.1", "react": "18.2.0", "react-dom": "18.2.0", @@ -21,7 +28,8 @@ "tslib": "^2.3.0", "undici": "^5.13.0", "uuid": "^9.0.0", - "wrangler": "^2.6.2" + "wrangler": "^2.6.2", + "ws": "^8.12.0" }, "devDependencies": { "@babel/preset-react": "^7.14.5", @@ -40,6 +48,7 @@ "@nrwl/workspace": "15.2.4", "@testing-library/react": "13.4.0", "@types/jest": "28.1.1", + "@types/lodash": "^4.14.191", "@types/node": "18.11.9", "@types/qrcode": "^1.5.0", "@types/react": "18.0.25",