feat:add domain config
This commit is contained in:
@@ -3,6 +3,7 @@ import path from 'path';
|
||||
export const getContentType = (filePath: string) => {
|
||||
const extname = path.extname(filePath);
|
||||
const contentType = {
|
||||
'.txt': 'text/plain',
|
||||
'.html': 'text/html',
|
||||
'.js': 'text/javascript',
|
||||
'.css': 'text/css',
|
||||
|
||||
@@ -9,7 +9,7 @@ import { pipeline } from 'stream';
|
||||
import { promisify } from 'util';
|
||||
const pipelineAsync = promisify(pipeline);
|
||||
|
||||
const { resources } = useConfig<{ resources: string }>();
|
||||
const { resources, api } = useConfig<{ resources: string; api: { host: string; testHost: string } }>();
|
||||
const fileStore = useFileStore('upload');
|
||||
|
||||
const demoData = {
|
||||
@@ -36,30 +36,6 @@ const demoData = {
|
||||
],
|
||||
},
|
||||
};
|
||||
const demoData2 = {
|
||||
user: 'root',
|
||||
key: 'codeflow',
|
||||
appType: 'web-single', //
|
||||
version: '0.0.1',
|
||||
domain: null,
|
||||
type: 'oss', // 是否使用oss
|
||||
data: {
|
||||
files: [
|
||||
{
|
||||
name: 'index.html',
|
||||
path: 'root/codeflow/0.0.1/index.html',
|
||||
},
|
||||
{
|
||||
name: 'assets/index-14y4J8dP.js',
|
||||
path: 'root/codeflow/0.0.1/assets/index-14y4J8dP.js',
|
||||
},
|
||||
{
|
||||
name: 'assets/index-C-libw4a.css',
|
||||
path: 'root/codeflow/0.0.1/assets/index-C-libw4a.css',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
type UserAppOptions = {
|
||||
user: string;
|
||||
app: string;
|
||||
@@ -94,16 +70,89 @@ export class UserApp {
|
||||
const value = await redis.hget(key, appFileUrl);
|
||||
return value;
|
||||
}
|
||||
static async getDomainApp(domain: string) {
|
||||
const key = 'domain:' + domain;
|
||||
const value = await redis.get(key);
|
||||
if (value) {
|
||||
const [_user, _app] = value.split(':');
|
||||
return {
|
||||
user: _user,
|
||||
app: _app,
|
||||
};
|
||||
}
|
||||
|
||||
// 获取域名对应的用户和应用
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
const fetchTestUrl = 'http://' + api.testHost + '/api/router';
|
||||
const fetchUrl = 'http://' + api.host + '/api/router';
|
||||
const fetchRes = await fetch(isDev ? fetchTestUrl : fetchUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
path: 'app',
|
||||
key: 'getDomainApp',
|
||||
data: {
|
||||
domain,
|
||||
},
|
||||
}),
|
||||
}).then((res) => res.json());
|
||||
if (fetchRes?.code !== 200) {
|
||||
console.log('fetchRes is error', fetchRes);
|
||||
return null;
|
||||
}
|
||||
const fetchData = fetchRes.data;
|
||||
const data = {
|
||||
user: fetchData.user,
|
||||
app: fetchData.key,
|
||||
};
|
||||
redis.set(key, data.user + ':' + data.app, 'EX', 60 * 60 * 24 * 7); // 24小时
|
||||
return data;
|
||||
}
|
||||
async setCacheData() {
|
||||
const app = this.app;
|
||||
const user = this.user;
|
||||
const key = 'user:app:' + app + ':' + user;
|
||||
// 如果demoData 不存在则返回
|
||||
if (!demoData2) {
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
const fetchTestUrl = 'http://' + api.testHost + '/api/router';
|
||||
const fetchUrl = 'http://' + api.host + '/api/router';
|
||||
const fetchRes = await fetch(isDev ? fetchTestUrl : fetchUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
path: 'app',
|
||||
key: 'getApp',
|
||||
data: {
|
||||
user,
|
||||
key: app,
|
||||
},
|
||||
}),
|
||||
}).then((res) => res.json());
|
||||
if (fetchRes?.code !== 200) {
|
||||
console.log('fetchRes is error', fetchRes);
|
||||
return false;
|
||||
}
|
||||
const value = await downloadUserAppFiles(user, app, demoData2);
|
||||
const valueIndexHtml = value.data.files.find((file) => file.name === 'index.html');
|
||||
const fetchData = fetchRes.data;
|
||||
if (!fetchData.type) {
|
||||
// console.error('fetchData type is error', fetchData);
|
||||
// return false;
|
||||
fetchData.type = 'oss';
|
||||
}
|
||||
const value = await downloadUserAppFiles(user, app, fetchData);
|
||||
if (value.data.files.length === 0) {
|
||||
console.error('root files length is zero', user, app);
|
||||
return false;
|
||||
}
|
||||
let valueIndexHtml = value.data.files.find((file) => file.name === 'index.html');
|
||||
if (!valueIndexHtml) {
|
||||
valueIndexHtml = value.data.files.find((file) => file.name === 'index.js');
|
||||
if (!valueIndexHtml) {
|
||||
valueIndexHtml = value.data.files[0];
|
||||
}
|
||||
}
|
||||
await redis.set(key, JSON.stringify(value));
|
||||
await redis.set('user:app:exist:' + app + ':' + user, valueIndexHtml.path, 'EX', 60 * 60 * 24 * 7); // 24小时
|
||||
const files = value.data.files;
|
||||
@@ -140,9 +189,6 @@ export class UserApp {
|
||||
// 删除所有文件
|
||||
deleteUserAppFiles(user, app);
|
||||
}
|
||||
async getData() {
|
||||
return demoData;
|
||||
}
|
||||
async close() {
|
||||
// 关闭连接
|
||||
await redis.quit();
|
||||
|
||||
@@ -7,11 +7,12 @@ import fs from 'fs';
|
||||
import { useConfig } from '@abearxiong/use-config';
|
||||
import { redis } from './redis/redis.ts';
|
||||
import { getContentType } from './get-content-type.ts';
|
||||
const { api, domain } = useConfig<{
|
||||
const { api, domain, allowedOrigins } = useConfig<{
|
||||
api: {
|
||||
host: string;
|
||||
};
|
||||
domain: string;
|
||||
allowedOrigins: string[];
|
||||
}>();
|
||||
|
||||
const fileStore = useFileStore('upload');
|
||||
@@ -19,6 +20,18 @@ console.log('filePath', fileStore);
|
||||
const noProxyUrl = ['/', '/favicon.ico'];
|
||||
export const handleRequest = async (req: http.IncomingMessage, res: http.ServerResponse) => {
|
||||
const dns = getDNS(req);
|
||||
// 配置可以跨域
|
||||
// 配置可以访问的域名 localhost, xiongxiao.me
|
||||
const _orings = allowedOrigins || [];
|
||||
const host = dns.hostName;
|
||||
if (
|
||||
_orings.some((item) => {
|
||||
return host.includes(item);
|
||||
})
|
||||
) {
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
}
|
||||
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
||||
|
||||
let user, app;
|
||||
let domainApp = false;
|
||||
@@ -33,21 +46,19 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR
|
||||
if (dns.hostName !== domain) {
|
||||
// redis获取域名对应的用户和应用
|
||||
domainApp = true;
|
||||
const key = 'domain:' + dns.hostName;
|
||||
const value = await redis.get(key);
|
||||
if (!value) {
|
||||
const data = await UserApp.getDomainApp(dns.hostName);
|
||||
if (!data) {
|
||||
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
||||
res.write('Invalid domain\n');
|
||||
return res.end();
|
||||
}
|
||||
const [_user, _app] = value.split(':');
|
||||
if (!_user || !_app) {
|
||||
if (!data.user || !data.app) {
|
||||
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
||||
res.write('Invalid domain, Config error\n');
|
||||
res.write('Invalid domain config\n');
|
||||
return res.end();
|
||||
}
|
||||
user = _user;
|
||||
app = _app;
|
||||
user = data.user;
|
||||
app = data.app;
|
||||
}
|
||||
}
|
||||
const url = req.url;
|
||||
@@ -145,8 +156,10 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR
|
||||
const appFile = await userApp.getFile(appFileUrl);
|
||||
if (!appFile) {
|
||||
const [indexFilePath, etag] = indexFile.split('||');
|
||||
const contentType = getContentType(indexFilePath);
|
||||
const isHTML = contentType.includes('html');
|
||||
// 不存在的文件,返回indexFile的文件
|
||||
res.writeHead(200, { 'Content-Type': 'text/html', 'Cache-Control': 'no-cache' });
|
||||
res.writeHead(200, { 'Content-Type': contentType, 'Cache-Control': isHTML ? 'no-cache' : 'public, max-age=3600' });
|
||||
const filePath = path.join(fileStore, indexFilePath);
|
||||
const readStream = fs.createReadStream(filePath);
|
||||
readStream.pipe(res);
|
||||
@@ -161,9 +174,10 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR
|
||||
}
|
||||
const filePath = path.join(fileStore, appFilePath);
|
||||
let contentType = getContentType(filePath);
|
||||
const isHTML = contentType.includes('html');
|
||||
res.writeHead(200, {
|
||||
'Content-Type': contentType,
|
||||
'Cache-Control': 'public, max-age=3600', // 设置缓存时间为 1 小时
|
||||
'Cache-Control': isHTML ? 'no-cache' : 'public, max-age=3600', // 设置缓存时间为 1 小时
|
||||
ETag: eTag,
|
||||
});
|
||||
const readStream = fs.createReadStream(filePath);
|
||||
|
||||
@@ -31,7 +31,8 @@ const clearData = async () => {
|
||||
};
|
||||
|
||||
// clearData();
|
||||
clearAllUserApp();
|
||||
// clearAllUserApp();
|
||||
|
||||
|
||||
const expireData = async () => {
|
||||
await redis.set('user:app:exist:' + 'codeflow:root', 'value', 'EX', 2);
|
||||
@@ -39,3 +40,10 @@ const expireData = async () => {
|
||||
};
|
||||
|
||||
// expireData();
|
||||
|
||||
const keysData = async () => {
|
||||
const keys = await redis.keys('user:app:exist:*');
|
||||
console.log('keys', keys);
|
||||
process.exit(0);
|
||||
}
|
||||
keysData();
|
||||
Reference in New Issue
Block a user