后端改为使用日志记录器管理日志
This commit is contained in:
+127
-18
@@ -4,6 +4,11 @@ const { Base64 } = require('js-base64');
|
|||||||
const { stringify } = require('qs');
|
const { stringify } = require('qs');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const { ProxyAgent } = require('proxy-agent');
|
const { ProxyAgent } = require('proxy-agent');
|
||||||
|
const { defaultLogger } = require('./utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('PixivAuth');
|
||||||
|
|
||||||
|
|
||||||
// OAuth 2.0 配置
|
// OAuth 2.0 配置
|
||||||
const CLIENT_ID = 'MOBrBDS8blbauoSck0ZfDbtuzpyT';
|
const CLIENT_ID = 'MOBrBDS8blbauoSck0ZfDbtuzpyT';
|
||||||
@@ -20,6 +25,8 @@ class PixivAuth {
|
|||||||
this.proxy = proxy;
|
this.proxy = proxy;
|
||||||
this.isRefreshing = false;
|
this.isRefreshing = false;
|
||||||
this.failedQueue = [];
|
this.failedQueue = [];
|
||||||
|
this.refreshTimer = null; // 添加定时器引用
|
||||||
|
this.onTokenUpdate = null; // 添加token更新回调
|
||||||
|
|
||||||
// 创建 axios 实例,支持代理
|
// 创建 axios 实例,支持代理
|
||||||
this.axiosInstance = this.createAxiosInstance();
|
this.axiosInstance = this.createAxiosInstance();
|
||||||
@@ -39,13 +46,13 @@ class PixivAuth {
|
|||||||
|
|
||||||
// 如果设置了代理,添加代理配置
|
// 如果设置了代理,添加代理配置
|
||||||
if (this.proxy) {
|
if (this.proxy) {
|
||||||
console.log('使用代理:', this.proxy);
|
logger.info('使用代理:', this.proxy);
|
||||||
config.httpsAgent = new ProxyAgent(this.proxy);
|
config.httpsAgent = new ProxyAgent(this.proxy);
|
||||||
} else {
|
} else {
|
||||||
// 尝试使用系统代理
|
// 尝试使用系统代理
|
||||||
const systemProxy = process.env.HTTP_PROXY || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.https_proxy;
|
const systemProxy = process.env.HTTP_PROXY || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.https_proxy;
|
||||||
if (systemProxy) {
|
if (systemProxy) {
|
||||||
console.log('使用系统代理:', systemProxy);
|
logger.info('使用系统代理:', systemProxy);
|
||||||
config.httpsAgent = new ProxyAgent(systemProxy);
|
config.httpsAgent = new ProxyAgent(systemProxy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,7 +90,7 @@ class PixivAuth {
|
|||||||
this.isRefreshing = true;
|
this.isRefreshing = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('检测到token过期,正在自动刷新...');
|
logger.info('检测到token过期,正在自动刷新...');
|
||||||
const result = await this.refreshAccessToken(this.refreshToken);
|
const result = await this.refreshAccessToken(this.refreshToken);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
@@ -94,6 +101,9 @@ class PixivAuth {
|
|||||||
this.user = result.user;
|
this.user = result.user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 触发token更新回调
|
||||||
|
this.triggerTokenUpdate();
|
||||||
|
|
||||||
// 处理队列中的请求
|
// 处理队列中的请求
|
||||||
this.processQueue(null, result.access_token);
|
this.processQueue(null, result.access_token);
|
||||||
|
|
||||||
@@ -104,7 +114,7 @@ class PixivAuth {
|
|||||||
throw new Error('Token刷新失败');
|
throw new Error('Token刷新失败');
|
||||||
}
|
}
|
||||||
} catch (refreshError) {
|
} catch (refreshError) {
|
||||||
console.error('自动刷新token失败:', refreshError.message);
|
logger.error('自动刷新token失败:', refreshError.message);
|
||||||
this.processQueue(refreshError, null);
|
this.processQueue(refreshError, null);
|
||||||
return Promise.reject(refreshError);
|
return Promise.reject(refreshError);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -149,6 +159,101 @@ class PixivAuth {
|
|||||||
if (user) {
|
if (user) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 启动主动定时刷新
|
||||||
|
this.startProactiveRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置token更新回调
|
||||||
|
*/
|
||||||
|
setTokenUpdateCallback(callback) {
|
||||||
|
this.onTokenUpdate = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 触发token更新回调
|
||||||
|
*/
|
||||||
|
triggerTokenUpdate() {
|
||||||
|
if (this.onTokenUpdate && typeof this.onTokenUpdate === 'function') {
|
||||||
|
try {
|
||||||
|
this.onTokenUpdate({
|
||||||
|
access_token: this.accessToken,
|
||||||
|
refresh_token: this.refreshToken,
|
||||||
|
user: this.user
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Token更新回调执行失败:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动主动定时刷新token
|
||||||
|
*/
|
||||||
|
startProactiveRefresh() {
|
||||||
|
// 清除之前的定时器
|
||||||
|
if (this.refreshTimer) {
|
||||||
|
clearTimeout(this.refreshTimer);
|
||||||
|
this.refreshTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有refreshToken,不启动定时刷新
|
||||||
|
if (!this.refreshToken) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算下次刷新时间(在token过期前30分钟刷新)
|
||||||
|
// Pixiv的access_token通常有效期为1小时,我们提前30分钟刷新
|
||||||
|
const refreshInterval = 30 * 60 * 1000; // 30分钟
|
||||||
|
|
||||||
|
this.refreshTimer = setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
logger.info('主动刷新token...');
|
||||||
|
const result = await this.refreshAccessToken(this.refreshToken);
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
logger.info('主动刷新token成功');
|
||||||
|
// 更新token
|
||||||
|
this.accessToken = result.access_token;
|
||||||
|
this.refreshToken = result.refresh_token;
|
||||||
|
if (result.user) {
|
||||||
|
this.user = result.user;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 触发token更新回调
|
||||||
|
this.triggerTokenUpdate();
|
||||||
|
|
||||||
|
// 重新启动定时刷新
|
||||||
|
this.startProactiveRefresh();
|
||||||
|
} else {
|
||||||
|
logger.error('主动刷新token失败:', result.error);
|
||||||
|
// 刷新失败,5分钟后重试
|
||||||
|
setTimeout(() => {
|
||||||
|
this.startProactiveRefresh();
|
||||||
|
}, 5 * 60 * 1000);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('主动刷新token异常:', error.message);
|
||||||
|
// 发生异常,5分钟后重试
|
||||||
|
setTimeout(() => {
|
||||||
|
this.startProactiveRefresh();
|
||||||
|
}, 5 * 60 * 1000);
|
||||||
|
}
|
||||||
|
}, refreshInterval);
|
||||||
|
|
||||||
|
logger.info(`主动刷新定时器已启动,${refreshInterval / 1000 / 60}分钟后刷新token`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止主动定时刷新
|
||||||
|
*/
|
||||||
|
stopProactiveRefresh() {
|
||||||
|
if (this.refreshTimer) {
|
||||||
|
clearTimeout(this.refreshTimer);
|
||||||
|
this.refreshTimer = null;
|
||||||
|
logger.info('主动刷新定时器已停止');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -205,9 +310,9 @@ class PixivAuth {
|
|||||||
*/
|
*/
|
||||||
async getAccessToken(code, codeVerifier) {
|
async getAccessToken(code, codeVerifier) {
|
||||||
try {
|
try {
|
||||||
console.log('正在获取访问令牌...');
|
logger.info('正在获取访问令牌...');
|
||||||
console.log('Code:', code);
|
logger.info('Code:', code);
|
||||||
console.log('Code Verifier:', codeVerifier);
|
logger.info('Code Verifier:', codeVerifier);
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
client_id: CLIENT_ID,
|
client_id: CLIENT_ID,
|
||||||
@@ -235,7 +340,7 @@ class PixivAuth {
|
|||||||
this.refreshToken = tokenData.refresh_token;
|
this.refreshToken = tokenData.refresh_token;
|
||||||
this.user = tokenData.user;
|
this.user = tokenData.user;
|
||||||
|
|
||||||
console.log('获取访问令牌成功');
|
logger.info('获取访问令牌成功');
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
access_token: tokenData.access_token,
|
access_token: tokenData.access_token,
|
||||||
@@ -244,11 +349,11 @@ class PixivAuth {
|
|||||||
};
|
};
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取访问令牌失败:');
|
logger.error('获取访问令牌失败:');
|
||||||
console.error('错误对象:', error);
|
logger.error('错误对象:', error);
|
||||||
console.error('响应状态:', error.response?.status);
|
logger.error('响应状态:', error.response?.status);
|
||||||
console.error('响应数据:', error.response?.data);
|
logger.error('响应数据:', error.response?.data);
|
||||||
console.error('错误消息:', error.message);
|
logger.error('错误消息:', error.message);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
@@ -262,7 +367,7 @@ class PixivAuth {
|
|||||||
*/
|
*/
|
||||||
async refreshAccessToken(refreshToken) {
|
async refreshAccessToken(refreshToken) {
|
||||||
try {
|
try {
|
||||||
console.log('正在刷新访问令牌...');
|
logger.info('正在刷新访问令牌...');
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
client_id: CLIENT_ID,
|
client_id: CLIENT_ID,
|
||||||
@@ -293,7 +398,7 @@ class PixivAuth {
|
|||||||
this.user = tokenData.user;
|
this.user = tokenData.user;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('刷新访问令牌成功');
|
logger.info('刷新访问令牌成功');
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
access_token: tokenData.access_token,
|
access_token: tokenData.access_token,
|
||||||
@@ -302,7 +407,7 @@ class PixivAuth {
|
|||||||
};
|
};
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('刷新访问令牌失败:', error.response?.data || error.message);
|
logger.error('刷新访问令牌失败:', error.response?.data || error.message);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: error.response?.data || error.message
|
error: error.response?.data || error.message
|
||||||
@@ -334,7 +439,7 @@ class PixivAuth {
|
|||||||
};
|
};
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取用户信息失败:', error.response?.data || error.message);
|
logger.error('获取用户信息失败:', error.response?.data || error.message);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: error.response?.data || error.message
|
error: error.response?.data || error.message
|
||||||
@@ -349,7 +454,11 @@ class PixivAuth {
|
|||||||
this.accessToken = null;
|
this.accessToken = null;
|
||||||
this.refreshToken = null;
|
this.refreshToken = null;
|
||||||
this.user = null;
|
this.user = null;
|
||||||
console.log('已登出');
|
|
||||||
|
// 停止主动刷新
|
||||||
|
this.stopProactiveRefresh();
|
||||||
|
|
||||||
|
logger.info('已登出');
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -1,3 +1,8 @@
|
|||||||
|
const { defaultLogger } = require('./utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('ProxyConfig');
|
||||||
|
|
||||||
// 代理配置
|
// 代理配置
|
||||||
const proxyConfig = {
|
const proxyConfig = {
|
||||||
// 系统代理配置
|
// 系统代理配置
|
||||||
@@ -19,7 +24,7 @@ const proxyConfig = {
|
|||||||
process.env.http_proxy = this.proxyUrl;
|
process.env.http_proxy = this.proxyUrl;
|
||||||
process.env.https_proxy = this.proxyUrl;
|
process.env.https_proxy = this.proxyUrl;
|
||||||
|
|
||||||
console.log('代理环境变量已设置:', this.proxyUrl);
|
logger.info('代理环境变量已设置:', this.proxyUrl);
|
||||||
},
|
},
|
||||||
|
|
||||||
// 清除环境变量
|
// 清除环境变量
|
||||||
@@ -29,7 +34,7 @@ const proxyConfig = {
|
|||||||
delete process.env.http_proxy;
|
delete process.env.http_proxy;
|
||||||
delete process.env.https_proxy;
|
delete process.env.https_proxy;
|
||||||
|
|
||||||
console.log('代理环境变量已清除');
|
logger.info('代理环境变量已清除');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
const fs = require('fs').promises;
|
const fs = require('fs').promises;
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('CacheConfigManager');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缓存配置管理器
|
* 缓存配置管理器
|
||||||
@@ -58,10 +63,10 @@ class CacheConfigManager {
|
|||||||
const configDir = path.dirname(this.configPath);
|
const configDir = path.dirname(this.configPath);
|
||||||
if (!require('fs').existsSync(configDir)) {
|
if (!require('fs').existsSync(configDir)) {
|
||||||
require('fs').mkdirSync(configDir, { recursive: true });
|
require('fs').mkdirSync(configDir, { recursive: true });
|
||||||
// console.log('缓存配置目录创建成功:', configDir);
|
// logger.info('缓存配置目录创建成功:', configDir);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('创建缓存配置目录失败:', error);
|
logger.error('创建缓存配置目录失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,10 +77,10 @@ class CacheConfigManager {
|
|||||||
try {
|
try {
|
||||||
// 检查配置文件是否存在
|
// 检查配置文件是否存在
|
||||||
await fs.access(this.configPath);
|
await fs.access(this.configPath);
|
||||||
// console.log('缓存配置文件已存在');
|
// logger.info('缓存配置文件已存在');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 配置文件不存在,创建默认配置
|
// 配置文件不存在,创建默认配置
|
||||||
console.log('创建默认缓存配置文件...');
|
logger.info('创建默认缓存配置文件...');
|
||||||
await this.createDefaultConfig();
|
await this.createDefaultConfig();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,9 +92,9 @@ class CacheConfigManager {
|
|||||||
try {
|
try {
|
||||||
const configContent = JSON.stringify(this.defaultConfig, null, 2);
|
const configContent = JSON.stringify(this.defaultConfig, null, 2);
|
||||||
await fs.writeFile(this.configPath, configContent, 'utf8');
|
await fs.writeFile(this.configPath, configContent, 'utf8');
|
||||||
// console.log('默认缓存配置文件创建成功:', this.configPath);
|
// logger.info('默认缓存配置文件创建成功:', this.configPath);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('创建默认缓存配置文件失败:', error);
|
logger.error('创建默认缓存配置文件失败:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,7 +110,7 @@ class CacheConfigManager {
|
|||||||
// 合并默认配置,确保所有字段都存在
|
// 合并默认配置,确保所有字段都存在
|
||||||
return { ...this.defaultConfig, ...config };
|
return { ...this.defaultConfig, ...config };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载缓存配置失败:', error);
|
logger.error('加载缓存配置失败:', error);
|
||||||
return this.defaultConfig;
|
return this.defaultConfig;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,9 +125,9 @@ class CacheConfigManager {
|
|||||||
|
|
||||||
const configContent = JSON.stringify(config, null, 2);
|
const configContent = JSON.stringify(config, null, 2);
|
||||||
await fs.writeFile(this.configPath, configContent, 'utf8');
|
await fs.writeFile(this.configPath, configContent, 'utf8');
|
||||||
console.log('缓存配置保存成功');
|
logger.info('缓存配置保存成功');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存缓存配置失败:', error);
|
logger.error('保存缓存配置失败:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,7 +142,7 @@ class CacheConfigManager {
|
|||||||
await this.saveConfig(newConfig);
|
await this.saveConfig(newConfig);
|
||||||
return newConfig;
|
return newConfig;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('更新缓存配置失败:', error);
|
logger.error('更新缓存配置失败:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,10 +153,10 @@ class CacheConfigManager {
|
|||||||
async resetToDefault() {
|
async resetToDefault() {
|
||||||
try {
|
try {
|
||||||
await this.saveConfig(this.defaultConfig);
|
await this.saveConfig(this.defaultConfig);
|
||||||
console.log('缓存配置已重置为默认值');
|
logger.info('缓存配置已重置为默认值');
|
||||||
return this.defaultConfig;
|
return this.defaultConfig;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('重置缓存配置失败:', error);
|
logger.error('重置缓存配置失败:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
const fs = require('fs').promises
|
const fs = require('fs').promises
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('ConfigManager');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配置管理器
|
* 配置管理器
|
||||||
@@ -41,10 +46,10 @@ class ConfigManager {
|
|||||||
const configDirPath = path.dirname(this.configDir)
|
const configDirPath = path.dirname(this.configDir)
|
||||||
if (!require('fs').existsSync(configDirPath)) {
|
if (!require('fs').existsSync(configDirPath)) {
|
||||||
require('fs').mkdirSync(configDirPath, { recursive: true })
|
require('fs').mkdirSync(configDirPath, { recursive: true })
|
||||||
console.log('配置目录创建成功:', configDirPath)
|
logger.info('配置目录创建成功:', configDirPath)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('创建配置目录失败:', error)
|
logger.error('创建配置目录失败:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,10 +61,10 @@ class ConfigManager {
|
|||||||
try {
|
try {
|
||||||
// 检查配置文件是否存在
|
// 检查配置文件是否存在
|
||||||
await fs.access(this.configDir)
|
await fs.access(this.configDir)
|
||||||
console.log('用户配置文件已存在')
|
logger.info('用户配置文件已存在')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 配置文件不存在,创建默认配置
|
// 配置文件不存在,创建默认配置
|
||||||
console.log('创建默认用户配置文件...')
|
logger.info('创建默认用户配置文件...')
|
||||||
await this.createDefaultConfig()
|
await this.createDefaultConfig()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,9 +81,9 @@ class ConfigManager {
|
|||||||
// 检查目录是否创建成功
|
// 检查目录是否创建成功
|
||||||
try {
|
try {
|
||||||
await fs.access(configDirPath)
|
await fs.access(configDirPath)
|
||||||
console.log('配置目录确认存在:', configDirPath)
|
logger.info('配置目录确认存在:', configDirPath)
|
||||||
} catch (accessError) {
|
} catch (accessError) {
|
||||||
console.error('配置目录访问失败:', accessError)
|
logger.error('配置目录访问失败:', accessError)
|
||||||
throw new Error(`无法访问配置目录: ${configDirPath}`)
|
throw new Error(`无法访问配置目录: ${configDirPath}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,13 +94,13 @@ class ConfigManager {
|
|||||||
// 验证文件是否写入成功
|
// 验证文件是否写入成功
|
||||||
try {
|
try {
|
||||||
await fs.access(this.configDir)
|
await fs.access(this.configDir)
|
||||||
console.log('默认配置文件创建成功:', this.configDir)
|
logger.info('默认配置文件创建成功:', this.configDir)
|
||||||
} catch (verifyError) {
|
} catch (verifyError) {
|
||||||
console.error('配置文件验证失败:', verifyError)
|
logger.error('配置文件验证失败:', verifyError)
|
||||||
throw new Error('配置文件创建后无法访问')
|
throw new Error('配置文件创建后无法访问')
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('创建默认配置文件失败:', error)
|
logger.error('创建默认配置文件失败:', error)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,7 +113,7 @@ class ConfigManager {
|
|||||||
// 首先检查文件是否存在
|
// 首先检查文件是否存在
|
||||||
const exists = await this.configExists()
|
const exists = await this.configExists()
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
console.log('配置文件不存在,创建默认配置...')
|
logger.info('配置文件不存在,创建默认配置...')
|
||||||
await this.createDefaultConfig()
|
await this.createDefaultConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,14 +123,14 @@ class ConfigManager {
|
|||||||
// 合并默认配置,确保所有必要的字段都存在
|
// 合并默认配置,确保所有必要的字段都存在
|
||||||
return { ...this.defaultConfig, ...config }
|
return { ...this.defaultConfig, ...config }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('读取配置文件失败:', error)
|
logger.error('读取配置文件失败:', error)
|
||||||
console.log('使用默认配置...')
|
logger.info('使用默认配置...')
|
||||||
// 如果读取失败,尝试创建默认配置
|
// 如果读取失败,尝试创建默认配置
|
||||||
try {
|
try {
|
||||||
await this.createDefaultConfig()
|
await this.createDefaultConfig()
|
||||||
return { ...this.defaultConfig }
|
return { ...this.defaultConfig }
|
||||||
} catch (createError) {
|
} catch (createError) {
|
||||||
console.error('创建默认配置也失败:', createError)
|
logger.error('创建默认配置也失败:', createError)
|
||||||
// 最后返回内存中的默认配置
|
// 最后返回内存中的默认配置
|
||||||
return { ...this.defaultConfig }
|
return { ...this.defaultConfig }
|
||||||
}
|
}
|
||||||
@@ -149,10 +154,10 @@ class ConfigManager {
|
|||||||
'utf8'
|
'utf8'
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log('配置文件保存成功')
|
logger.info('配置文件保存成功')
|
||||||
return true
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存配置文件失败:', error)
|
logger.error('保存配置文件失败:', error)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,7 +172,7 @@ class ConfigManager {
|
|||||||
await this.saveConfig(newConfig)
|
await this.saveConfig(newConfig)
|
||||||
return newConfig
|
return newConfig
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('更新配置失败:', error)
|
logger.error('更新配置失败:', error)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -178,10 +183,10 @@ class ConfigManager {
|
|||||||
async resetToDefault() {
|
async resetToDefault() {
|
||||||
try {
|
try {
|
||||||
await this.saveConfig(this.defaultConfig)
|
await this.saveConfig(this.defaultConfig)
|
||||||
console.log('配置已重置为默认值')
|
logger.info('配置已重置为默认值')
|
||||||
return this.defaultConfig
|
return this.defaultConfig
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('重置配置失败:', error)
|
logger.error('重置配置失败:', error)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-18
@@ -2,11 +2,14 @@ const Fse = require('fs-extra');
|
|||||||
const Path = require('path');
|
const Path = require('path');
|
||||||
const PixivAuth = require('./auth');
|
const PixivAuth = require('./auth');
|
||||||
const DownloadService = require('./services/download');
|
const DownloadService = require('./services/download');
|
||||||
|
const { defaultLogger } = require('./utils/logger');
|
||||||
// 配置文件路径
|
// 配置文件路径
|
||||||
const CONFIG_FILE_DIR = require('appdata-path').getAppDataPath('pmanager');
|
const CONFIG_FILE_DIR = require('appdata-path').getAppDataPath('pmanager');
|
||||||
const CONFIG_FILE = Path.resolve(CONFIG_FILE_DIR, 'config.json');
|
const CONFIG_FILE = Path.resolve(CONFIG_FILE_DIR, 'config.json');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('PixivBackend');
|
||||||
|
|
||||||
// 默认配置
|
// 默认配置
|
||||||
const defaultConfig = {
|
const defaultConfig = {
|
||||||
download: {
|
download: {
|
||||||
@@ -32,7 +35,7 @@ class PixivBackend {
|
|||||||
* 初始化后端
|
* 初始化后端
|
||||||
*/
|
*/
|
||||||
async init() {
|
async init() {
|
||||||
console.log('正在初始化 Pixiv 后端...');
|
logger.info('正在初始化 Pixiv 后端...');
|
||||||
|
|
||||||
// 初始化配置
|
// 初始化配置
|
||||||
this.initConfig();
|
this.initConfig();
|
||||||
@@ -41,6 +44,15 @@ class PixivBackend {
|
|||||||
// 创建认证实例,传入代理配置
|
// 创建认证实例,传入代理配置
|
||||||
this.auth = new PixivAuth(this.config.proxy);
|
this.auth = new PixivAuth(this.config.proxy);
|
||||||
|
|
||||||
|
// 设置token更新回调
|
||||||
|
this.auth.setTokenUpdateCallback((tokens) => {
|
||||||
|
this.config.access_token = tokens.access_token;
|
||||||
|
this.config.refresh_token = tokens.refresh_token;
|
||||||
|
this.config.user = tokens.user;
|
||||||
|
this.saveConfig();
|
||||||
|
logger.info('Token已更新并保存到配置文件');
|
||||||
|
});
|
||||||
|
|
||||||
// 同步已保存的token状态
|
// 同步已保存的token状态
|
||||||
if (this.config.access_token && this.config.refresh_token) {
|
if (this.config.access_token && this.config.refresh_token) {
|
||||||
this.auth.syncTokens(
|
this.auth.syncTokens(
|
||||||
@@ -56,10 +68,10 @@ class PixivBackend {
|
|||||||
|
|
||||||
// 检查登录状态
|
// 检查登录状态
|
||||||
if (this.config.refresh_token) {
|
if (this.config.refresh_token) {
|
||||||
console.log('检测到已保存的登录信息,正在验证...');
|
logger.info('检测到已保存的登录信息,正在验证...');
|
||||||
await this.relogin();
|
await this.relogin();
|
||||||
} else {
|
} else {
|
||||||
console.log('未检测到登录信息,需要先登录');
|
logger.info('未检测到登录信息,需要先登录');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 启动token同步定时任务
|
// 启动token同步定时任务
|
||||||
@@ -79,7 +91,7 @@ class PixivBackend {
|
|||||||
}
|
}
|
||||||
}, 5 * 60 * 1000); // 5分钟
|
}, 5 * 60 * 1000); // 5分钟
|
||||||
|
|
||||||
console.log('Token同步定时任务已启动');
|
logger.info('Token同步定时任务已启动');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -101,7 +113,7 @@ class PixivBackend {
|
|||||||
// 合并默认配置
|
// 合并默认配置
|
||||||
return { ...defaultConfig, ...config };
|
return { ...defaultConfig, ...config };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('读取配置文件失败:', error.message);
|
logger.error('读取配置文件失败:', error.message);
|
||||||
return { ...defaultConfig };
|
return { ...defaultConfig };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,9 +124,9 @@ class PixivBackend {
|
|||||||
saveConfig() {
|
saveConfig() {
|
||||||
try {
|
try {
|
||||||
Fse.writeJsonSync(CONFIG_FILE, this.config);
|
Fse.writeJsonSync(CONFIG_FILE, this.config);
|
||||||
console.log('配置已保存');
|
logger.info('配置已保存');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存配置失败:', error.message);
|
logger.error('保存配置失败:', error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +149,7 @@ class PixivBackend {
|
|||||||
*/
|
*/
|
||||||
async handleLoginCallback(code) {
|
async handleLoginCallback(code) {
|
||||||
try {
|
try {
|
||||||
console.log('正在处理登录回调...');
|
logger.info('正在处理登录回调...');
|
||||||
|
|
||||||
if (!this.config.code_verifier) {
|
if (!this.config.code_verifier) {
|
||||||
throw new Error('缺少 code_verifier,请重新获取登录URL');
|
throw new Error('缺少 code_verifier,请重新获取登录URL');
|
||||||
@@ -152,13 +164,20 @@ class PixivBackend {
|
|||||||
this.config.access_token = result.access_token;
|
this.config.access_token = result.access_token;
|
||||||
this.config.user = result.user;
|
this.config.user = result.user;
|
||||||
|
|
||||||
|
// 同步到auth实例并启动主动刷新
|
||||||
|
this.auth.syncTokens(
|
||||||
|
result.access_token,
|
||||||
|
result.refresh_token,
|
||||||
|
result.user
|
||||||
|
);
|
||||||
|
|
||||||
// 清理临时数据
|
// 清理临时数据
|
||||||
delete this.config.code_verifier;
|
delete this.config.code_verifier;
|
||||||
|
|
||||||
this.saveConfig();
|
this.saveConfig();
|
||||||
this.isLoggedIn = true;
|
this.isLoggedIn = true;
|
||||||
|
|
||||||
console.log(`登录成功!用户: ${result.user.account}`);
|
logger.info(`登录成功!用户: ${result.user.account}`);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
user: result.user
|
user: result.user
|
||||||
@@ -168,7 +187,7 @@ class PixivBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('登录失败:', error.message);
|
logger.error('登录失败:', error.message);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: error.message
|
error: error.message
|
||||||
@@ -185,7 +204,7 @@ class PixivBackend {
|
|||||||
throw new Error('没有保存的登录信息');
|
throw new Error('没有保存的登录信息');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('正在使用保存的登录信息重新登录...');
|
logger.info('正在使用保存的登录信息重新登录...');
|
||||||
|
|
||||||
const result = await this.auth.refreshAccessToken(this.config.refresh_token);
|
const result = await this.auth.refreshAccessToken(this.config.refresh_token);
|
||||||
|
|
||||||
@@ -199,7 +218,7 @@ class PixivBackend {
|
|||||||
this.config.user = result.user;
|
this.config.user = result.user;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同步到auth实例
|
// 同步到auth实例并启动主动刷新
|
||||||
this.auth.syncTokens(
|
this.auth.syncTokens(
|
||||||
result.access_token,
|
result.access_token,
|
||||||
result.refresh_token,
|
result.refresh_token,
|
||||||
@@ -209,7 +228,7 @@ class PixivBackend {
|
|||||||
this.saveConfig();
|
this.saveConfig();
|
||||||
|
|
||||||
this.isLoggedIn = true;
|
this.isLoggedIn = true;
|
||||||
console.log('重新登录成功!');
|
logger.info('重新登录成功!');
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} else {
|
} else {
|
||||||
@@ -217,7 +236,7 @@ class PixivBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('重新登录失败:', error.message);
|
logger.error('重新登录失败:', error.message);
|
||||||
// 清除无效的登录信息
|
// 清除无效的登录信息
|
||||||
this.config.refresh_token = null;
|
this.config.refresh_token = null;
|
||||||
this.config.access_token = null;
|
this.config.access_token = null;
|
||||||
@@ -243,7 +262,7 @@ class PixivBackend {
|
|||||||
this.isLoggedIn = false;
|
this.isLoggedIn = false;
|
||||||
|
|
||||||
this.saveConfig();
|
this.saveConfig();
|
||||||
console.log('已登出');
|
logger.info('已登出');
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
@@ -266,7 +285,7 @@ class PixivBackend {
|
|||||||
setDownloadPath(path) {
|
setDownloadPath(path) {
|
||||||
this.config.download.path = path;
|
this.config.download.path = path;
|
||||||
this.saveConfig();
|
this.saveConfig();
|
||||||
console.log(`下载路径已设置为: ${path}`);
|
logger.info(`下载路径已设置为: ${path}`);
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,7 +307,7 @@ class PixivBackend {
|
|||||||
this.config.proxy = proxy;
|
this.config.proxy = proxy;
|
||||||
this.auth.setProxy(proxy);
|
this.auth.setProxy(proxy);
|
||||||
this.saveConfig();
|
this.saveConfig();
|
||||||
console.log(`代理已设置为: ${proxy}`);
|
logger.info(`代理已设置为: ${proxy}`);
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* 全局错误处理中间件
|
* 全局错误处理中间件
|
||||||
*/
|
*/
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('ErrorHandler');
|
||||||
|
|
||||||
const errorHandler = (err, req, res, next) => {
|
const errorHandler = (err, req, res, next) => {
|
||||||
console.error('错误详情:', err);
|
logger.error('错误详情', err);
|
||||||
|
|
||||||
// 默认错误信息
|
// 默认错误信息
|
||||||
let statusCode = 500;
|
let statusCode = 500;
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('AuthRouter');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取登录状态
|
* 获取登录状态
|
||||||
@@ -152,7 +157,7 @@ router.post('/refresh-token', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('手动刷新token失败:', error);
|
logger.error('手动刷新token失败:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
error: error.message
|
error: error.message
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const DownloadService = require('../services/download');
|
const DownloadService = require('../services/download');
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('DownloadRouter');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下载单个作品
|
* 下载单个作品
|
||||||
@@ -43,7 +48,7 @@ router.post('/artwork/:id', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('下载路由错误:', error);
|
logger.error('下载路由错误:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
error: error.message
|
error: error.message
|
||||||
@@ -655,7 +660,7 @@ router.get('/stream/:taskId', async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('SSE写入失败:', error);
|
logger.error('SSE写入失败:', error);
|
||||||
// 连接可能已断开,清理监听器
|
// 连接可能已断开,清理监听器
|
||||||
downloadService.removeProgressListener(taskId, progressListener);
|
downloadService.removeProgressListener(taskId, progressListener);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,11 @@ const express = require('express');
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const ImageCacheService = require('../services/image-cache');
|
const ImageCacheService = require('../services/image-cache');
|
||||||
const ApiCacheService = require('../services/api-cache');
|
const ApiCacheService = require('../services/api-cache');
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('ProxyRouter');
|
||||||
|
|
||||||
|
|
||||||
// 创建缓存服务实例
|
// 创建缓存服务实例
|
||||||
const imageCache = new ImageCacheService();
|
const imageCache = new ImageCacheService();
|
||||||
@@ -40,7 +45,7 @@ router.get('/image', async (req, res) => {
|
|||||||
res.send(imageData);
|
res.send(imageData);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Image proxy error:', error.message);
|
logger.error('Image proxy error:', error.message);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Failed to load image'
|
error: 'Failed to load image'
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const ArtworkService = require('../services/artwork');
|
const ArtworkService = require('../services/artwork');
|
||||||
const ResponseUtil = require('../utils/response');
|
const ResponseUtil = require('../utils/response');
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
const logger = defaultLogger.child('RankingRouter');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取排行榜数据
|
* 获取排行榜数据
|
||||||
@@ -34,7 +37,7 @@ router.get('/', async (req, res) => {
|
|||||||
|
|
||||||
res.json(ResponseUtil.success(result));
|
res.json(ResponseUtil.success(result));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取排行榜失败:', error);
|
logger.error('获取排行榜失败:', error);
|
||||||
res.status(500).json(ResponseUtil.error(error.message));
|
res.status(500).json(ResponseUtil.error(error.message));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
const fs = require('fs').promises
|
const fs = require('fs').promises
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const fsExtra = require('fs-extra')
|
const fsExtra = require('fs-extra')
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('MigrateDownloads');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换现有的下载格式为仓库管理格式
|
* 转换现有的下载格式为仓库管理格式
|
||||||
@@ -11,7 +16,7 @@ async function migrateDownloads() {
|
|||||||
const downloadsPath = path.join(__dirname, '../../downloads')
|
const downloadsPath = path.join(__dirname, '../../downloads')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('开始转换下载格式...')
|
logger.info('开始转换下载格式...')
|
||||||
|
|
||||||
// 读取downloads目录
|
// 读取downloads目录
|
||||||
const entries = await fs.readdir(downloadsPath, { withFileTypes: true })
|
const entries = await fs.readdir(downloadsPath, { withFileTypes: true })
|
||||||
@@ -20,12 +25,12 @@ async function migrateDownloads() {
|
|||||||
if (!entry.isDirectory()) continue
|
if (!entry.isDirectory()) continue
|
||||||
|
|
||||||
const oldDirName = entry.name
|
const oldDirName = entry.name
|
||||||
console.log(`处理目录: ${oldDirName}`)
|
logger.info(`处理目录: ${oldDirName}`)
|
||||||
|
|
||||||
// 解析目录名: {artistName}_{artworkId}
|
// 解析目录名: {artistName}_{artworkId}
|
||||||
const match = oldDirName.match(/^(.+)_(\d+)$/)
|
const match = oldDirName.match(/^(.+)_(\d+)$/)
|
||||||
if (!match) {
|
if (!match) {
|
||||||
console.log(`跳过不符合格式的目录: ${oldDirName}`)
|
logger.info(`跳过不符合格式的目录: ${oldDirName}`)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +51,7 @@ async function migrateDownloads() {
|
|||||||
const newArtworkDirName = `${artworkId}_${artworkTitle}`
|
const newArtworkDirName = `${artworkId}_${artworkTitle}`
|
||||||
const newArtworkPath = path.join(newArtistDir, newArtworkDirName)
|
const newArtworkPath = path.join(newArtistDir, newArtworkDirName)
|
||||||
|
|
||||||
console.log(`转换: ${oldArtworkPath} -> ${newArtworkPath}`)
|
logger.info(`转换: ${oldArtworkPath} -> ${newArtworkPath}`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 创建新的作者目录
|
// 创建新的作者目录
|
||||||
@@ -55,9 +60,9 @@ async function migrateDownloads() {
|
|||||||
// 移动作品目录
|
// 移动作品目录
|
||||||
await fsExtra.move(oldArtworkPath, newArtworkPath)
|
await fsExtra.move(oldArtworkPath, newArtworkPath)
|
||||||
|
|
||||||
console.log(`✓ 成功转换: ${artworkTitle}`)
|
logger.info(`✓ 成功转换: ${artworkTitle}`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`✗ 转换失败: ${artworkTitle}`, error.message)
|
logger.error(`✗ 转换失败: ${artworkTitle}`, error.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,17 +71,17 @@ async function migrateDownloads() {
|
|||||||
const remainingEntries = await fs.readdir(oldDirPath)
|
const remainingEntries = await fs.readdir(oldDirPath)
|
||||||
if (remainingEntries.length === 0) {
|
if (remainingEntries.length === 0) {
|
||||||
await fsExtra.remove(oldDirPath)
|
await fsExtra.remove(oldDirPath)
|
||||||
console.log(`删除空目录: ${oldDirPath}`)
|
logger.info(`删除空目录: ${oldDirPath}`)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`删除目录失败: ${oldDirPath}`, error.message)
|
logger.error(`删除目录失败: ${oldDirPath}`, error.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('转换完成!')
|
logger.info('转换完成!')
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('转换过程中发生错误:', error)
|
logger.error('转换过程中发生错误:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+32
-15
@@ -3,6 +3,9 @@ const cors = require('cors');
|
|||||||
const morgan = require('morgan');
|
const morgan = require('morgan');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
|
// 导入logger
|
||||||
|
const { defaultLogger } = require('./utils/logger');
|
||||||
|
|
||||||
// 导入路由模块
|
// 导入路由模块
|
||||||
const authRoutes = require('./routes/auth');
|
const authRoutes = require('./routes/auth');
|
||||||
const artworkRoutes = require('./routes/artwork');
|
const artworkRoutes = require('./routes/artwork');
|
||||||
@@ -20,6 +23,9 @@ const { authMiddleware } = require('./middleware/auth');
|
|||||||
const PixivBackend = require('./core');
|
const PixivBackend = require('./core');
|
||||||
const proxyConfig = require('./config');
|
const proxyConfig = require('./config');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('Server');
|
||||||
|
|
||||||
// 自定义日志中间件
|
// 自定义日志中间件
|
||||||
function customLogger(req, res, next) {
|
function customLogger(req, res, next) {
|
||||||
// 过滤掉静态资源请求和图片代理请求
|
// 过滤掉静态资源请求和图片代理请求
|
||||||
@@ -43,10 +49,21 @@ function customLogger(req, res, next) {
|
|||||||
const isImageProxy = req.path === '/api/proxy/image';
|
const isImageProxy = req.path === '/api/proxy/image';
|
||||||
|
|
||||||
// 过滤掉下载任务状态查询请求
|
// 过滤掉下载任务状态查询请求
|
||||||
const isDownloadTasksQuery = req.path === '/api/download/tasks';
|
const isDownloadTasksQuery =
|
||||||
|
req.path === '/api/download/tasks' ||
|
||||||
|
req.path === '/api/download/tasks/active' ||
|
||||||
|
req.path === '/api/download/tasks/summary' ||
|
||||||
|
req.path === '/api/download/tasks/changes' ||
|
||||||
|
req.path === '/api/download/tasks/completed';
|
||||||
|
|
||||||
// 只记录API请求和重要请求,排除静态资源、图片代理和下载任务查询
|
// 过滤掉仓库预览请求(图片预览)
|
||||||
if (!isStaticResource && !isImageProxy && !isDownloadTasksQuery) {
|
const isRepositoryPreview = req.path === '/api/repository/preview';
|
||||||
|
|
||||||
|
// 过滤掉健康检查请求
|
||||||
|
const isHealthCheck = req.path === '/health';
|
||||||
|
|
||||||
|
// 只记录重要的API请求,排除静态资源、图片代理、下载任务查询、仓库预览和健康检查
|
||||||
|
if (!isStaticResource && !isImageProxy && !isDownloadTasksQuery && !isRepositoryPreview && !isHealthCheck) {
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
|
|
||||||
// 原始响应结束方法
|
// 原始响应结束方法
|
||||||
@@ -110,7 +127,7 @@ function customLogger(req, res, next) {
|
|||||||
const logMessage = `${statusColor}${statusIcon} ${methodIcon} ${method} ${url} ${statusCode} ${duration}ms\x1b[0m`;
|
const logMessage = `${statusColor}${statusIcon} ${methodIcon} ${method} ${url} ${statusCode} ${duration}ms\x1b[0m`;
|
||||||
|
|
||||||
// 输出日志
|
// 输出日志
|
||||||
console.log(`[${timeStr}] ${logMessage}`);
|
logger.info(`${statusIcon} ${methodIcon} ${method} ${url} ${statusCode} ${duration}ms`);
|
||||||
|
|
||||||
// 调用原始的end方法
|
// 调用原始的end方法
|
||||||
originalEnd.call(this, chunk, encoding);
|
originalEnd.call(this, chunk, encoding);
|
||||||
@@ -131,7 +148,7 @@ class PixivServer {
|
|||||||
* 初始化服务器
|
* 初始化服务器
|
||||||
*/
|
*/
|
||||||
async init() {
|
async init() {
|
||||||
console.log('\x1b[34m🔧 正在初始化 Pixiv 后端服务器...\x1b[0m');
|
logger.info('🔧 正在初始化 Pixiv 后端服务器...');
|
||||||
|
|
||||||
// 重新设置端口(从环境变量获取)
|
// 重新设置端口(从环境变量获取)
|
||||||
this.port = process.env.PORT || 3000;
|
this.port = process.env.PORT || 3000;
|
||||||
@@ -152,7 +169,7 @@ class PixivServer {
|
|||||||
// 配置错误处理 - 临时注释掉
|
// 配置错误处理 - 临时注释掉
|
||||||
this.setupErrorHandling();
|
this.setupErrorHandling();
|
||||||
|
|
||||||
console.log('\x1b[32m✅ 服务器初始化完成\x1b[0m');
|
logger.info('✅ 服务器初始化完成');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -239,14 +256,14 @@ class PixivServer {
|
|||||||
*/
|
*/
|
||||||
start() {
|
start() {
|
||||||
this.app.listen(this.port, () => {
|
this.app.listen(this.port, () => {
|
||||||
console.log('\x1b[32m✅ Pixiv 后端服务器已启动\x1b[0m');
|
logger.info('✅ Pixiv 后端服务器已启动');
|
||||||
console.log(`\x1b[36m📍 服务地址: http://localhost:${this.port}\x1b[0m`);
|
logger.info(`📍 服务地址: http://localhost:${this.port}`);
|
||||||
console.log(`\x1b[36m🔗 健康检查: http://localhost:${this.port}/health\x1b[0m`);
|
logger.info(`🔗 健康检查: http://localhost:${this.port}/health`);
|
||||||
console.log(`\x1b[33m📊 登录状态: ${this.backend.isLoggedIn ? '已登录' : '未登录'}\x1b[0m`);
|
logger.info(`📊 登录状态: ${this.backend.isLoggedIn ? '已登录' : '未登录'}`);
|
||||||
if (this.backend.isLoggedIn) {
|
if (this.backend.isLoggedIn) {
|
||||||
console.log(`\x1b[33m👤 用户: ${this.backend.config.user?.account}\x1b[0m`);
|
logger.info(`👤 用户: ${this.backend.config.user?.account}`);
|
||||||
}
|
}
|
||||||
console.log('\x1b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m');
|
logger.info('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,10 +271,10 @@ class PixivServer {
|
|||||||
* 优雅关闭
|
* 优雅关闭
|
||||||
*/
|
*/
|
||||||
async shutdown() {
|
async shutdown() {
|
||||||
console.log('\x1b[33m🔄 正在关闭服务器...\x1b[0m');
|
logger.info('🔄 正在关闭服务器...');
|
||||||
// 清理代理环境变量
|
// 清理代理环境变量
|
||||||
proxyConfig.clearEnvironmentVariables();
|
proxyConfig.clearEnvironmentVariables();
|
||||||
console.log('\x1b[32m✅ 服务器已关闭\x1b[0m');
|
logger.info('✅ 服务器已关闭');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -274,7 +291,7 @@ if (require.main === module) {
|
|||||||
server
|
server
|
||||||
.init()
|
.init()
|
||||||
.then(() => server.start())
|
.then(() => server.start())
|
||||||
.catch(console.error);
|
.catch((error) => logger.error('服务器启动失败', error));
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = PixivServer;
|
module.exports = PixivServer;
|
||||||
|
|||||||
@@ -2,6 +2,11 @@ const fs = require('fs').promises;
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const CacheConfigManager = require('../config/cache-config');
|
const CacheConfigManager = require('../config/cache-config');
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('ApiCacheService');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API缓存服务
|
* API缓存服务
|
||||||
@@ -72,9 +77,9 @@ class ApiCacheService {
|
|||||||
// 启动定期清理任务
|
// 启动定期清理任务
|
||||||
this.startCleanupTask();
|
this.startCleanupTask();
|
||||||
|
|
||||||
// console.log('API缓存服务初始化完成');
|
// logger.info('API缓存服务初始化完成');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('API缓存服务初始化失败:', error);
|
logger.error('API缓存服务初始化失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,9 +89,9 @@ class ApiCacheService {
|
|||||||
async ensureCacheDir() {
|
async ensureCacheDir() {
|
||||||
try {
|
try {
|
||||||
await fs.mkdir(this.cacheDir, { recursive: true });
|
await fs.mkdir(this.cacheDir, { recursive: true });
|
||||||
// console.log('API缓存目录创建成功:', this.cacheDir);
|
// logger.info('API缓存目录创建成功:', this.cacheDir);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('创建API缓存目录失败:', error);
|
logger.error('创建API缓存目录失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +193,7 @@ class ApiCacheService {
|
|||||||
|
|
||||||
return JSON.parse(data);
|
return JSON.parse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('读取API缓存失败:', error);
|
logger.error('读取API缓存失败:', error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,7 +213,7 @@ class ApiCacheService {
|
|||||||
// 检查缓存大小,如果超过限制则清理
|
// 检查缓存大小,如果超过限制则清理
|
||||||
await this.checkCacheSize();
|
await this.checkCacheSize();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存API缓存失败:', error);
|
logger.error('保存API缓存失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,7 +308,7 @@ class ApiCacheService {
|
|||||||
|
|
||||||
// 如果超过最大大小,删除最旧的文件
|
// 如果超过最大大小,删除最旧的文件
|
||||||
if (totalSize > this.config.maxSize) {
|
if (totalSize > this.config.maxSize) {
|
||||||
console.log(`API缓存大小 ${totalSize} 超过限制 ${this.config.maxSize},开始清理...`);
|
logger.info(`API缓存大小 ${totalSize} 超过限制 ${this.config.maxSize},开始清理...`);
|
||||||
|
|
||||||
// 按修改时间排序,删除最旧的文件
|
// 按修改时间排序,删除最旧的文件
|
||||||
fileStats.sort((a, b) => a.mtime.getTime() - b.mtime.getTime());
|
fileStats.sort((a, b) => a.mtime.getTime() - b.mtime.getTime());
|
||||||
@@ -317,10 +322,10 @@ class ApiCacheService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`API缓存清理完成,当前大小: ${totalSize}`);
|
logger.info(`API缓存清理完成,当前大小: ${totalSize}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('检查API缓存大小失败:', error);
|
logger.error('检查API缓存大小失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,10 +350,10 @@ class ApiCacheService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cleanedCount > 0) {
|
if (cleanedCount > 0) {
|
||||||
console.log(`清理了 ${cleanedCount} 个过期API缓存文件`);
|
logger.info(`清理了 ${cleanedCount} 个过期API缓存文件`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('清理过期API缓存失败:', error);
|
logger.error('清理过期API缓存失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,7 +363,7 @@ class ApiCacheService {
|
|||||||
startCleanupTask() {
|
startCleanupTask() {
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
this.cleanupExpiredCache().catch(error => {
|
this.cleanupExpiredCache().catch(error => {
|
||||||
console.error('定期清理API缓存任务失败:', error);
|
logger.error('定期清理API缓存任务失败:', error);
|
||||||
});
|
});
|
||||||
}, this.config.cleanupInterval);
|
}, this.config.cleanupInterval);
|
||||||
}
|
}
|
||||||
@@ -376,9 +381,9 @@ class ApiCacheService {
|
|||||||
await fs.unlink(filePath);
|
await fs.unlink(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('所有API缓存已清理');
|
logger.info('所有API缓存已清理');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('清理所有API缓存失败:', error);
|
logger.error('清理所有API缓存失败:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -410,7 +415,7 @@ class ApiCacheService {
|
|||||||
config: this.config
|
config: this.config
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取API缓存统计失败:', error);
|
logger.error('获取API缓存统计失败:', error);
|
||||||
return {
|
return {
|
||||||
fileCount: 0,
|
fileCount: 0,
|
||||||
totalSize: 0,
|
totalSize: 0,
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
const { stringify } = require('qs');
|
const { stringify } = require('qs');
|
||||||
const ApiCacheService = require('./api-cache');
|
const ApiCacheService = require('./api-cache');
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('ArtistService');
|
||||||
|
|
||||||
|
|
||||||
class ArtistService {
|
class ArtistService {
|
||||||
constructor(auth) {
|
constructor(auth) {
|
||||||
@@ -197,7 +202,7 @@ class ArtistService {
|
|||||||
limit,
|
limit,
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(`请求关注列表: offset=${currentOffset}, limit=${limit}`);
|
logger.info(`请求关注列表: offset=${currentOffset}, limit=${limit}`);
|
||||||
const response = await this.makeRequest('GET', `/v1/user/following?${stringify(params)}`);
|
const response = await this.makeRequest('GET', `/v1/user/following?${stringify(params)}`);
|
||||||
|
|
||||||
// 转换数据格式以匹配前端期望
|
// 转换数据格式以匹配前端期望
|
||||||
@@ -210,12 +215,12 @@ class ArtistService {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
allArtists.push(...artists);
|
allArtists.push(...artists);
|
||||||
console.log(`本次获取到 ${artists.length} 个作者,累计 ${allArtists.length} 个`);
|
logger.info(`本次获取到 ${artists.length} 个作者,累计 ${allArtists.length} 个`);
|
||||||
|
|
||||||
// 如果返回的数量少于limit,说明已经获取完所有数据
|
// 如果返回的数量少于limit,说明已经获取完所有数据
|
||||||
if (artists.length < limit) {
|
if (artists.length < limit) {
|
||||||
hasMore = false;
|
hasMore = false;
|
||||||
console.log('已获取完所有关注的作者');
|
logger.info('已获取完所有关注的作者');
|
||||||
} else {
|
} else {
|
||||||
currentOffset += artists.length;
|
currentOffset += artists.length;
|
||||||
}
|
}
|
||||||
@@ -229,7 +234,7 @@ class ArtistService {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取关注作者列表失败:', error.message);
|
logger.error('获取关注作者列表失败:', error.message);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: error.message,
|
error: error.message,
|
||||||
@@ -335,11 +340,11 @@ class ArtistService {
|
|||||||
try {
|
try {
|
||||||
const cachedData = await this.apiCache.get(method, endpoint, data || {});
|
const cachedData = await this.apiCache.get(method, endpoint, data || {});
|
||||||
if (cachedData) {
|
if (cachedData) {
|
||||||
// console.log(`API缓存命中: ${method} ${endpoint}`);
|
// logger.info(`API缓存命中: ${method} ${endpoint}`);
|
||||||
return cachedData;
|
return cachedData;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('读取API缓存失败:', error);
|
logger.error('读取API缓存失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,15 +383,15 @@ class ArtistService {
|
|||||||
if (method === 'GET') {
|
if (method === 'GET') {
|
||||||
try {
|
try {
|
||||||
await this.apiCache.set(method, endpoint, data || {}, responseData);
|
await this.apiCache.set(method, endpoint, data || {}, responseData);
|
||||||
console.log(`API缓存已保存: ${method} ${endpoint}`);
|
logger.info(`API缓存已保存: ${method} ${endpoint}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存API缓存失败:', error);
|
logger.error('保存API缓存失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return responseData;
|
return responseData;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('API请求失败:', {
|
logger.error('API请求失败:', {
|
||||||
method,
|
method,
|
||||||
endpoint,
|
endpoint,
|
||||||
status: error.response?.status,
|
status: error.response?.status,
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
const { stringify } = require('qs');
|
const { stringify } = require('qs');
|
||||||
const ApiCacheService = require('./api-cache');
|
const ApiCacheService = require('./api-cache');
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('ArtworkService');
|
||||||
|
|
||||||
|
|
||||||
class ArtworkService {
|
class ArtworkService {
|
||||||
constructor(auth) {
|
constructor(auth) {
|
||||||
@@ -205,8 +210,8 @@ class ArtworkService {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Search error:', error.message);
|
logger.error('Search error:', error.message);
|
||||||
console.error('Search error details:', error.response?.data);
|
logger.error('Search error details:', error.response?.data);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
@@ -293,7 +298,7 @@ class ArtworkService {
|
|||||||
try {
|
try {
|
||||||
// TODO: 需要研究新的 Pixiv API 端点
|
// TODO: 需要研究新的 Pixiv API 端点
|
||||||
// 当前所有收藏相关的 API 端点都返回 404 错误
|
// 当前所有收藏相关的 API 端点都返回 404 错误
|
||||||
console.log(`尝试${action === 'add' ? '添加' : '删除'}收藏 ${artworkId},但API端点不可用`);
|
logger.info(`尝试${action === 'add' ? '添加' : '删除'}收藏 ${artworkId},但API端点不可用`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
@@ -348,7 +353,7 @@ class ArtworkService {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取收藏列表失败:', {
|
logger.error('获取收藏列表失败:', {
|
||||||
message: error.message,
|
message: error.message,
|
||||||
status: error.response?.status,
|
status: error.response?.status,
|
||||||
data: error.response?.data,
|
data: error.response?.data,
|
||||||
@@ -370,11 +375,11 @@ class ArtworkService {
|
|||||||
try {
|
try {
|
||||||
const cachedData = await this.apiCache.get(method, endpoint, data || {});
|
const cachedData = await this.apiCache.get(method, endpoint, data || {});
|
||||||
if (cachedData) {
|
if (cachedData) {
|
||||||
// console.log(`API缓存命中: ${method} ${endpoint}`);
|
// logger.info(`API缓存命中: ${method} ${endpoint}`);
|
||||||
return cachedData;
|
return cachedData;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('读取API缓存失败:', error);
|
logger.error('读取API缓存失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,15 +420,15 @@ class ArtworkService {
|
|||||||
if (method === 'GET') {
|
if (method === 'GET') {
|
||||||
try {
|
try {
|
||||||
await this.apiCache.set(method, endpoint, data || {}, responseData);
|
await this.apiCache.set(method, endpoint, data || {}, responseData);
|
||||||
// console.log(`API缓存已保存: ${method} ${endpoint}`);
|
// logger.info(`API缓存已保存: ${method} ${endpoint}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存API缓存失败:', error);
|
logger.error('保存API缓存失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return responseData;
|
return responseData;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('API request failed:', {
|
logger.error('API request failed:', {
|
||||||
method,
|
method,
|
||||||
endpoint,
|
endpoint,
|
||||||
error: error.message,
|
error: error.message,
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('DownloadExecutor');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下载执行器 - 负责具体的下载逻辑执行
|
* 下载执行器 - 负责具体的下载逻辑执行
|
||||||
@@ -27,7 +32,7 @@ class DownloadExecutor {
|
|||||||
|
|
||||||
// 检查是否应该暂停
|
// 检查是否应该暂停
|
||||||
if (this.shouldPause(task.id)) {
|
if (this.shouldPause(task.id)) {
|
||||||
console.log('任务已暂停,停止下载:', task.id);
|
logger.info('任务已暂停,停止下载:', task.id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +60,7 @@ class DownloadExecutor {
|
|||||||
|
|
||||||
// 确保imageUrl是字符串
|
// 确保imageUrl是字符串
|
||||||
if (typeof imageUrl !== 'string') {
|
if (typeof imageUrl !== 'string') {
|
||||||
console.error(`图片URL不是字符串:`, imageUrl);
|
logger.error(`图片URL不是字符串:`, imageUrl);
|
||||||
task.failed_files++;
|
task.failed_files++;
|
||||||
this.progressManager.notifyProgressUpdate(task.id, task);
|
this.progressManager.notifyProgressUpdate(task.id, task);
|
||||||
results.push({ success: false, error: '图片URL格式错误' });
|
results.push({ success: false, error: '图片URL格式错误' });
|
||||||
@@ -78,7 +83,7 @@ class DownloadExecutor {
|
|||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
// 文件不完整,删除重新下载
|
// 文件不完整,删除重新下载
|
||||||
console.log(`文件不完整,重新下载: ${filePath}`);
|
logger.info(`文件不完整,重新下载: ${filePath}`);
|
||||||
await this.fileManager.safeDeleteFile(filePath);
|
await this.fileManager.safeDeleteFile(filePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,7 +109,7 @@ class DownloadExecutor {
|
|||||||
results.push({ success: true, file: fileName });
|
results.push({ success: true, file: fileName });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
task.failed_files++;
|
task.failed_files++;
|
||||||
console.error(`下载图片失败 ${index + 1}: ${error.message}`);
|
logger.error(`下载图片失败 ${index + 1}: ${error.message}`);
|
||||||
this.progressManager.notifyProgressUpdate(task.id, task);
|
this.progressManager.notifyProgressUpdate(task.id, task);
|
||||||
results.push({ success: false, error: error.message });
|
results.push({ success: false, error: error.message });
|
||||||
}
|
}
|
||||||
@@ -139,7 +144,7 @@ class DownloadExecutor {
|
|||||||
|
|
||||||
await this.historyManager.addHistoryItem(historyItem);
|
await this.historyManager.addHistoryItem(historyItem);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('异步下载执行失败:', error);
|
logger.error('异步下载执行失败:', error);
|
||||||
task.status = 'failed';
|
task.status = 'failed';
|
||||||
task.error = error.message;
|
task.error = error.message;
|
||||||
task.end_time = new Date();
|
task.end_time = new Date();
|
||||||
@@ -166,7 +171,7 @@ class DownloadExecutor {
|
|||||||
|
|
||||||
// 检查是否应该暂停
|
// 检查是否应该暂停
|
||||||
if (this.shouldPause(task.id)) {
|
if (this.shouldPause(task.id)) {
|
||||||
console.log('批量下载任务已暂停,停止下载:', task.id);
|
logger.info('批量下载任务已暂停,停止下载:', task.id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,7 +513,7 @@ class DownloadExecutor {
|
|||||||
*/
|
*/
|
||||||
getFileExtension(url) {
|
getFileExtension(url) {
|
||||||
if (typeof url !== 'string') {
|
if (typeof url !== 'string') {
|
||||||
console.warn('URL不是字符串,使用默认扩展名:', url);
|
logger.warn('URL不是字符串,使用默认扩展名:', url);
|
||||||
return 'jpg';
|
return 'jpg';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -551,7 +556,7 @@ class DownloadExecutor {
|
|||||||
} else if (task.type === 'batch' || task.type === 'artist') {
|
} else if (task.type === 'batch' || task.type === 'artist') {
|
||||||
// 批量下载和作者下载的恢复逻辑
|
// 批量下载和作者下载的恢复逻辑
|
||||||
// 这里需要根据具体实现来恢复
|
// 这里需要根据具体实现来恢复
|
||||||
console.log('恢复批量下载任务:', taskId);
|
logger.info('恢复批量下载任务:', taskId);
|
||||||
// TODO: 实现批量下载的恢复逻辑
|
// TODO: 实现批量下载的恢复逻辑
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ const ProgressManager = require('./progress-manager');
|
|||||||
const HistoryManager = require('./history-manager');
|
const HistoryManager = require('./history-manager');
|
||||||
const DownloadExecutor = require('./download-executor');
|
const DownloadExecutor = require('./download-executor');
|
||||||
const fs = require('fs-extra'); // Added for fs-extra
|
const fs = require('fs-extra'); // Added for fs-extra
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('DownloadService');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下载服务 - 主服务类,协调各个管理器
|
* 下载服务 - 主服务类,协调各个管理器
|
||||||
@@ -56,7 +61,7 @@ class DownloadService {
|
|||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
// 下载服务初始化完成
|
// 下载服务初始化完成
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('下载服务初始化失败:', error);
|
logger.error('下载服务初始化失败:', error);
|
||||||
this.initialized = false;
|
this.initialized = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -352,7 +357,7 @@ class DownloadService {
|
|||||||
|
|
||||||
return files.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
|
return files.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取下载文件列表失败:', error);
|
logger.error('获取下载文件列表失败:', error);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -396,7 +401,7 @@ class DownloadService {
|
|||||||
|
|
||||||
return Array.from(downloadedIds);
|
return Array.from(downloadedIds);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取已下载作品ID列表失败:', error);
|
logger.error('获取已下载作品ID列表失败:', error);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -430,7 +435,7 @@ class DownloadService {
|
|||||||
const infoContent = await fs.readFile(infoPath, 'utf8');
|
const infoContent = await fs.readFile(infoPath, 'utf8');
|
||||||
artworkInfo = JSON.parse(infoContent);
|
artworkInfo = JSON.parse(infoContent);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(`作品 ${artworkId} 缺少信息文件,认为未下载`);
|
logger.info(`作品 ${artworkId} 缺少信息文件,认为未下载`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,19 +444,19 @@ class DownloadService {
|
|||||||
const imageFiles = files.filter(file => /\.(jpg|jpeg|png|gif|webp)$/i.test(file) && file !== 'artwork_info.json');
|
const imageFiles = files.filter(file => /\.(jpg|jpeg|png|gif|webp)$/i.test(file) && file !== 'artwork_info.json');
|
||||||
|
|
||||||
if (imageFiles.length === 0) {
|
if (imageFiles.length === 0) {
|
||||||
console.log(`作品 ${artworkId} 有信息文件但没有图片文件,认为未下载`);
|
logger.info(`作品 ${artworkId} 有信息文件但没有图片文件,认为未下载`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查图片数量是否与artwork_info.json中记录的一致
|
// 检查图片数量是否与artwork_info.json中记录的一致
|
||||||
const expectedImageCount = artworkInfo.page_count || 1;
|
const expectedImageCount = artworkInfo.page_count || 1;
|
||||||
if (imageFiles.length < expectedImageCount) {
|
if (imageFiles.length < expectedImageCount) {
|
||||||
console.log(`作品 ${artworkId} 图片数量不匹配: 期望 ${expectedImageCount} 个,实际 ${imageFiles.length} 个`);
|
logger.info(`作品 ${artworkId} 图片数量不匹配: 期望 ${expectedImageCount} 个,实际 ${imageFiles.length} 个`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 有信息文件、有图片文件且数量匹配,认为已下载
|
// 有信息文件、有图片文件且数量匹配,认为已下载
|
||||||
// console.log(`作品 ${artworkId} 已完整下载,有信息文件和 ${imageFiles.length}/${expectedImageCount} 个图片文件`);
|
// logger.info(`作品 ${artworkId} 已完整下载,有信息文件和 ${imageFiles.length}/${expectedImageCount} 个图片文件`);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -459,7 +464,7 @@ class DownloadService {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('检查作品下载状态失败:', error);
|
logger.error('检查作品下载状态失败:', error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -508,7 +513,7 @@ class DownloadService {
|
|||||||
|
|
||||||
// 如果是重新下载,先删除现有目录
|
// 如果是重新下载,先删除现有目录
|
||||||
if (!skipExisting && (await this.fileManager.directoryExists(artworkDir))) {
|
if (!skipExisting && (await this.fileManager.directoryExists(artworkDir))) {
|
||||||
console.log(`删除现有作品目录: ${artworkDir}`);
|
logger.info(`删除现有作品目录: ${artworkDir}`);
|
||||||
await this.fileManager.removeDirectory(artworkDir);
|
await this.fileManager.removeDirectory(artworkDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -553,7 +558,7 @@ class DownloadService {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('下载作品失败:', error);
|
logger.error('下载作品失败:', error);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: error.message,
|
error: error.message,
|
||||||
@@ -630,7 +635,7 @@ class DownloadService {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('批量下载失败:', error);
|
logger.error('批量下载失败:', error);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: error.message,
|
error: error.message,
|
||||||
@@ -678,7 +683,7 @@ class DownloadService {
|
|||||||
|
|
||||||
// 如果是重新下载,先删除现有目录
|
// 如果是重新下载,先删除现有目录
|
||||||
if (!skipExisting && (await this.fileManager.directoryExists(artworkDir))) {
|
if (!skipExisting && (await this.fileManager.directoryExists(artworkDir))) {
|
||||||
console.log(`删除现有作品目录: ${artworkDir}`);
|
logger.info(`删除现有作品目录: ${artworkDir}`);
|
||||||
await this.fileManager.removeDirectory(artworkDir);
|
await this.fileManager.removeDirectory(artworkDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -719,7 +724,7 @@ class DownloadService {
|
|||||||
|
|
||||||
// 确保imageUrl是字符串
|
// 确保imageUrl是字符串
|
||||||
if (typeof imageUrl !== 'string') {
|
if (typeof imageUrl !== 'string') {
|
||||||
console.error(`图片URL不是字符串:`, imageUrl);
|
logger.error(`图片URL不是字符串:`, imageUrl);
|
||||||
results.push({ success: false, error: '图片URL格式错误' });
|
results.push({ success: false, error: '图片URL格式错误' });
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -739,7 +744,7 @@ class DownloadService {
|
|||||||
await this.fileManager.downloadFile(imageUrl, filePath);
|
await this.fileManager.downloadFile(imageUrl, filePath);
|
||||||
results.push({ success: true, file: fileName });
|
results.push({ success: true, file: fileName });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`下载图片失败 ${index + 1}: ${error.message}`);
|
logger.error(`下载图片失败 ${index + 1}: ${error.message}`);
|
||||||
results.push({ success: false, error: error.message });
|
results.push({ success: false, error: error.message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -763,7 +768,7 @@ class DownloadService {
|
|||||||
results: results,
|
results: results,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`下载作品 ${artworkId} 失败:`, error);
|
logger.error(`下载作品 ${artworkId} 失败:`, error);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: error.message,
|
error: error.message,
|
||||||
@@ -777,7 +782,7 @@ class DownloadService {
|
|||||||
*/
|
*/
|
||||||
getFileExtension(url) {
|
getFileExtension(url) {
|
||||||
if (typeof url !== 'string') {
|
if (typeof url !== 'string') {
|
||||||
console.warn('URL不是字符串,使用默认扩展名:', url);
|
logger.warn('URL不是字符串,使用默认扩展名:', url);
|
||||||
return 'jpg';
|
return 'jpg';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -800,7 +805,7 @@ class DownloadService {
|
|||||||
artistName = artistResult.data.name || `作者 ${artistId}`;
|
artistName = artistResult.data.name || `作者 ${artistId}`;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(`获取作者 ${artistId} 信息失败:`, err.message);
|
logger.warn(`获取作者 ${artistId} 信息失败:`, err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建任务记录
|
// 创建任务记录
|
||||||
@@ -901,7 +906,7 @@ class DownloadService {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('作者作品下载失败:', error);
|
logger.error('作者作品下载失败:', error);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: error.message,
|
error: error.message,
|
||||||
@@ -1013,7 +1018,7 @@ class DownloadService {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('排行榜作品下载失败:', error);
|
logger.error('排行榜作品下载失败:', error);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: error.message,
|
error: error.message,
|
||||||
@@ -1046,7 +1051,7 @@ class DownloadService {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取排行榜失败:', error);
|
logger.error('获取排行榜失败:', error);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: error.message,
|
error: error.message,
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ const crypto = require('crypto');
|
|||||||
const ConfigManager = require('../config/config-manager');
|
const ConfigManager = require('../config/config-manager');
|
||||||
const FileUtils = require('../utils/file-utils');
|
const FileUtils = require('../utils/file-utils');
|
||||||
const ErrorHandler = require('../utils/error-handler');
|
const ErrorHandler = require('../utils/error-handler');
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('FileManager');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件管理器 - 负责文件下载、检查和目录管理
|
* 文件管理器 - 负责文件下载、检查和目录管理
|
||||||
@@ -36,7 +40,7 @@ class FileManager {
|
|||||||
? downloadDir
|
? downloadDir
|
||||||
: path.resolve(process.cwd(), downloadDir);
|
: path.resolve(process.cwd(), downloadDir);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取下载路径失败:', error);
|
logger.error('获取下载路径失败:', error);
|
||||||
// 返回默认路径
|
// 返回默认路径
|
||||||
return path.resolve(process.cwd(), 'downloads');
|
return path.resolve(process.cwd(), 'downloads');
|
||||||
}
|
}
|
||||||
@@ -141,7 +145,7 @@ class FileManager {
|
|||||||
fs.access(filePath)
|
fs.access(filePath)
|
||||||
.then(() => resolve())
|
.then(() => resolve())
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error(`文件写入验证失败: ${filePath}`, error.message);
|
logger.error(`文件写入验证失败: ${filePath}`, error.message);
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -150,7 +154,7 @@ class FileManager {
|
|||||||
try {
|
try {
|
||||||
await this.safeDeleteFile(filePath);
|
await this.safeDeleteFile(filePath);
|
||||||
} catch (removeError) {
|
} catch (removeError) {
|
||||||
console.warn('清理失败文件时出错:', removeError.message);
|
logger.warn('清理失败文件时出错:', removeError.message);
|
||||||
}
|
}
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
@@ -161,7 +165,7 @@ class FileManager {
|
|||||||
// 处理文件系统错误
|
// 处理文件系统错误
|
||||||
const errorResult = ErrorHandler.handleFileSystemError(error, filePath, 'download');
|
const errorResult = ErrorHandler.handleFileSystemError(error, filePath, 'download');
|
||||||
|
|
||||||
console.error(`下载文件失败 (尝试 ${attempt}/${maxRetries}): ${filePath}`, error.message);
|
logger.error(`下载文件失败 (尝试 ${attempt}/${maxRetries}): ${filePath}`, error.message);
|
||||||
|
|
||||||
// 如果不是可重试的错误,直接抛出
|
// 如果不是可重试的错误,直接抛出
|
||||||
if (!errorResult.retryable) {
|
if (!errorResult.retryable) {
|
||||||
@@ -170,13 +174,13 @@ class FileManager {
|
|||||||
|
|
||||||
// 如果是最后一次尝试,抛出错误
|
// 如果是最后一次尝试,抛出错误
|
||||||
if (attempt === maxRetries) {
|
if (attempt === maxRetries) {
|
||||||
console.error(`下载文件最终失败: ${filePath}`, error.message);
|
logger.error(`下载文件最终失败: ${filePath}`, error.message);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 等待后重试
|
// 等待后重试
|
||||||
const retryDelay = ErrorHandler.getRetryDelay(error, attempt);
|
const retryDelay = ErrorHandler.getRetryDelay(error, attempt);
|
||||||
console.log(`等待 ${retryDelay}ms 后重试下载: ${filePath}`);
|
logger.info(`等待 ${retryDelay}ms 后重试下载: ${filePath}`);
|
||||||
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -323,7 +327,7 @@ class FileManager {
|
|||||||
await fs.unlink(filePath);
|
await fs.unlink(filePath);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`文件删除失败: ${filePath}`, error.message);
|
logger.error(`文件删除失败: ${filePath}`, error.message);
|
||||||
// 不抛出错误,避免影响其他操作
|
// 不抛出错误,避免影响其他操作
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('HistoryManager');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 历史记录管理器 - 负责下载历史的管理
|
* 历史记录管理器 - 负责下载历史的管理
|
||||||
@@ -28,9 +33,9 @@ class HistoryManager {
|
|||||||
await this.cleanupHistory();
|
await this.cleanupHistory();
|
||||||
|
|
||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
console.log('历史记录管理器初始化完成');
|
logger.info('历史记录管理器初始化完成');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('历史记录管理器初始化失败:', error);
|
logger.error('历史记录管理器初始化失败:', error);
|
||||||
this.initialized = false;
|
this.initialized = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,7 +49,7 @@ class HistoryManager {
|
|||||||
this.history = await fs.readJson(this.historyFile);
|
this.history = await fs.readJson(this.historyFile);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载下载历史失败:', error);
|
logger.error('加载下载历史失败:', error);
|
||||||
this.history = [];
|
this.history = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,7 +61,7 @@ class HistoryManager {
|
|||||||
try {
|
try {
|
||||||
await fs.writeJson(this.historyFile, this.history, { spaces: 2 });
|
await fs.writeJson(this.historyFile, this.history, { spaces: 2 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存下载历史失败:', error);
|
logger.error('保存下载历史失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +103,7 @@ class HistoryManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`清理历史记录: ${this.history.length} -> ${this.maxHistoryItems}`);
|
logger.info(`清理历史记录: ${this.history.length} -> ${this.maxHistoryItems}`);
|
||||||
|
|
||||||
// 保留最新的记录
|
// 保留最新的记录
|
||||||
this.history = this.history.slice(0, this.maxHistoryItems);
|
this.history = this.history.slice(0, this.maxHistoryItems);
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ const path = require('path');
|
|||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
const CacheConfigManager = require('../config/cache-config');
|
const CacheConfigManager = require('../config/cache-config');
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('ImageCache');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 图片缓存服务
|
* 图片缓存服务
|
||||||
@@ -61,9 +65,9 @@ class ImageCacheService {
|
|||||||
// 启动定期清理任务
|
// 启动定期清理任务
|
||||||
this.startCleanupTask();
|
this.startCleanupTask();
|
||||||
|
|
||||||
console.log('图片缓存服务初始化完成');
|
logger.info('图片缓存服务初始化完成');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('图片缓存服务初始化失败:', error);
|
logger.error('图片缓存服务初始化失败', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,9 +77,9 @@ class ImageCacheService {
|
|||||||
async ensureCacheDir() {
|
async ensureCacheDir() {
|
||||||
try {
|
try {
|
||||||
await fs.mkdir(this.cacheDir, { recursive: true });
|
await fs.mkdir(this.cacheDir, { recursive: true });
|
||||||
console.log('图片缓存目录创建成功:', this.cacheDir);
|
logger.info('图片缓存目录创建成功', { cacheDir: this.cacheDir });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('创建图片缓存目录失败:', error);
|
logger.error('创建图片缓存目录失败', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +157,7 @@ class ImageCacheService {
|
|||||||
|
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('读取缓存失败:', error);
|
logger.error('读取缓存失败:', error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,7 +176,7 @@ class ImageCacheService {
|
|||||||
// 检查缓存大小,如果超过限制则清理
|
// 检查缓存大小,如果超过限制则清理
|
||||||
await this.checkCacheSize();
|
await this.checkCacheSize();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存缓存失败:', error);
|
logger.error('保存缓存失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,7 +206,7 @@ class ImageCacheService {
|
|||||||
// 异步保存到缓存(不等待完成)
|
// 异步保存到缓存(不等待完成)
|
||||||
if (this.config.enabled) {
|
if (this.config.enabled) {
|
||||||
this.saveToCache(url, data).catch(error => {
|
this.saveToCache(url, data).catch(error => {
|
||||||
console.error('异步保存缓存失败:', error);
|
logger.error('异步保存缓存失败:', error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +274,7 @@ class ImageCacheService {
|
|||||||
|
|
||||||
// 如果超过最大大小,删除最旧的文件
|
// 如果超过最大大小,删除最旧的文件
|
||||||
if (totalSize > this.config.maxSize) {
|
if (totalSize > this.config.maxSize) {
|
||||||
console.log(`缓存大小 ${totalSize} 超过限制 ${this.config.maxSize},开始清理...`);
|
logger.info(`缓存大小 ${totalSize} 超过限制 ${this.config.maxSize},开始清理...`);
|
||||||
|
|
||||||
// 按修改时间排序,删除最旧的文件
|
// 按修改时间排序,删除最旧的文件
|
||||||
fileStats.sort((a, b) => a.mtime.getTime() - b.mtime.getTime());
|
fileStats.sort((a, b) => a.mtime.getTime() - b.mtime.getTime());
|
||||||
@@ -284,10 +288,10 @@ class ImageCacheService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`缓存清理完成,当前大小: ${totalSize}`);
|
logger.info(`缓存清理完成,当前大小: ${totalSize}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('检查缓存大小失败:', error);
|
logger.error('检查缓存大小失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,10 +316,10 @@ class ImageCacheService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cleanedCount > 0) {
|
if (cleanedCount > 0) {
|
||||||
console.log(`清理了 ${cleanedCount} 个过期缓存文件`);
|
logger.info(`清理了 ${cleanedCount} 个过期缓存文件`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('清理过期缓存失败:', error);
|
logger.error('清理过期缓存失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +329,7 @@ class ImageCacheService {
|
|||||||
startCleanupTask() {
|
startCleanupTask() {
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
this.cleanupExpiredCache().catch(error => {
|
this.cleanupExpiredCache().catch(error => {
|
||||||
console.error('定期清理任务失败:', error);
|
logger.error('定期清理任务失败:', error);
|
||||||
});
|
});
|
||||||
}, this.config.cleanupInterval);
|
}, this.config.cleanupInterval);
|
||||||
}
|
}
|
||||||
@@ -343,9 +347,9 @@ class ImageCacheService {
|
|||||||
await fs.unlink(filePath);
|
await fs.unlink(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('所有缓存已清理');
|
logger.info('所有缓存已清理');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('清理所有缓存失败:', error);
|
logger.error('清理所有缓存失败:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -376,7 +380,7 @@ class ImageCacheService {
|
|||||||
config: this.config
|
config: this.config
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取缓存统计失败:', error);
|
logger.error('获取缓存统计失败:', error);
|
||||||
return {
|
return {
|
||||||
fileCount: 0,
|
fileCount: 0,
|
||||||
totalSize: 0,
|
totalSize: 0,
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('ProgressManager');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 进度管理器 - 负责处理下载进度的监听和通知
|
* 进度管理器 - 负责处理下载进度的监听和通知
|
||||||
*/
|
*/
|
||||||
@@ -89,7 +95,7 @@ class ProgressManager {
|
|||||||
try {
|
try {
|
||||||
listener(task);
|
listener(task);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('进度监听器执行失败:', error);
|
logger.error('进度监听器执行失败:', error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,12 @@ const { promisify } = require('util')
|
|||||||
const { exec } = require('child_process')
|
const { exec } = require('child_process')
|
||||||
const ConfigManager = require('../config/config-manager')
|
const ConfigManager = require('../config/config-manager')
|
||||||
const execAsync = promisify(exec)
|
const execAsync = promisify(exec)
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('RepositoryService');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RepositoryService {
|
class RepositoryService {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -64,7 +70,7 @@ class RepositoryService {
|
|||||||
try {
|
try {
|
||||||
this.config = await this.configManager.readConfig()
|
this.config = await this.configManager.readConfig()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载配置失败:', error)
|
logger.error('加载配置失败:', error)
|
||||||
// 如果加载失败,使用默认配置对象
|
// 如果加载失败,使用默认配置对象
|
||||||
this.config = {
|
this.config = {
|
||||||
downloadDir: "./downloads",
|
downloadDir: "./downloads",
|
||||||
@@ -266,10 +272,10 @@ class RepositoryService {
|
|||||||
usagePercent: Math.round((used / total) * 100)
|
usagePercent: Math.round((used / total) * 100)
|
||||||
}
|
}
|
||||||
} catch (statfsError) {
|
} catch (statfsError) {
|
||||||
console.log('fs.statfs 调用失败:', statfsError.message)
|
logger.info('fs.statfs 调用失败:', statfsError.message)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('fs.statfs 在打包环境中不可用,尝试使用系统命令')
|
logger.info('fs.statfs 在打包环境中不可用,尝试使用系统命令')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果 fs.statfs 不可用,尝试使用系统命令
|
// 如果 fs.statfs 不可用,尝试使用系统命令
|
||||||
@@ -305,7 +311,7 @@ class RepositoryService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('PowerShell 方法失败:', error.message)
|
logger.info('PowerShell 方法失败:', error.message)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -340,7 +346,7 @@ class RepositoryService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('wmic 方法失败:', error.message)
|
logger.info('wmic 方法失败:', error.message)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -357,7 +363,7 @@ class RepositoryService {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(`磁盘使用情况获取方法失败:`, error.message)
|
logger.info(`磁盘使用情况获取方法失败:`, error.message)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -383,7 +389,7 @@ class RepositoryService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (dfError) {
|
} catch (dfError) {
|
||||||
console.log('df 命令失败:', dfError.message)
|
logger.info('df 命令失败:', dfError.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,7 +397,7 @@ class RepositoryService {
|
|||||||
return await this.getCachedDiskUsage(currentBaseDir, forceRefresh)
|
return await this.getCachedDiskUsage(currentBaseDir, forceRefresh)
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取磁盘使用情况失败:', error)
|
logger.error('获取磁盘使用情况失败:', error)
|
||||||
return {
|
return {
|
||||||
total: 0,
|
total: 0,
|
||||||
used: 0,
|
used: 0,
|
||||||
@@ -486,12 +492,12 @@ class RepositoryService {
|
|||||||
const expectedImageCount = artworkInfo.page_count || 1
|
const expectedImageCount = artworkInfo.page_count || 1
|
||||||
if (files.length < expectedImageCount) {
|
if (files.length < expectedImageCount) {
|
||||||
// 图片文件数量不足,认为下载不完整
|
// 图片文件数量不足,认为下载不完整
|
||||||
console.log(`作品 ${artworkId} 图片数量不匹配: 期望 ${expectedImageCount} 个,实际 ${files.length} 个`)
|
logger.info(`作品 ${artworkId} 图片数量不匹配: 期望 ${expectedImageCount} 个,实际 ${files.length} 个`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 有信息文件、有图片文件且数量匹配,认为已下载
|
// 有信息文件、有图片文件且数量匹配,认为已下载
|
||||||
// console.log(`作品 ${artworkId} 已完整下载: ${files.length}/${expectedImageCount} 个图片文件`)
|
// logger.info(`作品 ${artworkId} 已完整下载: ${files.length}/${expectedImageCount} 个图片文件`)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -499,7 +505,7 @@ class RepositoryService {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('检查作品下载状态失败:', error)
|
logger.error('检查作品下载状态失败:', error)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -772,12 +778,12 @@ class RepositoryService {
|
|||||||
if (cacheAge < maxCacheAge) {
|
if (cacheAge < maxCacheAge) {
|
||||||
this.diskUsageCache.data = cache.data
|
this.diskUsageCache.data = cache.data
|
||||||
this.diskUsageCache.timestamp = cache.timestamp
|
this.diskUsageCache.timestamp = cache.timestamp
|
||||||
console.log('已加载持久化缓存,缓存年龄:', Math.round(cacheAge / 1000 / 60), '分钟')
|
logger.info('已加载持久化缓存,缓存年龄:', Math.round(cacheAge / 1000 / 60), '分钟')
|
||||||
} else {
|
} else {
|
||||||
console.log('持久化缓存已过期,将重新计算')
|
logger.info('持久化缓存已过期,将重新计算')
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('加载持久化缓存失败,将使用内存缓存:', error.message)
|
logger.info('加载持久化缓存失败,将使用内存缓存:', error.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -795,9 +801,9 @@ class RepositoryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await fs.writeFile(this.cacheFilePath, JSON.stringify(cacheData, null, 2), 'utf8')
|
await fs.writeFile(this.cacheFilePath, JSON.stringify(cacheData, null, 2), 'utf8')
|
||||||
console.log('持久化缓存已保存')
|
logger.info('持久化缓存已保存')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存持久化缓存失败:', error.message)
|
logger.error('保存持久化缓存失败:', error.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -812,9 +818,9 @@ class RepositoryService {
|
|||||||
if (this.cacheFilePath) {
|
if (this.cacheFilePath) {
|
||||||
try {
|
try {
|
||||||
await fs.unlink(this.cacheFilePath)
|
await fs.unlink(this.cacheFilePath)
|
||||||
console.log('持久化缓存文件已删除')
|
logger.info('持久化缓存文件已删除')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('删除持久化缓存文件失败:', error.message)
|
logger.info('删除持久化缓存文件失败:', error.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -831,7 +837,7 @@ class RepositoryService {
|
|||||||
// 检查内存缓存是否有效(除非强制刷新)
|
// 检查内存缓存是否有效(除非强制刷新)
|
||||||
if (!forceRefresh && this.diskUsageCache.data &&
|
if (!forceRefresh && this.diskUsageCache.data &&
|
||||||
(now - this.diskUsageCache.timestamp) < this.diskUsageCache.cacheDuration) {
|
(now - this.diskUsageCache.timestamp) < this.diskUsageCache.cacheDuration) {
|
||||||
console.log('使用内存缓存的磁盘使用情况')
|
logger.info('使用内存缓存的磁盘使用情况')
|
||||||
return this.diskUsageCache.data
|
return this.diskUsageCache.data
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -862,7 +868,7 @@ class RepositoryService {
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('快速估算失败,返回默认值:', error.message)
|
logger.info('快速估算失败,返回默认值:', error.message)
|
||||||
return {
|
return {
|
||||||
total: 0,
|
total: 0,
|
||||||
used: 0,
|
used: 0,
|
||||||
@@ -916,7 +922,7 @@ class RepositoryService {
|
|||||||
|
|
||||||
return totalSize
|
return totalSize
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('快速目录大小估算失败:', error)
|
logger.error('快速目录大小估算失败:', error)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -938,14 +944,14 @@ class RepositoryService {
|
|||||||
totalSize += stats.size
|
totalSize += stats.size
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 忽略无法访问的文件
|
// 忽略无法访问的文件
|
||||||
console.log(`无法访问文件: ${fullPath}`)
|
logger.info(`无法访问文件: ${fullPath}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return totalSize
|
return totalSize
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('计算目录大小失败:', error)
|
logger.error('计算目录大小失败:', error)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { v4: uuidv4 } = require('uuid');
|
const { v4: uuidv4 } = require('uuid');
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('TaskManager');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 任务管理器 - 负责下载任务的生命周期管理
|
* 任务管理器 - 负责下载任务的生命周期管理
|
||||||
@@ -29,9 +33,9 @@ class TaskManager {
|
|||||||
await this.cleanupCompletedTasks();
|
await this.cleanupCompletedTasks();
|
||||||
|
|
||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
console.log('任务管理器初始化完成');
|
logger.info('任务管理器初始化完成');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('任务管理器初始化失败:', error);
|
logger.error('任务管理器初始化失败', error);
|
||||||
this.initialized = false;
|
this.initialized = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,7 +57,7 @@ class TaskManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载任务状态失败:', error);
|
logger.error('加载任务状态失败', error);
|
||||||
this.tasks = new Map();
|
this.tasks = new Map();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,7 +70,7 @@ class TaskManager {
|
|||||||
const tasksData = Object.fromEntries(this.tasks);
|
const tasksData = Object.fromEntries(this.tasks);
|
||||||
await fs.writeJson(this.tasksFile, tasksData, { spaces: 2 });
|
await fs.writeJson(this.tasksFile, tasksData, { spaces: 2 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存任务状态失败:', error);
|
logger.error('保存任务状态失败', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +203,7 @@ class TaskManager {
|
|||||||
|
|
||||||
if (cleanedCount > 0) {
|
if (cleanedCount > 0) {
|
||||||
await this.saveTasks();
|
await this.saveTasks();
|
||||||
console.log(`清理已完成任务: ${cleanedCount} 个`);
|
logger.info(`清理已完成任务: ${cleanedCount} 个`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return cleanedCount;
|
return cleanedCount;
|
||||||
|
|||||||
+12
-8
@@ -5,6 +5,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const PixivServer = require('./server');
|
const PixivServer = require('./server');
|
||||||
|
const { defaultLogger } = require('./utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('Start');
|
||||||
|
|
||||||
// 解析命令行参数
|
// 解析命令行参数
|
||||||
function parseArguments() {
|
function parseArguments() {
|
||||||
@@ -54,39 +58,39 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
|||||||
// 如果提供了代理端口,设置环境变量
|
// 如果提供了代理端口,设置环境变量
|
||||||
if (cliOptions.proxyPort) {
|
if (cliOptions.proxyPort) {
|
||||||
process.env.PROXY_PORT = cliOptions.proxyPort.toString();
|
process.env.PROXY_PORT = cliOptions.proxyPort.toString();
|
||||||
console.log(`\x1b[36m📡 代理端口已设置为: ${cliOptions.proxyPort}\x1b[0m`);
|
logger.info(`📡 代理端口已设置为: ${cliOptions.proxyPort}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果提供了服务器端口,设置环境变量
|
// 如果提供了服务器端口,设置环境变量
|
||||||
if (cliOptions.serverPort) {
|
if (cliOptions.serverPort) {
|
||||||
process.env.PORT = cliOptions.serverPort.toString();
|
process.env.PORT = cliOptions.serverPort.toString();
|
||||||
console.log(`\x1b[36m🌐 服务器端口已设置为: ${cliOptions.serverPort}\x1b[0m`);
|
logger.info(`🌐 服务器端口已设置为: ${cliOptions.serverPort}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('\x1b[35m🚀 启动 Pixiv 后端服务器...\x1b[0m');
|
logger.info('🚀 启动 Pixiv 后端服务器...');
|
||||||
|
|
||||||
// 创建服务器实例
|
// 创建服务器实例
|
||||||
const server = new PixivServer();
|
const server = new PixivServer();
|
||||||
|
|
||||||
// 处理进程信号
|
// 处理进程信号
|
||||||
process.on('SIGINT', async () => {
|
process.on('SIGINT', async () => {
|
||||||
console.log('\n\x1b[33m🛑 收到 SIGINT 信号,正在关闭服务器...\x1b[0m');
|
logger.info('🛑 收到 SIGINT 信号,正在关闭服务器...');
|
||||||
await server.shutdown();
|
await server.shutdown();
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('SIGTERM', async () => {
|
process.on('SIGTERM', async () => {
|
||||||
console.log('\n\x1b[33m🛑 收到 SIGTERM 信号,正在关闭服务器...\x1b[0m');
|
logger.info('🛑 收到 SIGTERM 信号,正在关闭服务器...');
|
||||||
await server.shutdown();
|
await server.shutdown();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 处理未捕获的异常
|
// 处理未捕获的异常
|
||||||
process.on('uncaughtException', error => {
|
process.on('uncaughtException', error => {
|
||||||
console.error('\x1b[31m❌ 未捕获的异常:\x1b[0m', error);
|
logger.error('❌ 未捕获的异常', error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('unhandledRejection', (reason, promise) => {
|
process.on('unhandledRejection', (reason, promise) => {
|
||||||
console.error('\x1b[31m❌ 未处理的 Promise 拒绝:\x1b[0m', reason);
|
logger.error('❌ 未处理的 Promise 拒绝', reason);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -95,6 +99,6 @@ server
|
|||||||
.init()
|
.init()
|
||||||
.then(() => server.start())
|
.then(() => server.start())
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('\x1b[31m❌ 服务器启动失败:\x1b[0m', error);
|
logger.error('❌ 服务器启动失败', error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|||||||
+49
-44
@@ -1,6 +1,11 @@
|
|||||||
const PixivBackend = require('./core');
|
const PixivBackend = require('./core');
|
||||||
const proxyConfig = require('./config');
|
const proxyConfig = require('./config');
|
||||||
const readline = require('readline');
|
const readline = require('readline');
|
||||||
|
const { defaultLogger } = require('./utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('TestLogin');
|
||||||
|
|
||||||
|
|
||||||
// 创建命令行交互接口
|
// 创建命令行交互接口
|
||||||
const rl = readline.createInterface({
|
const rl = readline.createInterface({
|
||||||
@@ -19,84 +24,84 @@ function askQuestion(question) {
|
|||||||
|
|
||||||
// 测试登录流程
|
// 测试登录流程
|
||||||
async function testLogin() {
|
async function testLogin() {
|
||||||
console.log('=== Pixiv 登录测试脚本 ===\n');
|
logger.info('=== Pixiv 登录测试脚本 ===\n');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. 设置代理环境变量
|
// 1. 设置代理环境变量
|
||||||
console.log('1. 设置代理配置...');
|
logger.info('1. 设置代理配置...');
|
||||||
proxyConfig.setEnvironmentVariables();
|
proxyConfig.setEnvironmentVariables();
|
||||||
|
|
||||||
// 2. 初始化后端
|
// 2. 初始化后端
|
||||||
console.log('\n2. 初始化 Pixiv 后端...');
|
logger.info('\n2. 初始化 Pixiv 后端...');
|
||||||
const backend = new PixivBackend();
|
const backend = new PixivBackend();
|
||||||
await backend.init();
|
await backend.init();
|
||||||
|
|
||||||
// 3. 检查登录状态
|
// 3. 检查登录状态
|
||||||
console.log('\n3. 检查当前登录状态...');
|
logger.info('\n3. 检查当前登录状态...');
|
||||||
const loginStatus = backend.getLoginStatus();
|
const loginStatus = backend.getLoginStatus();
|
||||||
console.log('登录状态:', loginStatus);
|
logger.info('登录状态:', loginStatus);
|
||||||
|
|
||||||
if (loginStatus.isLoggedIn) {
|
if (loginStatus.isLoggedIn) {
|
||||||
console.log('✅ 已登录,用户:', loginStatus.username);
|
logger.info('✅ 已登录,用户:', loginStatus.username);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 获取登录URL
|
// 4. 获取登录URL
|
||||||
console.log('\n4. 获取登录URL...');
|
logger.info('\n4. 获取登录URL...');
|
||||||
const loginData = backend.getLoginUrl();
|
const loginData = backend.getLoginUrl();
|
||||||
console.log('请访问以下URL进行登录:');
|
logger.info('请访问以下URL进行登录:');
|
||||||
console.log(loginData.login_url);
|
logger.info(loginData.login_url);
|
||||||
console.log('\n登录完成后,请复制回调URL中的code参数');
|
logger.info('\n登录完成后,请复制回调URL中的code参数');
|
||||||
|
|
||||||
// 5. 等待用户输入授权码
|
// 5. 等待用户输入授权码
|
||||||
const code = await askQuestion('\n请输入授权码 (code参数): ');
|
const code = await askQuestion('\n请输入授权码 (code参数): ');
|
||||||
|
|
||||||
if (!code || code.trim() === '') {
|
if (!code || code.trim() === '') {
|
||||||
console.log('❌ 未输入授权码,测试终止');
|
logger.info('❌ 未输入授权码,测试终止');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. 处理登录回调
|
// 6. 处理登录回调
|
||||||
console.log('\n5. 处理登录回调...');
|
logger.info('\n5. 处理登录回调...');
|
||||||
const loginResult = await backend.handleLoginCallback(code.trim());
|
const loginResult = await backend.handleLoginCallback(code.trim());
|
||||||
|
|
||||||
if (loginResult.success) {
|
if (loginResult.success) {
|
||||||
console.log('✅ 登录成功!');
|
logger.info('✅ 登录成功!');
|
||||||
console.log('用户信息:', loginResult.user);
|
logger.info('用户信息:', loginResult.user);
|
||||||
|
|
||||||
// 7. 再次检查登录状态
|
// 7. 再次检查登录状态
|
||||||
console.log('\n6. 验证登录状态...');
|
logger.info('\n6. 验证登录状态...');
|
||||||
const finalStatus = backend.getLoginStatus();
|
const finalStatus = backend.getLoginStatus();
|
||||||
console.log('最终登录状态:', finalStatus);
|
logger.info('最终登录状态:', finalStatus);
|
||||||
|
|
||||||
// 8. 测试获取用户信息
|
// 8. 测试获取用户信息
|
||||||
console.log('\n7. 测试获取用户信息...');
|
logger.info('\n7. 测试获取用户信息...');
|
||||||
const auth = backend.getAuth();
|
const auth = backend.getAuth();
|
||||||
const userInfo = await auth.getUserInfo();
|
const userInfo = await auth.getUserInfo();
|
||||||
|
|
||||||
if (userInfo.success) {
|
if (userInfo.success) {
|
||||||
console.log('✅ 获取用户信息成功:', userInfo.user);
|
logger.info('✅ 获取用户信息成功:', userInfo.user);
|
||||||
} else {
|
} else {
|
||||||
console.log('❌ 获取用户信息失败:', userInfo.error);
|
logger.info('❌ 获取用户信息失败:', userInfo.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
console.log('❌ 登录失败:', loginResult.error);
|
logger.info('❌ 登录失败:', loginResult.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ 测试过程中发生错误:', error.message);
|
logger.error('❌ 测试过程中发生错误:', error.message);
|
||||||
console.error('错误详情:', error);
|
logger.error('错误详情:', error);
|
||||||
} finally {
|
} finally {
|
||||||
// 清理资源
|
// 清理资源
|
||||||
rl.close();
|
rl.close();
|
||||||
console.log('\n=== 测试完成 ===');
|
logger.info('\n=== 测试完成 ===');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 测试重新登录功能
|
// 测试重新登录功能
|
||||||
async function testRelogin() {
|
async function testRelogin() {
|
||||||
console.log('=== 测试重新登录功能 ===\n');
|
logger.info('=== 测试重新登录功能 ===\n');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 设置代理
|
// 设置代理
|
||||||
@@ -110,21 +115,21 @@ async function testRelogin() {
|
|||||||
const loginStatus = backend.getLoginStatus();
|
const loginStatus = backend.getLoginStatus();
|
||||||
|
|
||||||
if (loginStatus.isLoggedIn) {
|
if (loginStatus.isLoggedIn) {
|
||||||
console.log('✅ 检测到已保存的登录信息');
|
logger.info('✅ 检测到已保存的登录信息');
|
||||||
console.log('用户:', loginStatus.username);
|
logger.info('用户:', loginStatus.username);
|
||||||
console.log('用户ID:', loginStatus.user_id);
|
logger.info('用户ID:', loginStatus.user_id);
|
||||||
} else {
|
} else {
|
||||||
console.log('❌ 没有保存的登录信息,无法测试重新登录');
|
logger.info('❌ 没有保存的登录信息,无法测试重新登录');
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ 重新登录测试失败:', error.message);
|
logger.error('❌ 重新登录测试失败:', error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 测试登出功能
|
// 测试登出功能
|
||||||
async function testLogout() {
|
async function testLogout() {
|
||||||
console.log('=== 测试登出功能 ===\n');
|
logger.info('=== 测试登出功能 ===\n');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 设置代理
|
// 设置代理
|
||||||
@@ -138,27 +143,27 @@ async function testLogout() {
|
|||||||
const logoutResult = backend.logout();
|
const logoutResult = backend.logout();
|
||||||
|
|
||||||
if (logoutResult.success) {
|
if (logoutResult.success) {
|
||||||
console.log('✅ 登出成功');
|
logger.info('✅ 登出成功');
|
||||||
|
|
||||||
// 验证登出状态
|
// 验证登出状态
|
||||||
const loginStatus = backend.getLoginStatus();
|
const loginStatus = backend.getLoginStatus();
|
||||||
console.log('登出后状态:', loginStatus);
|
logger.info('登出后状态:', loginStatus);
|
||||||
} else {
|
} else {
|
||||||
console.log('❌ 登出失败');
|
logger.info('❌ 登出失败');
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ 登出测试失败:', error.message);
|
logger.error('❌ 登出测试失败:', error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 主函数
|
// 主函数
|
||||||
async function main() {
|
async function main() {
|
||||||
console.log('请选择测试功能:');
|
logger.info('请选择测试功能:');
|
||||||
console.log('1. 测试完整登录流程');
|
logger.info('1. 测试完整登录流程');
|
||||||
console.log('2. 测试重新登录');
|
logger.info('2. 测试重新登录');
|
||||||
console.log('3. 测试登出');
|
logger.info('3. 测试登出');
|
||||||
console.log('4. 运行所有测试');
|
logger.info('4. 运行所有测试');
|
||||||
|
|
||||||
const choice = await askQuestion('\n请输入选择 (1-4): ');
|
const choice = await askQuestion('\n请输入选择 (1-4): ');
|
||||||
|
|
||||||
@@ -173,22 +178,22 @@ async function main() {
|
|||||||
await testLogout();
|
await testLogout();
|
||||||
break;
|
break;
|
||||||
case '4':
|
case '4':
|
||||||
console.log('\n=== 运行所有测试 ===\n');
|
logger.info('\n=== 运行所有测试 ===\n');
|
||||||
await testLogin();
|
await testLogin();
|
||||||
console.log('\n' + '='.repeat(50) + '\n');
|
logger.info('\n' + '='.repeat(50) + '\n');
|
||||||
await testRelogin();
|
await testRelogin();
|
||||||
console.log('\n' + '='.repeat(50) + '\n');
|
logger.info('\n' + '='.repeat(50) + '\n');
|
||||||
await testLogout();
|
await testLogout();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.log('❌ 无效选择');
|
logger.info('❌ 无效选择');
|
||||||
rl.close();
|
rl.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果直接运行此脚本
|
// 如果直接运行此脚本
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
main().catch(console.error);
|
main().catch(logger.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('ErrorHandler');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 错误处理工具类 - 专门处理打包后的权限问题
|
* 错误处理工具类 - 专门处理打包后的权限问题
|
||||||
@@ -19,7 +24,7 @@ class ErrorHandler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 记录错误信息
|
// 记录错误信息
|
||||||
console.error(`文件系统错误 [${operation}]:`, {
|
logger.error(`文件系统错误 [${operation}]:`, {
|
||||||
filePath: filePath,
|
filePath: filePath,
|
||||||
errorCode: error.code,
|
errorCode: error.code,
|
||||||
errorMessage: error.message,
|
errorMessage: error.message,
|
||||||
@@ -52,25 +57,25 @@ class ErrorHandler {
|
|||||||
static handlePermissionError(errorInfo) {
|
static handlePermissionError(errorInfo) {
|
||||||
const { filePath, isPkg, platform } = errorInfo;
|
const { filePath, isPkg, platform } = errorInfo;
|
||||||
|
|
||||||
console.error('权限错误 (EPERM) 解决方案:');
|
logger.error('权限错误 (EPERM) 解决方案:');
|
||||||
|
|
||||||
if (platform === 'win32') {
|
if (platform === 'win32') {
|
||||||
console.error('Windows 权限问题解决方案:');
|
logger.error('Windows 权限问题解决方案:');
|
||||||
console.error('1. 以管理员身份运行程序');
|
logger.error('1. 以管理员身份运行程序');
|
||||||
console.error('2. 检查文件/目录权限');
|
logger.error('2. 检查文件/目录权限');
|
||||||
console.error('3. 检查防病毒软件是否阻止访问');
|
logger.error('3. 检查防病毒软件是否阻止访问');
|
||||||
console.error('4. 检查文件是否被其他程序占用');
|
logger.error('4. 检查文件是否被其他程序占用');
|
||||||
|
|
||||||
if (isPkg) {
|
if (isPkg) {
|
||||||
console.error('5. 打包环境特殊处理:');
|
logger.error('5. 打包环境特殊处理:');
|
||||||
console.error(' - 确保程序有写入权限');
|
logger.error(' - 确保程序有写入权限');
|
||||||
console.error(' - 尝试使用用户目录而不是程序目录');
|
logger.error(' - 尝试使用用户目录而不是程序目录');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error('Unix/Linux 权限问题解决方案:');
|
logger.error('Unix/Linux 权限问题解决方案:');
|
||||||
console.error('1. 检查文件权限: chmod 755 <file>');
|
logger.error('1. 检查文件权限: chmod 755 <file>');
|
||||||
console.error('2. 检查目录权限: chmod 755 <directory>');
|
logger.error('2. 检查目录权限: chmod 755 <directory>');
|
||||||
console.error('3. 检查用户权限');
|
logger.error('3. 检查用户权限');
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -87,14 +92,14 @@ class ErrorHandler {
|
|||||||
static handleAccessError(errorInfo) {
|
static handleAccessError(errorInfo) {
|
||||||
const { filePath, platform } = errorInfo;
|
const { filePath, platform } = errorInfo;
|
||||||
|
|
||||||
console.error('访问错误 (EACCES) 解决方案:');
|
logger.error('访问错误 (EACCES) 解决方案:');
|
||||||
console.error('1. 检查文件/目录是否存在');
|
logger.error('1. 检查文件/目录是否存在');
|
||||||
console.error('2. 检查用户是否有访问权限');
|
logger.error('2. 检查用户是否有访问权限');
|
||||||
console.error('3. 检查文件系统权限');
|
logger.error('3. 检查文件系统权限');
|
||||||
|
|
||||||
if (platform === 'win32') {
|
if (platform === 'win32') {
|
||||||
console.error('4. 检查 Windows 安全设置');
|
logger.error('4. 检查 Windows 安全设置');
|
||||||
console.error('5. 尝试以管理员身份运行');
|
logger.error('5. 尝试以管理员身份运行');
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -111,11 +116,11 @@ class ErrorHandler {
|
|||||||
static handleBusyError(errorInfo) {
|
static handleBusyError(errorInfo) {
|
||||||
const { filePath } = errorInfo;
|
const { filePath } = errorInfo;
|
||||||
|
|
||||||
console.error('文件占用错误 (EBUSY) 解决方案:');
|
logger.error('文件占用错误 (EBUSY) 解决方案:');
|
||||||
console.error('1. 关闭可能占用文件的程序');
|
logger.error('1. 关闭可能占用文件的程序');
|
||||||
console.error('2. 等待文件释放后重试');
|
logger.error('2. 等待文件释放后重试');
|
||||||
console.error('3. 重启相关程序');
|
logger.error('3. 重启相关程序');
|
||||||
console.error('4. 检查是否有其他进程在使用文件');
|
logger.error('4. 检查是否有其他进程在使用文件');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'BUSY_ERROR',
|
type: 'BUSY_ERROR',
|
||||||
@@ -132,10 +137,10 @@ class ErrorHandler {
|
|||||||
static handleNotFoundError(errorInfo) {
|
static handleNotFoundError(errorInfo) {
|
||||||
const { filePath } = errorInfo;
|
const { filePath } = errorInfo;
|
||||||
|
|
||||||
console.error('文件不存在错误 (ENOENT) 解决方案:');
|
logger.error('文件不存在错误 (ENOENT) 解决方案:');
|
||||||
console.error('1. 检查文件路径是否正确');
|
logger.error('1. 检查文件路径是否正确');
|
||||||
console.error('2. 确保目录存在');
|
logger.error('2. 确保目录存在');
|
||||||
console.error('3. 检查文件名是否正确');
|
logger.error('3. 检查文件名是否正确');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'NOT_FOUND_ERROR',
|
type: 'NOT_FOUND_ERROR',
|
||||||
@@ -151,9 +156,9 @@ class ErrorHandler {
|
|||||||
static handleIsDirectoryError(errorInfo) {
|
static handleIsDirectoryError(errorInfo) {
|
||||||
const { filePath } = errorInfo;
|
const { filePath } = errorInfo;
|
||||||
|
|
||||||
console.error('是目录错误 (EISDIR) 解决方案:');
|
logger.error('是目录错误 (EISDIR) 解决方案:');
|
||||||
console.error('1. 检查路径是否指向目录而不是文件');
|
logger.error('1. 检查路径是否指向目录而不是文件');
|
||||||
console.error('2. 确保使用正确的文件路径');
|
logger.error('2. 确保使用正确的文件路径');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'IS_DIRECTORY_ERROR',
|
type: 'IS_DIRECTORY_ERROR',
|
||||||
@@ -169,9 +174,9 @@ class ErrorHandler {
|
|||||||
static handleNotDirectoryError(errorInfo) {
|
static handleNotDirectoryError(errorInfo) {
|
||||||
const { filePath } = errorInfo;
|
const { filePath } = errorInfo;
|
||||||
|
|
||||||
console.error('不是目录错误 (ENOTDIR) 解决方案:');
|
logger.error('不是目录错误 (ENOTDIR) 解决方案:');
|
||||||
console.error('1. 检查路径是否指向文件而不是目录');
|
logger.error('1. 检查路径是否指向文件而不是目录');
|
||||||
console.error('2. 确保使用正确的目录路径');
|
logger.error('2. 确保使用正确的目录路径');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'NOT_DIRECTORY_ERROR',
|
type: 'NOT_DIRECTORY_ERROR',
|
||||||
@@ -187,10 +192,10 @@ class ErrorHandler {
|
|||||||
static handleGenericError(errorInfo) {
|
static handleGenericError(errorInfo) {
|
||||||
const { filePath, errorCode, errorMessage } = errorInfo;
|
const { filePath, errorCode, errorMessage } = errorInfo;
|
||||||
|
|
||||||
console.error(`通用文件系统错误 (${errorCode}): ${errorMessage}`);
|
logger.error(`通用文件系统错误 (${errorCode}): ${errorMessage}`);
|
||||||
console.error('1. 检查文件系统状态');
|
logger.error('1. 检查文件系统状态');
|
||||||
console.error('2. 检查磁盘空间');
|
logger.error('2. 检查磁盘空间');
|
||||||
console.error('3. 检查文件系统权限');
|
logger.error('3. 检查文件系统权限');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'GENERIC_ERROR',
|
type: 'GENERIC_ERROR',
|
||||||
|
|||||||
+16
-11
@@ -1,5 +1,10 @@
|
|||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const { defaultLogger } = require('./logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
const logger = defaultLogger.child('FileUtils');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件操作工具类 - 确保与 pkg 打包兼容
|
* 文件操作工具类 - 确保与 pkg 打包兼容
|
||||||
@@ -22,7 +27,7 @@ class FileUtils {
|
|||||||
await nativeFs.unlink(filePath);
|
await nativeFs.unlink(filePath);
|
||||||
return true;
|
return true;
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
console.error(`文件删除失败: ${filePath}`, nativeError.message);
|
logger.error(`文件删除失败: ${filePath}`, nativeError.message);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,7 +49,7 @@ class FileUtils {
|
|||||||
await nativeFs.mkdir(dirPath, { recursive: true });
|
await nativeFs.mkdir(dirPath, { recursive: true });
|
||||||
return true;
|
return true;
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
console.error(`目录创建失败: ${dirPath}`, nativeError.message);
|
logger.error(`目录创建失败: ${dirPath}`, nativeError.message);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,7 +75,7 @@ class FileUtils {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`增强目录创建失败: ${dirPath}`, error.message);
|
logger.error(`增强目录创建失败: ${dirPath}`, error.message);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,7 +107,7 @@ class FileUtils {
|
|||||||
// 验证创建是否成功
|
// 验证创建是否成功
|
||||||
await fs.access(currentPath);
|
await fs.access(currentPath);
|
||||||
} catch (mkdirError) {
|
} catch (mkdirError) {
|
||||||
console.error(`创建目录失败: ${currentPath}`, mkdirError.message);
|
logger.error(`创建目录失败: ${currentPath}`, mkdirError.message);
|
||||||
throw mkdirError;
|
throw mkdirError;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -113,7 +118,7 @@ class FileUtils {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`递归创建目录失败: ${dirPath}`, error.message);
|
logger.error(`递归创建目录失败: ${dirPath}`, error.message);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,7 +142,7 @@ class FileUtils {
|
|||||||
// 尝试删除现有文件
|
// 尝试删除现有文件
|
||||||
await fs.remove(filePath);
|
await fs.remove(filePath);
|
||||||
} catch (removeError) {
|
} catch (removeError) {
|
||||||
console.warn(`删除现有文件失败: ${filePath}`, removeError.message);
|
logger.warn(`删除现有文件失败: ${filePath}`, removeError.message);
|
||||||
// 继续尝试写入,可能会覆盖
|
// 继续尝试写入,可能会覆盖
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -150,7 +155,7 @@ class FileUtils {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`安全写入文件失败: ${filePath}`, error.message);
|
logger.error(`安全写入文件失败: ${filePath}`, error.message);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,14 +178,14 @@ class FileUtils {
|
|||||||
try {
|
try {
|
||||||
await fs.remove(filePath);
|
await fs.remove(filePath);
|
||||||
} catch (removeError) {
|
} catch (removeError) {
|
||||||
console.warn(`删除现有文件失败: ${filePath}`, removeError.message);
|
logger.warn(`删除现有文件失败: ${filePath}`, removeError.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建写入流
|
// 创建写入流
|
||||||
return fs.createWriteStream(filePath);
|
return fs.createWriteStream(filePath);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`创建写入流失败: ${filePath}`, error.message);
|
logger.error(`创建写入流失败: ${filePath}`, error.message);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,7 +222,7 @@ class FileUtils {
|
|||||||
const nativeFs = require('fs').promises;
|
const nativeFs = require('fs').promises;
|
||||||
return await nativeFs.readdir(dirPath);
|
return await nativeFs.readdir(dirPath);
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
console.error(`读取目录失败: ${dirPath}`, nativeError.message);
|
logger.error(`读取目录失败: ${dirPath}`, nativeError.message);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -239,7 +244,7 @@ class FileUtils {
|
|||||||
await nativeFs.writeFile(filePath, jsonString, 'utf8');
|
await nativeFs.writeFile(filePath, jsonString, 'utf8');
|
||||||
return true;
|
return true;
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
console.error(`JSON 写入失败: ${filePath}`, nativeError.message);
|
logger.error(`JSON 写入失败: ${filePath}`, nativeError.message);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,324 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志级别枚举
|
||||||
|
*/
|
||||||
|
const LogLevel = {
|
||||||
|
ERROR: 0,
|
||||||
|
WARN: 1,
|
||||||
|
INFO: 2,
|
||||||
|
DEBUG: 3,
|
||||||
|
TRACE: 4
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志级别名称映射
|
||||||
|
*/
|
||||||
|
const LogLevelNames = {
|
||||||
|
[LogLevel.ERROR]: 'ERROR',
|
||||||
|
[LogLevel.WARN]: 'WARN',
|
||||||
|
[LogLevel.INFO]: 'INFO',
|
||||||
|
[LogLevel.DEBUG]: 'DEBUG',
|
||||||
|
[LogLevel.TRACE]: 'TRACE'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志级别颜色映射
|
||||||
|
*/
|
||||||
|
const LogLevelColors = {
|
||||||
|
[LogLevel.ERROR]: '\x1b[31m', // 红色
|
||||||
|
[LogLevel.WARN]: '\x1b[33m', // 黄色
|
||||||
|
[LogLevel.INFO]: '\x1b[36m', // 青色
|
||||||
|
[LogLevel.DEBUG]: '\x1b[35m', // 紫色
|
||||||
|
[LogLevel.TRACE]: '\x1b[90m' // 灰色
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模块颜色映射 - 为不同模块设置不同颜色
|
||||||
|
*/
|
||||||
|
const ModuleColors = {
|
||||||
|
'Server': '\x1b[32m', // 绿色
|
||||||
|
'Start': '\x1b[34m', // 蓝色
|
||||||
|
'PixivBackend': '\x1b[35m', // 紫色
|
||||||
|
'PixivAuth': '\x1b[36m', // 青色
|
||||||
|
'TaskManager': '\x1b[33m', // 黄色
|
||||||
|
'ImageCache': '\x1b[37m', // 白色
|
||||||
|
'HistoryManager': '\x1b[90m', // 灰色
|
||||||
|
'ProxyConfig': '\x1b[95m', // 亮紫色
|
||||||
|
'Download': '\x1b[93m', // 亮黄色
|
||||||
|
'Artwork': '\x1b[96m', // 亮青色
|
||||||
|
'Artist': '\x1b[92m', // 亮绿色
|
||||||
|
'Repository': '\x1b[94m', // 亮蓝色
|
||||||
|
'ErrorHandler': '\x1b[91m', // 亮红色
|
||||||
|
'API': '\x1b[97m', // 亮白色
|
||||||
|
'FileManager': '\x1b[98m', // 亮青色
|
||||||
|
'ProgressManager': '\x1b[99m', // 亮紫色
|
||||||
|
'Default': '\x1b[39m' // 默认颜色
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置颜色
|
||||||
|
*/
|
||||||
|
const RESET_COLOR = '\x1b[0m';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志图标映射
|
||||||
|
*/
|
||||||
|
const LogLevelIcons = {
|
||||||
|
[LogLevel.ERROR]: '❌',
|
||||||
|
[LogLevel.WARN]: '⚠️',
|
||||||
|
[LogLevel.INFO]: 'ℹ️',
|
||||||
|
[LogLevel.DEBUG]: '🔧',
|
||||||
|
[LogLevel.TRACE]: '🔍'
|
||||||
|
};
|
||||||
|
|
||||||
|
class Logger {
|
||||||
|
constructor(options = {}) {
|
||||||
|
this.level = options.level || LogLevel.INFO;
|
||||||
|
this.enableConsole = options.enableConsole !== false;
|
||||||
|
this.enableFile = options.enableFile || false;
|
||||||
|
this.logDir = options.logDir || path.join(__dirname, '../logs');
|
||||||
|
this.maxFileSize = options.maxFileSize || 10 * 1024 * 1024; // 10MB
|
||||||
|
this.maxFiles = options.maxFiles || 5;
|
||||||
|
this.enableColors = options.enableColors !== false;
|
||||||
|
this.module = options.module || 'App';
|
||||||
|
|
||||||
|
// 确保日志目录存在
|
||||||
|
if (this.enableFile) {
|
||||||
|
this.ensureLogDir();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确保日志目录存在
|
||||||
|
*/
|
||||||
|
ensureLogDir() {
|
||||||
|
if (!fs.existsSync(this.logDir)) {
|
||||||
|
fs.mkdirSync(this.logDir, { recursive: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前时间字符串
|
||||||
|
*/
|
||||||
|
getTimeString() {
|
||||||
|
const now = new Date();
|
||||||
|
return now.toLocaleTimeString('zh-CN', {
|
||||||
|
hour12: false,
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取日期字符串
|
||||||
|
*/
|
||||||
|
getDateString() {
|
||||||
|
const now = new Date();
|
||||||
|
return now.toISOString().split('T')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化日志消息
|
||||||
|
*/
|
||||||
|
formatMessage(level, message, data = null) {
|
||||||
|
const timeStr = this.getTimeString();
|
||||||
|
const levelName = LogLevelNames[level];
|
||||||
|
const icon = LogLevelIcons[level];
|
||||||
|
|
||||||
|
let formattedMessage = `[${timeStr}] [${levelName}] [${this.module}] ${icon} ${message}`;
|
||||||
|
|
||||||
|
if (data !== null && data !== undefined) {
|
||||||
|
if (typeof data === 'object') {
|
||||||
|
formattedMessage += ` ${JSON.stringify(data, null, 2)}`;
|
||||||
|
} else {
|
||||||
|
formattedMessage += ` ${data}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return formattedMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写入文件日志
|
||||||
|
*/
|
||||||
|
writeToFile(message) {
|
||||||
|
if (!this.enableFile) return;
|
||||||
|
|
||||||
|
const dateStr = this.getDateString();
|
||||||
|
const logFile = path.join(this.logDir, `${dateStr}.log`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 检查文件大小
|
||||||
|
if (fs.existsSync(logFile)) {
|
||||||
|
const stats = fs.statSync(logFile);
|
||||||
|
if (stats.size > this.maxFileSize) {
|
||||||
|
this.rotateLogFile(logFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.appendFileSync(logFile, message + '\n', 'utf8');
|
||||||
|
} catch (error) {
|
||||||
|
// 如果写入文件失败,至少输出到控制台
|
||||||
|
if (this.enableConsole) {
|
||||||
|
console.error('Failed to write to log file:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 轮转日志文件
|
||||||
|
*/
|
||||||
|
rotateLogFile(logFile) {
|
||||||
|
try {
|
||||||
|
// 删除最旧的文件
|
||||||
|
for (let i = this.maxFiles - 1; i >= 1; i--) {
|
||||||
|
const oldFile = `${logFile}.${i}`;
|
||||||
|
const newFile = `${logFile}.${i + 1}`;
|
||||||
|
if (fs.existsSync(oldFile)) {
|
||||||
|
if (i === this.maxFiles - 1) {
|
||||||
|
fs.unlinkSync(oldFile);
|
||||||
|
} else {
|
||||||
|
fs.renameSync(oldFile, newFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重命名当前文件
|
||||||
|
fs.renameSync(logFile, `${logFile}.1`);
|
||||||
|
} catch (error) {
|
||||||
|
// 如果轮转失败,删除当前文件
|
||||||
|
try {
|
||||||
|
fs.unlinkSync(logFile);
|
||||||
|
} catch (e) {
|
||||||
|
// 忽略删除错误
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出到控制台
|
||||||
|
*/
|
||||||
|
writeToConsole(message, level) {
|
||||||
|
if (!this.enableConsole) return;
|
||||||
|
|
||||||
|
if (this.enableColors && level !== undefined) {
|
||||||
|
const levelColor = LogLevelColors[level];
|
||||||
|
const moduleColor = ModuleColors[this.module] || ModuleColors['Default'];
|
||||||
|
|
||||||
|
// 解析消息,为模块名添加颜色
|
||||||
|
const coloredMessage = message.replace(
|
||||||
|
new RegExp(`\\[${this.module}\\]`, 'g'),
|
||||||
|
`${moduleColor}[${this.module}]${RESET_COLOR}`
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`${levelColor}${coloredMessage}${RESET_COLOR}`);
|
||||||
|
} else {
|
||||||
|
console.log(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录日志
|
||||||
|
*/
|
||||||
|
log(level, message, data = null) {
|
||||||
|
if (level > this.level) return;
|
||||||
|
|
||||||
|
const formattedMessage = this.formatMessage(level, message, data);
|
||||||
|
|
||||||
|
this.writeToConsole(formattedMessage, level);
|
||||||
|
this.writeToFile(formattedMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误日志
|
||||||
|
*/
|
||||||
|
error(message, data = null) {
|
||||||
|
this.log(LogLevel.ERROR, message, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 警告日志
|
||||||
|
*/
|
||||||
|
warn(message, data = null) {
|
||||||
|
this.log(LogLevel.WARN, message, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 信息日志
|
||||||
|
*/
|
||||||
|
info(message, data = null) {
|
||||||
|
this.log(LogLevel.INFO, message, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调试日志
|
||||||
|
*/
|
||||||
|
debug(message, data = null) {
|
||||||
|
this.log(LogLevel.DEBUG, message, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跟踪日志
|
||||||
|
*/
|
||||||
|
trace(message, data = null) {
|
||||||
|
this.log(LogLevel.TRACE, message, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建子logger
|
||||||
|
*/
|
||||||
|
child(module) {
|
||||||
|
return new Logger({
|
||||||
|
level: this.level,
|
||||||
|
enableConsole: this.enableConsole,
|
||||||
|
enableFile: this.enableFile,
|
||||||
|
logDir: this.logDir,
|
||||||
|
maxFileSize: this.maxFileSize,
|
||||||
|
maxFiles: this.maxFiles,
|
||||||
|
enableColors: this.enableColors,
|
||||||
|
module: module
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置日志级别
|
||||||
|
*/
|
||||||
|
setLevel(level) {
|
||||||
|
this.level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用/禁用控制台输出
|
||||||
|
*/
|
||||||
|
setConsoleOutput(enabled) {
|
||||||
|
this.enableConsole = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用/禁用文件输出
|
||||||
|
*/
|
||||||
|
setFileOutput(enabled) {
|
||||||
|
this.enableFile = enabled;
|
||||||
|
if (enabled) {
|
||||||
|
this.ensureLogDir();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建默认logger实例
|
||||||
|
const defaultLogger = new Logger({
|
||||||
|
level: process.env.LOG_LEVEL ? LogLevel[process.env.LOG_LEVEL.toUpperCase()] : LogLevel.INFO,
|
||||||
|
enableConsole: true,
|
||||||
|
enableFile: process.env.LOG_TO_FILE === 'true',
|
||||||
|
enableColors: process.env.LOG_COLORS !== 'false'
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Logger,
|
||||||
|
LogLevel,
|
||||||
|
LogLevelNames,
|
||||||
|
defaultLogger
|
||||||
|
};
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const { defaultLogger } = require('../backend/utils/logger');
|
||||||
|
|
||||||
|
// 创建logger实例
|
||||||
|
|
||||||
async function createPortable() {
|
async function createPortable() {
|
||||||
const distDir = path.join(__dirname, '..', 'dist');
|
const distDir = path.join(__dirname, '..', 'dist');
|
||||||
@@ -91,9 +94,9 @@ pause
|
|||||||
await fs.ensureDir(path.join(portableDir, 'data'));
|
await fs.ensureDir(path.join(portableDir, 'data'));
|
||||||
await fs.ensureDir(path.join(portableDir, 'downloads'));
|
await fs.ensureDir(path.join(portableDir, 'downloads'));
|
||||||
|
|
||||||
console.log('✅ 便携版创建完成!');
|
logger.info('✅ 便携版创建完成!');
|
||||||
console.log(`📁 位置: ${portableDir}`);
|
logger.info(`📁 位置: ${portableDir}`);
|
||||||
console.log('📦 可以将整个文件夹打包分发给用户');
|
logger.info('📦 可以将整个文件夹打包分发给用户');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ 创建便携版失败:', error);
|
console.error('❌ 创建便携版失败:', error);
|
||||||
|
|||||||
+52
-1
@@ -10,6 +10,7 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
});
|
});
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const error = ref<string | null>(null);
|
const error = ref<string | null>(null);
|
||||||
|
const statusCheckTimer = ref<number | null>(null); // 添加状态检查定时器
|
||||||
|
|
||||||
// 计算属性
|
// 计算属性
|
||||||
const isLoggedIn = computed(() => loginStatus.value.isLoggedIn);
|
const isLoggedIn = computed(() => loginStatus.value.isLoggedIn);
|
||||||
@@ -24,15 +25,62 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
const response = await authService.getLoginStatus();
|
const response = await authService.getLoginStatus();
|
||||||
if (response.success && response.data) {
|
if (response.success && response.data) {
|
||||||
loginStatus.value = response.data;
|
loginStatus.value = response.data;
|
||||||
|
|
||||||
|
// 如果登录状态发生变化,启动或停止状态检查
|
||||||
|
if (response.data.isLoggedIn) {
|
||||||
|
startStatusCheck();
|
||||||
|
} else {
|
||||||
|
stopStatusCheck();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error.value = err instanceof Error ? err.message : '获取登录状态失败';
|
error.value = err instanceof Error ? err.message : '获取登录状态失败';
|
||||||
console.error('获取登录状态失败:', err);
|
console.error('获取登录状态失败:', err);
|
||||||
|
|
||||||
|
// 如果获取状态失败,可能是token过期,停止状态检查
|
||||||
|
stopStatusCheck();
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 启动定期状态检查
|
||||||
|
const startStatusCheck = () => {
|
||||||
|
// 清除之前的定时器
|
||||||
|
stopStatusCheck();
|
||||||
|
|
||||||
|
// 每5分钟检查一次登录状态
|
||||||
|
statusCheckTimer.value = window.setInterval(async () => {
|
||||||
|
try {
|
||||||
|
const response = await authService.getLoginStatus();
|
||||||
|
if (response.success && response.data) {
|
||||||
|
// 更新登录状态
|
||||||
|
loginStatus.value = response.data;
|
||||||
|
|
||||||
|
// 如果已登出,停止检查
|
||||||
|
if (!response.data.isLoggedIn) {
|
||||||
|
stopStatusCheck();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('定期检查登录状态失败:', err);
|
||||||
|
// 检查失败,可能是网络问题或token过期,停止检查
|
||||||
|
stopStatusCheck();
|
||||||
|
}
|
||||||
|
}, 5 * 60 * 1000); // 5分钟
|
||||||
|
|
||||||
|
console.log('登录状态定期检查已启动');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 停止定期状态检查
|
||||||
|
const stopStatusCheck = () => {
|
||||||
|
if (statusCheckTimer.value) {
|
||||||
|
clearInterval(statusCheckTimer.value);
|
||||||
|
statusCheckTimer.value = null;
|
||||||
|
console.log('登录状态定期检查已停止');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 获取登录URL
|
// 获取登录URL
|
||||||
const getLoginUrl = async () => {
|
const getLoginUrl = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -100,6 +148,7 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
const response = await authService.logout();
|
const response = await authService.logout();
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
loginStatus.value = { isLoggedIn: false };
|
loginStatus.value = { isLoggedIn: false };
|
||||||
|
stopStatusCheck(); // 停止状态检查
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
throw new Error(response.error || '登出失败');
|
throw new Error(response.error || '登出失败');
|
||||||
@@ -134,6 +183,8 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
handleLoginCallback,
|
handleLoginCallback,
|
||||||
relogin,
|
relogin,
|
||||||
logout,
|
logout,
|
||||||
clearError
|
clearError,
|
||||||
|
startStatusCheck,
|
||||||
|
stopStatusCheck
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user