Compare commits
12 Commits
f96a69e846
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 6abae17fea | |||
| da140d5415 | |||
| 4cefbbbcd6 | |||
| a4127509af | |||
| 6c3b3928f9 | |||
| 54bc260453 | |||
| d48e0f2d9f | |||
| 9bdc1bd9b3 | |||
| 8cb074660a | |||
| 804f996c69 | |||
| 735d3e0677 | |||
| c9ccb3435d |
@@ -159,7 +159,27 @@ mkdir -p data logs
|
|||||||
sudo chown -R 1000:1000 data logs
|
sudo chown -R 1000:1000 data logs
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. 性能优化
|
### 3. 日志文件权限问题
|
||||||
|
如果遇到 `EACCES: permission denied` 错误:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux/Mac
|
||||||
|
mkdir -p logs
|
||||||
|
chmod 777 logs # 或者 chmod 755 logs
|
||||||
|
|
||||||
|
# Windows PowerShell (如果使用Docker Desktop)
|
||||||
|
New-Item -ItemType Directory -Path logs -Force
|
||||||
|
# 在Docker Desktop设置中,确保目录有正确权限
|
||||||
|
|
||||||
|
# 或者删除logs目录,让容器自动创建
|
||||||
|
rm -rf logs
|
||||||
|
# 然后重新启动容器
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意**:容器内使用 `node` 用户(UID 1000)运行,确保挂载的logs目录对该用户有写入权限。
|
||||||
|
|
||||||
|
### 4. 性能优化
|
||||||
```bash
|
```bash
|
||||||
# 限制内存使用
|
# 限制内存使用
|
||||||
docker run -d --memory=512m proxy-ip-manager
|
docker run -d --memory=512m proxy-ip-manager
|
||||||
|
|||||||
13
Dockerfile
13
Dockerfile
@@ -21,22 +21,23 @@ RUN apk add --no-cache \
|
|||||||
|
|
||||||
# 配置npm使用国内镜像源
|
# 配置npm使用国内镜像源
|
||||||
# RUN npm config set registry https://registry.npmmirror.com
|
# RUN npm config set registry https://registry.npmmirror.com
|
||||||
RUN npm -g i pnpm
|
|
||||||
# 复制package.json和package-lock.json(如果存在)
|
# 复制package.json和package-lock.json(如果存在)
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
|
|
||||||
# 安装项目依赖
|
# 安装项目依赖
|
||||||
RUN pnpm i
|
#RUN npm config set proyx http://192.168.3.135:1084
|
||||||
|
RUN npm config set https-proxy http://192.168.3.135:1084
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
# 复制项目文件
|
# 复制项目文件
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# 创建数据目录并设置权限
|
# 创建数据目录和日志目录并设置权限
|
||||||
RUN mkdir -p /app/data && \
|
RUN mkdir -p /app/data /app/logs
|
||||||
chown -R node:node /app
|
# chown -R node:node /app
|
||||||
|
|
||||||
# 切换到非root用户
|
# 切换到非root用户
|
||||||
USER node
|
# USER node
|
||||||
|
|
||||||
# 暴露端口
|
# 暴露端口
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|||||||
@@ -1,9 +1,38 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// 数据库文件路径配置
|
||||||
|
// 在Docker环境中使用 /app/data,本地开发使用项目根目录的data文件夹
|
||||||
|
let dbPath;
|
||||||
|
if (process.env.DB_PATH) {
|
||||||
|
// 如果设置了环境变量,使用环境变量的路径
|
||||||
|
dbPath = process.env.DB_PATH;
|
||||||
|
} else if (fs.existsSync('/app/data')) {
|
||||||
|
// Docker容器内,使用/app/data目录
|
||||||
|
dbPath = '/app/data/proxies.db';
|
||||||
|
} else {
|
||||||
|
// 本地开发环境,使用项目根目录的data文件夹
|
||||||
|
const dataDir = path.join(__dirname, '../data');
|
||||||
|
dbPath = path.join(dataDir, 'proxies.db');
|
||||||
|
|
||||||
|
// 确保data目录存在
|
||||||
|
if (!fs.existsSync(dataDir)) {
|
||||||
|
try {
|
||||||
|
fs.mkdirSync(dataDir, { recursive: true });
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('无法创建data目录,将使用项目根目录:', error.message);
|
||||||
|
// 如果创建失败,回退到项目根目录
|
||||||
|
dbPath = path.join(__dirname, '../proxies.db');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`数据库文件路径: ${dbPath}`);
|
||||||
|
|
||||||
const dbConfig = {
|
const dbConfig = {
|
||||||
development: {
|
development: {
|
||||||
dialect: 'sqlite',
|
dialect: 'sqlite',
|
||||||
storage: path.join(__dirname, '../proxies.db')
|
storage: dbPath
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,11 @@
|
|||||||
<i class="bi bi-activity"></i> 系统监控
|
<i class="bi bi-activity"></i> 系统监控
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="logs.html">
|
||||||
|
<i class="bi bi-journal-text"></i> 实时日志
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="navbar-nav">
|
<div class="navbar-nav">
|
||||||
|
|||||||
@@ -43,6 +43,11 @@
|
|||||||
<i class="bi bi-activity"></i> 系统监控
|
<i class="bi bi-activity"></i> 系统监控
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="logs.html">
|
||||||
|
<i class="bi bi-journal-text"></i> 实时日志
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="navbar-nav">
|
<div class="navbar-nav">
|
||||||
|
|||||||
@@ -333,6 +333,42 @@ class ProxyManager {
|
|||||||
document.getElementById('validationStatus').textContent = '验证完成';
|
document.getElementById('validationStatus').textContent = '验证完成';
|
||||||
|
|
||||||
// 显示结果
|
// 显示结果
|
||||||
|
let responseResultHtml = '';
|
||||||
|
|
||||||
|
if (result.data.responseStatus !== null && result.data.responseStatus !== undefined) {
|
||||||
|
responseResultHtml = `
|
||||||
|
<hr>
|
||||||
|
<h6>响应结果:</h6>
|
||||||
|
<p><strong>状态码:</strong> <span class="badge ${result.data.responseStatus >= 200 && result.data.responseStatus < 300 ? 'bg-success' : 'bg-warning'}">${result.data.responseStatus}</span></p>
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (result.data.responseData) {
|
||||||
|
// 限制显示长度,避免内容过长
|
||||||
|
let displayData = result.data.responseData;
|
||||||
|
// const maxLength = 300;
|
||||||
|
// if (displayData.length > maxLength) {
|
||||||
|
// displayData = displayData.substring(0, maxLength) + '... (内容已截断,完整内容请查看日志)';
|
||||||
|
// }
|
||||||
|
|
||||||
|
responseResultHtml += `
|
||||||
|
<p><strong>响应内容:</strong></p>
|
||||||
|
<pre class="bg-light p-2 border rounded" style="max-height: 200px; overflow-y: auto; font-size: 12px; white-space: pre-wrap; word-wrap: break-word;">${this.escapeHtml(displayData)}</pre>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.data.testUrl) {
|
||||||
|
responseResultHtml += `
|
||||||
|
<p class="text-muted small"><strong>测试URL:</strong> ${result.data.testUrl}</p>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
} else if (result.data.error) {
|
||||||
|
responseResultHtml = `
|
||||||
|
<hr>
|
||||||
|
<h6>响应结果:</h6>
|
||||||
|
<p class="text-danger"><strong>错误:</strong> ${result.data.error}</p>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('validationResults').innerHTML = `
|
document.getElementById('validationResults').innerHTML = `
|
||||||
<div class="alert ${result.data.isValid ? 'alert-success' : 'alert-danger'}">
|
<div class="alert ${result.data.isValid ? 'alert-success' : 'alert-danger'}">
|
||||||
<h6>验证结果: ${result.data.isValid ? '成功' : '失败'}</h6>
|
<h6>验证结果: ${result.data.isValid ? '成功' : '失败'}</h6>
|
||||||
@@ -340,6 +376,7 @@ class ProxyManager {
|
|||||||
<p><strong>响应时间:</strong> ${result.data.responseTime}ms</p>
|
<p><strong>响应时间:</strong> ${result.data.responseTime}ms</p>
|
||||||
${result.data.error ? `<p><strong>错误信息:</strong> ${result.data.error}</p>` : ''}
|
${result.data.error ? `<p><strong>错误信息:</strong> ${result.data.error}</p>` : ''}
|
||||||
</div>
|
</div>
|
||||||
|
${responseResultHtml}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// 延迟刷新列表
|
// 延迟刷新列表
|
||||||
@@ -581,6 +618,13 @@ class ProxyManager {
|
|||||||
return icons[type] || 'info-circle-fill';
|
return icons[type] || 'info-circle-fill';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
escapeHtml(text) {
|
||||||
|
if (!text) return '';
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.textContent = text;
|
||||||
|
return div.innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
convertToCSV(data) {
|
convertToCSV(data) {
|
||||||
if (!data || data.length === 0) return '';
|
if (!data || data.length === 0) return '';
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,11 @@
|
|||||||
<i class="bi bi-activity"></i> 系统监控
|
<i class="bi bi-activity"></i> 系统监控
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="logs.html">
|
||||||
|
<i class="bi bi-journal-text"></i> 实时日志
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="navbar-nav">
|
<div class="navbar-nav">
|
||||||
|
|||||||
@@ -43,6 +43,11 @@
|
|||||||
<i class="bi bi-activity"></i> 系统监控
|
<i class="bi bi-activity"></i> 系统监控
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="logs.html">
|
||||||
|
<i class="bi bi-journal-text"></i> 实时日志
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="navbar-nav">
|
<div class="navbar-nav">
|
||||||
|
|||||||
@@ -9,15 +9,19 @@ class Database {
|
|||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
const dbPath = config.development.storage;
|
||||||
|
console.log(`正在连接数据库: ${dbPath}`);
|
||||||
|
|
||||||
this.db = new sqlite3.Database(
|
this.db = new sqlite3.Database(
|
||||||
config.development.storage,
|
dbPath,
|
||||||
sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE,
|
sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE,
|
||||||
(err) => {
|
(err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error('数据库连接失败:', err.message);
|
console.error('数据库连接失败:', err.message);
|
||||||
|
console.error('数据库路径:', dbPath);
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
console.log('已连接到 SQLite 数据库');
|
console.log(`已连接到 SQLite 数据库: ${dbPath}`);
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,11 +28,17 @@ class Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ensureLogsDirectory() {
|
ensureLogsDirectory() {
|
||||||
|
try {
|
||||||
if (!fs.existsSync(this.logsDir)) {
|
if (!fs.existsSync(this.logsDir)) {
|
||||||
fs.mkdirSync(this.logsDir, { recursive: true });
|
fs.mkdirSync(this.logsDir, { recursive: true });
|
||||||
// 使用原始console,避免循环调用
|
// 使用原始console,避免循环调用
|
||||||
this.originalConsole.log(`创建日志目录: ${this.logsDir}`);
|
this.originalConsole.log(`创建日志目录: ${this.logsDir}`);
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 如果创建目录失败(比如权限问题),警告但不阻止应用启动
|
||||||
|
this.originalConsole.warn(`无法创建日志目录 ${this.logsDir}:`, error.message);
|
||||||
|
this.originalConsole.warn('日志将仅输出到控制台,不会保存到文件');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getLogFileName(date = null) {
|
getLogFileName(date = null) {
|
||||||
@@ -48,9 +54,15 @@ class Logger {
|
|||||||
this.currentDate = today;
|
this.currentDate = today;
|
||||||
this.currentLogFile = this.getLogFileName();
|
this.currentLogFile = this.getLogFileName();
|
||||||
|
|
||||||
// 确保文件存在
|
// 确保文件存在 - 使用try-catch处理权限问题
|
||||||
if (!fs.existsSync(this.currentLogFile)) {
|
if (!fs.existsSync(this.currentLogFile)) {
|
||||||
fs.writeFileSync(this.currentLogFile, '');
|
try {
|
||||||
|
// 尝试创建空文件,如果失败则会在下次写入时自动创建
|
||||||
|
fs.writeFileSync(this.currentLogFile, '', { flag: 'a' });
|
||||||
|
} catch (error) {
|
||||||
|
// 如果创建文件失败(比如权限问题),不抛出错误,会在后续写入时处理
|
||||||
|
this.originalConsole.warn(`无法创建日志文件 ${this.currentLogFile}:`, error.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,8 +83,15 @@ class Logger {
|
|||||||
const timestamp = this.formatTimestamp();
|
const timestamp = this.formatTimestamp();
|
||||||
const logLine = `[${timestamp}] [${level.toUpperCase()}] ${message}\n`;
|
const logLine = `[${timestamp}] [${level.toUpperCase()}] ${message}\n`;
|
||||||
|
|
||||||
// 写入文件
|
// 写入文件 - 使用flag: 'a'确保文件不存在时自动创建
|
||||||
fs.appendFileSync(this.currentLogFile, logLine, 'utf8');
|
try {
|
||||||
|
fs.appendFileSync(this.currentLogFile, logLine, { encoding: 'utf8', flag: 'a' });
|
||||||
|
} catch (error) {
|
||||||
|
// 如果写入失败(比如权限问题),输出到原始console
|
||||||
|
this.originalConsole.error('写入日志文件失败:', error.message);
|
||||||
|
this.originalConsole[level](message);
|
||||||
|
return; // 不继续处理
|
||||||
|
}
|
||||||
|
|
||||||
// 添加到缓冲区用于实时推送
|
// 添加到缓冲区用于实时推送
|
||||||
const logEntry = {
|
const logEntry = {
|
||||||
@@ -102,9 +121,12 @@ class Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 如果文件写入失败,至少输出到原始console
|
// 如果文件写入失败(已经在上面处理了),这里只是防止其他错误
|
||||||
this.originalConsole.error('写入日志文件失败:', error);
|
// 如果上面的appendFileSync已经捕获了错误,这里不会执行
|
||||||
this.originalConsole[level](message);
|
// 这里主要是处理其他可能的错误
|
||||||
|
if (error.code !== 'EACCES' && !error.message.includes('permission')) {
|
||||||
|
this.originalConsole.error('日志处理失败:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,19 +91,25 @@ class ProxyScraper {
|
|||||||
return null; // 无本地代理,使用直连
|
return null; // 无本地代理,使用直连
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试几个代理,找到可用的
|
// 尝试几个代理,找到可用的(最多尝试3个,避免耗时过长)
|
||||||
for (let i = 0; i < Math.min(5, this.localProxies.length); i++) {
|
const maxAttempts = Math.min(3, this.localProxies.length);
|
||||||
|
for (let i = 0; i < maxAttempts; i++) {
|
||||||
const proxyConfig = this.getNextLocalProxy();
|
const proxyConfig = this.getNextLocalProxy();
|
||||||
|
|
||||||
|
try {
|
||||||
if (await this.testProxyForScraping(proxyConfig)) {
|
if (await this.testProxyForScraping(proxyConfig)) {
|
||||||
console.log(`✓ 代理 ${proxyConfig.host}:${proxyConfig.port} 可用`);
|
console.log(`✓ 代理 ${proxyConfig.host}:${proxyConfig.port} 可用`);
|
||||||
return proxyConfig;
|
return proxyConfig;
|
||||||
} else {
|
} else {
|
||||||
console.log(`✗ 代理 ${proxyConfig.host}:${proxyConfig.port} 不可用,尝试下一个`);
|
console.log(`✗ 代理 ${proxyConfig.host}:${proxyConfig.port} 不可用,尝试下一个`);
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(`✗ 代理 ${proxyConfig.host}:${proxyConfig.port} 测试出错: ${error.message}`);
|
||||||
|
// 继续尝试下一个代理
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('测试的本地代理都不可用,使用直连');
|
console.log(`测试了 ${maxAttempts} 个本地代理都不可用,将使用直连`);
|
||||||
return null; // 所有测试的代理都不可用,使用直连
|
return null; // 所有测试的代理都不可用,使用直连
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,6 +187,8 @@ class ProxyScraper {
|
|||||||
|
|
||||||
console.log(`正在抓取第 ${pageNum} 页: ${url}`);
|
console.log(`正在抓取第 ${pageNum} 页: ${url}`);
|
||||||
|
|
||||||
|
let useDirectConnection = false; // 标志:是否应该直接使用直连
|
||||||
|
|
||||||
for (let attempt = 1; attempt <= retryCount; attempt++) {
|
for (let attempt = 1; attempt <= retryCount; attempt++) {
|
||||||
let proxyConfig = null;
|
let proxyConfig = null;
|
||||||
let proxyUsed = '';
|
let proxyUsed = '';
|
||||||
@@ -188,8 +196,14 @@ class ProxyScraper {
|
|||||||
try {
|
try {
|
||||||
const userAgent = this.getRandomUserAgent();
|
const userAgent = this.getRandomUserAgent();
|
||||||
|
|
||||||
// 获取可用代理配置(每次请求都尝试不同的代理)
|
// 如果之前使用代理失败过,或者标记为使用直连,则跳过代理
|
||||||
|
if (!useDirectConnection && this.localProxies.length > 0) {
|
||||||
|
// 尝试获取可用代理配置
|
||||||
proxyConfig = await this.getWorkingProxy();
|
proxyConfig = await this.getWorkingProxy();
|
||||||
|
} else {
|
||||||
|
proxyConfig = null;
|
||||||
|
console.log('跳过代理,直接使用直连');
|
||||||
|
}
|
||||||
|
|
||||||
const requestConfig = {
|
const requestConfig = {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -242,6 +256,12 @@ class ProxyScraper {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`第 ${attempt} 次尝试抓取第 ${pageNum} 页失败 (${proxyUsed}):`, error.message);
|
console.error(`第 ${attempt} 次尝试抓取第 ${pageNum} 页失败 (${proxyUsed}):`, error.message);
|
||||||
|
|
||||||
|
// 如果使用代理失败,下次重试时使用直连
|
||||||
|
if (proxyConfig) {
|
||||||
|
console.log(`代理 ${proxyConfig.host}:${proxyConfig.port} 抓取失败,下次重试将使用直连`);
|
||||||
|
useDirectConnection = true; // 标记为使用直连
|
||||||
|
}
|
||||||
|
|
||||||
if (attempt === retryCount) {
|
if (attempt === retryCount) {
|
||||||
throw new Error(`抓取第 ${pageNum} 页失败,已重试 ${retryCount} 次: ${error.message}`);
|
throw new Error(`抓取第 ${pageNum} 页失败,已重试 ${retryCount} 次: ${error.message}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,16 +76,16 @@ class ProxyValidator {
|
|||||||
|
|
||||||
// 检查响应内容 - 根据不同的测试URL使用不同的验证逻辑
|
// 检查响应内容 - 根据不同的测试URL使用不同的验证逻辑
|
||||||
if (response.status >= 200 && response.status < 300) {
|
if (response.status >= 200 && response.status < 300) {
|
||||||
if (testUrl.includes('baidu.com')) {
|
// if (testUrl.includes('baidu.com')) {
|
||||||
isValid = response.data && response.data.includes('百度');
|
// isValid = response.data && response.data.includes('baidu');
|
||||||
} else if (testUrl.includes('httpbin.org')) {
|
// } else if (testUrl.includes('httpbin.org')) {
|
||||||
isValid = response.data && (response.data.includes('origin') || response.data.includes('ip'));
|
// isValid = response.data && (response.data.includes('origin') || response.data.includes('ip'));
|
||||||
} else if (testUrl.includes('google.com')) {
|
// } else if (testUrl.includes('google.com')) {
|
||||||
isValid = response.data && response.data.toLowerCase().includes('google');
|
// isValid = response.data && response.data.toLowerCase().includes('google');
|
||||||
} else {
|
// } else {
|
||||||
// 对于其他URL,只要能连接就认为有效
|
// 对于其他URL,只要能连接就认为有效
|
||||||
isValid = true;
|
isValid = true;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 只要有一次验证成功,立即返回成功结果
|
// 只要有一次验证成功,立即返回成功结果
|
||||||
@@ -94,13 +94,33 @@ class ProxyValidator {
|
|||||||
console.log(`✓ 代理 ${ip}:${port} 第 ${attemptNumber} 次尝试验证成功,响应时间: ${responseTime}ms`);
|
console.log(`✓ 代理 ${ip}:${port} 第 ${attemptNumber} 次尝试验证成功,响应时间: ${responseTime}ms`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 提取响应信息用于显示
|
||||||
|
let responseData = '';
|
||||||
|
let responseStatus = response.status;
|
||||||
|
try {
|
||||||
|
// 只取响应数据的前500个字符,避免数据过大
|
||||||
|
const dataStr = typeof response.data === 'string'
|
||||||
|
? response.data
|
||||||
|
: JSON.stringify(response.data);
|
||||||
|
// responseData = dataStr.substring(0, 500);
|
||||||
|
responseData = dataStr;
|
||||||
|
// if (dataStr.length > 500) {
|
||||||
|
// responseData += '... (已截断)';
|
||||||
|
// }
|
||||||
|
} catch (e) {
|
||||||
|
responseData = '无法解析响应内容';
|
||||||
|
}
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
ip: ip,
|
ip: ip,
|
||||||
port: parseInt(port),
|
port: parseInt(port),
|
||||||
isValid: true,
|
isValid: true,
|
||||||
responseTime: responseTime,
|
responseTime: responseTime,
|
||||||
error: null,
|
error: null,
|
||||||
testUrl: testUrl
|
testUrl: testUrl,
|
||||||
|
responseStatus: responseStatus,
|
||||||
|
responseData: responseData,
|
||||||
|
responseHeaders: response.headers || {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 更新数据库中的验证结果(如果需要)
|
// 更新数据库中的验证结果(如果需要)
|
||||||
@@ -124,13 +144,34 @@ class ProxyValidator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lastError = new Error('响应内容验证失败');
|
lastError = new Error('响应内容验证失败');
|
||||||
|
// 提取响应信息
|
||||||
|
let responseData = '';
|
||||||
|
let responseStatus = response ? response.status : null;
|
||||||
|
try {
|
||||||
|
if (response && response.data) {
|
||||||
|
const dataStr = typeof response.data === 'string'
|
||||||
|
? response.data
|
||||||
|
: JSON.stringify(response.data);
|
||||||
|
responseData = dataStr;
|
||||||
|
// responseData = dataStr.substring(0, 500);
|
||||||
|
// if (dataStr.length > 500) {
|
||||||
|
// responseData += '... (已截断)';
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
responseData = '无法解析响应内容';
|
||||||
|
}
|
||||||
|
|
||||||
lastResult = {
|
lastResult = {
|
||||||
ip: ip,
|
ip: ip,
|
||||||
port: parseInt(port),
|
port: parseInt(port),
|
||||||
isValid: false,
|
isValid: false,
|
||||||
responseTime: responseTime,
|
responseTime: responseTime,
|
||||||
error: '响应内容验证失败',
|
error: '响应内容验证失败',
|
||||||
testUrl: testUrl
|
testUrl: testUrl,
|
||||||
|
responseStatus: responseStatus,
|
||||||
|
responseData: responseData,
|
||||||
|
responseHeaders: response ? (response.headers || {}) : {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 如果还有重试机会,继续尝试
|
// 如果还有重试机会,继续尝试
|
||||||
@@ -147,7 +188,10 @@ class ProxyValidator {
|
|||||||
isValid: false,
|
isValid: false,
|
||||||
responseTime: responseTime,
|
responseTime: responseTime,
|
||||||
error: error.message,
|
error: error.message,
|
||||||
testUrl: testUrl
|
testUrl: testUrl,
|
||||||
|
responseStatus: null,
|
||||||
|
responseData: null,
|
||||||
|
responseHeaders: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (logResult) {
|
if (logResult) {
|
||||||
@@ -184,7 +228,10 @@ class ProxyValidator {
|
|||||||
isValid: false,
|
isValid: false,
|
||||||
responseTime: Date.now() - startTime,
|
responseTime: Date.now() - startTime,
|
||||||
error: lastError ? lastError.message : '所有验证尝试都失败',
|
error: lastError ? lastError.message : '所有验证尝试都失败',
|
||||||
testUrl: testUrl
|
testUrl: testUrl,
|
||||||
|
responseStatus: null,
|
||||||
|
responseData: null,
|
||||||
|
responseHeaders: {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user