add router

This commit is contained in:
xion 2024-10-15 18:53:09 +08:00
parent 6b5eec89ed
commit 175e685480
6 changed files with 67 additions and 113 deletions

View File

@ -32,7 +32,6 @@
"@abearxiong/router": "0.0.1-alpha.43",
"@abearxiong/use-config": "^0.0.2",
"@abearxiong/use-file-store": "^0.0.1",
"http-proxy": "^1.18.1",
"ioredis": "^5.4.1",
"nanoid": "^5.0.7"
},

View File

@ -7,6 +7,7 @@ import crypto from 'crypto';
import { nanoid } from 'nanoid';
import { pipeline } from 'stream';
import { promisify } from 'util';
import { fetchApp, fetchDomain, fetchTest } from './query/get-router.ts';
const pipelineAsync = promisify(pipeline);
const { resources, api } = useConfig<{ resources: string; api: { host: string; path: string } }>();
@ -44,9 +45,13 @@ type UserAppOptions = {
export class UserApp {
user: string;
app: string;
isTest: boolean;
constructor(options: UserAppOptions) {
this.user = options.user;
this.app = options.app;
if (this.user === 'test') {
this.isTest = true;
}
}
async getExist() {
const app = this.app;
@ -63,6 +68,7 @@ export class UserApp {
if (!value) {
return null;
}
return JSON.parse(value);
}
async getFile(appFileUrl: string) {
const app = this.app;
@ -83,24 +89,7 @@ export class UserApp {
}
// 获取域名对应的用户和应用
const fetchUrl = 'http://' + api.host + api.path;
const fetchRes = await fetch(fetchUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
path: 'app',
key: 'getDomainApp',
data: {
domain,
},
}),
})
.then((res) => {
return res.json();
})
.catch((err) => {
const fetchRes = await fetchDomain(domain).catch((err) => {
return {
code: 500,
message: err,
@ -139,28 +128,16 @@ export class UserApp {
async setCacheData() {
const app = this.app;
const user = this.user;
const isTest = this.isTest;
const key = 'user:app:' + app + ':' + user;
const fetchUrl = 'http://' + api.host + api.path;
if (status[key]) {
return {
loading: true,
};
}
status[key] = true;
const fetchRes = await fetch(fetchUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
path: 'app',
key: 'getApp',
data: {
user,
key: app,
},
}),
}).then((res) => res.json());
const fetchRes = isTest ? await fetchTest(app) : await fetchApp({ user, app });
if (fetchRes?.code !== 200) {
console.log('fetchRes is error', fetchRes);
this.setLoaded();

View File

@ -8,7 +8,6 @@ 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;
@ -71,10 +70,7 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR
res.end('not catch api');
return;
}
if (req.url.startsWith('/test')) {
handleProxyRequest(req, res);
return;
}
const dns = getDNS(req);
// 配置可以跨域
// 配置可以访问的域名 localhost, xiongxiao.me

View File

@ -1,64 +0,0 @@
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);
}

View File

@ -2,11 +2,13 @@ import { useConfig } from '@abearxiong/use-config';
const { resources, api } = useConfig<{
resources: string;
api: { host: string };
api: { host: string; path: string };
ƒ;
}>();
const apiPath = api.path || '/api/router';
export const fetchTest = async (id: string) => {
const fetchUrl = 'http://' + api.host + '/api/router';
const fetchUrl = 'http://' + api.host + apiPath;
const fetchRes = await fetch(fetchUrl, {
method: 'POST',
headers: {
@ -22,7 +24,7 @@ export const fetchTest = async (id: string) => {
};
export const fetchDomain = async (domain: string) => {
const fetchUrl = 'http://' + api.host + '/api/router';
const fetchUrl = 'http://' + api.host + apiPath;
const fetchRes = await fetch(fetchUrl, {
method: 'POST',
headers: {
@ -38,3 +40,22 @@ export const fetchDomain = async (domain: string) => {
}).then((res) => res.json());
return fetchRes;
};
export const fetchApp = async ({ user, app }) => {
const fetchUrl = 'http://' + api.host + apiPath;
const fetchRes = await fetch(fetchUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
path: 'app',
key: 'getApp',
data: {
user,
key: app,
},
}),
}).then((res) => res.json());
return fetchRes;
};

View File

@ -59,7 +59,7 @@ app
app
.route({
path: 'app',
key: 'deleteAllForce',
key: 'clear',
})
.define(async (ctx) => {
const keys = await redis.keys('user:app:*');
@ -69,4 +69,29 @@ app
ctx.body = {
keys,
};
});
})
.addTo(app);
app
.route({
path: 'app',
key: 'get',
})
.define(async (ctx) => {
const { user, app } = ctx.query.data || {};
if (!user || !app) {
if (!user) {
throw new CustomError('user is required');
}
if (!app) {
throw new CustomError('app is required');
}
}
const userApp = new UserApp({ user, app });
const cache = await userApp.getCache();
if (!cache) {
throw new CustomError('Not Found App');
}
ctx.body = cache;
})
.addTo(app);