添加nginx相关配置,首页系统概览添加用户id查询
This commit is contained in:
@@ -0,0 +1,279 @@
|
||||
# 多客户端部署方案说明
|
||||
|
||||
## 概述
|
||||
|
||||
本方案实现了基于PM2和Nginx的多客户端部署架构,允许不同地区的客户通过不同端口访问同一个应用服务。
|
||||
|
||||
## 架构设计
|
||||
|
||||
```
|
||||
客户端A (51701) ──┐
|
||||
客户端B (51702) ──┼── Nginx反向代理 ──→ 主服务 (51703)
|
||||
客户端C (51704) ──┘
|
||||
```
|
||||
|
||||
### 端口分配
|
||||
|
||||
- **主服务**: `10.79.97.17:51703` - 核心应用服务
|
||||
- **客户端A**: `10.79.97.17:51701` - 地区A客户访问
|
||||
- **客户端B**: `10.79.97.17:51702` - 地区B客户访问
|
||||
- **客户端C**: `10.79.97.17:51704` - 地区C客户访问
|
||||
|
||||
## 文件说明
|
||||
|
||||
### 1. ecosystem.config.cjs
|
||||
PM2部署配置文件,定义了4个应用实例:
|
||||
- `docreview-main`: 主服务 (端口51703)
|
||||
- `docreview-client-a`: 客户端A代理 (端口51701)
|
||||
- `docreview-client-b`: 客户端B代理 (端口51702)
|
||||
- `docreview-client-c`: 客户端C代理 (端口51704)
|
||||
|
||||
### 2. api-config.ts
|
||||
应用配置文件,支持根据`CLIENT_ID`环境变量加载不同客户端配置:
|
||||
- 默认配置 (main)
|
||||
- 客户端A配置 (client-a)
|
||||
- 客户端B配置 (client-b)
|
||||
- 客户端C配置 (client-c)
|
||||
|
||||
### 3. nginx-multi-client.conf
|
||||
Nginx反向代理配置文件,为每个客户端端口配置独立的代理规则。
|
||||
|
||||
### 4. 部署脚本
|
||||
- `deploy-multi-client.sh`: Linux/macOS部署脚本
|
||||
- `deploy-multi-client.bat`: Windows部署脚本
|
||||
|
||||
## 部署步骤
|
||||
|
||||
### Windows环境部署
|
||||
|
||||
1. **检查环境依赖**
|
||||
```bash
|
||||
node --version
|
||||
npm --version
|
||||
pm2 --version
|
||||
```
|
||||
|
||||
2. **使用部署脚本**
|
||||
```bash
|
||||
# 完整部署
|
||||
deploy-multi-client.bat deploy
|
||||
|
||||
# 仅构建项目
|
||||
deploy-multi-client.bat build
|
||||
|
||||
# 仅部署PM2
|
||||
deploy-multi-client.bat pm2
|
||||
|
||||
# 检查状态
|
||||
deploy-multi-client.bat status
|
||||
```
|
||||
|
||||
3. **手动配置Nginx** (Windows)
|
||||
- 安装Nginx for Windows
|
||||
- 将`nginx-multi-client.conf`内容添加到nginx配置中
|
||||
- 重启Nginx服务
|
||||
|
||||
### Linux/macOS环境部署
|
||||
|
||||
1. **使用部署脚本**
|
||||
```bash
|
||||
chmod +x deploy-multi-client.sh
|
||||
|
||||
# 完整部署(包含Nginx配置)
|
||||
./deploy-multi-client.sh deploy
|
||||
|
||||
# 检查状态
|
||||
./deploy-multi-client.sh status
|
||||
```
|
||||
|
||||
### 手动部署步骤
|
||||
|
||||
1. **构建项目**
|
||||
```bash
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
2. **启动PM2应用**
|
||||
```bash
|
||||
pm2 start ecosystem.config.cjs
|
||||
pm2 save
|
||||
pm2 startup
|
||||
```
|
||||
|
||||
3. **配置Nginx**
|
||||
```bash
|
||||
# 复制配置文件
|
||||
sudo cp nginx-multi-client.conf /etc/nginx/sites-available/docreview-multi-client
|
||||
sudo ln -s /etc/nginx/sites-available/docreview-multi-client /etc/nginx/sites-enabled/
|
||||
|
||||
# 测试配置
|
||||
sudo nginx -t
|
||||
|
||||
# 重载配置
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
## 配置说明
|
||||
|
||||
### 客户端特定配置
|
||||
|
||||
每个客户端可以有独立的配置,在`api-config.ts`中定义:
|
||||
|
||||
```typescript
|
||||
const clientConfigs = {
|
||||
'client-a': {
|
||||
baseUrl: 'http://10.79.97.17:51701/api',
|
||||
uploadUrl: 'http://10.79.97.17:51701/api/upload',
|
||||
oauth: {
|
||||
serverUrl: 'http://10.79.97.17:51701/oauth',
|
||||
clientId: 'client-a-id',
|
||||
// ... 其他配置
|
||||
}
|
||||
},
|
||||
// ... 其他客户端配置
|
||||
};
|
||||
```
|
||||
|
||||
### 环境变量
|
||||
|
||||
每个PM2应用实例都有独立的环境变量:
|
||||
|
||||
- `CLIENT_ID`: 客户端标识 (main, client-a, client-b, client-c)
|
||||
- `PROXY_TARGET`: 代理目标地址 (仅客户端实例)
|
||||
- `PORT`: 监听端口
|
||||
|
||||
## 监控和管理
|
||||
|
||||
### PM2管理命令
|
||||
|
||||
```bash
|
||||
# 查看所有应用状态
|
||||
pm2 status
|
||||
|
||||
# 查看特定应用日志
|
||||
pm2 logs docreview-main
|
||||
pm2 logs docreview-client-a
|
||||
|
||||
# 重启应用
|
||||
pm2 restart docreview-main
|
||||
pm2 restart all
|
||||
|
||||
# 停止应用
|
||||
pm2 stop docreview-main
|
||||
pm2 stop all
|
||||
|
||||
# 删除应用
|
||||
pm2 delete docreview-main
|
||||
pm2 delete all
|
||||
```
|
||||
|
||||
### 日志文件位置
|
||||
|
||||
**PM2日志**:
|
||||
- 主服务: `logs/main-out.log`, `logs/main-error.log`
|
||||
- 客户端A: `logs/client-a-out.log`, `logs/client-a-error.log`
|
||||
- 客户端B: `logs/client-b-out.log`, `logs/client-b-error.log`
|
||||
- 客户端C: `logs/client-c-out.log`, `logs/client-c-error.log`
|
||||
|
||||
**Nginx日志**:
|
||||
- 客户端A: `/var/log/nginx/client-a-access.log`, `/var/log/nginx/client-a-error.log`
|
||||
- 客户端B: `/var/log/nginx/client-b-access.log`, `/var/log/nginx/client-b-error.log`
|
||||
- 客户端C: `/var/log/nginx/client-c-access.log`, `/var/log/nginx/client-c-error.log`
|
||||
|
||||
### 健康检查
|
||||
|
||||
每个客户端端口都提供健康检查接口:
|
||||
|
||||
```bash
|
||||
# 检查各端口状态
|
||||
curl http://10.79.97.17:51701/health # 客户端A
|
||||
curl http://10.79.97.17:51702/health # 客户端B
|
||||
curl http://10.79.97.17:51703/health # 主服务
|
||||
curl http://10.79.97.17:51704/health # 客户端C
|
||||
```
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **端口被占用**
|
||||
```bash
|
||||
# 查看端口占用
|
||||
netstat -tlnp | grep :51703
|
||||
|
||||
# 杀死占用进程
|
||||
kill -9 <PID>
|
||||
```
|
||||
|
||||
2. **PM2应用启动失败**
|
||||
```bash
|
||||
# 查看详细错误日志
|
||||
pm2 logs docreview-main --lines 50
|
||||
|
||||
# 重新加载配置
|
||||
pm2 reload ecosystem.config.cjs
|
||||
```
|
||||
|
||||
3. **Nginx代理失败**
|
||||
```bash
|
||||
# 检查nginx配置
|
||||
sudo nginx -t
|
||||
|
||||
# 查看nginx错误日志
|
||||
sudo tail -f /var/log/nginx/error.log
|
||||
```
|
||||
|
||||
4. **客户端配置不生效**
|
||||
- 检查`CLIENT_ID`环境变量是否正确设置
|
||||
- 确认`api-config.ts`中的客户端配置是否正确
|
||||
- 重启相关PM2应用
|
||||
|
||||
### 调试模式
|
||||
|
||||
启用调试模式查看详细日志:
|
||||
|
||||
```bash
|
||||
# 设置调试环境变量
|
||||
export DEBUG=*
|
||||
|
||||
# 重启应用
|
||||
pm2 restart all
|
||||
```
|
||||
|
||||
## 扩展和优化
|
||||
|
||||
### 添加新客户端
|
||||
|
||||
1. 在`ecosystem.config.cjs`中添加新的应用配置
|
||||
2. 在`api-config.ts`中添加客户端特定配置
|
||||
3. 在`nginx-multi-client.conf`中添加新的server块
|
||||
4. 重新部署应用
|
||||
|
||||
### 性能优化
|
||||
|
||||
1. **启用Nginx缓存**
|
||||
2. **配置负载均衡**
|
||||
3. **启用Gzip压缩**
|
||||
4. **配置SSL/TLS**
|
||||
|
||||
### 安全加固
|
||||
|
||||
1. **配置防火墙规则**
|
||||
2. **启用访问控制**
|
||||
3. **配置SSL证书**
|
||||
4. **设置访问频率限制**
|
||||
|
||||
## 联系支持
|
||||
|
||||
如果在部署过程中遇到问题,请检查:
|
||||
1. 系统依赖是否完整安装
|
||||
2. 端口是否被其他服务占用
|
||||
3. 配置文件语法是否正确
|
||||
4. 日志文件中的错误信息
|
||||
|
||||
部署完成后,可以通过以下地址访问不同客户端:
|
||||
- 客户端A: http://10.79.97.17:51701
|
||||
- 客户端B: http://10.79.97.17:51702
|
||||
- 客户端C: http://10.79.97.17:51704
|
||||
- 主服务: http://10.79.97.17:51703
|
||||
+112
-113
@@ -1,4 +1,4 @@
|
||||
import { postgrestGet, type PostgrestParams } from "../postgrest-client";
|
||||
import { postgrestGet, postgrestPost, type PostgrestParams } from "../postgrest-client";
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
/**
|
||||
@@ -96,7 +96,7 @@ function buildTypeFilter(reviewType: string | null): string {
|
||||
* @param reviewType 从客户端传入的 reviewType 值
|
||||
* @returns 主页数据
|
||||
*/
|
||||
export async function getHomeData(reviewType?: string | null): Promise<HomeStatistics> {
|
||||
export async function getHomeData(reviewType?: string | null,userId?: string | number): Promise<HomeStatistics> {
|
||||
try {
|
||||
// 获取当前日期和时间相关值
|
||||
const startOfToday = dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||
@@ -105,7 +105,8 @@ export async function getHomeData(reviewType?: string | null): Promise<HomeStati
|
||||
const startOfLastMonth = dayjs().subtract(1, 'month').startOf('month').format('YYYY-MM-DD HH:mm:ss');
|
||||
const endOfLastMonth = dayjs().subtract(1, 'month').endOf('month').format('YYYY-MM-DD HH:mm:ss');
|
||||
|
||||
// console.log('传入的 reviewType', reviewType);
|
||||
console.log('传入的 reviewType', reviewType);
|
||||
console.log('传入的 userId', userId);
|
||||
|
||||
// 基于 reviewType 构建类型过滤条件
|
||||
const typeFilter = buildTypeFilter(reviewType || null);
|
||||
@@ -146,7 +147,8 @@ export async function getHomeData(reviewType?: string | null): Promise<HomeStati
|
||||
filter: {
|
||||
or: `(audit_status.eq.0,audit_status.eq.2,audit_status.is.null)`,
|
||||
created_at: `gte.${startOfToday}`,
|
||||
is_test_document: `eq.false`
|
||||
is_test_document: `eq.false`,
|
||||
user_id: `eq.${userId}`
|
||||
}
|
||||
};
|
||||
|
||||
@@ -180,7 +182,8 @@ export async function getHomeData(reviewType?: string | null): Promise<HomeStati
|
||||
filter: {
|
||||
and: `(audit_status.neq.0,audit_status.neq.2)`,
|
||||
updated_at: `gte.${startOfThisMonth}`,
|
||||
is_test_document: `eq.false`
|
||||
is_test_document: `eq.false`,
|
||||
user_id: `eq.${userId}`
|
||||
}
|
||||
};
|
||||
|
||||
@@ -211,7 +214,8 @@ export async function getHomeData(reviewType?: string | null): Promise<HomeStati
|
||||
filter: {
|
||||
or: `(audit_status.eq.1,audit_status.eq.-1)`,
|
||||
and: `(updated_at.gte.${startOfLastMonth},updated_at.lte.${endOfLastMonth})`,
|
||||
is_test_document: `eq.false`
|
||||
is_test_document: `eq.false`,
|
||||
user_id: `eq.${userId}`
|
||||
}
|
||||
};
|
||||
|
||||
@@ -258,7 +262,8 @@ export async function getHomeData(reviewType?: string | null): Promise<HomeStati
|
||||
filter: {
|
||||
audit_status: `eq.1`,
|
||||
created_at: `gte.${startOfThisMonth}`,
|
||||
is_test_document: `eq.false`
|
||||
is_test_document: `eq.false`,
|
||||
user_id: `eq.${userId}`
|
||||
}
|
||||
};
|
||||
|
||||
@@ -294,7 +299,8 @@ export async function getHomeData(reviewType?: string | null): Promise<HomeStati
|
||||
filter: {
|
||||
audit_status: `eq.1`,
|
||||
and: `(updated_at.gte.${startOfLastMonth},updated_at.lte.${endOfLastMonth})`,
|
||||
is_test_document: `eq.false`
|
||||
is_test_document: `eq.false`,
|
||||
user_id: `eq.${userId}`
|
||||
}
|
||||
};
|
||||
|
||||
@@ -352,15 +358,18 @@ export async function getHomeData(reviewType?: string | null): Promise<HomeStati
|
||||
// 根据 reviewType 设置要查询的文档类型
|
||||
if (reviewType === 'contract') {
|
||||
// 合同类型 - 直接查询类型 1
|
||||
const typeToQuery = 1;
|
||||
const typeToQuery = [1];
|
||||
|
||||
// 调用数据库函数获取本月指定类型的问题数量
|
||||
|
||||
const thisMonthIssuesResponse = await handleApiResponse<{ count: number }[]>(
|
||||
postgrestGet(`rpc/count_evaluation_results_by_type?type_val=${typeToQuery}&start_time=${startOfThisMonth}&end_time=${endOfThisMonth}`, {
|
||||
select: '*',
|
||||
filter: {}
|
||||
postgrestPost('rpc/count_evaluation_results_by_type', {
|
||||
start_time: startOfThisMonth,
|
||||
end_time: endOfThisMonth,
|
||||
type_val: typeToQuery,
|
||||
userid: parseInt(userId as string)
|
||||
}),
|
||||
'获取本月问题数据失败',
|
||||
'获取合同本月问题数据失败',
|
||||
[]
|
||||
);
|
||||
|
||||
@@ -369,9 +378,11 @@ export async function getHomeData(reviewType?: string | null): Promise<HomeStati
|
||||
|
||||
// 调用数据库函数获取上月指定类型的问题数量
|
||||
const lastMonthIssuesResponse = await handleApiResponse<{ count: number }[]>(
|
||||
postgrestGet(`rpc/count_evaluation_results_by_type?type_val=${typeToQuery}&start_time=${startOfLastMonth}&end_time=${endOfLastMonth}`, {
|
||||
select: '*',
|
||||
filter: {}
|
||||
postgrestPost('rpc/count_evaluation_results_by_type', {
|
||||
start_time: startOfLastMonth,
|
||||
end_time: endOfLastMonth,
|
||||
type_val: typeToQuery,
|
||||
userid: parseInt(userId as string)
|
||||
}),
|
||||
'获取上月问题数据失败',
|
||||
[]
|
||||
@@ -382,120 +393,108 @@ export async function getHomeData(reviewType?: string | null): Promise<HomeStati
|
||||
|
||||
} else if (reviewType === 'record') {
|
||||
// 记录类型 - 需要查询类型 2 和类型 3,并合并结果
|
||||
const typeToQuery = [2,3];
|
||||
|
||||
// 查询类型 2 的本月问题数量
|
||||
const thisMonthType2Response = await handleApiResponse<{ count: number }[]>(
|
||||
postgrestGet(`rpc/count_evaluation_results_by_type?type_val=2&start_time=${startOfThisMonth}&end_time=${endOfThisMonth}`, {
|
||||
select: '*',
|
||||
filter: {}
|
||||
postgrestPost('rpc/count_evaluation_results_by_type', {
|
||||
start_time: startOfThisMonth,
|
||||
end_time: endOfThisMonth,
|
||||
type_val: typeToQuery,
|
||||
userid: parseInt(userId as string)
|
||||
}),
|
||||
'获取本月类型2问题数据失败',
|
||||
'获取本月许可卷宗类型2问题数据失败',
|
||||
[]
|
||||
);
|
||||
|
||||
// 查询类型 3 的本月问题数量
|
||||
const thisMonthType3Response = await handleApiResponse<{ count: number }[]>(
|
||||
postgrestGet(`rpc/count_evaluation_results_by_type?type_val=3&start_time=${startOfThisMonth}&end_time=${endOfThisMonth}`, {
|
||||
select: '*',
|
||||
filter: {}
|
||||
}),
|
||||
'获取本月类型3问题数据失败',
|
||||
[]
|
||||
);
|
||||
|
||||
// 合并本月两种类型的问题数量
|
||||
// 本月两种类型的问题数量
|
||||
const thisMonthType2Count = thisMonthType2Response[0]?.count || 0;
|
||||
const thisMonthType3Count = thisMonthType3Response[0]?.count || 0;
|
||||
thisMonthIssuesCount = thisMonthType2Count + thisMonthType3Count;
|
||||
thisMonthIssuesCount = thisMonthType2Count
|
||||
|
||||
// 查询类型 2 的上月问题数量
|
||||
// 上月两种类型的问题数量
|
||||
const lastMonthType2Response = await handleApiResponse<{ count: number }[]>(
|
||||
postgrestGet(`rpc/count_evaluation_results_by_type?type_val=2&start_time=${startOfLastMonth}&end_time=${endOfLastMonth}`, {
|
||||
select: '*',
|
||||
filter: {}
|
||||
postgrestPost('rpc/count_evaluation_results_by_type', {
|
||||
start_time: startOfLastMonth,
|
||||
end_time: endOfLastMonth,
|
||||
type_val: typeToQuery,
|
||||
userid: parseInt(userId as string)
|
||||
}),
|
||||
'获取上月类型2问题数据失败',
|
||||
'获取上月许可卷宗类型2问题数据失败',
|
||||
[]
|
||||
);
|
||||
|
||||
// 查询类型 3 的上月问题数量
|
||||
const lastMonthType3Response = await handleApiResponse<{ count: number }[]>(
|
||||
postgrestGet(`rpc/count_evaluation_results_by_type?type_val=3&start_time=${startOfLastMonth}&end_time=${endOfLastMonth}`, {
|
||||
select: '*',
|
||||
filter: {}
|
||||
}),
|
||||
'获取上月类型3问题数据失败',
|
||||
[]
|
||||
);
|
||||
|
||||
|
||||
// 合并上月两种类型的问题数量
|
||||
// 上月两种类型的问题数量
|
||||
const lastMonthType2Count = lastMonthType2Response[0]?.count || 0;
|
||||
const lastMonthType3Count = lastMonthType3Response[0]?.count || 0;
|
||||
lastMonthIssuesCount = lastMonthType2Count + lastMonthType3Count;
|
||||
lastMonthIssuesCount = lastMonthType2Count
|
||||
|
||||
} else {
|
||||
// 如果没有指定类型,则使用原来的查询方式获取所有类型的问题数量
|
||||
const thisMonthIssuesParams: PostgrestParams = {
|
||||
select: 'count',
|
||||
filter: {
|
||||
and: `(created_at.gte.${startOfThisMonth},created_at.lte.${endOfThisMonth})`,
|
||||
'evaluated_results->result': 'eq.false' // 使用->操作符访问JSONB字段
|
||||
}
|
||||
};
|
||||
|
||||
// 添加类型过滤条件
|
||||
if (typeFilter) {
|
||||
if (typeFilter.startsWith('(')) {
|
||||
thisMonthIssuesParams.or = typeFilter;
|
||||
} else {
|
||||
const [field, op, value] = typeFilter.split('.');
|
||||
if (!thisMonthIssuesParams.filter) {
|
||||
thisMonthIssuesParams.filter = {};
|
||||
}
|
||||
thisMonthIssuesParams.filter[field] = `${op}.${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
const thisMonthIssuesResponse = await handleApiResponse<{ count: number }[]>(
|
||||
postgrestGet('evaluation_results', thisMonthIssuesParams),
|
||||
'获取本月问题数据失败',
|
||||
[]
|
||||
);
|
||||
|
||||
// 本月问题数量
|
||||
thisMonthIssuesCount = thisMonthIssuesResponse[0]?.count || 0;
|
||||
|
||||
// 上月问题数量
|
||||
const lastMonthIssuesParams: PostgrestParams = {
|
||||
select: 'count',
|
||||
filter: {
|
||||
and: `(created_at.gte.${startOfLastMonth},created_at.lte.${endOfLastMonth})`,
|
||||
'evaluated_results->result': 'eq.false' // 使用->操作符访问JSONB字段
|
||||
}
|
||||
};
|
||||
|
||||
// 添加类型过滤条件
|
||||
if (typeFilter) {
|
||||
if (typeFilter.startsWith('(')) {
|
||||
lastMonthIssuesParams.or = typeFilter;
|
||||
} else {
|
||||
const [field, op, value] = typeFilter.split('.');
|
||||
if (!lastMonthIssuesParams.filter) {
|
||||
lastMonthIssuesParams.filter = {};
|
||||
}
|
||||
lastMonthIssuesParams.filter[field] = `${op}.${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
const lastMonthIssuesResponse = await handleApiResponse<{ count: number }[]>(
|
||||
postgrestGet('evaluation_results', lastMonthIssuesParams),
|
||||
'获取上月问题数据失败',
|
||||
[]
|
||||
);
|
||||
|
||||
// 上月问题数量
|
||||
lastMonthIssuesCount = lastMonthIssuesResponse[0]?.count || 0;
|
||||
}
|
||||
// 暂时不会存在没有指定类型得情况,暂不实现。
|
||||
// else {
|
||||
// // 如果没有指定类型,则使用原来的查询方式获取所有类型的问题数量
|
||||
// const thisMonthIssuesParams: PostgrestParams = {
|
||||
// select: 'count',
|
||||
// filter: {
|
||||
// and: `(created_at.gte.${startOfThisMonth},created_at.lte.${endOfThisMonth})`,
|
||||
// 'evaluated_results->result': 'eq.false',
|
||||
// user_id: `eq.${userId}`
|
||||
// }
|
||||
// };
|
||||
|
||||
// // 添加类型过滤条件
|
||||
// if (typeFilter) {
|
||||
// if (typeFilter.startsWith('(')) {
|
||||
// thisMonthIssuesParams.or = typeFilter;
|
||||
// } else {
|
||||
// const [field, op, value] = typeFilter.split('.');
|
||||
// if (!thisMonthIssuesParams.filter) {
|
||||
// thisMonthIssuesParams.filter = {};
|
||||
// }
|
||||
// thisMonthIssuesParams.filter[field] = `${op}.${value}`;
|
||||
// }
|
||||
// }
|
||||
|
||||
// const thisMonthIssuesResponse = await handleApiResponse<{ count: number }[]>(
|
||||
// postgrestGet('evaluation_results', thisMonthIssuesParams),
|
||||
// '获取本月问题数据失败',
|
||||
// []
|
||||
// );
|
||||
|
||||
// // 本月问题数量
|
||||
// thisMonthIssuesCount = thisMonthIssuesResponse[0]?.count || 0;
|
||||
|
||||
// // 上月问题数量
|
||||
// const lastMonthIssuesParams: PostgrestParams = {
|
||||
// select: 'count',
|
||||
// filter: {
|
||||
// and: `(created_at.gte.${startOfLastMonth},created_at.lte.${endOfLastMonth})`,
|
||||
// 'evaluated_results->result': 'eq.false',
|
||||
// user_id: `eq.${userId}`
|
||||
// }
|
||||
// };
|
||||
|
||||
// // 添加类型过滤条件
|
||||
// if (typeFilter) {
|
||||
// if (typeFilter.startsWith('(')) {
|
||||
// lastMonthIssuesParams.or = typeFilter;
|
||||
// } else {
|
||||
// const [field, op, value] = typeFilter.split('.');
|
||||
// if (!lastMonthIssuesParams.filter) {
|
||||
// lastMonthIssuesParams.filter = {};
|
||||
// }
|
||||
// lastMonthIssuesParams.filter[field] = `${op}.${value}`;
|
||||
// }
|
||||
// }
|
||||
|
||||
// const lastMonthIssuesResponse = await handleApiResponse<{ count: number }[]>(
|
||||
// postgrestGet('evaluation_results', lastMonthIssuesParams),
|
||||
// '获取上月问题数据失败',
|
||||
// []
|
||||
// );
|
||||
|
||||
// // 上月问题数量
|
||||
// lastMonthIssuesCount = lastMonthIssuesResponse[0]?.count || 0;
|
||||
// }
|
||||
|
||||
// 计算问题数量同比增长
|
||||
let issuesGrowthValue = 0;
|
||||
|
||||
+110
-6
@@ -44,7 +44,7 @@ const configs: Record<string, ApiConfig> = {
|
||||
serverUrl: 'http://10.79.112.85', // IDaaS服务器地址
|
||||
clientId: '54d2a619fe5c81ae1250434c441fccccqMtKwh7H4fO',
|
||||
clientSecret: 'VYk1AC5XIJEfnEXwyq0u9JEY3fi3byCfSD58zANGeb', // 需要替换为实际的Client Secret
|
||||
redirectUri: 'http://10.79.97.17/', // 回调地址
|
||||
redirectUri: 'http://10.79.97.17/callback', // 回调地址
|
||||
appId: 'idaasoauth2' // 应用ID,用于登出
|
||||
}
|
||||
},
|
||||
@@ -75,7 +75,8 @@ const configs: Record<string, ApiConfig> = {
|
||||
serverUrl: 'http://10.79.112.85', // IDaaS服务器地址
|
||||
clientId: '54d2a619fe5c81ae1250434c441fccccqMtKwh7H4fO',
|
||||
clientSecret: 'VYk1AC5XIJEfnEXwyq0u9JEY3fi3byCfSD58zANGeb', // 需要替换为实际的Client Secret
|
||||
redirectUri: 'http://10.79.97.17/', // 回调地址
|
||||
redirectUri: 'http://10.79.97.17/callback', // 回调地址
|
||||
|
||||
appId: 'idaasoauth2' // 应用ID,用于登出
|
||||
}
|
||||
},
|
||||
@@ -95,12 +96,97 @@ const configs: Record<string, ApiConfig> = {
|
||||
}
|
||||
};
|
||||
|
||||
// 客户端特定配置 - 支持多客户端部署
|
||||
// 根据环境自动选择配置
|
||||
const getClientConfigs = (env: string): Record<string, Partial<ApiConfig>> => {
|
||||
if (env === 'development') {
|
||||
// 开发环境 - 本地nginx代理配置
|
||||
return {
|
||||
'client-a': {
|
||||
baseUrl: 'http://localhost:8001',
|
||||
uploadUrl: 'http://localhost:8001/admin/documents',
|
||||
oauth: {
|
||||
serverUrl: 'http://10.79.112.85',
|
||||
clientId: '54d2a619fe5c81ae1250434c441fccccqMtKwh7H4fO',
|
||||
clientSecret: 'VYk1AC5XIJEfnEXwyq0u9JEY3fi3byCfSD58zANGeb',
|
||||
redirectUri: 'http://localhost:8001/callback',
|
||||
appId: 'idaasoauth2'
|
||||
}
|
||||
},
|
||||
'client-b': {
|
||||
baseUrl: 'http://localhost:8002',
|
||||
uploadUrl: 'http://localhost:8002/admin/documents',
|
||||
oauth: {
|
||||
serverUrl: 'http://10.79.112.85',
|
||||
clientId: '54d2a619fe5c81ae1250434c441fccccqMtKwh7H4fO',
|
||||
clientSecret: 'VYk1AC5XIJEfnEXwyq0u9JEY3fi3byCfSD58zANGeb',
|
||||
redirectUri: 'http://localhost:8002/callback',
|
||||
appId: 'idaasoauth2'
|
||||
}
|
||||
},
|
||||
'client-c': {
|
||||
baseUrl: 'http://localhost:8003',
|
||||
uploadUrl: 'http://localhost:8003/admin/documents',
|
||||
oauth: {
|
||||
serverUrl: 'http://10.79.112.85',
|
||||
clientId: '54d2a619fe5c81ae1250434c441fccccqMtKwh7H4fO',
|
||||
clientSecret: 'VYk1AC5XIJEfnEXwyq0u9JEY3fi3byCfSD58zANGeb',
|
||||
redirectUri: 'http://localhost:8003/callback',
|
||||
appId: 'idaasoauth2'
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// 生产环境 - 服务器配置
|
||||
return {
|
||||
'client-a': {
|
||||
baseUrl: 'http://10.79.97.17:51701',
|
||||
uploadUrl: 'http://10.79.97.17:51701/admin/documents',
|
||||
oauth: {
|
||||
serverUrl: 'http://10.79.112.85',
|
||||
clientId: '54d2a619fe5c81ae1250434c441fccccqMtKwh7H4fO',
|
||||
clientSecret: 'VYk1AC5XIJEfnEXwyq0u9JEY3fi3byCfSD58zANGeb',
|
||||
redirectUri: 'http://10.79.97.17:51701/callback',
|
||||
appId: 'idaasoauth2'
|
||||
}
|
||||
},
|
||||
'client-b': {
|
||||
baseUrl: 'http://10.79.97.17:51702',
|
||||
uploadUrl: 'http://10.79.97.17:51702/admin/documents',
|
||||
oauth: {
|
||||
serverUrl: 'http://10.79.112.85',
|
||||
clientId: '54d2a619fe5c81ae1250434c441fccccqMtKwh7H4fO',
|
||||
clientSecret: 'VYk1AC5XIJEfnEXwyq0u9JEY3fi3byCfSD58zANGeb',
|
||||
redirectUri: 'http://10.79.97.17:51702/callback',
|
||||
appId: 'idaasoauth2'
|
||||
}
|
||||
},
|
||||
'client-c': {
|
||||
baseUrl: 'http://10.79.97.17:51704',
|
||||
uploadUrl: 'http://10.79.97.17:51704/admin/documents',
|
||||
oauth: {
|
||||
serverUrl: 'http://10.79.112.85',
|
||||
clientId: '54d2a619fe5c81ae1250434c441fccccqMtKwh7H4fO',
|
||||
clientSecret: 'VYk1AC5XIJEfnEXwyq0u9JEY3fi3byCfSD58zANGeb',
|
||||
redirectUri: 'http://10.79.97.17:51704/callback',
|
||||
appId: 'idaasoauth2'
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// 获取当前环境,默认为development
|
||||
const getCurrentEnvironment = (): string => {
|
||||
// 优先使用环境变量,然后使用 NODE_ENV
|
||||
return process.env.NEXT_PUBLIC_API_ENV || process.env.NODE_ENV || 'development';
|
||||
};
|
||||
|
||||
// 获取客户端ID
|
||||
const getClientId = (): string => {
|
||||
return process.env.CLIENT_ID || process.env.NEXT_PUBLIC_CLIENT_ID || 'main';
|
||||
};
|
||||
|
||||
// 从环境变量获取配置,如果环境变量不存在则使用默认配置
|
||||
const getConfigFromEnv = (defaultConfig: ApiConfig): ApiConfig => {
|
||||
return {
|
||||
@@ -117,17 +203,35 @@ const getConfigFromEnv = (defaultConfig: ApiConfig): ApiConfig => {
|
||||
};
|
||||
};
|
||||
|
||||
// 获取当前配置
|
||||
// 获取当前配置 - 支持客户端特定配置
|
||||
const getCurrentConfig = (): ApiConfig => {
|
||||
const env = getCurrentEnvironment();
|
||||
const clientId = getClientId();
|
||||
const defaultConfig = configs[env] || configs.development;
|
||||
|
||||
// 获取当前环境的客户端特定配置
|
||||
const clientConfigs = getClientConfigs(env);
|
||||
const clientConfig = clientConfigs[clientId];
|
||||
|
||||
// 合并默认配置和客户端特定配置
|
||||
let finalConfig = defaultConfig;
|
||||
if (clientConfig) {
|
||||
finalConfig = {
|
||||
...defaultConfig,
|
||||
...clientConfig,
|
||||
oauth: {
|
||||
...defaultConfig.oauth,
|
||||
...clientConfig.oauth
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 如果是浏览器环境,尝试从环境变量覆盖配置
|
||||
if (typeof window !== 'undefined' || process.env.NEXT_PUBLIC_API_BASE_URL) {
|
||||
return getConfigFromEnv(defaultConfig);
|
||||
return getConfigFromEnv(finalConfig);
|
||||
}
|
||||
|
||||
return defaultConfig;
|
||||
return finalConfig;
|
||||
};
|
||||
|
||||
// 导出当前环境的配置
|
||||
@@ -155,4 +259,4 @@ export const setEnvironment = (env: string): ApiConfig => {
|
||||
// environment: getCurrentEnvironment(),
|
||||
// config: apiConfig
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
+18
-11
@@ -11,7 +11,7 @@ import { getDocuments, type DocumentUI, type DocumentSearchParams } from "~/api/
|
||||
import { useState, useEffect } from "react";
|
||||
import { getHomeData } from "~/api/home/home";
|
||||
import dayjs from 'dayjs';
|
||||
import type { UserRole } from '~/api/login/auth.server';
|
||||
// import type { UserRole } from '~/api/login/auth.server';
|
||||
import { type ActionFunctionArgs, type LoaderFunctionArgs } from "@remix-run/node";
|
||||
import { logout, getUserSession } from "~/api/login/auth.server";
|
||||
|
||||
@@ -48,7 +48,8 @@ export const meta: MetaFunction = () => {
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
try {
|
||||
// 从根loader获取用户角色
|
||||
const { userRole } = await getUserSession(request);
|
||||
const { userRole, userInfo, frontendJWT } = await getUserSession(request);
|
||||
|
||||
|
||||
// 返回默认值,实际数据将在客户端根据 sessionStorage 加载
|
||||
return Response.json({
|
||||
@@ -63,7 +64,9 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
||||
},
|
||||
recentFiles: [],
|
||||
reviewType: null,
|
||||
userRole: userRole
|
||||
userRole: userRole,
|
||||
userInfo,
|
||||
frontendJWT
|
||||
});
|
||||
} catch (error) {
|
||||
// 错误处理
|
||||
@@ -89,7 +92,7 @@ export async function action({ request }: ActionFunctionArgs) {
|
||||
|
||||
export default function Home() {
|
||||
const navigate = useNavigate();
|
||||
const { homeData: initialHomeData, recentFiles: initialRecentFiles, userRole: serverUserRole } = useLoaderData<typeof loader>();
|
||||
const { homeData: initialHomeData, recentFiles: initialRecentFiles, userRole: serverUserRole, userInfo } = useLoaderData<typeof loader>();
|
||||
const [recentFiles, setRecentFiles] = useState<DocumentUI[]>(initialRecentFiles || []);
|
||||
const [homeData, setHomeData] = useState(initialHomeData);
|
||||
const [currentDateTime, setCurrentDateTime] = useState({
|
||||
@@ -97,7 +100,7 @@ export default function Home() {
|
||||
time: ''
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const userRole = serverUserRole as UserRole;
|
||||
// const userRole = serverUserRole as UserRole;
|
||||
|
||||
// 打印服务器端传递的用户角色
|
||||
useEffect(() => {
|
||||
@@ -155,7 +158,7 @@ export default function Home() {
|
||||
const reviewType = sessionStorage.getItem('reviewType');
|
||||
|
||||
// 加载主页数据
|
||||
const newHomeData = await getHomeData(reviewType || undefined);
|
||||
const newHomeData = await getHomeData(reviewType || undefined,userInfo.user_id);
|
||||
setHomeData(newHomeData);
|
||||
|
||||
// 加载文档数据
|
||||
@@ -177,7 +180,8 @@ export default function Home() {
|
||||
try {
|
||||
const documentSearchParams: DocumentSearchParams = {
|
||||
page: 1,
|
||||
pageSize: 10
|
||||
pageSize: 10,
|
||||
userId: userInfo.user_id
|
||||
};
|
||||
|
||||
// 根据 reviewType 添加过滤条件
|
||||
@@ -186,6 +190,7 @@ export default function Home() {
|
||||
|
||||
const response = await getDocuments(documentSearchParams);
|
||||
if (!response.error && response.data) {
|
||||
// console.log('合同文档数据',response.data.documents);
|
||||
return response.data.documents;
|
||||
}
|
||||
} else if (reviewType === 'record') {
|
||||
@@ -209,6 +214,7 @@ export default function Home() {
|
||||
);
|
||||
|
||||
// 限制数量
|
||||
// console.log('卷宗文档数据',mergedDocs);
|
||||
return mergedDocs.slice(0, documentSearchParams.pageSize);
|
||||
}
|
||||
} else {
|
||||
@@ -236,7 +242,7 @@ export default function Home() {
|
||||
setIsLoading(true);
|
||||
|
||||
// 更新主页数据
|
||||
const newHomeData = await getHomeData(currentReviewType || undefined);
|
||||
const newHomeData = await getHomeData(currentReviewType || undefined,userInfo.user_id);
|
||||
setHomeData(newHomeData);
|
||||
|
||||
// 更新文档数据
|
||||
@@ -301,14 +307,15 @@ export default function Home() {
|
||||
</div>
|
||||
<div className="user-profile p-4 border-b border-gray-100 flex items-center">
|
||||
<div className="avatar w-10 h-10 rounded-full bg-primary text-white flex items-center justify-center">
|
||||
<span>{userRole === 'developer' ? '管' : '用'}</span>
|
||||
<span>{userInfo.nick_name.charAt(userInfo.nick_name.length-1)}</span>
|
||||
</div>
|
||||
<div className="ml-1">
|
||||
<p className="text-sm font-medium mb-0">{userRole === 'developer' ? '系统管理员' : '普通用户'}</p>
|
||||
{/* <p className="text-sm font-medium mb-0">{userRole === 'developer' ? '系统管理员' : '普通用户'}</p> */}
|
||||
<p className="text-sm font-medium mb-0">{userInfo.nick_name}</p>
|
||||
{/* <p className="text-xs text-gray-500 mb-0">{userRole === 'developer' ? '超级管理员' : '标准权限'}</p> */}
|
||||
</div>
|
||||
</div>
|
||||
{/* 登出操作 */}
|
||||
{/* 登出操作 */}
|
||||
<Button
|
||||
type="default"
|
||||
size="small"
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
#!/bin/bash
|
||||
# 多客户端部署脚本
|
||||
# 用于部署和管理3个不同地区客户端的反向代理服务
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查依赖
|
||||
check_dependencies() {
|
||||
log_info "检查系统依赖..."
|
||||
|
||||
# 检查PM2
|
||||
if ! command -v pm2 &> /dev/null; then
|
||||
log_error "PM2 未安装,请先安装: npm install -g pm2"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查Nginx
|
||||
if ! command -v nginx &> /dev/null; then
|
||||
log_warning "Nginx 未安装,请手动安装并配置"
|
||||
fi
|
||||
|
||||
# 检查Node.js
|
||||
if ! command -v node &> /dev/null; then
|
||||
log_error "Node.js 未安装"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "依赖检查完成"
|
||||
}
|
||||
|
||||
# 构建项目
|
||||
build_project() {
|
||||
log_info "构建项目..."
|
||||
|
||||
# 安装依赖
|
||||
if [ -f "package-lock.json" ]; then
|
||||
npm ci
|
||||
else
|
||||
npm install
|
||||
fi
|
||||
|
||||
# 构建项目
|
||||
npm run build
|
||||
|
||||
log_success "项目构建完成"
|
||||
}
|
||||
|
||||
# 创建日志目录
|
||||
create_log_dirs() {
|
||||
log_info "创建日志目录..."
|
||||
|
||||
mkdir -p logs
|
||||
mkdir -p /var/log/nginx 2>/dev/null || log_warning "无法创建nginx日志目录,请手动创建"
|
||||
|
||||
log_success "日志目录创建完成"
|
||||
}
|
||||
|
||||
# 部署PM2应用
|
||||
deploy_pm2() {
|
||||
log_info "部署PM2应用..."
|
||||
|
||||
# 停止现有应用
|
||||
pm2 delete all 2>/dev/null || log_warning "没有运行中的PM2应用"
|
||||
|
||||
# 启动新应用
|
||||
pm2 start ecosystem.config.cjs
|
||||
|
||||
# 保存PM2配置
|
||||
pm2 save
|
||||
|
||||
# 设置开机自启
|
||||
pm2 startup
|
||||
|
||||
log_success "PM2应用部署完成"
|
||||
}
|
||||
|
||||
# 配置Nginx
|
||||
configure_nginx() {
|
||||
log_info "配置Nginx..."
|
||||
|
||||
# 检查配置文件
|
||||
if [ ! -f "nginx-multi-client.conf" ]; then
|
||||
log_error "nginx-multi-client.conf 文件不存在"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 复制配置文件到nginx目录
|
||||
if [ -d "/etc/nginx/sites-available" ]; then
|
||||
sudo cp nginx-multi-client.conf /etc/nginx/sites-available/docreview-multi-client
|
||||
sudo ln -sf /etc/nginx/sites-available/docreview-multi-client /etc/nginx/sites-enabled/
|
||||
elif [ -d "/etc/nginx/conf.d" ]; then
|
||||
sudo cp nginx-multi-client.conf /etc/nginx/conf.d/docreview-multi-client.conf
|
||||
else
|
||||
log_warning "请手动配置Nginx,配置文件: nginx-multi-client.conf"
|
||||
return
|
||||
fi
|
||||
|
||||
# 测试nginx配置
|
||||
sudo nginx -t
|
||||
|
||||
# 重载nginx
|
||||
sudo systemctl reload nginx
|
||||
|
||||
log_success "Nginx配置完成"
|
||||
}
|
||||
|
||||
# 检查服务状态
|
||||
check_status() {
|
||||
log_info "检查服务状态..."
|
||||
|
||||
echo "\n=== PM2 应用状态 ==="
|
||||
pm2 status
|
||||
|
||||
echo "\n=== 端口监听状态 ==="
|
||||
netstat -tlnp | grep -E ':(51701|51702|51703|51704)'
|
||||
|
||||
echo "\n=== 服务健康检查 ==="
|
||||
for port in 51701 51702 51703 51704; do
|
||||
if curl -s "http://10.79.97.17:$port/health" > /dev/null; then
|
||||
log_success "端口 $port: 正常"
|
||||
else
|
||||
log_error "端口 $port: 异常"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "多客户端部署脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [选项]"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " deploy 完整部署(构建+PM2+Nginx)"
|
||||
echo " build 仅构建项目"
|
||||
echo " pm2 仅部署PM2应用"
|
||||
echo " nginx 仅配置Nginx"
|
||||
echo " status 检查服务状态"
|
||||
echo " stop 停止所有服务"
|
||||
echo " restart 重启所有服务"
|
||||
echo " logs 查看日志"
|
||||
echo " help 显示帮助信息"
|
||||
echo ""
|
||||
echo "客户端访问地址:"
|
||||
echo " 客户端A: http://10.79.97.17:51701"
|
||||
echo " 客户端B: http://10.79.97.17:51702"
|
||||
echo " 客户端C: http://10.79.97.17:51704"
|
||||
echo " 主服务: http://10.79.97.17:51703"
|
||||
}
|
||||
|
||||
# 停止服务
|
||||
stop_services() {
|
||||
log_info "停止服务..."
|
||||
pm2 stop all
|
||||
log_success "服务已停止"
|
||||
}
|
||||
|
||||
# 重启服务
|
||||
restart_services() {
|
||||
log_info "重启服务..."
|
||||
pm2 restart all
|
||||
log_success "服务已重启"
|
||||
}
|
||||
|
||||
# 查看日志
|
||||
show_logs() {
|
||||
echo "选择要查看的日志:"
|
||||
echo "1) 主服务日志"
|
||||
echo "2) 客户端A日志"
|
||||
echo "3) 客户端B日志"
|
||||
echo "4) 客户端C日志"
|
||||
echo "5) 所有日志"
|
||||
read -p "请选择 (1-5): " choice
|
||||
|
||||
case $choice in
|
||||
1) pm2 logs docreview-main ;;
|
||||
2) pm2 logs docreview-client-a ;;
|
||||
3) pm2 logs docreview-client-b ;;
|
||||
4) pm2 logs docreview-client-c ;;
|
||||
5) pm2 logs ;;
|
||||
*) log_error "无效选择" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
case "${1:-help}" in
|
||||
"deploy")
|
||||
check_dependencies
|
||||
create_log_dirs
|
||||
build_project
|
||||
deploy_pm2
|
||||
configure_nginx
|
||||
check_status
|
||||
;;
|
||||
"build")
|
||||
build_project
|
||||
;;
|
||||
"pm2")
|
||||
check_dependencies
|
||||
create_log_dirs
|
||||
deploy_pm2
|
||||
;;
|
||||
"nginx")
|
||||
configure_nginx
|
||||
;;
|
||||
"status")
|
||||
check_status
|
||||
;;
|
||||
"stop")
|
||||
stop_services
|
||||
;;
|
||||
"restart")
|
||||
restart_services
|
||||
;;
|
||||
"logs")
|
||||
show_logs
|
||||
;;
|
||||
"help")
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
log_error "未知选项: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 执行主函数
|
||||
main "$@"
|
||||
+99
-5
@@ -1,9 +1,11 @@
|
||||
// ecosystem.config.cjs - CommonJS 版本
|
||||
// 多客户端部署配置:支持3个不同地区客户端通过不同端口访问
|
||||
|
||||
module.exports = {
|
||||
apps: [
|
||||
// 主服务 - 生产环境 (端口: 51703)
|
||||
{
|
||||
name: 'docreview-frontend',
|
||||
name: 'docreview-main',
|
||||
script: 'node',
|
||||
args: [
|
||||
'-r', 'dotenv/config',
|
||||
@@ -17,15 +19,107 @@ module.exports = {
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
PORT: 51703,
|
||||
CLIENT_ID: 'main'
|
||||
},
|
||||
env_testing: {
|
||||
NODE_ENV: 'testing',
|
||||
PORT: 51703,
|
||||
CLIENT_ID: 'main'
|
||||
},
|
||||
error_file: './logs/err.log',
|
||||
out_file: './logs/out.log',
|
||||
log_file: './logs/combined.log',
|
||||
error_file: './logs/main-err.log',
|
||||
out_file: './logs/main-out.log',
|
||||
log_file: './logs/main-combined.log',
|
||||
time: true
|
||||
},
|
||||
// 客户端A - 反向代理服务 (端口: 51701)
|
||||
{
|
||||
name: 'docreview-client-a',
|
||||
script: 'node',
|
||||
args: [
|
||||
'-r', 'dotenv/config',
|
||||
'./node_modules/.bin/remix-serve',
|
||||
'./build/server/index.js'
|
||||
],
|
||||
instances: 1,
|
||||
autorestart: true,
|
||||
watch: false,
|
||||
max_memory_restart: '1G',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
PORT: 51701,
|
||||
CLIENT_ID: 'client-a',
|
||||
PROXY_TARGET: 'http://10.79.97.17:51703'
|
||||
},
|
||||
env_testing: {
|
||||
NODE_ENV: 'testing',
|
||||
PORT: 51701,
|
||||
CLIENT_ID: 'client-a',
|
||||
PROXY_TARGET: 'http://10.79.97.17:51703'
|
||||
},
|
||||
error_file: './logs/client-a-err.log',
|
||||
out_file: './logs/client-a-out.log',
|
||||
log_file: './logs/client-a-combined.log',
|
||||
time: true
|
||||
},
|
||||
// 客户端B - 反向代理服务 (端口: 51702)
|
||||
{
|
||||
name: 'docreview-client-b',
|
||||
script: 'node',
|
||||
args: [
|
||||
'-r', 'dotenv/config',
|
||||
'./node_modules/.bin/remix-serve',
|
||||
'./build/server/index.js'
|
||||
],
|
||||
instances: 1,
|
||||
autorestart: true,
|
||||
watch: false,
|
||||
max_memory_restart: '1G',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
PORT: 51702,
|
||||
CLIENT_ID: 'client-b',
|
||||
PROXY_TARGET: 'http://10.79.97.17:51703'
|
||||
},
|
||||
env_testing: {
|
||||
NODE_ENV: 'testing',
|
||||
PORT: 51702,
|
||||
CLIENT_ID: 'client-b',
|
||||
PROXY_TARGET: 'http://10.79.97.17:51703'
|
||||
},
|
||||
error_file: './logs/client-b-err.log',
|
||||
out_file: './logs/client-b-out.log',
|
||||
log_file: './logs/client-b-combined.log',
|
||||
time: true
|
||||
},
|
||||
// 客户端C - 反向代理服务 (端口: 51704)
|
||||
{
|
||||
name: 'docreview-client-c',
|
||||
script: 'node',
|
||||
args: [
|
||||
'-r', 'dotenv/config',
|
||||
'./node_modules/.bin/remix-serve',
|
||||
'./build/server/index.js'
|
||||
],
|
||||
instances: 1,
|
||||
autorestart: true,
|
||||
watch: false,
|
||||
max_memory_restart: '1G',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
PORT: 51704,
|
||||
CLIENT_ID: 'client-c',
|
||||
PROXY_TARGET: 'http://10.79.97.17:51703'
|
||||
},
|
||||
env_testing: {
|
||||
NODE_ENV: 'testing',
|
||||
PORT: 51704,
|
||||
CLIENT_ID: 'client-c',
|
||||
PROXY_TARGET: 'http://10.79.97.17:51703'
|
||||
},
|
||||
error_file: './logs/client-c-err.log',
|
||||
out_file: './logs/client-c-out.log',
|
||||
log_file: './logs/client-c-combined.log',
|
||||
time: true
|
||||
}
|
||||
],
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,169 @@
|
||||
# Nginx本地开发环境多客户端配置
|
||||
# 基于api-config.ts中的开发环境配置
|
||||
# 用于本地测试多客户端反向代理功能
|
||||
|
||||
# 上游服务器配置 - 指向本地开发服务器
|
||||
upstream docreview_local {
|
||||
server 127.0.0.1:5173; # Vite开发服务器
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
# 客户端A - 端口8001 (本地测试)
|
||||
server {
|
||||
listen 8001;
|
||||
server_name localhost 127.0.0.1;
|
||||
|
||||
# 访问日志
|
||||
access_log logs/local-client-a-access.log;
|
||||
error_log logs/local-client-a-error.log;
|
||||
|
||||
# 客户端标识
|
||||
set $client_id "client-a";
|
||||
|
||||
location / {
|
||||
# 反向代理到本地开发服务器
|
||||
proxy_pass http://docreview_local;
|
||||
|
||||
# 设置代理头部
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Client-ID $client_id;
|
||||
|
||||
# 开发环境特殊配置
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# 连接设置
|
||||
proxy_connect_timeout 30s;
|
||||
proxy_send_timeout 30s;
|
||||
proxy_read_timeout 30s;
|
||||
|
||||
# 禁用缓冲以支持热重载
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
|
||||
# 支持WebSocket (Vite HMR)
|
||||
proxy_http_version 1.1;
|
||||
}
|
||||
|
||||
# 健康检查
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "Local Client A - OK";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
||||
|
||||
# 客户端B - 端口8002 (本地测试)
|
||||
server {
|
||||
listen 8002;
|
||||
server_name localhost 127.0.0.1;
|
||||
|
||||
# 访问日志
|
||||
access_log logs/local-client-b-access.log;
|
||||
error_log logs/local-client-b-error.log;
|
||||
|
||||
# 客户端标识
|
||||
set $client_id "client-b";
|
||||
|
||||
location / {
|
||||
# 反向代理到本地开发服务器
|
||||
proxy_pass http://docreview_local;
|
||||
|
||||
# 设置代理头部
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Client-ID $client_id;
|
||||
|
||||
# 开发环境特殊配置
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# 连接设置
|
||||
proxy_connect_timeout 30s;
|
||||
proxy_send_timeout 30s;
|
||||
proxy_read_timeout 30s;
|
||||
|
||||
# 禁用缓冲以支持热重载
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
|
||||
# 支持WebSocket (Vite HMR)
|
||||
proxy_http_version 1.1;
|
||||
}
|
||||
|
||||
# 健康检查
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "Local Client B - OK";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
||||
|
||||
# 客户端C - 端口8003 (本地测试)
|
||||
server {
|
||||
listen 8003;
|
||||
server_name localhost 127.0.0.1;
|
||||
|
||||
# 访问日志
|
||||
access_log logs/local-client-c-access.log;
|
||||
error_log logs/local-client-c-error.log;
|
||||
|
||||
# 客户端标识
|
||||
set $client_id "client-c";
|
||||
|
||||
location / {
|
||||
# 反向代理到本地开发服务器
|
||||
proxy_pass http://docreview_local;
|
||||
|
||||
# 设置代理头部
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Client-ID $client_id;
|
||||
|
||||
# 开发环境特殊配置
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# 连接设置
|
||||
proxy_connect_timeout 30s;
|
||||
proxy_send_timeout 30s;
|
||||
proxy_read_timeout 30s;
|
||||
|
||||
# 禁用缓冲以支持热重载
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
|
||||
# 支持WebSocket (Vite HMR)
|
||||
proxy_http_version 1.1;
|
||||
}
|
||||
|
||||
# 健康检查
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "Local Client C - OK";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
||||
|
||||
# 全局配置
|
||||
# 错误页面
|
||||
error_page 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root html;
|
||||
}
|
||||
|
||||
# 开发环境安全头部(相对宽松)
|
||||
add_header X-Frame-Options SAMEORIGIN;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
|
||||
# 开发环境CORS支持
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
|
||||
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,X-Client-ID";
|
||||
@@ -0,0 +1,162 @@
|
||||
# Nginx多客户端反向代理配置
|
||||
# 为3个不同地区客户端提供独立端口访问
|
||||
# 所有请求最终转发到主服务 10.79.97.17:51703
|
||||
|
||||
# 上游服务器配置
|
||||
upstream docreview_main {
|
||||
server 10.79.97.17:51703;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
# 客户端A - 端口51701
|
||||
server {
|
||||
listen 51701;
|
||||
server_name 10.79.97.17;
|
||||
|
||||
# 访问日志
|
||||
access_log /var/log/nginx/client-a-access.log;
|
||||
error_log /var/log/nginx/client-a-error.log;
|
||||
|
||||
# 客户端标识
|
||||
set $client_id "client-a";
|
||||
|
||||
location / {
|
||||
# 反向代理到主服务
|
||||
proxy_pass http://docreview_main;
|
||||
|
||||
# 设置代理头部
|
||||
proxy_set_header Host $host:51703;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Client-ID $client_id;
|
||||
|
||||
# 连接设置
|
||||
proxy_connect_timeout 30s;
|
||||
proxy_send_timeout 30s;
|
||||
proxy_read_timeout 30s;
|
||||
|
||||
# 缓冲设置
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 8 4k;
|
||||
|
||||
# WebSocket支持
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
|
||||
# 健康检查
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "Client A - OK";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
||||
|
||||
# 客户端B - 端口51702
|
||||
server {
|
||||
listen 51702;
|
||||
server_name 10.79.97.17;
|
||||
|
||||
# 访问日志
|
||||
access_log /var/log/nginx/client-b-access.log;
|
||||
error_log /var/log/nginx/client-b-error.log;
|
||||
|
||||
# 客户端标识
|
||||
set $client_id "client-b";
|
||||
|
||||
location / {
|
||||
# 反向代理到主服务
|
||||
proxy_pass http://docreview_main;
|
||||
|
||||
# 设置代理头部
|
||||
proxy_set_header Host $host:51703;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Client-ID $client_id;
|
||||
|
||||
# 连接设置
|
||||
proxy_connect_timeout 30s;
|
||||
proxy_send_timeout 30s;
|
||||
proxy_read_timeout 30s;
|
||||
|
||||
# 缓冲设置
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 8 4k;
|
||||
|
||||
# WebSocket支持
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
|
||||
# 健康检查
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "Client B - OK";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
||||
|
||||
# 客户端C - 端口51704
|
||||
server {
|
||||
listen 51704;
|
||||
server_name 10.79.97.17;
|
||||
|
||||
# 访问日志
|
||||
access_log /var/log/nginx/client-c-access.log;
|
||||
error_log /var/log/nginx/client-c-error.log;
|
||||
|
||||
# 客户端标识
|
||||
set $client_id "client-c";
|
||||
|
||||
location / {
|
||||
# 反向代理到主服务
|
||||
proxy_pass http://docreview_main;
|
||||
|
||||
# 设置代理头部
|
||||
proxy_set_header Host $host:51703;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Client-ID $client_id;
|
||||
|
||||
# 连接设置
|
||||
proxy_connect_timeout 30s;
|
||||
proxy_send_timeout 30s;
|
||||
proxy_read_timeout 30s;
|
||||
|
||||
# 缓冲设置
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 8 4k;
|
||||
|
||||
# WebSocket支持
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
|
||||
# 健康检查
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "Client C - OK";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
||||
|
||||
# 全局配置
|
||||
# 错误页面
|
||||
error_page 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
|
||||
# 安全头部
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
Reference in New Issue
Block a user