暂存
This commit is contained in:
9
src/app.ts
Normal file
9
src/app.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { App } from '@abearxiong/router';
|
||||
|
||||
export const app = new App({
|
||||
serverOptions: {
|
||||
path: '/api/proxy',
|
||||
},
|
||||
});
|
||||
|
||||
// app.server.on(callback);
|
||||
25
src/index.ts
25
src/index.ts
@@ -1,14 +1,21 @@
|
||||
import http from 'http';
|
||||
import { handleRequest } from './module/index.ts';
|
||||
import { useConfig } from '@abearxiong/use-config';
|
||||
import { app } from './app.ts';
|
||||
import './route/route.ts'
|
||||
const { port } = useConfig<{ port: number }>();
|
||||
const server = http.createServer((req, res) => {
|
||||
// res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
// const pathname = new URL(req.url, `http://${dns.hostName}`).pathname;
|
||||
handleRequest(req, res);
|
||||
// res.write(`Request from ${dns.hostName} with IP: ${dns.ip}\n`);
|
||||
// res.end('Hello World\n');
|
||||
});
|
||||
server.listen(port, () => {
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'hello',
|
||||
key: '0',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
ctx.body = 'hello world';
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server running at http://localhost:${port}/`);
|
||||
});
|
||||
|
||||
app.server.on(handleRequest);
|
||||
|
||||
@@ -8,9 +8,11 @@ import { useConfig } from '@abearxiong/use-config';
|
||||
import { redis } from './redis/redis.ts';
|
||||
import { getContentType } from './get-content-type.ts';
|
||||
import { sleep } from '@/utils/sleep.ts';
|
||||
import { handleProxyRequest } from './proxy.ts';
|
||||
const { api, domain, allowedOrigins } = useConfig<{
|
||||
api: {
|
||||
host: string;
|
||||
port?: number;
|
||||
};
|
||||
domain: string;
|
||||
allowedOrigins: string[];
|
||||
@@ -19,6 +21,60 @@ const { api, domain, allowedOrigins } = useConfig<{
|
||||
const fileStore = useFileStore('upload');
|
||||
const noProxyUrl = ['/', '/favicon.ico'];
|
||||
export const handleRequest = async (req: http.IncomingMessage, res: http.ServerResponse) => {
|
||||
if (req.url === '/favicon.ico') {
|
||||
return;
|
||||
}
|
||||
if (req.url.startsWith('/api/router')) {
|
||||
// 代理到 http://codeflow.xiongxiao.me/api
|
||||
const _u = new URL(req.url, `http://${api.host}`);
|
||||
// 设置代理请求的目标 URL 和请求头
|
||||
let header: any = {};
|
||||
if (req.headers?.['Authroization']) {
|
||||
header.Authorization = req.headers?.['Authroization'];
|
||||
}
|
||||
if (req.headers?.['Content-Type']) {
|
||||
header['Content-Type'] = req.headers?.['Content-Type'];
|
||||
}
|
||||
const options = {
|
||||
host: _u.hostname,
|
||||
path: req.url,
|
||||
method: req.method,
|
||||
headers: {
|
||||
...header,
|
||||
},
|
||||
};
|
||||
if (_u.port) {
|
||||
// @ts-ignore
|
||||
options.port = _u.port;
|
||||
}
|
||||
// 创建代理请求
|
||||
const proxyReq = http.request(options, (proxyRes) => {
|
||||
// 将代理服务器的响应头和状态码返回给客户端
|
||||
res.writeHead(proxyRes.statusCode, proxyRes.headers);
|
||||
// 将代理响应流写入客户端响应
|
||||
proxyRes.pipe(res, { end: true });
|
||||
});
|
||||
// 处理代理请求的错误事件
|
||||
proxyReq.on('error', (err) => {
|
||||
console.error(`Proxy request error: ${err.message}`);
|
||||
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
||||
res.write(`Proxy request error: ${err.message}`);
|
||||
});
|
||||
// 处理 POST 请求的请求体(传递数据到目标服务器)
|
||||
req.pipe(proxyReq, { end: true });
|
||||
return;
|
||||
}
|
||||
if (req.url.startsWith('/api/proxy')) {
|
||||
return;
|
||||
}
|
||||
if (req.url.startsWith('/api')) {
|
||||
res.end('not catch api');
|
||||
return;
|
||||
}
|
||||
if (req.url.startsWith('/test')) {
|
||||
handleProxyRequest(req, res);
|
||||
return;
|
||||
}
|
||||
const dns = getDNS(req);
|
||||
// 配置可以跨域
|
||||
// 配置可以访问的域名 localhost, xiongxiao.me
|
||||
@@ -87,42 +143,7 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR
|
||||
user = _user;
|
||||
app = _app;
|
||||
}
|
||||
const [_, _api] = req.url.split('/');
|
||||
if (_api === 'api') {
|
||||
// 代理到 http://codeflow.xiongxiao.me/api
|
||||
// 设置代理请求的目标 URL 和请求头
|
||||
let header: any = {};
|
||||
if (req.headers?.['Authroization']) {
|
||||
header.Authorization = req.headers?.['Authroization'];
|
||||
}
|
||||
if (req.headers?.['Content-Type']) {
|
||||
header['Content-Type'] = req.headers?.['Content-Type'];
|
||||
}
|
||||
const options = {
|
||||
host: api.host,
|
||||
path: req.url,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...header,
|
||||
},
|
||||
};
|
||||
// 创建代理请求
|
||||
const proxyReq = http.request(options, (proxyRes) => {
|
||||
// 将代理服务器的响应头和状态码返回给客户端
|
||||
res.writeHead(proxyRes.statusCode, proxyRes.headers);
|
||||
// 将代理响应流写入客户端响应
|
||||
proxyRes.pipe(res, { end: true });
|
||||
});
|
||||
// 处理代理请求的错误事件
|
||||
proxyReq.on('error', (err) => {
|
||||
console.error(`Proxy request error: ${err.message}`);
|
||||
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
||||
res.write(`Proxy request error: ${err.message}`);
|
||||
});
|
||||
// 处理 POST 请求的请求体(传递数据到目标服务器)
|
||||
req.pipe(proxyReq, { end: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const userApp = new UserApp({ user, app });
|
||||
let isExist = await userApp.getExist();
|
||||
if (!isExist) {
|
||||
|
||||
64
src/module/proxy.ts
Normal file
64
src/module/proxy.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import http from 'http';
|
||||
import httpProxy from 'http-proxy';
|
||||
import { useConfig } from '@abearxiong/use-config';
|
||||
|
||||
const { resources, api } = useConfig<{
|
||||
resources: string;
|
||||
api: { host: string };
|
||||
}>();
|
||||
const proxy = httpProxy.createProxyServer({});
|
||||
const fetchTest = async (id: string) => {
|
||||
const fetchUrl = 'http://' + api.host + '/api/router';
|
||||
const fetchRes = await fetch(fetchUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
path: 'user-app',
|
||||
key: 'test',
|
||||
id: id,
|
||||
}),
|
||||
}).then((res) => res.json());
|
||||
return fetchRes;
|
||||
};
|
||||
// 60939f5e-f51b-4563-8c96-7a98ac5ac259
|
||||
export const handleProxyRequest = async (req: http.IncomingMessage, res: http.ServerResponse) => {
|
||||
const url = req.url;
|
||||
const urls = url.split('/');
|
||||
const [_, test, id] = urls;
|
||||
const error = (msg: string) => {
|
||||
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
||||
res.write(msg);
|
||||
res.end();
|
||||
};
|
||||
if (test !== 'test') {
|
||||
error('Not Found');
|
||||
return;
|
||||
}
|
||||
if (!id) {
|
||||
error('Need Test ID');
|
||||
return;
|
||||
}
|
||||
// 判断id是uuid
|
||||
if (!isUUID(id)) {
|
||||
error('Need Test ID is UUID');
|
||||
return;
|
||||
}
|
||||
const result = await fetchTest(id);
|
||||
console.log('data', result);
|
||||
if (result.code !== 200) {
|
||||
error('fetch error');
|
||||
return;
|
||||
}
|
||||
const files = result.data?.data?.files;
|
||||
const appFileUrl = (url + '').replace(`/${test}/${id}/`, '');
|
||||
const pathFile = files.find((file: any) => file.name === appFileUrl);
|
||||
const target = `https://${resources}/${pathFile.path}`;
|
||||
console.log('target', target);
|
||||
proxy.web(req, res, { target: target, secure: false });
|
||||
};
|
||||
function isUUID(id: string): boolean {
|
||||
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
return uuidRegex.test(id);
|
||||
}
|
||||
40
src/module/query/get-router.ts
Normal file
40
src/module/query/get-router.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { useConfig } from '@abearxiong/use-config';
|
||||
|
||||
const { resources, api } = useConfig<{
|
||||
resources: string;
|
||||
api: { host: string };
|
||||
}>();
|
||||
|
||||
export const fetchTest = async (id: string) => {
|
||||
const fetchUrl = 'http://' + api.host + '/api/router';
|
||||
const fetchRes = await fetch(fetchUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
path: 'user-app',
|
||||
key: 'test',
|
||||
id: id,
|
||||
}),
|
||||
}).then((res) => res.json());
|
||||
return fetchRes;
|
||||
};
|
||||
|
||||
export const fetchDomain = async (domain: string) => {
|
||||
const fetchUrl = 'http://' + api.host + '/api/router';
|
||||
const fetchRes = await fetch(fetchUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
path: 'app',
|
||||
key: 'getDomainApp',
|
||||
data: {
|
||||
domain,
|
||||
},
|
||||
}),
|
||||
}).then((res) => res.json());
|
||||
return fetchRes;
|
||||
};
|
||||
1
src/route/app/index.ts
Normal file
1
src/route/app/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './list.ts'
|
||||
72
src/route/app/list.ts
Normal file
72
src/route/app/list.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { UserApp } from '@/module/get-user-app.ts';
|
||||
import { app } from '../../app.ts';
|
||||
import { redis } from '@/module/redis/redis.ts';
|
||||
import { CustomError } from '@abearxiong/router';
|
||||
import fs from 'fs';
|
||||
import { useFileStore } from '@abearxiong/use-file-store';
|
||||
const fileStore = useFileStore('upload');
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'app',
|
||||
key: 'list',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const keys = await redis.keys('user:app:*');
|
||||
// const keys = await redis.keys('user:app:exist:*');
|
||||
// const data = await redis.mget(...keys);
|
||||
ctx.body = {
|
||||
// data: data,
|
||||
keys,
|
||||
};
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'app',
|
||||
key: 'delete',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { user, app } = ctx.query;
|
||||
try {
|
||||
const userApp = new UserApp({ user, app });
|
||||
await userApp.clearCacheData();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw new CustomError('删除失败');
|
||||
}
|
||||
ctx.body = 'successfully';
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'app',
|
||||
key: 'deleteAll',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const keys = await redis.keys('user:app:*');
|
||||
for (const key of keys) {
|
||||
await redis.set(key, '', 'EX', 1);
|
||||
}
|
||||
ctx.body = {
|
||||
keys,
|
||||
};
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'app',
|
||||
key: 'deleteAllForce',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const keys = await redis.keys('user:app:*');
|
||||
await redis.del(...keys);
|
||||
fs.rmSync(fileStore, { recursive: true });
|
||||
|
||||
ctx.body = {
|
||||
keys,
|
||||
};
|
||||
});
|
||||
1
src/route/route.ts
Normal file
1
src/route/route.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './app/index.ts'
|
||||
Reference in New Issue
Block a user