mirror of
https://github.com/chaos-zhu/easynode.git
synced 2026-10-07 16:07:52 +08:00
feat: 优化日志打印
This commit is contained in:
+1
-1
@@ -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,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' // 是否记录日志
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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 })
|
||||
|
||||
@@ -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
@@ -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 }
|
||||
|
||||
@@ -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,3 +1,4 @@
|
||||
require('./logs')
|
||||
const { httpServer } = require('./server')
|
||||
const initDB = require('./db')
|
||||
const scheduleJob = require('./schedule')
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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') // 静态目录
|
||||
|
||||
@@ -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('日志系统启动')
|
||||
@@ -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: {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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服务
|
||||
|
||||
@@ -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 }`)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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('已停止进度广播定时器')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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 处理器会处理
|
||||
|
||||
@@ -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 }`)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ function decryptAndExecuteAsync(plusPath) {
|
||||
)
|
||||
resolve(exports)
|
||||
} catch (error) {
|
||||
consola.info('解锁plus功能失败: ', error.message)
|
||||
logger.info('解锁plus功能失败: ', error.message)
|
||||
resolve(null)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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
@@ -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 @@
|
||||
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=
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user