feat: 优化日志打印

This commit is contained in:
chaos-zhu
2025-11-24 23:34:43 +08:00
parent cfab115439
commit 9190538080
34 changed files with 498 additions and 376 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
// 规则参见:https://cn.eslint.org/docs/rules/
module.exports = {
root: true, // 当前配置文件不能往父级查找
'globals': { 'consola': true },
'globals': { 'logger': true },
env: {
node: true,
es6: true
+1 -3
View File
@@ -1,7 +1,5 @@
const path = require('path')
consola.info('debug日志:', process.env.DEBUG === '1' ? '开启' : '关闭')
module.exports = {
httpPort: 8082,
uploadDir: path.join(process.cwd(),'app/db'),
@@ -28,6 +26,6 @@ module.exports = {
apiPrefix: '/api/v1',
logConfig: {
outDir: path.join(process.cwd(),'./app/db/logs'),
recordLog: process.env.DEBUG === '1' // 是否记录日志
DEBUG: process.env.DEBUG === '1' || process.env.DEBUG === 'true' // 是否记录日志
}
}
+1 -1
View File
@@ -14,7 +14,7 @@ async function getHostList({ res }) {
const isConfig = Boolean(authType && item[authType])
Object.assign(item, { id, isConfig, password: '', privateKey: '', credential })
} catch (error) {
consola.error('getHostList error: ', error.message)
logger.error('getHostList error: ', error.message)
}
}
res.success({ data })
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -30,7 +30,7 @@ const addSSH = async ({ res, request }) => {
record[authType] = await AESEncryptAsync(clearSSHKey)
// console.log(`${ authType }__commonKey加密存储: `, record[authType])
await credentialsDB.insertAsync({ ...record, date: Date.now() })
consola.info('添加凭证:', name)
logger.info('添加凭证:', name)
res.success({ data: '保存成功' })
}
@@ -54,7 +54,7 @@ const updateSSH = async ({ res, request }) => {
// console.log(`${ authType }__commonKey加密存储: `, record[authType])
}
await credentialsDB.updateAsync({ _id: id }, record)
consola.info('修改凭证:', name)
logger.info('修改凭证:', name)
res.success({ data: '保存成功' })
}
@@ -75,7 +75,7 @@ const removeSSH = async ({ res, request }) => {
}
}
await hostListDB.compactDatafileAsync()
consola.info('移除凭证:', id)
logger.info('移除凭证:', id)
await credentialsDB.removeAsync({ _id: id })
res.success({ data: '移除成功' })
}
@@ -85,7 +85,7 @@ const getCommand = async ({ res, request }) => {
if (!hostId) return res.fail({ data: false, msg: '参数错误' })
let hostInfo = await hostListDB.findAsync({})
let record = hostInfo?.find(item => item._id === hostId)
consola.info('查询登录后执行的指令:', hostId)
logger.info('查询登录后执行的指令:', hostId)
if (!record) return res.fail({ data: false, msg: 'host not found' })
const { command } = record
if (!command) return res.success({ data: false })
+7 -7
View File
@@ -82,7 +82,7 @@ const login = async ({ res, request }) => {
const beforeLoginHandler = async (clientIp, jwtExpires) => {
loginErrCount = loginErrTotal = 0 // 登录成功, 清空错误次数
// consola.success('登录成功, 准备生成token', new Date())
logger.info('登录成功, 准备生成token')
// 生产token
let { commonKey, user } = await keyDB.findOneAsync({})
let token = jwt.sign({ date: Date.now() }, `${ user }-${ commonKey }`, { expiresIn: jwtExpires }) // 生成token
@@ -91,7 +91,7 @@ const beforeLoginHandler = async (clientIp, jwtExpires) => {
// 记录客户端登录IP(用于判断是否异地且只保留最近10次)
const clientIPInfo = await getNetIPInfo(clientIp)
const { ip, country, city } = clientIPInfo || {}
consola.info('登录成功:', new Date(), { ip, country, city })
logger.info('登录成功:', new Date(), { ip, country, city })
// 登录通知
sendNoticeAsync('login', '登录提醒', `地点:${ country + city }\nIP: ${ ip }`)
@@ -122,7 +122,7 @@ const getEasynodeVersion = async ({ res }) => {
const { data } = await axios.get('https://get-easynode-latest-version.chaoszhu.workers.dev/version')
res.success({ data, msg: 'success' })
} catch (error) {
consola.error('Failed to fetch Easynode latest version:', error)
logger.error('Failed to fetch Easynode latest version:', error)
res.fail({ msg: 'Failed to fetch Easynode latest version' })
}
}
@@ -155,7 +155,7 @@ const enableMFA2 = async ({ res, request }) => {
await keyDB.updateAsync({ _id: keyConfig._id }, { $set: keyConfig })
res.success({ msg: '验证成功' })
} catch (error) {
consola.error('MFA2验证失败:', error.message)
logger.error('MFA2验证失败:', error.message)
res.fail({ msg: `验证失败: ${ error.message }` })
}
}
@@ -178,7 +178,7 @@ const disableMFA2 = async ({ res, request }) => {
await keyDB.updateAsync({ _id: keyConfig._id }, { $set: keyConfig })
res.success({ msg: '禁用成功' })
} catch (error) {
consola.error('禁用MFA2失败:', error.message)
logger.error('禁用MFA2失败:', error.message)
res.fail({ msg: `禁用失败: ${ error.message }` })
}
}
@@ -202,11 +202,11 @@ const getPlusDiscount = async ({ res } = {}) => {
}
// 如果是403或其他错误状态码
consola.error('获取折扣信息失败,状态码:', response.status)
logger.error('获取折扣信息失败,状态码:', response.status)
return res.success({ discount: false })
} catch (error) {
consola.error('获取折扣信息失败:', error.message)
logger.error('获取折扣信息失败:', error.message)
return res.success({ discount: false })
}
}
+12 -13
View File
@@ -10,7 +10,7 @@ async function initKeyDB() {
const { _id, ipWhiteList = [] } = keyData
let allowedIPs = process.env.ALLOWED_IPS ? process.env.ALLOWED_IPS.split(',') : []
if (allowedIPs.length > 0) {
consola.info('[存在白名单IP环境变量,合并到本地数据库中]')
logger.info('[存在白名单IP环境变量,合并到本地数据库中]')
allowedIPs = [...new Set([...ipWhiteList, ...allowedIPs])].filter(item => item)
await keyDB.updateAsync({ _id }, { $set: { ipWhiteList: allowedIPs } })
}
@@ -18,9 +18,9 @@ async function initKeyDB() {
let { ipWhiteList = [] } = await keyDB.findOneAsync({})
if (ipWhiteList.length > 0) global.ALLOWED_IPS = ipWhiteList
} catch (error) {
consola.error('设置全局IP白名单失败:', error)
logger.error('设置全局IP白名单失败:', error)
}
consola.info('公私钥已存在[重新生成会导致已保存的ssh密钥信息失效]')
logger.info('公私钥已存在[重新生成会导致已保存的ssh密钥信息失效]')
return
}
@@ -44,20 +44,19 @@ async function initKeyDB() {
await keyDB.updateAsync({}, { $set: newConfig }, { upsert: true })
// 在控制台打印随机生成的账号密码
consola.info('========================================')
consola.info('EasyNode 默认登录凭据 (请及时更改):')
consola.info(`用户名: ${ randomUsername }`)
consola.info(`密码: ${ randomPassword }`)
consola.info('========================================')
logger.info('========================================')
logger.info('EasyNode 默认登录凭据 (请及时更改):')
logger.info(`用户名: ${ randomUsername }`)
logger.info(`密码: ${ randomPassword }`)
logger.info('========================================')
consola.info('Task: 已生成新的非对称加密公私钥')
}
async function initGroupDB() {
const groupDB = new GroupDB().getInstance()
let count = await groupDB.countAsync({})
if (count === 0) {
consola.log('初始化groupDB✔')
logger.info('初始化groupDB✔')
const defaultData = [{ '_id': 'default', 'name': '默认分组', 'index': 0 }]
return groupDB.insertAsync(defaultData)
}
@@ -68,7 +67,7 @@ async function initNotifyDB() {
const notifyDB = new NotifyDB().getInstance()
let count = await notifyDB.countAsync({})
if (count !== 0) return
consola.log('初始化notifyDB✔')
logger.info('初始化notifyDB✔')
let defaultData = [{
'type': 'login',
'desc': '登录面板提醒',
@@ -100,7 +99,7 @@ async function initNotifyDB() {
async function initNotifyConfigDB() {
const notifyConfigDB = new NotifyConfigDB().getInstance()
let notifyConfig = await notifyConfigDB.findOneAsync({})
consola.log('初始化NotifyConfigDB✔')
logger.info('初始化NotifyConfigDB✔')
const defaultData = {
type: 'sct',
sct: {
@@ -128,7 +127,7 @@ async function initScriptGroupDB() {
const scriptGroupDB = new ScriptGroupDB().getInstance()
let count = await scriptGroupDB.countAsync({})
if (count === 0) {
consola.log('初始化ScriptGroupDB✔')
logger.info('初始化ScriptGroupDB✔')
const defaultData = [
{ '_id': 'default', 'name': '默认分组', 'index': 0 },
{ '_id': 'builtin', 'name': '内置脚本', 'index': -1 }
+29
View File
@@ -0,0 +1,29 @@
const log4js = require('log4js')
const { outDir } = require('./config').logConfig
log4js.configure({
appenders: {
console: {
type: 'stdout',
layout: {
type: 'pattern',
pattern: '%[%d{yyyy-MM-dd hh:mm:ss.SSS} [%p] -%] %m'
}
},
file: {
type: 'file',
maxLogSize: 10 * 1024 * 10, // 10MB
filename: `${ outDir }/debug.log`,
backups: 10,
compress: true,
keepFileExt: true
}
},
categories: {
default: {
appenders: ['console', 'file'],
level: 'debug'
}
}
})
const logger = log4js.getLogger()
global.logger = logger
+1
View File
@@ -1,3 +1,4 @@
require('./logs')
const { httpServer } = require('./server')
const initDB = require('./db')
const scheduleJob = require('./schedule')
+3 -3
View File
@@ -5,11 +5,11 @@ let whitePath = [
'/login',
'/get-pub-pem'
].map(item => (apiPrefix + item))
consola.info('路由白名单:', whitePath)
logger.warn('路由白名单:', whitePath)
const useAuth = async ({ request, res }, next) => {
const { path, headers: { token, uid } } = request
// consola.info('verify path: ', path)
logger.info('verify path: ', path)
if (whitePath.includes(path)) return next()
if (!token) return res.fail({ msg: '未登录(token)', status: 401 })
if (!uid) return res.fail({ msg: '未登录(uid)', status: 401 })
@@ -23,7 +23,7 @@ const useAuth = async ({ request, res }, next) => {
case enumLoginCode.ERROR_TOKEN:
return res.fail({ msg: 'TOKEN校验失败, 请重新登录', status: 401 })
case enumLoginCode.ERROR_UID:
consola.warn('用户id校验失败(可能存在外部攻击): ', path)
logger.error('用户id校验失败(可能存在外部攻击): ', path)
return res.fail({ msg: 'UID错误!!!', status: 403 })
}
}
+1 -1
View File
@@ -2,7 +2,7 @@ const ipFilter = require('./ipFilter') // IP过滤
const responseHandler = require('./response') // 统一返回格式, 错误捕获
const useAuth = require('./auth') // 鉴权
// const useCors = require('./cors') // 处理跨域[暂时禁止]
const useLog = require('./log4') // 记录日志,需要等待路由处理完成,所以得放路由前
const useLog = require('./useLog') // 记录日志,需要等待路由处理完成,所以得放路由前
const useKoaBody = require('./body') // 处理body参数 【请求需先走该中间件】
const { useRoutes, useAllowedMethods } = require('./router') // 路由管理
const useStatic = require('./static') // 静态目录
-62
View File
@@ -1,62 +0,0 @@
const log4js = require('log4js')
const { outDir, recordLog } = require('../config').logConfig
log4js.configure({
appenders: {
console: {
type: 'stdout',
layout: {
type: 'pattern',
pattern: '%[%d{yyyy-MM-dd hh:mm:ss.SSS} [%p] -%] %m'
}
},
cheese: {
type: 'file',
maxLogSize: 10 * 1024 * 1024, // unit: bytes 1KB = 1024bytes
filename: `${ outDir }/debug.log`,
backups: 10,
compress: true,
keepFileExt: true
}
},
categories: {
default: {
appenders: ['console', 'cheese'],
level: 'debug'
}
}
})
const logger = log4js.getLogger()
const useLog = () => {
return async (ctx, next) => {
const { method, path, origin, query, body, headers, ip } = ctx.request
const data = {
method,
path,
origin,
query,
body,
ip,
headers
}
await next() // 等待路由处理完成,再开始记录日志
// 是否记录日志
if (recordLog) {
const { status, params } = ctx
data.status = status
data.params = params
data.result = ctx.body || 'no content'
if (String(status).startsWith(4) || String(status).startsWith(5))
logger.error(JSON.stringify(data))
else
logger.info(JSON.stringify(data))
}
}
}
module.exports = useLog()
// 可以先测试一下日志是否正常工作
logger.info('日志系统启动')
+2 -2
View File
@@ -9,11 +9,11 @@ const createRdpProxyMiddleware = () => {
const RDP_HOST = process.env.RDP_HOST || '127.0.0.1' // 使用127.0.0.1更可靠
const target = `http://${ RDP_HOST }:${ RDP_PORT }`
console.log('Creating RDP proxy middleware with target:', target)
logger.info('创建RDP代理转发:', target)
// 创建WebSocket代理
const wsProxy = createProxyMiddleware({
target: target,
target,
ws: true,
changeOrigin: true,
pathRewrite: {
+1 -1
View File
@@ -23,7 +23,7 @@ const responseHandler = async (ctx, next) => {
await next() // 每个中间件都需等待next完成调用,不然会返回404给前端!!!
} catch (err) {
console.dir(err)
consola.error('中间件错误:', err)
logger.error('中间件错误:', err)
if (err.status)
ctx.res.fail({ status: err.status, msg: err.message }) // 自己主动抛出的错误 throwError
else
+169
View File
@@ -0,0 +1,169 @@
// log4.js
const { DEBUG } = require('../config').logConfig
// ------------------ 脱敏 ------------------
// 可能包含敏感信息的 header key(小写比较)
const SENSITIVE_HEADER_KEYS = ['authorization', 'cookie', 'token', 'uid']
// 需要打码的 body 字段(小写比较)
const SENSITIVE_BODY_KEYS = [
'password', 'pwd', 'code', 'captcha', 'token',
'oldloginname', 'oldpwd', 'newloginname', 'newpwd',
'loginname', 'ciphertext', 'jwtexpires', 'mfa2token'
]
const MAX_BODY_LOG_LEN = 1000 // 请求 body 最多记录长度
const MAX_RESULT_LOG_LEN = 1000 // 响应体最多记录长度
function maskSensitiveHeaders(headers = {}) {
const safeHeaders = {}
for (const [key, value] of Object.entries(headers)) {
const lowerKey = key.toLowerCase()
if (SENSITIVE_HEADER_KEYS.includes(lowerKey)) {
safeHeaders[key] = '[MASKED]'
} else {
safeHeaders[key] = value
}
}
return safeHeaders
}
function maskSensitiveBody(body) {
if (!body || typeof body !== 'object') return body
// 简单深拷贝 + 递归打码
const clone = Array.isArray(body) ? [] : {}
for (const [key, value] of Object.entries(body)) {
const lowerKey = key.toLowerCase()
if (SENSITIVE_BODY_KEYS.includes(lowerKey)) {
clone[key] = '[MASKED]'
} else if (value && typeof value === 'object') {
clone[key] = maskSensitiveBody(value)
} else {
clone[key] = value
}
}
return clone
}
function truncateString(str, maxLen) {
if (typeof str !== 'string') return str
if (!maxLen || str.length <= maxLen) return str
return str.slice(0, maxLen) + '... [truncated]'
}
function safeStringify(obj, maxLen) {
try {
const json = JSON.stringify(obj)
return truncateString(json, maxLen) // 避免循环引用
} catch (e) {
return '[Unserializable object]'
}
}
// 格式化请求 body,用于写入日志
function formatBodyForLog(body) {
if (!body) return 'no body'
// 如果是字符串,直接截断
if (typeof body === 'string') {
return truncateString(body, MAX_BODY_LOG_LEN)
}
// 其它类型(对象、数组等)先脱敏再 stringify
const masked = maskSensitiveBody(body)
return safeStringify(masked, MAX_BODY_LOG_LEN)
}
// 格式化响应 result,用于写入日志
function formatResultForLog(result) {
if (result === null) return 'no content'
// 字符串直接截断
if (typeof result === 'string') {
return truncateString(result, MAX_RESULT_LOG_LEN)
}
// Buffer
if (Buffer.isBuffer(result)) {
return `[buffer length=${ result.length }]`
}
// Stream(简单判断)
if (result && typeof result.pipe === 'function') {
return '[stream]'
}
// 其它对象,直接 stringify 截断
return safeStringify(result, MAX_RESULT_LOG_LEN)
}
const useLog = () => {
return async (ctx, next) => {
const {
method,
path,
origin,
query,
body,
headers,
ip
} = ctx.request
const start = Date.now()
// 先让后续中间件 / 路由处理
try {
await next()
} catch (err) {
ctx._logError = err
throw err
} finally {
// eslint-disable-next-line no-unsafe-finally
if (!DEBUG) return
const cost = Date.now() - start
const logData = {
method,
path,
origin,
ip,
query,
cost, // 花费时间 ms
headers: maskSensitiveHeaders(headers),
body: formatBodyForLog(body),
status: ctx.status,
params: ctx.params,
result: formatResultForLog(ctx.body)
}
// 如果有未捕获异常,可以顺带记一下
if (ctx._logError) {
logData.error = {
message: ctx._logError.message,
stack: ctx._logError.stack
}
}
// 状态码分级:5xx error,4xx warn,其它 info
const status = Number(ctx.status) || 0
const text = safeStringify(logData)
try {
if (status >= 500) {
logger.error(text)
} else if (status >= 400) {
logger.warn(text)
} else {
logger.info(text)
}
} catch (e) {
logger.error('记录日志时发生错误', e)
}
}
}
}
module.exports = useLog()
+4 -4
View File
@@ -30,18 +30,18 @@ const startRdpServer = () => {
const guacamole = new GuacamoleLite(websocketOptions, guacdOptions, clientOptions)
guacamole.on('connection', () => {
consola.success('✔ RDP guacamole连接成功')
logger.info('✔ RDP guacamole连接成功')
})
guacamole.on('error', (err) => {
consola.error('❌ RDP guacamole连接错误', err)
logger.error('❌ RDP guacamole连接错误', err)
})
rdpServer.listen(RDP_PORT, () => {
consola.success(`RDP服务运行在端口: ${ RDP_PORT }`)
logger.info(`RDP服务运行在端口: ${ RDP_PORT }`)
})
} catch (error) {
consola.error('❌ RDP 初始化失败:', error.message)
logger.error('❌ RDP 初始化失败:', error.message)
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ const { HostListDB } = require('../utils/db-class')
const hostListDB = new HostListDB().getInstance()
const expiredNotifyJob = async () => {
consola.info('=====开始检测服务器到期时间=====', new Date())
logger.info('=====开始检测服务器到期时间=====', new Date())
const hostList = await hostListDB.findAsync({})
for (const item of hostList) {
if (!item.expiredNotify) continue
+1 -1
View File
@@ -31,7 +31,7 @@ const httpServer = () => {
// ws一直报跨域的错误:参照官方文档使用createServer API创建服务
server.listen(httpPort, () => {
consola.success(`Server(http) is running on: http://localhost:${ httpPort }`)
logger.info(`Server(http) is running on: http://localhost:${ httpPort }`)
})
// 启动独立的RDP服务
+6 -6
View File
@@ -17,7 +17,7 @@ function executeDockerLogsCommand(targetSSHClient, command) {
return new Promise((resolve, reject) => {
targetSSHClient.exec(command, (err, stream) => {
if (err) {
consola.error('执行Docker logs命令失败:', err)
logger.error('执行Docker logs命令失败:', err)
return reject(err)
}
@@ -27,12 +27,12 @@ function executeDockerLogsCommand(targetSSHClient, command) {
stream.on('close', () => { // code
// Docker logs 的输出主要在 stderr,合并所有输出
const allData = stdoutData + stderrData
// consola.info(`Docker logs 命令完成, 退出码: ${ code }, 输出长度: ${ allData.length }`)
// logger.info(`Docker logs 命令完成, 退出码: ${ code }, 输出长度: ${ allData.length }`)
if (allData.trim()) {
resolve(allData)
} else {
consola.warn('Docker logs 无输出:', command)
logger.warn('Docker logs 无输出:', command)
resolve('')
}
})
@@ -46,7 +46,7 @@ function executeDockerLogsCommand(targetSSHClient, command) {
})
stream.on('error', (err) => {
consola.error('Docker logs stream 错误:', err)
logger.error('Docker logs stream 错误:', err)
reject(err)
})
})
@@ -140,7 +140,7 @@ module.exports = (httpServer) => {
return
}
connectionCount++
consola.success(`docker websocket 已连接 - 当前连接数: ${ connectionCount }`)
logger.info(`docker websocket 已连接 - 当前连接数: ${ connectionCount }`)
let targetSSHClient = null
let jumpSshClients = []
@@ -197,7 +197,7 @@ module.exports = (httpServer) => {
jumpSshClients?.forEach(sshClient => sshClient && sshClient.end())
targetSSHClient = null
jumpSshClients = null
consola.info(`docker websocket 连接断开: ${ reason } - 当前连接数: ${ connectionCount }`)
logger.info(`docker websocket 连接断开: ${ reason } - 当前连接数: ${ connectionCount }`)
})
})
}
+36 -37
View File
@@ -1,7 +1,6 @@
const path = require('path')
const { Server } = require('socket.io')
const { Client: SSHClient } = require('ssh2')
const consola = require('consola')
const { verifyAuthSync } = require('../utils/verify-auth')
const { isAllowedIp, fileTransferThrottle } = require('../utils/tools')
const { getConnectionOptions } = require('./terminal')
@@ -67,7 +66,7 @@ module.exports = (httpServer) => {
transferIo.on('connection', async (socket) => {
connectionCount++
connectedSockets.add(socket)
consola.success(`file-transfer websocket 已连接 - 当前连接数: ${ connectionCount }`)
logger.info(`file-transfer websocket 已连接 - 当前连接数: ${ connectionCount }`)
// IP白名单检查
const requestIP = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address
@@ -113,7 +112,7 @@ module.exports = (httpServer) => {
const updatedTasks = await getSortedTasksList()
socket.emit('tasks_list', updatedTasks)
} catch (error) {
consola.error('启动传输任务失败:', error)
logger.error('启动传输任务失败:', error)
socket.emit('task_failed', {
taskId: transferConfig.taskId,
message: error.message
@@ -217,7 +216,7 @@ module.exports = (httpServer) => {
// 清理该socket的进度广播定时器
stopProgressBroadcast(socket)
consola.info(`file-transfer websocket 断开连接 - 当前连接数: ${ connectionCount }`)
logger.info(`file-transfer websocket 断开连接 - 当前连接数: ${ connectionCount }`)
})
})
@@ -261,7 +260,7 @@ async function executeTransfer(taskData, socket) {
await updateTaskStatus(taskId, 'completed', socket)
} catch (error) {
consola.error(`传输任务 ${ taskId } 失败:`, error)
logger.error(`传输任务 ${ taskId } 失败:`, error)
await updateTaskStatus(taskId, 'failed', socket, error.message)
} finally {
// 清理资源
@@ -284,7 +283,7 @@ async function executeRsyncTransfer(taskData, sshClient, socket) {
const targetOptions = targetConnectionData.authInfo
const targetHostAuthType = targetOptions.password ? 'password' : 'privateKey'
consola.info(`目标主机认证方式: ${ targetHostAuthType }`)
logger.info(`目标主机认证方式: ${ targetHostAuthType }`)
// 构建Rsync命令
let rsyncCmd = []
@@ -358,9 +357,9 @@ async function executeRsyncTransfer(taskData, sshClient, socket) {
rsyncCmd.push(...sourcePaths.map(item => item.path))
rsyncCmd.push(`${ targetOptions.username }@${ targetOptions.host }:"${ targetPath }"`)
consola.info(`执行Rsync命令: ${ rsyncCmd.join(' ') }`)
logger.info(`执行Rsync命令: ${ rsyncCmd.join(' ') }`)
if (Object.keys(envVars).length > 0) {
consola.info(`环境变量: ${ Object.keys(envVars).join(', ') }`)
logger.info(`环境变量: ${ Object.keys(envVars).join(', ') }`)
}
return new Promise((resolve, reject) => {
@@ -375,7 +374,7 @@ async function executeRsyncTransfer(taskData, sshClient, socket) {
finalCommand = `${ envString } ${ finalCommand }`
}
// consola.info(`最终Rsync命令: ${ finalCommand }`)
// logger.info(`最终Rsync命令: ${ finalCommand }`)
let start = false
sshClient.exec(finalCommand, (err, stream) => {
if (err) {
@@ -394,10 +393,10 @@ async function executeRsyncTransfer(taskData, sshClient, socket) {
activeTask.progressTracker.isVerifying = true
await updateTaskProgress(taskId, activeTask.progressTracker, socket)
}
consola.success(`Rsync传输完成: ${ taskId }`)
logger.info(`Rsync传输完成: ${ taskId }`)
resolve()
} else {
consola.error(`Rsync传输失败: ${ taskId }, 退出码: ${ code }`)
logger.error(`Rsync传输失败: ${ taskId }, 退出码: ${ code }`)
reject(new Error(`Rsync传输失败: ${ errorOutput || '未知错误' }`))
}
})
@@ -425,7 +424,7 @@ async function executeRsyncTransfer(taskData, sshClient, socket) {
}
const output = data.toString()
errorOutput += output
consola.warn(`Rsync stderr: ${ output }`)
logger.warn(`Rsync stderr: ${ output }`)
// 解析错误信息中的进度信息
fileTransferThrottle(parseRsyncProgress(output, taskId, socket))
@@ -460,7 +459,7 @@ function parseRsyncProgress(output, taskId, socket) {
const outputLine = output.trim()
// 添加调试日志
consola.info(`Rsync输出 [${ taskId }]: "${ outputLine }"`)
logger.info(`Rsync输出 [${ taskId }]: "${ outputLine }"`)
// 检测是否在校验阶段
if (outputLine.includes('verifying') ||
@@ -490,7 +489,7 @@ function parseRsyncProgress(output, taskId, socket) {
status: 'transferring'
})
}
consola.info(`单文件传输初始化: ${ tracker.currentFile }`)
logger.info(`单文件传输初始化: ${ tracker.currentFile }`)
}
}
@@ -509,7 +508,7 @@ function parseRsyncProgress(output, taskId, socket) {
status: 'transferring'
})
}
consola.info(`检测到新文件传输(itemize格式): ${ filePath }`)
logger.info(`检测到新文件传输(itemize格式): ${ filePath }`)
return
}
@@ -527,7 +526,7 @@ function parseRsyncProgress(output, taskId, socket) {
status: 'transferring'
})
}
consola.info(`检测到新文件传输(传统格式): ${ filePath }`)
logger.info(`检测到新文件传输(传统格式): ${ filePath }`)
return
}
@@ -546,7 +545,7 @@ function parseRsyncProgress(output, taskId, socket) {
const unitFactor = { 'B/s': 1, 'KB/s': 1024, 'MB/s': 1024 ** 2, 'GB/s': 1024 ** 3, 'TB/s': 1024 ** 4 }
speed = parseFloat(speedVal) * (unitFactor[speedUnit] || 1)
eta = parseInt(hours) * 3600 + parseInt(minutes) * 60 + parseInt(seconds)
consola.info(`Rsync标准格式解析 [${ taskId }]: ${ fileProgress }%, 速度: ${ speedVal }${ speedUnit }, ETA: ${ eta }s`)
logger.info(`Rsync标准格式解析 [${ taskId }]: ${ fileProgress }%, 速度: ${ speedVal }${ speedUnit }, ETA: ${ eta }s`)
} else {
// 模式2: 简化格式 - 100% 1.23MB/s
match = outputLine.match(/(\d+)%\s+([\d.]+)([KMGT]?B\/s)/)
@@ -555,13 +554,13 @@ function parseRsyncProgress(output, taskId, socket) {
const unit = match[3]
const unitFactor = { 'B/s': 1, 'KB/s': 1024, 'MB/s': 1024 ** 2, 'GB/s': 1024 ** 3, 'TB/s': 1024 ** 4 }
speed = parseFloat(match[2]) * (unitFactor[unit] || 1)
consola.info(`Rsync简化格式解析 [${ taskId }]: ${ fileProgress }%, 速度: ${ match[2] }${ unit }`)
logger.info(`Rsync简化格式解析 [${ taskId }]: ${ fileProgress }%, 速度: ${ match[2] }${ unit }`)
} else {
// 模式3: 最简格式 - 只有百分比
match = outputLine.match(/(\d+)%/)
if (match) {
fileProgress = parseInt(match[1])
consola.info(`Rsync百分比解析 [${ taskId }]: ${ fileProgress }%`)
logger.info(`Rsync百分比解析 [${ taskId }]: ${ fileProgress }%`)
}
}
}
@@ -582,12 +581,12 @@ function parseRsyncProgress(output, taskId, socket) {
fileInfo.status = 'completed'
fileInfo.transferred = transferred
// 清除当前文件,为下一个文件做准备
consola.info(`文件传输完成: ${ tracker.currentFile }`)
logger.info(`文件传输完成: ${ tracker.currentFile }`)
tracker.currentFile = null
}
}
consola.info(`传输进度: ${ completed }/${ total } 文件完成`)
logger.info(`传输进度: ${ completed }/${ total } 文件完成`)
}
// 更新当前文件进度
@@ -613,13 +612,13 @@ function parseRsyncProgress(output, taskId, socket) {
if (fileProgress === 100) {
fileInfo.status = 'completed'
// 文件完成后清除当前文件,为下一个文件做准备
consola.info(`文件传输完成: ${ tracker.currentFile }`)
logger.info(`文件传输完成: ${ tracker.currentFile }`)
tracker.currentFile = null
}
}
} else {
// 如果仍然没有当前文件,记录警告但继续处理
consola.warn(`收到进度信息但没有当前文件 [${ taskId }]: ${ fileProgress }%`)
logger.warn(`收到进度信息但没有当前文件 [${ taskId }]: ${ fileProgress }%`)
}
}
@@ -692,7 +691,7 @@ async function updateTaskProgress(taskId, progressTracker, socket) {
}))
}
consola.info(`推送进度更新 [${ taskId }]:`, {
logger.info(`推送进度更新 [${ taskId }]:`, {
overall: progressData.overallProgress,
files: `${ progressData.completedFiles }/${ progressData.totalFiles }`,
current: progressData.currentFile,
@@ -705,7 +704,7 @@ async function updateTaskProgress(taskId, progressTracker, socket) {
socket.emit('task_progress', progressData)
}
} catch (error) {
consola.error('更新任务进度失败:', error)
logger.error('更新任务进度失败:', error)
}
}
@@ -739,7 +738,7 @@ async function updateTaskStatus(taskId, status, socket, errorMessage = null) {
errorMessage
})
} catch (error) {
consola.error('更新任务状态失败:', error)
logger.error('更新任务状态失败:', error)
}
}
@@ -848,14 +847,14 @@ async function createRemoteTempKeyFile(sshClient, privateKey) {
function cleanupRemoteKeyFile(sshClient, keyFile) {
if (!keyFile || !sshClient) return
consola.info(`清理远程密钥文件: ${ keyFile }`)
logger.info(`清理远程密钥文件: ${ keyFile }`)
// 先删除文件,再验证删除
// const cleanupCmd = `rm -f "${ keyFile }" && if [ -f "${ keyFile }" ]; then echo "CLEANUP_FAILED"; else echo "CLEANUP_SUCCESS"; fi`
const cleanupCmd = 'cd /tmp && rm -f easynode_key_* && if ls easynode_key_* 2>/dev/null; then echo "CLEANUP_FAILED"; else echo "CLEANUP_SUCCESS"; fi'
sshClient.exec(cleanupCmd, (err, stream) => {
if (err) {
consola.error('清理密钥文件时SSH错误:', err)
logger.error('清理密钥文件时SSH错误:', err)
return
}
@@ -866,21 +865,21 @@ function cleanupRemoteKeyFile(sshClient, keyFile) {
stream.on('close', () => {
if (output.includes('CLEANUP_SUCCESS')) {
consola.success(`密钥文件清理成功: ${ keyFile }`)
logger.info(`密钥文件清理成功: ${ keyFile }`)
} else if (output.includes('CLEANUP_FAILED')) {
consola.error(`密钥文件清理失败: ${ keyFile }`)
logger.error(`密钥文件清理失败: ${ keyFile }`)
// 强制清理尝试 - 覆盖后删除
const forceCleanup = `echo "" > "${ keyFile }" && rm -f "${ keyFile }"`
sshClient.exec(forceCleanup, () => {
consola.info(`强制清理密钥文件: ${ keyFile }`)
logger.info(`强制清理密钥文件: ${ keyFile }`)
})
} else {
consola.warn(`密钥文件清理状态未知: ${ keyFile }`)
logger.warn(`密钥文件清理状态未知: ${ keyFile }`)
}
})
stream.stderr.on('data', (data) => {
consola.warn('清理密钥文件stderr:', data.toString())
logger.warn('清理密钥文件stderr:', data.toString())
})
})
}
@@ -902,7 +901,7 @@ function startProgressBroadcast(socket) {
const runningTasks = Array.from(activeTasks.values()).filter(task => task.status === 'running')
if (runningTasks.length === 0) {
// 没有运行中的任务,停止广播
consola.info('没有运行中的任务,停止进度广播')
logger.info('没有运行中的任务,停止进度广播')
stopProgressBroadcast(socket)
return
}
@@ -931,13 +930,13 @@ function startProgressBroadcast(socket) {
}
}
} catch (error) {
consola.error('进度广播出错:', error)
logger.error('进度广播出错:', error)
stopProgressBroadcast(socket)
}
}, 1500)
progressBroadcastTimers.set(socket, timer)
consola.info('已启动进度广播定时器')
logger.info('已启动进度广播定时器')
}
// 停止进度广播
@@ -946,7 +945,7 @@ function stopProgressBroadcast(socket) {
if (timer) {
clearInterval(timer)
progressBroadcastTimers.delete(socket)
consola.info('已停止进度广播定时器')
logger.info('已停止进度广播定时器')
}
}
+9 -9
View File
@@ -112,7 +112,7 @@ module.exports = (httpServer) => {
socket.disconnect()
return
}
consola.success('onekey-terminal websocket 已连接')
logger.info('onekey-terminal websocket 已连接')
if (isExecuting) {
socket.emit('create_fail', '正在执行中, 请稍后再试')
socket.disconnect()
@@ -157,19 +157,19 @@ module.exports = (httpServer) => {
return
}
consola.info('准备连接终端执行一次性指令:', host)
consola.log('连接信息', { username: targetConnectionOptions.username, port: targetConnectionOptions.port, authType: hostInfo.authType })
logger.info('准备连接终端执行一次性指令:', host)
logger.info('连接信息', { username: targetConnectionOptions.username, port: targetConnectionOptions.port, authType: hostInfo.authType })
let sshClient = new SSHClient()
execClient.push(sshClient)
sshClient
.on('ready', () => {
consola.success('连接终端成功:', host)
logger.info('连接终端成功:', host)
execShell(socket, sshClient, curRes, resolve)
})
.on('error', (err) => {
console.log(err)
consola.error('onekey终端连接失败:', err.level)
logger.error('onekey终端连接失败:', err.level)
curRes.status = execStatusEnum.connectFail
curRes.result += err.message
socket.emit('output', execResult)
@@ -185,7 +185,7 @@ module.exports = (httpServer) => {
...targetConnectionOptions
})
} catch (err) {
consola.error('创建终端错误:', err.message)
logger.error('创建终端错误:', err.message)
curRes.status = execStatusEnum.connectFail
curRes.result += err.message
socket.emit('output', execResult)
@@ -197,11 +197,11 @@ module.exports = (httpServer) => {
})
try {
await Promise.all(execPromise)
consola.success('onekey执行完成')
logger.info('onekey执行完成')
socket.emit('exec_complete')
sendNoticeAsync('onekey_complete', '批量指令执行完成', '请登录面板查看执行结果')
} catch (error) {
consola.error('onekey执行超时:', error)
logger.error('onekey执行超时:', error)
const { connecting, executing } = execStatusEnum
execResult.forEach(item => {
// 连接中和执行中的状态变更为超时状态
@@ -218,7 +218,7 @@ module.exports = (httpServer) => {
})
socket.on('disconnect', async (reason) => {
consola.info('onekey终端连接断开:', reason)
logger.info('onekey终端连接断开:', reason)
disconnectAllExecClient()
const { execSuccess, connectFail, execFail, execTimeout } = execStatusEnum
execResult.forEach(item => {
File diff suppressed because one or more lines are too long
+40 -40
View File
@@ -46,7 +46,7 @@ module.exports = (httpServer) => {
}
connectionCount++
consola.success(`server-status websocket 已连接 - 当前连接数: ${ connectionCount }`)
logger.info(`server-status websocket 已连接 - 当前连接数: ${ connectionCount }`)
let targetSSHClient = null
let jumpSshClients = []
@@ -92,7 +92,7 @@ module.exports = (httpServer) => {
// 如果有正在创建的连接,等待它完成
if (pendingConnections.has(monitorKey)) {
try {
consola.info(`等待现有连接创建完成: ${ monitorKey }`)
logger.info(`等待现有连接创建完成: ${ monitorKey }`)
await pendingConnections.get(monitorKey)
// 连接创建完成后,应该能在monitorMap中找到了
if (monitorMap.has(monitorKey)) {
@@ -109,7 +109,7 @@ module.exports = (httpServer) => {
return
}
} catch (error) {
consola.error(`等待连接创建失败,继续创建: ${ error.message }`)
logger.error(`等待连接创建失败,继续创建: ${ error.message }`)
}
}
@@ -139,10 +139,10 @@ module.exports = (httpServer) => {
stopAll()
monitorMap.delete(monitorKey)
}
consola.info(`server-status socket断开: ${ reason }`)
logger.info(`server-status socket断开: ${ reason }`)
})
consola.success(`成功创建服务器监控: ${ monitorKey }`)
logger.info(`成功创建服务器监控: ${ monitorKey }`)
return entryObj
} finally {
@@ -158,7 +158,7 @@ module.exports = (httpServer) => {
await createConnectionPromise
} catch (error) {
consola.error('ws_server_status 事件处理失败:', error.message)
logger.error('ws_server_status 事件处理失败:', error.message)
socket.emit('server_status_error', `连接失败: ${ error.message }`)
// 全面清理资源,防止泄漏
@@ -169,7 +169,7 @@ module.exports = (httpServer) => {
pendingConnections.delete(monitorKey)
}
consola.info(`连接失败后已清理资源: ${ monitorKey || 'unknown' }`)
logger.info(`连接失败后已清理资源: ${ monitorKey || 'unknown' }`)
}
})
@@ -181,14 +181,14 @@ module.exports = (httpServer) => {
targetSSHClient.exec('/bin/bash --noprofile --norc -i', (err, stream) => {
if (err) {
consola.error('创建持久化 shell 失败:', err.message)
logger.error('创建持久化 shell 失败:', err.message)
return reject(err)
}
persistentShell = stream
shellReady = true
persistentShell.write('unset HISTFILE\n')
consola.info('server-status: 持久化 shell 已就绪')
logger.info('server-status: 持久化 shell 已就绪')
let buffer = ''
const handleData = (data) => {
@@ -219,7 +219,7 @@ module.exports = (httpServer) => {
stream.on('close', () => {
shellReady = false
persistentShell = null
consola.warn('server-status: 持久化 shell 已关闭')
logger.warn('server-status: 持久化 shell 已关闭')
})
resolve()
})
@@ -313,7 +313,7 @@ module.exports = (httpServer) => {
pendingConnections.delete(monitorKey)
}
consola.info(`已清理服务器监控资源: ${ monitorKey || 'unknown' } - 原因: ${ reason }`)
logger.info(`已清理服务器监控资源: ${ monitorKey || 'unknown' } - 原因: ${ reason }`)
}
// 检查是否是关键的服务器错误
@@ -353,7 +353,7 @@ module.exports = (httpServer) => {
cpuCount = cpuCountMatch ? parseInt(cpuCountMatch[1]) : 0
}
} catch (error) {
consola.warn('获取CPU核心数失败:', error.message)
logger.warn('获取CPU核心数失败:', error.message)
}
// 获取CPU型号
@@ -369,11 +369,11 @@ module.exports = (httpServer) => {
cpuModel = lscpuModelMatch ? lscpuModelMatch[1].trim() : 'Unknown'
}
} catch (error) {
consola.warn('获取CPU型号失败:', error.message)
logger.warn('获取CPU型号失败:', error.message)
// 检查是否是关键错误
if (isServerCriticalError(error.message)) {
consola.error(`执行命令失败:cat /proc/cpuinfo: ${ error.message }`)
logger.error(`执行命令失败:cat /proc/cpuinfo: ${ error.message }`)
return { cpuCount: 0, cpuModel: 'Unknown' }
}
}
@@ -431,11 +431,11 @@ module.exports = (httpServer) => {
previousCpuStats = { ...currentCpuStats, timestamp: now }
}
} catch (error) {
consola.warn('获取CPU使用率失败:', error.message)
logger.warn('获取CPU使用率失败:', error.message)
// 检查是否是关键错误
if (isServerCriticalError(error.message)) {
consola.error(`执行命令失败:cat /proc/stat: ${ error.message }`)
logger.error(`执行命令失败:cat /proc/stat: ${ error.message }`)
return { cpuUsage: 0, cpuCount: 0, cpuModel: 'Unknown' }
}
}
@@ -559,11 +559,11 @@ module.exports = (httpServer) => {
return { memInfo, swapInfo }
} catch (error) {
consola.error('获取内存信息失败:', error.message)
logger.error('获取内存信息失败:', error.message)
// 检查是否是关键错误
if (isServerCriticalError(error.message)) {
consola.error(`执行命令失败:free -m: ${ error.message }`)
logger.error(`执行命令失败:free -m: ${ error.message }`)
}
return defaultReturn
@@ -597,7 +597,7 @@ module.exports = (httpServer) => {
})
return drives
} catch (error) {
consola.error('获取磁盘信息失败:', error.message)
logger.error('获取磁盘信息失败:', error.message)
return []
}
}
@@ -685,7 +685,7 @@ module.exports = (httpServer) => {
return stats
} catch (error) {
consola.error('parseNetworkStats 失败:', error.message)
logger.error('parseNetworkStats 失败:', error.message)
return null
}
}
@@ -785,11 +785,11 @@ module.exports = (httpServer) => {
return netstatInfo
} catch (error) {
consola.error('获取网络信息失败:', error.message)
logger.error('获取网络信息失败:', error.message)
// 检查是否是关键错误
if (isServerCriticalError(error.message)) {
consola.error(`执行命令失败:cat /proc/net/dev: ${ error.message }`)
logger.error(`执行命令失败:cat /proc/net/dev: ${ error.message }`)
return { total: { inputMb: '0.000', outputMb: '0.000' } }
}
@@ -817,7 +817,7 @@ module.exports = (httpServer) => {
try {
hostname = await executeCommand('hostname') || 'Unknown'
} catch (e) {
consola.warn('获取hostname失败:', e.message)
logger.warn('获取hostname失败:', e.message)
}
// 通过读取 /etc/os-release 文件获取操作系统信息
@@ -850,18 +850,18 @@ module.exports = (httpServer) => {
}
})
} catch (e) {
consola.warn('读取 /etc/os-release 失败,尝试使用 uname 命令:', e.message)
logger.warn('读取 /etc/os-release 失败,尝试使用 uname 命令:', e.message)
// 如果读取 /etc/os-release 失败,回退到 uname 命令
try {
type = await executeCommand('uname -s') || 'Linux'
} catch (e2) {
consola.warn('获取系统类型失败:', e2.message)
logger.warn('获取系统类型失败:', e2.message)
}
try {
release = await executeCommand('uname -r') || 'Unknown'
} catch (e2) {
consola.warn('获取系统版本失败:', e2.message)
logger.warn('获取系统版本失败:', e2.message)
}
}
@@ -869,7 +869,7 @@ module.exports = (httpServer) => {
try {
arch = await executeCommand('uname -m') || 'Unknown'
} catch (e) {
consola.warn('获取系统架构失败:', e.message)
logger.warn('获取系统架构失败:', e.message)
}
const staticInfo = {
@@ -884,7 +884,7 @@ module.exports = (httpServer) => {
staticSystemInfo.osInfo = staticInfo
return staticInfo
} catch (error) {
consola.error('获取静态系统信息失败:', error.message)
logger.error('获取静态系统信息失败:', error.message)
staticSystemInfo.osInfo = defaultStaticInfo
return defaultStaticInfo
}
@@ -902,11 +902,11 @@ module.exports = (httpServer) => {
const uptimeStr = await executeCommand('cat /proc/uptime | cut -d" " -f1') || '0'
uptime = parseFloat(uptimeStr) || 0
} catch (e) {
consola.warn('获取系统运行时间失败:', e.message)
logger.warn('获取系统运行时间失败:', e.message)
// 检查是否是关键错误
if (isServerCriticalError(e.message)) {
consola.error(`执行命令失败:cat /proc/uptime: ${ e.message }`)
logger.error(`执行命令失败:cat /proc/uptime: ${ e.message }`)
return { ...staticInfo, uptime: 0 }
}
}
@@ -916,7 +916,7 @@ module.exports = (httpServer) => {
uptime
}
} catch (error) {
consola.error('获取系统信息失败:', error.message)
logger.error('获取系统信息失败:', error.message)
return {
hostname: 'Unknown',
type: 'Linux',
@@ -935,7 +935,7 @@ module.exports = (httpServer) => {
statusData.cpuInfo = cpuInfo
statusData.connect = true // 标记连接状态
} catch (error) {
consola.error('更新CPU信息失败:', error.message)
logger.error('更新CPU信息失败:', error.message)
statusData.cpuInfo = {}
}
}
@@ -948,7 +948,7 @@ module.exports = (httpServer) => {
statusData.swapInfo = swapInfo
statusData.connect = true
} catch (error) {
consola.error('更新内存信息失败:', error.message)
logger.error('更新内存信息失败:', error.message)
statusData.memInfo = {}
statusData.swapInfo = {}
}
@@ -961,7 +961,7 @@ module.exports = (httpServer) => {
statusData.drivesInfo = drives
statusData.connect = true
} catch (error) {
consola.error('更新磁盘信息失败:', error.message)
logger.error('更新磁盘信息失败:', error.message)
statusData.drivesInfo = []
}
}
@@ -973,7 +973,7 @@ module.exports = (httpServer) => {
statusData.netstatInfo = netstatInfo
statusData.connect = true
} catch (error) {
consola.error('更新网络信息失败:', error.message)
logger.error('更新网络信息失败:', error.message)
statusData.netstatInfo = {}
}
}
@@ -985,7 +985,7 @@ module.exports = (httpServer) => {
statusData.osInfo = osInfo
statusData.connect = true
} catch (error) {
consola.error('更新系统信息失败:', error.message)
logger.error('更新系统信息失败:', error.message)
statusData.osInfo = {}
}
}
@@ -1013,7 +1013,7 @@ module.exports = (httpServer) => {
}
} catch (error) {
consola.error('更新服务器状态过程中出错:', error.message)
logger.error('更新服务器状态过程中出错:', error.message)
statusData.connect = false
}
}
@@ -1026,7 +1026,7 @@ module.exports = (httpServer) => {
// 更新全局statusData
await updateServerStatus()
} catch (error) {
consola.error('数据收集过程中出错:', error.message)
logger.error('数据收集过程中出错:', error.message)
}
}
@@ -1042,7 +1042,7 @@ module.exports = (httpServer) => {
}
}
} catch (error) {
consola.error('数据发送过程中出错:', error.message)
logger.error('数据发送过程中出错:', error.message)
}
}
@@ -1071,7 +1071,7 @@ module.exports = (httpServer) => {
cleanupResources('socket断开')
}
consola.info(`server-status websocket 连接断开: ${ reason } - 当前连接数: ${ connectionCount }`)
logger.info(`server-status websocket 连接断开: ${ reason } - 当前连接数: ${ connectionCount }`)
})
})
}
+114 -114
View File
@@ -82,7 +82,7 @@ function detectEncoding(buffer) {
const confidence = Math.round(topResult.confidence || 50)
consola.info(`编码检测结果: ${ normalizedEncoding } (置信度: ${ confidence }%)`)
logger.info(`编码检测结果: ${ normalizedEncoding } (置信度: ${ confidence }%)`)
return {
encoding: normalizedEncoding,
@@ -90,7 +90,7 @@ function detectEncoding(buffer) {
reason: `chardet 检测为 ${ detected }`
}
} catch (error) {
consola.error('编码检测失败:', error.message)
logger.error('编码检测失败:', error.message)
return { encoding: 'utf8', confidence: 30, reason: '检测异常,默认 UTF-8' }
}
}
@@ -271,7 +271,7 @@ const listenAction = (sftpClient, socket) => {
try {
sftpClient.client.exec(cmd, (err, stream) => {
if (err) {
consola.error('执行命令失败:', cmd, err.message)
logger.error('执行命令失败:', cmd, err.message)
// 检查是否是连接相关错误
if (err.message.includes('Not connected') || err.message.includes('Connection lost')) {
return rej(new Error('SSH连接已断开,无法执行命令'))
@@ -300,7 +300,7 @@ const listenAction = (sftpClient, socket) => {
stream.destroy()
}
} catch (cleanupErr) {
consola.warn('清理stream失败:', cleanupErr.message)
logger.warn('清理stream失败:', cleanupErr.message)
}
}
}
@@ -320,7 +320,7 @@ const listenAction = (sftpClient, socket) => {
// 设置超时保护(60秒)
timeout = setTimeout(() => {
consola.warn('命令执行超时:', cmd)
logger.warn('命令执行超时:', cmd)
resolveOnce(new Error(`命令执行超时: ${ cmd }`), true)
}, 60000)
@@ -331,7 +331,7 @@ const listenAction = (sftpClient, socket) => {
// 处理错误事件
stream.on('error', (streamErr) => {
consola.error('Stream错误:', cmd, streamErr.message)
logger.error('Stream错误:', cmd, streamErr.message)
if (streamErr.message.includes('Not connected')) {
resolveOnce(new Error('SSH连接在命令执行过程中断开'), true)
} else {
@@ -342,10 +342,10 @@ const listenAction = (sftpClient, socket) => {
// 处理连接断开
stream.on('close', (code) => {
if (errMsg) {
consola.error('命令执行错误:', cmd, errMsg)
logger.error('命令执行错误:', cmd, errMsg)
resolveOnce(new Error(`命令执行失败: ${ code }: ${ errMsg }`), true)
} else {
consola.info('命令执行完成:', cmd)
logger.info('命令执行完成:', cmd)
resolveOnce('success')
}
})
@@ -353,11 +353,11 @@ const listenAction = (sftpClient, socket) => {
// 处理命令退出
stream.on('exit', (code) => {
if (code === 0) {
consola.info('命令执行成功:', cmd)
logger.info('命令执行成功:', cmd)
resolveOnce() // 成功
} else {
const errorMessage = errMsg || `命令退出码: ${ code }`
consola.error('命令执行失败:', cmd, errorMessage)
logger.error('命令执行失败:', cmd, errorMessage)
resolveOnce(new Error(errorMessage), true)
}
})
@@ -366,7 +366,7 @@ const listenAction = (sftpClient, socket) => {
stream.on('data', () => {})
})
} catch (execErr) {
consola.error('execCommand异常:', cmd, execErr.message)
logger.error('execCommand异常:', cmd, execErr.message)
rej(execErr)
}
})
@@ -387,7 +387,7 @@ const listenAction = (sftpClient, socket) => {
const formattedDirLs = formatFileList(dirLs)
socket.emit('dir_ls', formattedDirLs, dirPath)
} catch (err) {
consola.error('copy error:', err.message)
logger.error('copy error:', err.message)
socket.emit('copy_fail', err.message)
}
})
@@ -416,11 +416,11 @@ const listenAction = (sftpClient, socket) => {
}
if (type === 'folder') {
consola.info(`创建文件夹: ${ targetPath }`)
logger.info(`创建文件夹: ${ targetPath }`)
await sftpClient.mkdir(targetPath)
socket.emit('create_success', `文件夹 "${ trimmedName }" 创建成功`)
} else if (type === 'file') {
consola.info(`创建文件: ${ targetPath }`)
logger.info(`创建文件: ${ targetPath }`)
// 创建空文件,使用 touch 命令
const cmd = `touch "${ targetPath }"`
await execCommand(cmd)
@@ -434,7 +434,7 @@ const listenAction = (sftpClient, socket) => {
const formattedDirLs = formatFileList(dirLs)
socket.emit('dir_ls', formattedDirLs, dirPath)
} catch (err) {
consola.error('create error:', err.message)
logger.error('create error:', err.message)
socket.emit('create_fail', err.message)
}
})
@@ -472,9 +472,9 @@ const listenAction = (sftpClient, socket) => {
// 使用 tar 命令压缩
const tarCmd = `cd "${ dirPath }" && tar -czf "${ trimmedArchiveName }" ${ fileNames }`
consola.info(`开始压缩文件: ${ targets.map(t => t.name).join(', ') } -> ${ trimmedArchiveName }`)
logger.info(`开始压缩文件: ${ targets.map(t => t.name).join(', ') } -> ${ trimmedArchiveName }`)
await execCommand(tarCmd)
consola.info(`压缩完成: ${ trimmedArchiveName }`)
logger.info(`压缩完成: ${ trimmedArchiveName }`)
socket.emit('compress_success', `压缩文件 "${ trimmedArchiveName }" 创建成功`)
@@ -483,7 +483,7 @@ const listenAction = (sftpClient, socket) => {
const formattedDirLs = formatFileList(dirLs)
socket.emit('dir_ls', formattedDirLs, dirPath)
} catch (err) {
consola.error('compress error:', err.message)
logger.error('compress error:', err.message)
socket.emit('compress_fail', err.message)
}
})
@@ -527,7 +527,7 @@ const listenAction = (sftpClient, socket) => {
// 创建目标文件夹
await sftpClient.mkdir(targetDir)
consola.info(`创建目标文件夹: ${ targetDir }`)
logger.info(`创建目标文件夹: ${ targetDir }`)
}
// 根据文件扩展名选择解压命令
@@ -555,9 +555,9 @@ const listenAction = (sftpClient, socket) => {
}
}
consola.info(`开始解压文件: ${ trimmedFileName } -> ${ targetDirName }`)
logger.info(`开始解压文件: ${ trimmedFileName } -> ${ targetDirName }`)
await execCommand(decompressCmd)
consola.info(`解压完成: ${ trimmedFileName } -> ${ targetDirName }`)
logger.info(`解压完成: ${ trimmedFileName } -> ${ targetDirName }`)
socket.emit('decompress_success', `文件 "${ trimmedFileName }" 解压到${ targetDirName }成功`)
@@ -566,7 +566,7 @@ const listenAction = (sftpClient, socket) => {
const formattedDirLs = formatFileList(dirLs)
socket.emit('dir_ls', formattedDirLs, dirPath)
} catch (err) {
consola.error('decompress error:', err.message)
logger.error('decompress error:', err.message)
socket.emit('decompress_fail', err.message)
}
})
@@ -615,13 +615,13 @@ const listenAction = (sftpClient, socket) => {
}
// 在远端打包
consola.info(`开始打包文件夹: ${ srcPath }`)
logger.info(`开始打包文件夹: ${ srcPath }`)
const tarCmd = `cd "${ dirPath }" && tar -czf "${ remoteTarPath }" "${ target.name }"`
try {
await execCommand(tarCmd)
consola.info(`打包文件夹: ${ srcPath } 成功`)
logger.info(`打包文件夹: ${ srcPath } 成功`)
} catch (tarErr) {
consola.error('打包文件夹失败:', tarErr.message)
logger.error('打包文件夹失败:', tarErr.message)
throw new Error(`打包文件夹失败: ${ tarErr.message }`)
}
@@ -670,12 +670,12 @@ const listenAction = (sftpClient, socket) => {
const fileNames = targets.map(t => `"${ t.name }"`).join(' ')
const tarCmd = `cd "${ dirPath }" && tar -czf "${ remoteTarPath }" ${ fileNames }`
consola.info(`开始打包多个文件: ${ targets.map(t => t.name).join(', ') }`)
logger.info(`开始打包多个文件: ${ targets.map(t => t.name).join(', ') }`)
try {
await execCommand(tarCmd)
consola.info('打包多个文件成功')
logger.info('打包多个文件成功')
} catch (tarErr) {
consola.error('打包失败:', tarErr.message)
logger.error('打包失败:', tarErr.message)
throw new Error(`打包失败: ${ tarErr.message }`)
}
@@ -695,7 +695,7 @@ const listenAction = (sftpClient, socket) => {
downloadTasks.delete(taskId)
} catch (err) {
consola.error('下载失败:', err.message)
logger.error('下载失败:', err.message)
socket.emit('download_fail', err.message)
// 清理远程临时文件(如果还存在)
@@ -715,9 +715,9 @@ const listenAction = (sftpClient, socket) => {
if (!remoteTarPath) return
try {
await execCommand(`rm -f "${ remoteTarPath }"`)
consola.info(`已清理远程临时文件: ${ remoteTarPath }`)
logger.info(`已清理远程临时文件: ${ remoteTarPath }`)
} catch (cleanupErr) {
consola.warn('清理远程临时文件失败:', remoteTarPath, cleanupErr.message)
logger.warn('清理远程临时文件失败:', remoteTarPath, cleanupErr.message)
}
}
@@ -740,9 +740,9 @@ const listenAction = (sftpClient, socket) => {
const cleanupCacheDir = () => {
try {
fs.emptyDirSync(sftpCacheDir)
consola.success('已清理 sftpCacheDir:', sftpCacheDir)
logger.info('已清理 sftpCacheDir:', sftpCacheDir)
} catch (err) {
consola.warn('清理缓存目录失败:', err.message)
logger.warn('清理缓存目录失败:', err.message)
}
}
@@ -754,7 +754,7 @@ const listenAction = (sftpClient, socket) => {
const favorites = await favoriteSftpDB.findAsync({ hostId }, { sort: { createTime: -1 } })
socket.emit('favorites_list', favorites)
} catch (err) {
consola.error('获取收藏列表失败:', err.message)
logger.error('获取收藏列表失败:', err.message)
socket.emit('favorite_error', '获取收藏列表失败')
}
})
@@ -785,9 +785,9 @@ const listenAction = (sftpClient, socket) => {
await favoriteSftpDB.insertAsync(newFavorite)
socket.emit('favorite_added', `收藏 "${ name }" 成功`)
consola.info(`用户收藏了路径: ${ path }`)
logger.info(`用户收藏了路径: ${ path }`)
} catch (err) {
consola.error('添加收藏失败:', err.message)
logger.error('添加收藏失败:', err.message)
socket.emit('favorite_error', err.message)
}
})
@@ -807,9 +807,9 @@ const listenAction = (sftpClient, socket) => {
}
socket.emit('favorite_removed', '取消收藏成功')
consola.info(`用户取消收藏路径: ${ path }`)
logger.info(`用户取消收藏路径: ${ path }`)
} catch (err) {
consola.error('删除收藏失败:', err.message)
logger.error('删除收藏失败:', err.message)
socket.emit('favorite_error', err.message)
}
})
@@ -820,9 +820,9 @@ const listenAction = (sftpClient, socket) => {
socket.on('resolve_symlink', async ({ symlinkPath }) => {
try {
// 获取软链接的真实路径
consola.info(`解析软链接: ${ symlinkPath }`)
logger.info(`解析软链接: ${ symlinkPath }`)
const realPath = await sftpClient.realPath(symlinkPath)
consola.info(`软链接真实路径: ${ realPath }`)
logger.info(`软链接真实路径: ${ realPath }`)
// 检查真实路径是否存在
const stats = await sftpClient.stat(realPath)
@@ -834,10 +834,10 @@ const listenAction = (sftpClient, socket) => {
symlinkPath
})
consola.info(`软链接解析成功: ${ symlinkPath } -> ${ realPath } (${ isDirectory ? '目录' : '文件' })`)
logger.info(`软链接解析成功: ${ symlinkPath } -> ${ realPath } (${ isDirectory ? '目录' : '文件' })`)
} catch (err) {
consola.error('软链接解析失败:', err.message)
logger.error('软链接解析失败:', err.message)
socket.emit('symlink_resolve_error', {
error: err.message,
symlinkPath
@@ -869,23 +869,23 @@ const listenAction = (sftpClient, socket) => {
return
}
consola.info(`开始智能检测文件编码: ${ filePath }`)
logger.info(`开始智能检测文件编码: ${ filePath }`)
const buffer = await sftpClient.get(filePath)
const { encoding: detectedEncoding, confidence, reason } = detectEncoding(buffer)
consola.info(`检测结果: ${ detectedEncoding.toUpperCase() } (置信度: ${ confidence }%) - ${ reason }`)
logger.info(`检测结果: ${ detectedEncoding.toUpperCase() } (置信度: ${ confidence }%) - ${ reason }`)
// 根据检测到的编码解码文件内容
let content
try {
content = decodeBuffer(buffer, detectedEncoding)
} catch (decodeErr) {
consola.error('使用检测编码解码失败,尝试 UTF-8:', decodeErr.message)
logger.error('使用检测编码解码失败,尝试 UTF-8:', decodeErr.message)
// 解码失败,降级为 UTF-8
content = buffer.toString('utf8')
}
consola.info(`文件读取成功: ${ filePath },大小: ${ content.length } 字符,使用编码: ${ detectedEncoding.toUpperCase() }`)
logger.info(`文件读取成功: ${ filePath },大小: ${ content.length } 字符,使用编码: ${ detectedEncoding.toUpperCase() }`)
socket.emit('file_content_with_encoding', {
content,
@@ -895,7 +895,7 @@ const listenAction = (sftpClient, socket) => {
})
} catch (err) {
consola.error('智能读取文件失败:', err.message)
logger.error('智能读取文件失败:', err.message)
socket.emit('file_read_error', {
error: err.message,
filePath
@@ -917,7 +917,7 @@ const listenAction = (sftpClient, socket) => {
}
// 读取文件内容
consola.info(`开始读取文件: ${ filePath },使用编码: ${ encoding.toUpperCase() }`)
logger.info(`开始读取文件: ${ filePath },使用编码: ${ encoding.toUpperCase() }`)
const buffer = await sftpClient.get(filePath)
// 根据编码解码文件内容
@@ -925,7 +925,7 @@ const listenAction = (sftpClient, socket) => {
try {
content = decodeBuffer(buffer, encoding)
} catch (decodeErr) {
consola.error(`文件解码失败(编码: ${ encoding }):`, decodeErr.message)
logger.error(`文件解码失败(编码: ${ encoding }):`, decodeErr.message)
socket.emit('file_read_error', {
error: `文件解码失败,可能不是 ${ encoding.toUpperCase() } 编码`,
filePath
@@ -933,11 +933,11 @@ const listenAction = (sftpClient, socket) => {
return
}
consola.info(`文件读取成功: ${ filePath },大小: ${ content.length } 字符`)
logger.info(`文件读取成功: ${ filePath },大小: ${ content.length } 字符`)
socket.emit('file_content', { content, filePath })
} catch (err) {
consola.error('读取文件失败:', err.message)
logger.error('读取文件失败:', err.message)
socket.emit('file_read_error', {
error: err.message,
filePath
@@ -987,7 +987,7 @@ const listenAction = (sftpClient, socket) => {
const cacheFileName = `image_${ timestamp }_${ fileName }`
const localImagePath = rawPath.join(sftpCacheDir, cacheFileName)
consola.info(`开始下载图片到缓存: ${ filePath } -> ${ localImagePath }`)
logger.info(`开始下载图片到缓存: ${ filePath } -> ${ localImagePath }`)
// 下载图片到本地缓存
await sftpClient.fastGet(filePath, localImagePath)
@@ -995,7 +995,7 @@ const listenAction = (sftpClient, socket) => {
// 生成访问URL
const imageUrl = `/sftp-cache/${ cacheFileName }`
consola.info(`图片下载成功: ${ filePath },缓存路径: ${ localImagePath }`)
logger.info(`图片下载成功: ${ filePath },缓存路径: ${ localImagePath }`)
socket.emit('image_content', {
imageUrl,
@@ -1005,7 +1005,7 @@ const listenAction = (sftpClient, socket) => {
})
} catch (err) {
consola.error('图片预览失败:', err.message)
logger.error('图片预览失败:', err.message)
socket.emit('image_read_error', {
error: err.message,
filePath
@@ -1021,7 +1021,7 @@ const listenAction = (sftpClient, socket) => {
try {
buffer = encodeString(content, encoding)
} catch (encodeErr) {
consola.error(`文件编码失败(编码: ${ encoding }):`, encodeErr.message)
logger.error(`文件编码失败(编码: ${ encoding }):`, encodeErr.message)
socket.emit('file_save_error', {
error: `文件编码失败: ${ encodeErr.message }`,
filePath
@@ -1032,11 +1032,11 @@ const listenAction = (sftpClient, socket) => {
// 保存文件内容
await sftpClient.put(buffer, filePath)
consola.info(`文件保存成功: ${ filePath }`)
logger.info(`文件保存成功: ${ filePath }`)
socket.emit('file_saved', { filePath })
} catch (err) {
consola.error('保存文件失败:', err.message)
logger.error('保存文件失败:', err.message)
socket.emit('file_save_error', {
error: err.message,
filePath
@@ -1049,7 +1049,7 @@ const listenAction = (sftpClient, socket) => {
// 开始上传
socket.on('upload_start', async ({ taskId, fileName, fileSize, targetPath }) => {
try {
consola.info(`收到上传请求: ${ fileName }, 大小: ${ (fileSize / 1024 / 1024 / 1024).toFixed(2) }GB`)
logger.info(`收到上传请求: ${ fileName }, 大小: ${ (fileSize / 1024 / 1024 / 1024).toFixed(2) }GB`)
if (!taskId || !fileName || !fileSize || !targetPath) {
throw new Error('上传参数不完整')
@@ -1075,11 +1075,11 @@ const listenAction = (sftpClient, socket) => {
}
uploadTasks.set(taskId, uploadTask)
consola.info(`开始上传任务: ${ taskId } - ${ fileName }`)
logger.info(`开始上传任务: ${ taskId } - ${ fileName }`)
socket.emit('upload_started', { taskId, fileName })
} catch (err) {
consola.error('开始上传失败:', err.message)
logger.error('开始上传失败:', err.message)
socket.emit('upload_fail', { taskId, error: err.message })
}
})
@@ -1107,7 +1107,7 @@ const listenAction = (sftpClient, socket) => {
// 处理写入流错误(将错误标记到任务中)
task.writeStream.on('error', (err) => {
consola.error(`写入流错误: ${ taskId }`, err.message)
logger.error(`写入流错误: ${ taskId }`, err.message)
task.writeStreamError = err
// 销毁流,防止继续写入
if (!task.writeStream.destroyed) {
@@ -1164,7 +1164,7 @@ const listenAction = (sftpClient, socket) => {
task.lastProgressTime = now
}
// consola.info(`上传分片: ${ taskId } - ${ chunkIndex + 1 }/${ totalChunks }`) // 太频繁,注释掉
// logger.info(`上传分片: ${ taskId } - ${ chunkIndex + 1 }/${ totalChunks }`) // 太频繁,注释掉
socket.emit('upload_chunk_success', { taskId, chunkIndex })
// 如果是最后一个分片,完成文件写入并开始传输
@@ -1173,7 +1173,7 @@ const listenAction = (sftpClient, socket) => {
}
} catch (err) {
consola.error('上传分片失败:', err.message)
logger.error('上传分片失败:', err.message)
socket.emit('upload_chunk_fail', { taskId, chunkIndex, error: err.message })
// 清理资源
@@ -1187,7 +1187,7 @@ const listenAction = (sftpClient, socket) => {
// 完成上传的辅助函数
async function completeUpload(task) {
try {
consola.info(`文件接收完成,准备传输: ${ task.fileName }`)
logger.info(`文件接收完成,准备传输: ${ task.fileName }`)
// 关闭写入流
if (task.writeStream) {
@@ -1221,7 +1221,7 @@ const listenAction = (sftpClient, socket) => {
})
// 通过SFTP传输到远程服务器,使用fastPut带进度回调
consola.info(`开始传输文件到远程服务器: ${ task.targetPath }`)
logger.info(`开始传输文件到远程服务器: ${ task.targetPath }`)
let sftpStartTime = Date.now()
let lastSftpUpdateTime = sftpStartTime
@@ -1260,7 +1260,7 @@ const listenAction = (sftpClient, socket) => {
})
// 传输完成
consola.info(`文件上传成功: ${ task.fileName }`)
logger.info(`文件上传成功: ${ task.fileName }`)
socket.emit('upload_complete', {
taskId: task.taskId,
fileName: task.fileName,
@@ -1268,7 +1268,7 @@ const listenAction = (sftpClient, socket) => {
})
} catch (err) {
consola.error('完成上传失败:', err.message)
logger.error('完成上传失败:', err.message)
socket.emit('upload_fail', {
taskId: task.taskId,
error: err.message
@@ -1278,9 +1278,9 @@ const listenAction = (sftpClient, socket) => {
if (task.tempFilePath && fs.existsSync(task.tempFilePath)) {
try {
fs.unlinkSync(task.tempFilePath)
consola.info(`已清理临时文件: ${ task.tempFilePath }`)
logger.info(`已清理临时文件: ${ task.tempFilePath }`)
} catch (cleanupErr) {
consola.warn('清理临时文件失败:', cleanupErr.message)
logger.warn('清理临时文件失败:', cleanupErr.message)
}
}
@@ -1307,14 +1307,14 @@ const listenAction = (sftpClient, socket) => {
if (task.tempFilePath && fs.existsSync(task.tempFilePath)) {
try {
fs.unlinkSync(task.tempFilePath)
consola.info(`取消上传,已清理临时文件: ${ task.tempFilePath }`)
logger.info(`取消上传,已清理临时文件: ${ task.tempFilePath }`)
} catch (cleanupErr) {
consola.warn('清理临时文件失败:', cleanupErr.message)
logger.warn('清理临时文件失败:', cleanupErr.message)
}
}
uploadTasks.delete(taskId)
consola.info(`取消上传任务: ${ taskId }`)
logger.info(`取消上传任务: ${ taskId }`)
socket.emit('upload_cancelled', { taskId })
}
})
@@ -1322,7 +1322,7 @@ const listenAction = (sftpClient, socket) => {
// 监听连接断开,清理下载任务和缓存
socket.on('disconnect', async (reason) => {
try {
consola.info('SFTP连接断开,开始清理资源...', reason)
logger.info('SFTP连接断开,开始清理资源...', reason)
// 清理定时器
if (memoryCleanupInterval) {
@@ -1343,7 +1343,7 @@ const listenAction = (sftpClient, socket) => {
remoteCleanupPromises.push(cleanupRemoteTarFile(task.remoteTarPath))
}
} catch (taskError) {
consola.warn(`清理下载任务 ${ taskId } 失败:`, taskError.message)
logger.warn(`清理下载任务 ${ taskId } 失败:`, taskError.message)
}
}
@@ -1351,9 +1351,9 @@ const listenAction = (sftpClient, socket) => {
if (remoteCleanupPromises.length > 0) {
try {
await Promise.all(remoteCleanupPromises)
consola.info(`连接断开时已清理 ${ remoteCleanupPromises.length } 个远程临时文件`)
logger.info(`连接断开时已清理 ${ remoteCleanupPromises.length } 个远程临时文件`)
} catch (err) {
consola.warn('连接断开时清理远程临时文件部分失败:', err.message)
logger.warn('连接断开时清理远程临时文件部分失败:', err.message)
}
}
@@ -1371,13 +1371,13 @@ const listenAction = (sftpClient, socket) => {
if (task.tempFilePath && fs.existsSync(task.tempFilePath)) {
try {
fs.unlinkSync(task.tempFilePath)
consola.info(`连接断开,已清理临时文件: ${ task.tempFilePath }`)
logger.info(`连接断开,已清理临时文件: ${ task.tempFilePath }`)
} catch (cleanupErr) {
consola.warn('清理临时文件失败:', cleanupErr.message)
logger.warn('清理临时文件失败:', cleanupErr.message)
}
}
} catch (taskError) {
consola.warn(`清理上传任务 ${ taskId } 失败:`, taskError.message)
logger.warn(`清理上传任务 ${ taskId } 失败:`, taskError.message)
}
}
@@ -1389,12 +1389,12 @@ const listenAction = (sftpClient, socket) => {
try {
cleanupCacheDir()
} catch (cleanupError) {
consola.warn('清理本地缓存目录失败:', cleanupError.message)
logger.warn('清理本地缓存目录失败:', cleanupError.message)
}
consola.info('SFTP资源清理完成')
logger.info('SFTP资源清理完成')
} catch (disconnectError) {
consola.error('Socket断开连接清理过程中发生错误:', disconnectError.message)
logger.error('Socket断开连接清理过程中发生错误:', disconnectError.message)
}
})
@@ -1406,7 +1406,7 @@ const listenAction = (sftpClient, socket) => {
for (const [taskId, task] of uploadTasks) {
if (now - task.startTime > timeout) {
consola.warn(`清理超时上传任务: ${ taskId }`)
logger.warn(`清理超时上传任务: ${ taskId }`)
if (task.abortController) {
task.abortController.abort()
}
@@ -1418,9 +1418,9 @@ const listenAction = (sftpClient, socket) => {
if (task.tempFilePath && fs.existsSync(task.tempFilePath)) {
try {
fs.unlinkSync(task.tempFilePath)
consola.info(`清理超时任务临时文件: ${ task.tempFilePath }`)
logger.info(`清理超时任务临时文件: ${ task.tempFilePath }`)
} catch (cleanupErr) {
consola.warn('清理临时文件失败:', cleanupErr.message)
logger.warn('清理临时文件失败:', cleanupErr.message)
}
}
@@ -1431,7 +1431,7 @@ const listenAction = (sftpClient, socket) => {
// 检查并清理超时的下载任务
for (const [taskId, task] of downloadTasks) {
if (now - task.startTime > timeout) {
consola.warn(`清理超时下载任务: ${ taskId }`)
logger.warn(`清理超时下载任务: ${ taskId }`)
if (task.abortController) {
task.abortController.abort()
}
@@ -1580,12 +1580,12 @@ module.exports = (httpServer) => {
return
}
let sftpClient = new SFTPClient()
consola.success('sftp-v2 websocket 已连接')
logger.info('sftp-v2 websocket 已连接')
let jumpSshClients = []
// 添加socket本身的错误处理
socket.on('error', (err) => {
consola.error('SFTP-v2 Socket连接错误:', err.message)
logger.error('SFTP-v2 Socket连接错误:', err.message)
})
socket.on('ws_sftp', async ({ hostId }) => {
@@ -1610,14 +1610,14 @@ module.exports = (httpServer) => {
return
}
consola.info('准备连接sftp-v2 面板:', host)
consola.log('连接信息', { username, port, authType })
logger.info('准备连接sftp-v2 面板:', host)
logger.info('连接信息', { username, port, authType })
sftpClient.client = new SSHClient()
// 添加错误处理器,防止程序崩溃
sftpClient.client.on('error', (err) => {
consola.error('SFTP SSH连接错误:', err.message)
logger.error('SFTP SSH连接错误:', err.message)
try {
// 发送SSH连接错误事件
socket.emit('shell_connection_error', {
@@ -1625,40 +1625,40 @@ module.exports = (httpServer) => {
code: err.code || 'UNKNOWN'
})
} catch (emitError) {
consola.error('发送错误事件失败:', emitError.message)
logger.error('发送错误事件失败:', emitError.message)
}
})
sftpClient.client.on('end', () => {
consola.info('SSH连接正常结束')
logger.info('SSH连接正常结束')
})
sftpClient.client.on('close', (hadError) => {
if (hadError) {
consola.warn('SSH连接异常关闭')
logger.warn('SSH连接异常关闭')
try {
socket.emit('shell_connection_error', {
message: 'SSH连接异常关闭',
code: 'CONNECTION_CLOSED'
})
} catch (emitError) {
consola.error('发送连接关闭事件失败:', emitError.message)
logger.error('发送连接关闭事件失败:', emitError.message)
}
} else {
consola.info('SSH连接已关闭')
logger.info('SSH连接已关闭')
}
})
// 添加未处理异常捕获
// sftpClient.client.on('timeout', () => {
// consola.warn('SSH连接超时')
// logger.warn('SSH连接超时')
// try {
// socket.emit('shell_connection_error', {
// message: 'SSH连接超时',
// code: 'CONNECTION_TIMEOUT'
// })
// } catch (emitError) {
// consola.error('发送超时事件失败:', emitError.message)
// logger.error('发送超时事件失败:', emitError.message)
// }
// })
@@ -1678,23 +1678,23 @@ module.exports = (httpServer) => {
try {
rootList = await sftpClient.list('/')
consola.success('获取根目录成功')
logger.info('获取根目录成功')
} catch (error) {
consola.error('获取根目录失败:', error.message)
consola.info('尝试获取当前目录')
logger.error('获取根目录失败:', error.message)
logger.info('尝试获取当前目录')
isRootUser = false
try {
// 获取当前工作目录的绝对路径
currentWorkingDir = await sftpClient.cwd()
consola.info('当前工作目录:', currentWorkingDir)
logger.info('当前工作目录:', currentWorkingDir)
rootList = await sftpClient.list(currentWorkingDir)
consola.success('获取当前目录成功')
logger.info('获取当前目录成功')
} catch (cwdError) {
consola.warn('获取工作目录失败,使用相对路径:', cwdError.message)
logger.warn('获取工作目录失败,使用相对路径:', cwdError.message)
currentWorkingDir = '~'
rootList = await sftpClient.list('./')
consola.success('获取当前目录成功')
logger.info('获取当前目录成功')
}
}
@@ -1707,10 +1707,10 @@ module.exports = (httpServer) => {
isRootUser,
currentPath: currentWorkingDir
})
consola.success('连接sftp-v2 成功:', host)
logger.info('连接sftp-v2 成功:', host)
listenAction(sftpClient, socket)
} catch (error) {
consola.error('连接sftp-v2 失败:', error.message)
logger.error('连接sftp-v2 失败:', error.message)
// 发送详细的错误信息给前端
let errorMessage = error.message
@@ -1729,7 +1729,7 @@ module.exports = (httpServer) => {
await sftpClient.end()
}
} catch (cleanupError) {
consola.warn('清理SFTP客户端资源失败:', cleanupError.message)
logger.warn('清理SFTP客户端资源失败:', cleanupError.message)
}
// 清理跳板机连接
@@ -1737,9 +1737,9 @@ module.exports = (httpServer) => {
jumpSshClients.forEach((client, index) => {
try {
client.end()
consola.info(`已清理跳板机连接 ${ index + 1 }`)
logger.info(`已清理跳板机连接 ${ index + 1 }`)
} catch (cleanupError) {
consola.warn(`清理跳板机连接 ${ index + 1 } 失败:`, cleanupError.message)
logger.warn(`清理跳板机连接 ${ index + 1 } 失败:`, cleanupError.message)
}
})
}
@@ -1747,12 +1747,12 @@ module.exports = (httpServer) => {
socket.disconnect()
}
} catch (globalError) {
consola.error('SFTP连接处理过程中发生未预期错误:', globalError.message)
logger.error('SFTP连接处理过程中发生未预期错误:', globalError.message)
try {
socket.emit('connect_fail', `连接失败: ${ globalError.message }`)
socket.disconnect()
} catch (cleanupError) {
consola.error('清理失败连接时发生错误:', cleanupError.message)
logger.error('清理失败连接时发生错误:', cleanupError.message)
}
}
})
@@ -1760,9 +1760,9 @@ module.exports = (httpServer) => {
socket.on('disconnect', async () => {
try {
await sftpClient.end()
consola.info('sftp-v2 连接断开')
logger.info('sftp-v2 连接断开')
} catch (error) {
consola.info('sftp断开连接失败:', error.message)
logger.info('sftp断开连接失败:', error.message)
} finally {
sftpClient = null
// 这里不再清理缓存目录,因为在 listenAction 中的 disconnect 处理器会处理
+23 -23
View File
@@ -39,7 +39,7 @@ function createInteractiveShell(socket, targetSSHClient) {
// 检查SSH客户端连接状态
if (!targetSSHClient || !targetSSHClient._sock || !targetSSHClient._sock.writable) {
const errorMsg = 'SSH客户端连接已断开,无法创建交互式终端'
consola.error(errorMsg)
logger.error(errorMsg)
socket.emit('terminal_connect_fail', errorMsg)
return reject(new Error(errorMsg))
}
@@ -47,7 +47,7 @@ function createInteractiveShell(socket, targetSSHClient) {
try {
targetSSHClient.shell({ term: 'xterm-color' }, (err, stream) => {
if (err) {
consola.error('创建交互式终端失败:', err.message)
logger.error('创建交互式终端失败:', err.message)
socket.emit('terminal_connect_fail', err.message)
return reject(err)
}
@@ -59,18 +59,18 @@ function createInteractiveShell(socket, targetSSHClient) {
socket.emit('output', data.toString())
})
.on('close', () => {
consola.info('交互终端已关闭')
logger.info('交互终端已关闭')
targetSSHClient.end()
})
.on('error', (streamErr) => {
consola.error('终端流错误:', streamErr.message)
logger.error('终端流错误:', streamErr.message)
socket.emit('terminal_connect_fail', streamErr.message)
})
socket.emit('terminal_connect_shell_success') // 已连接终端,web端可以执行指令了
})
} catch (shellError) {
consola.error('调用shell方法失败:', shellError.message)
logger.error('调用shell方法失败:', shellError.message)
socket.emit('terminal_connect_fail', shellError.message)
reject(shellError)
}
@@ -96,7 +96,7 @@ async function getProxyConfig(proxyId) {
password: proxyInfo.password || ''
}
} catch (error) {
consola.error('获取代理配置失败:', error.message)
logger.error('获取代理配置失败:', error.message)
throw error
}
}
@@ -119,7 +119,7 @@ async function handleProxyAndJumpHostConnection(options) {
const proxyConfig = await getProxyConfig(proxyServer)
if (proxyConfig) {
const logMsg = `${ logPrefix }使用代理服务器: ${ proxyConfig.name } (${ proxyConfig.type.toUpperCase() }) - ${ proxyConfig.host }:${ proxyConfig.port }`
consola.info(logMsg)
logger.info(logMsg)
// 向前端发送代理信息(如果socket存在且有对应方法)
if (socket && socket.emit) {
@@ -146,7 +146,7 @@ async function handleProxyAndJumpHostConnection(options) {
}
targetConnectionOptions.sock = proxySocket
consola.success(`${ logPrefix }代理连接建立成功: ${ host }`)
logger.info(`${ logPrefix }代理连接建立成功: ${ host }`)
// 向前端发送成功信息
if (socket && socket.emit && typeof socket.emit === 'function') {
@@ -166,7 +166,7 @@ async function handleProxyAndJumpHostConnection(options) {
if (jumpHostResult) {
targetConnectionOptions.sock = jumpHostResult.sock
jumpSshClients = jumpHostResult.sshClients
consola.success(`${ logPrefix }跳板机连接成功`)
logger.info(`${ logPrefix }跳板机连接成功`)
}
}
@@ -175,13 +175,13 @@ async function handleProxyAndJumpHostConnection(options) {
jumpSshClients
}
} catch (error) {
consola.error(`${ logPrefix }连接失败:`, error.message)
logger.error(`${ logPrefix }连接失败:`, error.message)
throw error
}
}
async function createTerminal(hostId, socket, targetSSHClient, isInteractiveShell = true) {
consola.info(`准备创建${ isInteractiveShell ? '交互式' : '非交互式' }终端:${ hostId }`)
logger.info(`准备创建${ isInteractiveShell ? '交互式' : '非交互式' }终端:${ hostId }`)
return new Promise(async (resolve) => {
const targetHostInfo = await hostListDB.findOneAsync({ _id: hostId })
if (!targetHostInfo) return socket.emit('create_fail', `查找hostId【${ hostId }】凭证信息失败`)
@@ -206,13 +206,13 @@ async function createTerminal(hostId, socket, targetSSHClient, isInteractiveShel
socket.emit('terminal_print_info', `准备连接目标终端: ${ name } - ${ host }`)
socket.emit('terminal_print_info', `连接信息: ssh ${ username }@${ host } -p ${ port } -> ${ authType }`)
consola.info('准备连接目标终端:', host)
consola.log('连接信息', { username, port, authType })
logger.info('准备连接目标终端:', host)
logger.info('连接信息', { username, port, authType })
let closeNoticeFlag = false // 避免重复发送通知
targetSSHClient
.on('ready', async () => {
consola.success('终端连接成功:', host)
logger.info('终端连接成功:', host)
if (isInteractiveShell) {
sendNoticeAsync('host_login', '终端登录', `别名: ${ name } \n IP:${ host } \n 端口:${ port } \n 状态: 登录成功`)
socket.emit('terminal_print_info', `终端连接成功: ${ name } - ${ host }`)
@@ -222,7 +222,7 @@ async function createTerminal(hostId, socket, targetSSHClient, isInteractiveShel
let stream = await createInteractiveShell(socket, targetSSHClient)
resolve({ stream, jumpSshClients })
} catch (shellError) {
consola.error('创建交互式终端失败:', host, shellError.message)
logger.error('创建交互式终端失败:', host, shellError.message)
// 连接已经成功但创建shell失败,需要清理连接
targetSSHClient.end()
jumpSshClients?.forEach(sshClient => sshClient && sshClient.end())
@@ -234,13 +234,13 @@ async function createTerminal(hostId, socket, targetSSHClient, isInteractiveShel
.on('close', (err) => {
if (closeNoticeFlag) return closeNoticeFlag = false
const closeReason = err ? '发生错误导致连接断开' : '正常断开连接'
consola.info(`终端连接断开(${ closeReason }): ${ host }`)
logger.info(`终端连接断开(${ closeReason }): ${ host }`)
socket.emit('terminal_connect_close', { reason: closeReason })
})
.on('error', (err) => {
closeNoticeFlag = true
sendNoticeAsync('host_login', '终端登录', `别名: ${ name } \n IP:${ host } \n 端口:${ port } \n 状态: 登录失败`)
consola.error('连接终端失败:', host, err.message)
logger.error('连接终端失败:', host, err.message)
socket.emit('terminal_connect_fail', err.message)
})
.on('keyboard-interactive', function (name, instructions, instructionsLang, prompts, finish) {
@@ -252,7 +252,7 @@ async function createTerminal(hostId, socket, targetSSHClient, isInteractiveShel
// debug: (info) => console.log(info)
})
} catch (err) {
consola.error('创建终端失败: ', host, err.message)
logger.error('创建终端失败: ', host, err.message)
socket.emit('terminal_create_fail', err.message)
}
})
@@ -278,7 +278,7 @@ function createServerIo(serverIo) {
return
}
connectionCount++
consola.success(`terminal websocket 已连接 - 当前连接数: ${ connectionCount }`)
logger.info(`terminal websocket 已连接 - 当前连接数: ${ connectionCount }`)
let targetSSHClient = null
let jumpSshClients = []
socket.on('ws_terminal', async ({ hostId }) => {
@@ -288,7 +288,7 @@ function createServerIo(serverIo) {
// 如果创建终端失败,result可能为undefined
if (!result) {
consola.error('创建终端失败,未返回结果')
logger.error('创建终端失败,未返回结果')
return
}
@@ -297,7 +297,7 @@ function createServerIo(serverIo) {
const listenerInput = (key) => {
if (!targetSSHClient || !targetSSHClient._sock || !targetSSHClient._sock.writable) {
consola.info('终端连接已关闭,禁止输入')
logger.info('终端连接已关闭,禁止输入')
return
}
stream && stream.write(key)
@@ -310,7 +310,7 @@ function createServerIo(serverIo) {
socket.on('input', listenerInput)
socket.on('resize', resizeShell)
} catch (error) {
consola.error('ws_terminal事件处理失败:', error.message)
logger.error('ws_terminal事件处理失败:', error.message)
socket.emit('terminal_connect_fail', `连接失败: ${ error.message }`)
}
})
@@ -329,7 +329,7 @@ function createServerIo(serverIo) {
jumpSshClients?.forEach(sshClient => sshClient && sshClient.end())
targetSSHClient = null
jumpSshClients = null
consola.info(`终端socket连接断开: ${ reason } - 当前连接数: ${ connectionCount }`)
logger.info(`终端socket连接断开: ${ reason } - 当前连接数: ${ connectionCount }`)
})
})
}
+1 -1
View File
@@ -44,7 +44,7 @@ function decryptAndExecuteAsync(plusPath) {
)
resolve(exports)
} catch (error) {
consola.info('解锁plus功能失败: ', error.message)
logger.info('解锁plus功能失败: ', error.message)
resolve(null)
}
})
+7 -7
View File
@@ -11,15 +11,15 @@ async function getLicenseInfo(key = '') {
let ip = ''
if (global.serverIp && (Date.now() - global.getServerIpLastTime) / 1000 / 60 < 60) {
ip = global.serverIp
consola.log('get server ip by cache: ', ip)
logger.info('通过缓存获取服务器IP: ', ip)
} else {
ip = await getLocalNetIP()
global.serverIp = ip
global.getServerIpLastTime = Date.now()
consola.log('get server ip by net: ', ip)
logger.info('通过接口获取服务器IP: ', ip)
}
if (!ip) {
consola.error('activate plus failed: get public ip failed')
logger.error('😒激活PLUS功能失败: get public ip failed')
global.serverIp = ''
return { success: false, msg: 'get public ip failed' }
}
@@ -33,7 +33,7 @@ async function getLicenseInfo(key = '') {
const response = await requestWithFailover('/api/licenses/activate', requestOptions)
if (!response.ok) {
consola.log('activate plus failed: ', response.status)
logger.info('😒激活PLUS功能失败: ', response.status)
if (response.status === 403) {
const errMsg = await response.json()
throw { errMsg, clear: true }
@@ -45,7 +45,7 @@ async function getLicenseInfo(key = '') {
if (success) {
let { decryptKey, expiryDate, usedIPCount, maxIPs, usedIPs } = data
decryptKey = await AESEncryptAsync(decryptKey)
consola.success('activate plus success')
logger.info('🎉PLUS功能激活成功')
const plusData = { key, decryptKey, expiryDate, usedIPCount, maxIPs, usedIPs }
let count = await plusDB.countAsync({})
if (count === 0) {
@@ -56,10 +56,10 @@ async function getLicenseInfo(key = '') {
}
return { success: true, msg: '激活成功' }
}
consola.error('activate plus failed: ', data)
logger.error('😒激活PLUS功能失败: ', data)
return { success: false, msg: '激活失败' }
} catch (error) {
consola.error(`activate plus failed: ${ error.message || error.errMsg?.message }`)
logger.error(`😒激活PLUS功能失败: ${ error.message || error.errMsg?.message }`)
if (error.clear) {
await plusDB.removeAsync({}, { multi: true })
}
+14 -14
View File
@@ -10,7 +10,7 @@ const notifyDB = new NotifyDB().getInstance()
function sendServerChan(sendKey, title, content) {
return new Promise((async (resolve, reject) => {
try {
consola.info('server酱通知预发送: ', title)
logger.info('server酱通知预发送: ', title)
const url = `https://sctapi.ftqq.com/${ sendKey }.send`
const params = new URLSearchParams({ text: title, desp: content })
let { data } = await axios.post(url, params, {
@@ -19,10 +19,10 @@ function sendServerChan(sendKey, title, content) {
}
})
resolve(data)
consola.info('server酱通知发送成功: ', title)
logger.info('server酱通知发送成功: ', title)
} catch (error) {
reject(error)
consola.error('server酱通知发送失败: ', error)
logger.error('server酱通知发送失败: ', error)
}
}))
@@ -31,7 +31,7 @@ function sendServerChan(sendKey, title, content) {
function sendEmail({ service, user, pass }, title, content) {
return new Promise((async (resolve, reject) => {
try {
consola.info('邮箱通知预发送: ', title)
logger.info('邮箱通知预发送: ', title)
let transporter = nodemailer.createTransport({
service,
auth: {
@@ -46,11 +46,11 @@ function sendEmail({ service, user, pass }, title, content) {
// text: '', // 纯文本版本内容,如果收件人的邮件客户端不支持HTML显示,就会显示这个文本
html: commonTemp(content)
})
consola.info('邮件通知发送成功: ', title)
logger.info('邮件通知发送成功: ', title)
resolve()
} catch (error) {
reject(error)
consola.error('邮件通知发送失败: ', error)
logger.error('邮件通知发送失败: ', error)
}
}))
}
@@ -61,36 +61,36 @@ async function sendNoticeAsync(noticeAction, title, content) {
let notifyList = await notifyDB.findAsync({})
let { sw } = notifyList.find((item) => item.type === noticeAction) // 获取对应动作的通知开关
// console.log('notify swtich: ', noticeAction, sw)
if (!sw) return consola.info('通知开关关闭, 不发送通知: ', noticeAction)
if (!sw) return logger.info('通知开关关闭, 不发送通知: ', noticeAction)
let notifyConfig = await notifyConfigDB.findOneAsync({})
let { type } = notifyConfig
if (!type) return consola.error('通知类型不存在: ', type)
if (!type) return logger.error('通知类型不存在: ', type)
title = `EasyNode-${ title }`
content += `\n通知发送时间:${ new Date() }`
switch (type) {
case 'sct':
let { sendKey } = notifyConfig['sct']
if (!sendKey) return consola.info('未发送server酱通知, sendKey 为空')
if (!sendKey) return logger.info('未发送server酱通知, sendKey 为空')
await sendServerChan(sendKey, title, content)
break
case 'email':
let { service, user, pass } = notifyConfig['email']
if (!service || !user || !pass) return consola.info('未发送邮件通知通知, 未配置邮箱: ', { service, user, pass })
if (!service || !user || !pass) return logger.info('未发送邮件通知通知, 未配置邮箱: ', { service, user, pass })
await sendEmail({ service, user, pass }, title, content)
break
case 'tg':
let { token, chatId } = notifyConfig['tg']
if (!token || !chatId) return consola.info('未发送Telegram通知, 未配置token或chatId: ', { token, chatId })
if (!token || !chatId) return logger.info('未发送Telegram通知, 未配置token或chatId: ', { token, chatId })
let { sendTg } = await decryptAndExecuteAsync(path.join(__dirname, 'plus.js'))
if (!sendTg) return consola.info('未发送Telegram通知, Plus功能解析失败')
if (!sendTg) return logger.info('未发送Telegram通知, Plus功能解析失败')
await sendTg({ token, chatId }, title, content)
break
default:
consola.info('未配置通知类型: ', type)
logger.info('未配置通知类型: ', type)
break
}
} catch (error) {
consola.error('通知发送失败: ', error)
logger.error('通知发送失败: ', error)
}
}
+1 -1
View File
@@ -1 +1 @@
U2FsdGVkX193cbUwkwmNSHZRGKZ0AviTVx/Cp6OTfkZ958srbmgKihf5EeaIpKJtZDnqy9c9YiZVVOPfy5HTzYg30QVrOcDXnvTAIq1c1XSdeAQnINJsHFKDHoHbhI6iyixgey2hNbd6M8YvPAE78Hi3hrYRwz21t9DuRVU5G5qc387b3lWHTNb7OgHllNJ89QCP08n1IKfLB90qHVaGhRS457h3LTb6wPdaF9qt0CU8pSLUOE52FpFsgCb9iAedhBznij7dzL4wj2nohslS41PQ4U/lzKuxA2YPwJSQLOC3yBgqmN8s3nbEb6uN++K9udSnEuSCa0jJ6L4wFjGMokEY5/hii3iEzrTjwsXwIAAxZhe6fYieMOK4Uqc5c84X2jGTrrU0oScQbdylLePSWjX90eJtkyDUlemnp7AhHFYZjvtee+OLuQWQxdQlAyLb7nA34Zw3O6+f2Hs9FBEDbuxXvmT7wCPNWCa/iLo7YjmKRlrJ3dqYDrgGsMJtF1BRbx5PrSpUyyLveeeRZB0sNNmRyqEfhTyoiDtbykzpZ5oKvDjqdy1g6ANrhUFqiSK8d5feDI7rP0ELK1KEW1ZDViJ9DKzzgCg4XCcAA3Wvkjhs+Tns2GpUJG0nnsTJ6WjGk1l39lNYqQqw3QqtB099PGPgNedjsXYbe++B4eiBFCS2Kz384B51NmJ2bcskTpJTXLbMHS1h8paz2PGGqZoJMqqpMlPwkbRAdjpejzNJAuEiiZeUNf0RzIn/L8qT4SJAj7TSWs6F0csoXeYjKTMkJ5xjK0m3cUmDLqBwgIkUR2D+Yyqk7+nn0llL/fuIE2DVJUjzl8aOyYdConjW5Eg+TjH9eePOZZJ4CRRwLDmZ1IY=
U2FsdGVkX18MF+tYSR1XUu4k5TYtD8puiurP+LyWIdTwRj6w6OWxSHLkHsTP2YLCiYL26JpviTPA0tSKHOZ7AjUs37SZ8/wNT8p0kJ0+kt+zK8Qqy7DufTRiFOhb4ARa5W160OON5013372657nxzRfJF8meKptqVtHFuKGHzq5r2G3kTM/XPvuaePOyKTW/yiFsiGfB1/3mvsCd59Ly18IeN261C735Yn3cMH+WxRkOaHuo3lH+XcoSwIIolvOHu/zdjlllgBfS/Z7NwBkdeO7l6iIr16TYMlqfeyuUcO27NZwJVArjxwp6kxMVwOWe0BRxryUzags7goHVEj91iUrxua+cyY4tJ98qwHBhPLAFUGXfTOoaWTGSp9hkjKBIF1TiY7Uf2uMw5kY8xY+11Epj972Fvx+cIRaXPwNw2U2UF2f3hml9YQvtQmaL9B7rpOQbvsTeEjE4Qbws8BSiODDghAMMMgpSP3xhXMCU6H/vVy+bJpKEkR4LeZtkioA+gFo4oRHqWzyfYeHbzs42UAbkrqJKNy9Gjr8+3TRaM9OEnLTpXxY6r7P5GPSDZm6qTxA/z/9OKuUFez0Ys8B7Z8ywDxa/JOz4p7qdwlT47nb+o2Q3t7dhrxt3k+o7z5f93B/zGhHN8DcI+jxiWzea5DSpKn8HBbSjoMcC+LugVcDZc4xAdyzgJHmil/pWOnXmbhix8P4MvFVmaghKix3gCOTkJWR+d7KI6InSm6//gyUk/t6V9eD5zBndorIhMKg3rih2ISOWSBuQPB5z8tH+tCKkvpOGZXQFQh0xWag5QZcWp8FCmbrQKmxtSd8kLacIakjk3HLBzlcB4u8GgRAA0tzWAL+hoHmVJ4O0GMHUrCc=
+6 -9
View File
@@ -77,10 +77,8 @@ const getNetIPInfo = async (searchIp = '') => {
}
console.log(searchResult)
let validInfo = searchResult.find(item => Boolean(item.country))
consola.info('查询IP信息:', validInfo)
return validInfo || { ip: '获取IP信息API出错,请排查或更新API', country: '未知', city: '未知', date }
} catch (error) {
// consola.error('getIpInfo Error: ', error)
return {
ip: '未知',
country: '未知',
@@ -105,7 +103,6 @@ const getLocalNetIP = async () => {
}
if (ipCN.status === 'fulfilled') {
let ip = ipCN.value?.data?.ip
consola.log('ipCN:', ip)
if (ip) return ip
}
if (freeipapi.status === 'fulfilled') {
@@ -279,7 +276,7 @@ const isAllowedIp = (requestIP) => {
let allowedIPs = Array.isArray(global.ALLOWED_IPS) ? global.ALLOWED_IPS : []
if (allowedIPs.length === 0) return true
let flag = allowedIPs.some(item => requestIP.includes(item))
if (!flag) consola.warn(`requestIP:${ requestIP } 不在允许的IP列表中. 允许的IP列表:${ allowedIPs.join(',') }`)
if (!flag) logger.warn(`requestIP:${ requestIP } 不在允许的IP列表中. 允许的IP列表:${ allowedIPs.join(',') }`)
return flag
}
@@ -328,7 +325,7 @@ async function requestWithFailover(path, options = {}, timeout = 5000) {
for (let i = 0; i < plusServers.length; i++) {
const server = plusServers[i]
try {
consola.info(`Plus服务: ${ i + 1 }/${ plusServers.length }: ${ server }${ path }`)
logger.info(`Plus服务: ${ i + 1 }/${ plusServers.length }: ${ server }${ path }`)
const response = await fetch(server + path, {
method,
@@ -339,13 +336,13 @@ async function requestWithFailover(path, options = {}, timeout = 5000) {
// 如果状态码是200或403,不需要尝试下一个服务器
if (response.ok || response.status === 403) {
consola.info(`Plus服务: ${ i + 1 }/${ plusServers.length }: ${ server }${ path } 响应成功,状态码: ${ response.status }`)
logger.info(`Plus服务: ${ i + 1 }/${ plusServers.length }: ${ server }${ path } 响应成功,状态码: ${ response.status }`)
return response
}
// 尝试下一个服务器
if (i < plusServers.length - 1) {
consola.info(`Plus服务: ${ i + 1 }/${ plusServers.length }: ${ server }${ path } 响应状态码 ${ response.status },尝试下一个服务器`)
logger.info(`Plus服务: ${ i + 1 }/${ plusServers.length }: ${ server }${ path } 响应状态码 ${ response.status },尝试下一个服务器`)
continue
}
@@ -355,11 +352,11 @@ async function requestWithFailover(path, options = {}, timeout = 5000) {
} catch (error) {
// 如果不是最后一个服务,尝试下一个服务器[防止异常报错]
if (i < plusServers.length - 1) {
consola.info(`Plus服务: ${ i + 1 }/${ plusServers.length }: ${ server }${ path } 请求失败: ${ error.message },尝试下一个服务器`)
logger.info(`Plus服务: ${ i + 1 }/${ plusServers.length }: ${ server }${ path } 请求失败: ${ error.message },尝试下一个服务器`)
continue
}
// 如果是最后一个服务器,抛出错误
consola.error(`Plus服务: 所有服务器请求失败: ${ error.message }`)
logger.error(`Plus服务: 所有服务器请求失败: ${ error.message }`)
throw error
}
}
+1 -1
View File
@@ -23,7 +23,7 @@ const verifyAuthSync = async (token, userId) => {
}
return { code: enumLoginCode.SUCCESS, success: true } // 验证成功
} catch (err) {
consola.error('用户身份校验失败: ', err.message)
logger.error('用户身份校验失败: ', err.message)
return { code: enumLoginCode.ERROR_TOKEN }
}
}
-2
View File
@@ -1,5 +1,3 @@
const consola = require('consola')
global.consola = consola
global.rpdToken = Array.from({ length:32 },()=>Math.random().toString(36)[2]).join('')
require('dotenv').config()
require('./app/main.js')
-1
View File
@@ -23,7 +23,6 @@
"@seald-io/nedb": "^4.0.4",
"axios": "^1.12.0",
"chardet": "^2.1.1",
"consola": "^3.2.3",
"cross-env": "^7.0.3",
"crypto-js": "^4.2.0",
"dotenv": "^16.4.5",
-5
View File
@@ -2125,11 +2125,6 @@ confbox@^0.2.1:
resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.2.1.tgz#ae39f2c99699afa451d00206479f15f9a1208a8b"
integrity sha512-hkT3yDPFbs95mNCy1+7qNKC6Pro+/ibzYxtM2iqEigpf0sVw+bg4Zh9/snjsBcf990vfIsg5+1U7VyiyBb3etg==
consola@^3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/consola/-/consola-3.2.3.tgz#0741857aa88cfa0d6fd53f1cff0375136e98502f"
integrity sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==
content-disposition@~0.5.2:
version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"