feat: change env

This commit is contained in:
熊潇 2025-03-26 01:17:46 +08:00
parent 29d0709083
commit f4af7d5c7f
12 changed files with 136 additions and 47 deletions

18
.env.example Normal file
View File

@ -0,0 +1,18 @@
# 代理配置
PROXY_PORT=3005
PROXY_DOMAIN=localhost
PROXY_RESOURCES=http://localhost:9000/resources
PROXY_ALLOWED_ORIGINS=localhost,xiongxiao.me,zxj.im
PROXY_HOME=/ai/chat
# 后台代理
API_HOST=http://localhost:4005
API_PATH=/api/router
# Minio 配置
MINIO_ENDPOINT=localhost
MINIO_PORT=9000
MINIO_BUCKET_NAME=resources
MINIO_USE_SSL=false
MINIO_ACCESS_KEY=username
MINIO_SECRET_KEY=****

4
.gitignore vendored
View File

@ -16,3 +16,7 @@ proxy-upload/*
proxy-upload/.gitkeep proxy-upload/.gitkeep
.env .env
.env*
!.env.example
pack-dist

View File

@ -4,6 +4,7 @@
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"type": "module", "type": "module",
"basename": "/root/page-proxy",
"app": { "app": {
"key": "page-proxy", "key": "page-proxy",
"entry": "dist/app.mjs", "entry": "dist/app.mjs",
@ -22,7 +23,7 @@
"build": "rimraf dist && rollup -c", "build": "rimraf dist && rollup -c",
"start": "pm2 start dist/app.mjs --name page-proxy", "start": "pm2 start dist/app.mjs --name page-proxy",
"release": "node ./scripts/release/index.mjs", "release": "node ./scripts/release/index.mjs",
"deploy": "envision switch root && envision pack -p -u", "deploy": "envision pack -p -u",
"pub": "npm run build && npm run deploy", "pub": "npm run build && npm run deploy",
"ssl": "ssh -L 6379:localhost:6379 light" "ssl": "ssh -L 6379:localhost:6379 light"
}, },
@ -40,7 +41,7 @@
"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.36.0", "rollup": "^4.37.0",
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.8.2" "typescript": "^5.8.2"
}, },

View File

@ -1,4 +1,4 @@
import { useConfig } from '@kevisual/use-config'; import { useConfig } from '@kevisual/use-config/env';
import { useFileStore } from '@kevisual/use-config/file-store'; import { useFileStore } from '@kevisual/use-config/file-store';
export const fileStore = useFileStore('proxy-upload'); export const fileStore = useFileStore('proxy-upload');
@ -37,7 +37,7 @@ type ConfigType = {
* allow origin xiongxiao.me zxj.im silkyai.cn * allow origin xiongxiao.me zxj.im silkyai.cn
* 访 * 访
*/ */
allowOrigin: string[]; allowedOrigin: string[];
/** /**
* home * home
@ -46,4 +46,25 @@ type ConfigType = {
}; };
}; };
export const config = useConfig<ConfigType>(); // export const config = useConfig();
export const envConfig = useConfig() as any;
export const config: ConfigType = {
api: {
host: envConfig.API_HOST,
path: envConfig.API_PATH,
port: envConfig.PROXY_PORT,
},
apiList: [
{
path: '/api',
target: envConfig.API_HOST,
},
],
proxy: {
port: envConfig.PROXY_PORT,
domain: envConfig.PROXY_DOMAIN,
resources: envConfig.PROXY_RESOURCES,
allowedOrigin: (envConfig.PROXY_ALLOWED_ORIGINS as string)?.split(',') || [],
home: envConfig.PROXY_HOME,
},
};

View File

@ -129,6 +129,17 @@ export class UserApp {
app: fetchData.key, app: fetchData.key,
}; };
redis.set(key, data.user + ':' + data.app, 'EX', 60 * 60 * 24 * 7); // 7天 redis.set(key, data.user + ':' + data.app, 'EX', 60 * 60 * 24 * 7); // 7天
const userDomainApp = 'user:domain:app:' + data.user + ':' + data.app;
const domainKeys = await redis.get(userDomainApp);
let domainKeysList = domainKeys ? JSON.parse(domainKeys) : [];
domainKeysList.push(domain);
const uniq = (arr: string[]) => {
return [...new Set(arr)];
};
domainKeysList = uniq(domainKeysList);
await redis.set(userDomainApp, JSON.stringify(domainKeysList), 'EX', 60 * 60 * 24 * 7); // 7天
return data; return data;
} }
/** /**
@ -282,6 +293,16 @@ export class UserApp {
await redis.del('user:app:exist:' + app + ':' + user); await redis.del('user:app:exist:' + app + ':' + user);
await redis.del('user:app:set:' + app + ':' + user); await redis.del('user:app:set:' + app + ':' + user);
await redis.del('user:app:status:' + app + ':' + user); await redis.del('user:app:status:' + app + ':' + user);
const userDomainApp = 'user:domain:app:' + user + ':' + app;
const domainKeys = await redis.get(userDomainApp);
if (domainKeys) {
const domainKeysList = JSON.parse(domainKeys);
domainKeysList.forEach(async (domain: string) => {
await redis.del('domain:' + domain);
});
}
await redis.del(userDomainApp);
// 删除所有文件 // 删除所有文件
deleteUserAppFiles(user, app); deleteUserAppFiles(user, app);
} }

View File

@ -1,6 +1,5 @@
import { getDNS, isLocalhost } from '@/utils/dns.ts'; import { getDNS, isLocalhost } from '@/utils/dns.ts';
import http from 'http'; import http from 'http';
import https from 'https';
import { UserApp } from './get-user-app.ts'; import { UserApp } from './get-user-app.ts';
import { config, fileStore } from '../module/config.ts'; import { config, fileStore } from '../module/config.ts';
import path from 'path'; import path from 'path';
@ -8,12 +7,11 @@ 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 { fileProxy } from './proxy/file-proxy.ts';
import net from 'net';
import { httpProxy } from './proxy/http-proxy.ts'; import { httpProxy } from './proxy/http-proxy.ts';
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';
const allowedOrigins = config?.proxy?.allowOrigin || []; const allowedOrigins = config?.proxy?.allowedOrigin || [];
const home = config?.proxy?.home || '/ai/chat'; const home = config?.proxy?.home || '/ai/chat';
const noProxyUrl = ['/', '/favicon.ico']; const noProxyUrl = ['/', '/favicon.ico'];
@ -43,6 +41,9 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR
} }
// 提取req的headers中的非HOST的header // 提取req的headers中的非HOST的header
const headers = Object.keys(req.headers).filter((item) => item && item.toLowerCase() !== 'host'); const headers = Object.keys(req.headers).filter((item) => item && item.toLowerCase() !== 'host');
const host = req.headers['host'];
console.log('host', host);
console.log('headers', headers);
headers.forEach((item) => { headers.forEach((item) => {
header[item] = req.headers[item]; header[item] = req.headers[item];
}); });
@ -96,6 +97,7 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR
let user, app; let user, app;
let domainApp = false; let domainApp = false;
console.log('dns.hostName', dns.hostName, domain);
if (isLocalhost(dns.hostName)) { if (isLocalhost(dns.hostName)) {
// 本地开发环境 测试 // 本地开发环境 测试
// user = 'root'; // user = 'root';
@ -223,6 +225,7 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR
// console.log('isExist', isExist); // console.log('isExist', isExist);
if (!appFile) { if (!appFile) {
const [indexFilePath, etag] = indexFile.split('||'); const [indexFilePath, etag] = indexFile.split('||');
// console.log('indexFilePath', indexFile, path.join(fileStore, indexFilePath));
const contentType = getContentType(indexFilePath); const contentType = getContentType(indexFilePath);
const isHTML = contentType.includes('html'); const isHTML = contentType.includes('html');
const filePath = path.join(fileStore, indexFilePath); const filePath = path.join(fileStore, indexFilePath);

View File

@ -1,26 +1,21 @@
import { Client, ClientOptions } from 'minio'; import { Client } from 'minio';
import { useConfig } from '@kevisual/use-config'; import { useConfig } from '@kevisual/use-config/env';
const config = useConfig();
type MinioConfig = { const minioConfig = {
minio: ClientOptions & { bucketName: string }; bucketName: config.MINIO_BUCKET_NAME,
endPoint: config.MINIO_ENDPOINT,
port: config.MINIO_PORT,
useSSL: config.MINIO_USE_SSL === 'true',
accessKey: config.MINIO_ACCESS_KEY,
secretKey: config.MINIO_SECRET_KEY,
}; };
const config = useConfig<MinioConfig>(); // const config = useConfig<MinioConfig>();
const { bucketName, ...minioRest } = config.minio; const { port, endPoint, useSSL } = minioConfig;
const { port, endPoint, useSSL } = minioRest;
export const minioUrl = `http${useSSL ? 's' : ''}://${endPoint}:${port || 9000}`; export const minioUrl = `http${useSSL ? 's' : ''}://${endPoint}:${port || 9000}`;
export const minioResources = `${minioUrl}/resources`; export const minioResources = `${minioUrl}/resources`;
export const minioClient = new Client(minioRest); export const minioClient = new Client(minioConfig);
export { bucketName }; export const bucketName = minioConfig.bucketName;
if (!minioClient) { if (!minioClient) {
throw new Error('Minio client not initialized'); 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);
// })();

View File

@ -1,10 +1,12 @@
import { Redis } from 'ioredis'; import { Redis } from 'ioredis';
import { useConfig } from '@kevisual/use-config';
import { useContextKey } from '@kevisual/use-config/context'; import { useContextKey } from '@kevisual/use-config/context';
console.log(process.env.REDIS_HOST);
const config = useConfig<{ const redisConfig = {
redis: ConstructorParameters<typeof Redis>; host: 'localhost', // Redis 服务器的主机名或 IP 地址
}>(); port: 6379, // Redis 服务器的端口号
// password: 'your_password', // Redis 的密码 (如果有)
};
const init = () => { const init = () => {
return new Redis({ return new Redis({
host: 'localhost', // Redis 服务器的主机名或 IP 地址 host: 'localhost', // Redis 服务器的主机名或 IP 地址
@ -17,7 +19,7 @@ const init = () => {
return Math.min(times * 50, 2000); // 每次重试时延迟增加 return Math.min(times * 50, 2000); // 每次重试时延迟增加
}, },
maxRetriesPerRequest: null, // 允许请求重试的次数 (如果需要无限次重试) maxRetriesPerRequest: null, // 允许请求重试的次数 (如果需要无限次重试)
...config.redis, ...redisConfig,
}); });
}; };
// 配置 Redis 连接 // 配置 Redis 连接

View File

@ -1,19 +1,15 @@
import { useConfig } from '@kevisual/use-config';
import { Sequelize } from 'sequelize'; import { Sequelize } from 'sequelize';
import { useContextKey, useContext } from '@kevisual/use-config/context'; import { useContextKey, useContext } from '@kevisual/use-config/context';
import { useConfig } from '@kevisual/use-config/env';
const config = useConfig();
type PostgresConfig = { const postgresConfig = {
postgres: { username: config.POSTGRES_USERNAME,
username: string; password: config.POSTGRES_PASSWORD,
password: string; host: config.POSTGRES_HOST,
host: string; port: config.POSTGRES_PORT,
port: number; database: config.POSTGRES_DATABASE,
database: string;
};
}; };
const config = useConfig<PostgresConfig>();
const postgresConfig = config.postgres;
if (!postgresConfig) { if (!postgresConfig) {
console.error('postgres config is required'); console.error('postgres config is required');

View File

@ -23,14 +23,18 @@ app
path: 'app', path: 'app',
key: 'list', key: 'list',
middleware: ['auth-admin'], middleware: ['auth-admin'],
description: '获取应用列表',
isDebug: true,
}) })
.define(async (ctx) => { .define(async (ctx) => {
const keys = await redis.keys('user:app:*'); const keys = await redis.keys('user:app:*');
// const keys = await redis.keys('user:app:exist:*'); // const keys = await redis.keys('user:app:exist:*');
// const data = await redis.mget(...keys); // const data = await redis.mget(...keys);
const domainList = await redis.keys('domain:*');
ctx.body = { ctx.body = {
// data: data, // data: data,
keys, keys,
domainList,
}; };
}) })
.addTo(app); .addTo(app);

View File

@ -1,3 +1,5 @@
import { useConfig } from '@kevisual/use-config/env'; import { useConfig } from '@kevisual/use-config/env';
console.log(useConfig()); console.log(useConfig());
console.log(process.env);

View File

@ -0,0 +1,22 @@
const baseURL = 'https://kevisual.xiongxiao.me/api/router';
export const fetchCnfig = async () => {
const res = await fetch(baseURL, {
method: 'POST',
body: JSON.stringify({
path: 'app',
key: 'getApp',
data: {
user: 'root',
key: 'code-center',
},
}),
});
return res.json();
};
const main = async () => {
const res = await fetchCnfig();
// console.log(res);
console.log(JSON.stringify(res, null, 2));
};
main();