fix: 配置修改,添加socket代理
This commit is contained in:
parent
d548d3ab04
commit
bb86a7c507
@ -1,8 +1,18 @@
|
|||||||
{
|
{
|
||||||
api: {
|
api: {
|
||||||
host: 'localhost:4002', // 后台代理
|
target: 'http://localhost:4002', // 后台代理
|
||||||
path: '/api/router',
|
path: '/api/router',
|
||||||
},
|
},
|
||||||
|
apiList: [
|
||||||
|
{
|
||||||
|
path: '/api/router',
|
||||||
|
target: 'http://localhost:4002',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/v1',
|
||||||
|
target: 'http://localhost:3005',
|
||||||
|
},
|
||||||
|
],
|
||||||
proxy: {
|
proxy: {
|
||||||
port: 3005,
|
port: 3005,
|
||||||
domain: 'kevisual.xiongxiao.me',
|
domain: 'kevisual.xiongxiao.me',
|
||||||
|
21
package.json
21
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "page-proxy",
|
"name": "page-proxy",
|
||||||
"version": "0.0.2-beta.1",
|
"version": "0.0.2-beta.3",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@ -35,22 +35,25 @@
|
|||||||
"@rollup/plugin-node-resolve": "^16.0.0",
|
"@rollup/plugin-node-resolve": "^16.0.0",
|
||||||
"@rollup/plugin-typescript": "^12.1.2",
|
"@rollup/plugin-typescript": "^12.1.2",
|
||||||
"@types/http-proxy": "^1.17.16",
|
"@types/http-proxy": "^1.17.16",
|
||||||
"@types/node": "^22.13.5",
|
"@types/node": "^22.13.9",
|
||||||
|
"@types/send": "^0.17.4",
|
||||||
"concurrently": "^9.1.2",
|
"concurrently": "^9.1.2",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"nodemon": "^3.1.9",
|
"nodemon": "^3.1.9",
|
||||||
"rollup": "^4.34.8",
|
"rollup": "^4.34.9",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.7.3"
|
"typescript": "^5.8.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kevisual/code-center-module": "0.0.11-alpha.1",
|
"@kevisual/code-center-module": "0.0.11-alpha.3",
|
||||||
"@kevisual/router": "0.0.7",
|
"@kevisual/router": "0.0.9",
|
||||||
"@kevisual/use-config": "^1.0.8",
|
"@kevisual/use-config": "^1.0.9",
|
||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
"ioredis": "^5.5.0",
|
"ioredis": "^5.6.0",
|
||||||
|
"minio": "^8.0.4",
|
||||||
"nanoid": "^5.1.2",
|
"nanoid": "^5.1.2",
|
||||||
"sequelize": "^6.37.5"
|
"send": "^1.1.0",
|
||||||
|
"sequelize": "^6.37.6"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"picomatch": "^4.0.2"
|
"picomatch": "^4.0.2"
|
||||||
|
821
pnpm-lock.yaml
generated
821
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
45
src/index.ts
45
src/index.ts
@ -2,6 +2,7 @@ import { handleRequest } from './module/index.ts';
|
|||||||
import { config } from './module/config.ts';
|
import { config } from './module/config.ts';
|
||||||
import { app } from './app.ts';
|
import { app } from './app.ts';
|
||||||
import './route/route.ts';
|
import './route/route.ts';
|
||||||
|
import net from 'net';
|
||||||
const port = config?.proxy?.port || 3005;
|
const port = config?.proxy?.port || 3005;
|
||||||
|
|
||||||
app
|
app
|
||||||
@ -19,3 +20,47 @@ app.listen(port, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.server.on(handleRequest);
|
app.server.on(handleRequest);
|
||||||
|
|
||||||
|
const main = () => {
|
||||||
|
console.log('Upgrade initialization started');
|
||||||
|
|
||||||
|
app.server.server.on('upgrade', (req, socket, head) => {
|
||||||
|
const proxyApiList = config?.apiList || [];
|
||||||
|
const proxyApi = proxyApiList.find((item) => req.url.startsWith(item.path));
|
||||||
|
|
||||||
|
if (proxyApi) {
|
||||||
|
const _u = new URL(req.url, `${proxyApi.target}`);
|
||||||
|
const options = {
|
||||||
|
hostname: _u.hostname,
|
||||||
|
port: Number(_u.port) || 80,
|
||||||
|
path: _u.pathname,
|
||||||
|
headers: req.headers,
|
||||||
|
};
|
||||||
|
|
||||||
|
const proxySocket = net.connect(options.port, options.hostname, () => {
|
||||||
|
proxySocket.write(
|
||||||
|
`GET ${options.path} HTTP/1.1\r\n` +
|
||||||
|
`Host: ${options.hostname}\r\n` +
|
||||||
|
`Connection: Upgrade\r\n` +
|
||||||
|
`Upgrade: websocket\r\n` +
|
||||||
|
`Sec-WebSocket-Key: ${req.headers['sec-websocket-key']}\r\n` +
|
||||||
|
`Sec-WebSocket-Version: ${req.headers['sec-websocket-version']}\r\n` +
|
||||||
|
`\r\n`
|
||||||
|
);
|
||||||
|
proxySocket.pipe(socket);
|
||||||
|
socket.pipe(proxySocket);
|
||||||
|
});
|
||||||
|
|
||||||
|
proxySocket.on('error', (err) => {
|
||||||
|
console.error(`WebSocket proxy error: ${err.message}`);
|
||||||
|
socket.end();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
socket.end();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
main();
|
||||||
|
}, 1200);
|
||||||
|
@ -11,6 +11,17 @@ type ConfigType = {
|
|||||||
path?: string;
|
path?: string;
|
||||||
port?: number;
|
port?: number;
|
||||||
};
|
};
|
||||||
|
apiList: {
|
||||||
|
path: string;
|
||||||
|
/**
|
||||||
|
* url或者相对路径
|
||||||
|
*/
|
||||||
|
target: string;
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
type?: 'static' | 'dynamic' | 'minio';
|
||||||
|
}[];
|
||||||
proxy: {
|
proxy: {
|
||||||
port?: number;
|
port?: number;
|
||||||
/**
|
/**
|
||||||
|
@ -6,6 +6,8 @@ import path from 'path';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { getContentType } from './get-content-type.ts';
|
import { getContentType } from './get-content-type.ts';
|
||||||
import { createRefreshHtml } from './html/create-refresh-html.ts';
|
import { createRefreshHtml } from './html/create-refresh-html.ts';
|
||||||
|
import { fileProxy } from './proxy/file-proxy.ts';
|
||||||
|
import net from 'net';
|
||||||
|
|
||||||
const api = config?.api || { host: 'kevisual.xiongxiao.me', path: '/api/router' };
|
const api = config?.api || { host: 'kevisual.xiongxiao.me', path: '/api/router' };
|
||||||
const domain = config?.proxy?.domain || 'kevisual.xiongxiao.me';
|
const domain = config?.proxy?.domain || 'kevisual.xiongxiao.me';
|
||||||
@ -20,11 +22,18 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (req.url.startsWith('/api/proxy')) {
|
if (req.url.startsWith('/api/proxy')) {
|
||||||
|
// 已经代理过了
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (req.url.startsWith('/api')) {
|
console.log('req', req.url, 'len', config?.apiList?.length);
|
||||||
// 代理到 http://codeflow.xiongxiao.me/api
|
const proxyApiList = config?.apiList || [];
|
||||||
const _u = new URL(req.url, `http://${api.host}`);
|
const proxyApi = proxyApiList.find((item) => req.url.startsWith(item.path));
|
||||||
|
if (proxyApi && proxyApi?.type === 'static') {
|
||||||
|
return fileProxy(req, res, proxyApi);
|
||||||
|
}
|
||||||
|
if (proxyApi) {
|
||||||
|
const _u = new URL(req.url, `${proxyApi.target}`);
|
||||||
|
console.log('proxyApi', req.url, _u.href);
|
||||||
// 设置代理请求的目标 URL 和请求头
|
// 设置代理请求的目标 URL 和请求头
|
||||||
let header: any = {};
|
let header: any = {};
|
||||||
if (req.headers?.['Authorization']) {
|
if (req.headers?.['Authorization']) {
|
||||||
@ -32,9 +41,11 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR
|
|||||||
} else if (req.headers?.['authorization']) {
|
} else if (req.headers?.['authorization']) {
|
||||||
header.authorization = req.headers['authorization'];
|
header.authorization = req.headers['authorization'];
|
||||||
}
|
}
|
||||||
if (req.headers?.['Content-Type']) {
|
// 提取req的headers中的非HOST的header
|
||||||
header['Content-Type'] = req.headers?.['Content-Type'];
|
const headers = Object.keys(req.headers).filter((item) => item && item.toLowerCase() !== 'host');
|
||||||
}
|
headers.forEach((item) => {
|
||||||
|
header[item] = req.headers[item];
|
||||||
|
});
|
||||||
const options = {
|
const options = {
|
||||||
host: _u.hostname,
|
host: _u.hostname,
|
||||||
path: req.url,
|
path: req.url,
|
||||||
@ -64,7 +75,7 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR
|
|||||||
req.pipe(proxyReq, { end: true });
|
req.pipe(proxyReq, { end: true });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (req.url.startsWith('/api')) {
|
if (req.url.startsWith('/api') || req.url.startsWith('/v1')) {
|
||||||
res.end('not catch api');
|
res.end('not catch api');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -244,3 +255,4 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR
|
|||||||
console.error('getFile error', error);
|
console.error('getFile error', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
24
src/module/minio.ts
Normal file
24
src/module/minio.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Client, ClientOptions } from 'minio';
|
||||||
|
import { useConfig } from '@kevisual/use-config';
|
||||||
|
|
||||||
|
type MinioConfig = {
|
||||||
|
minio: ClientOptions & { bucketName: string };
|
||||||
|
};
|
||||||
|
const config = useConfig<MinioConfig>();
|
||||||
|
|
||||||
|
const { bucketName, ...minioRest } = config.minio;
|
||||||
|
export const minioClient = new Client(minioRest);
|
||||||
|
export { bucketName };
|
||||||
|
if (!minioClient) {
|
||||||
|
throw new Error('Minio client not initialized');
|
||||||
|
}
|
||||||
|
// 验证权限
|
||||||
|
// (async () => {
|
||||||
|
// const bucketExists = await minioClient.bucketExists(bucketName);
|
||||||
|
// if (!bucketExists) {
|
||||||
|
// await minioClient.makeBucket(bucketName);
|
||||||
|
// }
|
||||||
|
// const res = await minioClient.putObject(bucketName, 'private/test/a.b', 'test');
|
||||||
|
// console.log('minio putObject', res);
|
||||||
|
|
||||||
|
// })();
|
26
src/module/proxy/file-proxy.ts
Normal file
26
src/module/proxy/file-proxy.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import http from 'http';
|
||||||
|
import send from 'send';
|
||||||
|
import fs from 'fs';
|
||||||
|
import { fileIsExist } from '@kevisual/use-config';
|
||||||
|
import path from 'path';
|
||||||
|
export type ProxyInfo = {
|
||||||
|
path?: string;
|
||||||
|
target: string;
|
||||||
|
type?: 'static' | 'dynamic' | 'minio';
|
||||||
|
};
|
||||||
|
export const fileProxy = (req: http.IncomingMessage, res: http.ServerResponse, proxyApi: ProxyInfo) => {
|
||||||
|
// url开头的文件
|
||||||
|
const url = new URL(req.url, 'http://localhost');
|
||||||
|
const pathname = url.pathname;
|
||||||
|
// 检测文件是否存在,如果文件不存在,则返回404
|
||||||
|
const filePath = path.join(process.cwd(), proxyApi.target, pathname);
|
||||||
|
if (!fileIsExist(filePath)) {
|
||||||
|
res.statusCode = 404;
|
||||||
|
res.end('Not Found File');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const file = send(req, pathname, {
|
||||||
|
root: proxyApi.target,
|
||||||
|
});
|
||||||
|
file.pipe(res);
|
||||||
|
};
|
24
src/module/proxy/minio-proxy.ts
Normal file
24
src/module/proxy/minio-proxy.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import http from 'http';
|
||||||
|
import { minioClient } from '../minio.ts';
|
||||||
|
export type ProxyInfo = {
|
||||||
|
path?: string;
|
||||||
|
target: string;
|
||||||
|
type?: 'static' | 'dynamic' | 'minio';
|
||||||
|
};
|
||||||
|
export const minioProxy = async (req: http.IncomingMessage, res: http.ServerResponse, proxyApi: ProxyInfo) => {
|
||||||
|
try {
|
||||||
|
const requestUrl = new URL(req.url, 'http://localhost');
|
||||||
|
const objectPath = requestUrl.pathname;
|
||||||
|
const bucketName = proxyApi.target;
|
||||||
|
let objectName = objectPath.slice(1);
|
||||||
|
if (objectName.startsWith(bucketName)) {
|
||||||
|
objectName = objectName.slice(bucketName.length);
|
||||||
|
}
|
||||||
|
const objectStream = await minioClient.getObject(bucketName, objectName);
|
||||||
|
objectStream.pipe(res);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching object from MinIO:', error);
|
||||||
|
res.statusCode = 500;
|
||||||
|
res.end('Internal Server Error');
|
||||||
|
}
|
||||||
|
};
|
18
src/scripts/test-net-socket.ts
Normal file
18
src/scripts/test-net-socket.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import net from 'net';
|
||||||
|
|
||||||
|
const main = () => {
|
||||||
|
const options = {
|
||||||
|
port: 3003,
|
||||||
|
hostname: '192.168.31.220',
|
||||||
|
path: '/api/v1',
|
||||||
|
};
|
||||||
|
const proxySocket = net.connect(options.port, options.hostname, () => {
|
||||||
|
proxySocket.write(`GET ${options.path} HTTP/1.1\r\n` + `Host: ${options.hostname}\r\n` + `Connection: Upgrade\r\n` + `Upgrade: websocket\r\n` + `\r\n`);
|
||||||
|
});
|
||||||
|
|
||||||
|
proxySocket.on('data', (data) => {
|
||||||
|
console.log('data', data.toString());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
main();
|
Loading…
x
Reference in New Issue
Block a user