🚩 移除服务端监控服务&新增用户名
This commit is contained in:
parent
908558915d
commit
7b8014c36b
96
client/.eslintrc.js
Normal file
96
client/.eslintrc.js
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// 规则参见:https://cn.eslint.org/docs/rules/
|
||||||
|
module.exports = {
|
||||||
|
root: true, // 当前配置文件不能往父级查找
|
||||||
|
'globals': {
|
||||||
|
'consola': true
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
node: true,
|
||||||
|
es6: true
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended' // 应用Eslint全部默认规则
|
||||||
|
],
|
||||||
|
'parserOptions': {
|
||||||
|
'ecmaVersion': 'latest',
|
||||||
|
'sourceType': 'module' // 目标类型 Node项目得添加这个
|
||||||
|
},
|
||||||
|
// 自定义规则,可以覆盖 extends 的配置【安装Eslint插件可以静态检查本地文件是否符合以下规则】
|
||||||
|
'ignorePatterns': ['*.html', 'node-os-utils'],
|
||||||
|
rules: {
|
||||||
|
// 0: 关闭规则(允许) 1/2: 警告warning/错误error(不允许)
|
||||||
|
'no-console': 'off',
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||||
|
'template-curly-spacing': ['error', 'always'], // 模板字符串空格
|
||||||
|
'default-case': 0,
|
||||||
|
'no-empty': 0,
|
||||||
|
'object-curly-spacing': ['error', 'always'],
|
||||||
|
'no-multi-spaces': ['error'],
|
||||||
|
indent: ['error', 2, { 'SwitchCase': 1 }], // 缩进:2
|
||||||
|
quotes: ['error', 'single'], // 引号:single单引 double双引
|
||||||
|
semi: ['error', 'never'], // 结尾分号:never禁止 always必须
|
||||||
|
'comma-dangle': ['error', 'never'], // 对象拖尾逗号
|
||||||
|
'no-redeclare': ['error', { builtinGlobals: true }], // 禁止重复对象声明
|
||||||
|
'no-multi-assign': 0,
|
||||||
|
'no-restricted-globals': 0,
|
||||||
|
'space-before-function-paren': 0, // 函数定义时括号前面空格
|
||||||
|
'one-var': 0, // 允许连续声明
|
||||||
|
// 'no-undef': 0, // 允许未定义的变量【会使env配置无效】
|
||||||
|
'linebreak-style': 0, // 检测CRLF/LF检测【默认LF】
|
||||||
|
'no-extra-boolean-cast': 0, // 允许意外的Boolean值转换
|
||||||
|
'no-constant-condition': 0, // if语句中禁止常量表达式
|
||||||
|
'no-prototype-builtins': 0, // 允许使用Object.prototypes内置对象(如:xxx.hasOwnProperty)
|
||||||
|
'no-regex-spaces': 0, // 允许正则匹配多个空格
|
||||||
|
'no-unexpected-multiline': 0, // 允许多行表达式
|
||||||
|
'no-fallthrough': 0, // 允许switch穿透
|
||||||
|
'no-delete-var': 0, // 允许 delete 删除对象属性
|
||||||
|
'no-mixed-spaces-and-tabs': 0, // 允许空格tab混用
|
||||||
|
'no-class-assign': 0, // 允许修改class类型
|
||||||
|
'no-param-reassign': 0, // 允许对函数params赋值
|
||||||
|
'max-len': 0, // 允许长行
|
||||||
|
'func-names': 0, // 允许命名函数
|
||||||
|
'import/no-unresolved': 0, // 不检测模块not fund
|
||||||
|
'import/prefer-default-export': 0, // 允许单个导出
|
||||||
|
'no-const-assign': 1, // 警告:修改const命名的变量
|
||||||
|
'no-unused-vars': 1, // 警告:已声明未使用
|
||||||
|
'no-unsafe-negation': 1, // 警告:使用 in / instanceof 关系运算符时,左边表达式请勿使用 ! 否定操作符
|
||||||
|
'use-isnan': 1, // 警告:使用 isNaN() 检查 NaN
|
||||||
|
'no-var': 2, // 禁止使用var声明
|
||||||
|
'no-empty-pattern': 2, // 空解构赋值
|
||||||
|
'eqeqeq': 2, // 必须使用 全等=== 或 非全等 !==
|
||||||
|
'no-cond-assign': 2, // if语句中禁止赋值
|
||||||
|
'no-dupe-args': 2, // 禁止function重复参数
|
||||||
|
'no-dupe-keys': 2, // 禁止object重复key
|
||||||
|
'no-duplicate-case': 2,
|
||||||
|
'no-func-assign': 2, // 禁止重复声明函数
|
||||||
|
'no-inner-declarations': 2, // 禁止在嵌套的语句块中出现变量或 function 声明
|
||||||
|
'no-sparse-arrays': 2, // 禁止稀缺数组
|
||||||
|
'no-unreachable': 2, // 禁止非条件return、throw、continue 和 break 语句后出现代码
|
||||||
|
'no-unsafe-finally': 2, // 禁止finally出现控制流语句,如:return、throw等,因为这会导致try...catch捕获不到
|
||||||
|
'valid-typeof': 2, // 强制 typeof 表达式与有效的字符串进行比较
|
||||||
|
// auto format options
|
||||||
|
'prefer-const': 0, // 禁用声明自动化
|
||||||
|
'no-extra-parens': 0, // 允许函数周围出现不明括号
|
||||||
|
'no-extra-semi': 2, // 禁止不必要的分号
|
||||||
|
// curly: ['error', 'multi'], // if、else、for、while 语句单行代码时不使用大括号
|
||||||
|
'dot-notation': 0, // 允许使用点号或方括号来访问对象属性
|
||||||
|
'dot-location': ['error', 'property'], // 点操作符位置,要求跟随下一行
|
||||||
|
'no-else-return': 2, // 禁止if中有return后又else
|
||||||
|
'no-implicit-coercion': [2, { allow: ['!!', '~', '+'] }], // 禁止隐式转换,allow字段内符号允许
|
||||||
|
'no-trailing-spaces': 1, //一行结束后面不要有空格
|
||||||
|
'no-multiple-empty-lines': [1, { 'max': 1 }], // 空行最多不能超过1行
|
||||||
|
'no-useless-return': 2,
|
||||||
|
'wrap-iife': 0, // 允许自调用函数
|
||||||
|
'yoda': 0, // 允许yoda语句
|
||||||
|
'strict': 0, // 允许strict
|
||||||
|
'no-undef-init': 0, // 允许将变量初始化为undefined
|
||||||
|
'prefer-promise-reject-errors': 0, // 允许使用非 Error 对象作为 Promise 拒绝的原因
|
||||||
|
'consistent-return': 0, // 允许函数不使用return
|
||||||
|
'no-new': 0, // 允许单独new
|
||||||
|
'no-restricted-syntax': 0, // 允许特定的语法
|
||||||
|
'no-plusplus': 0,
|
||||||
|
'import/extensions': 0, // 忽略扩展名
|
||||||
|
'global-require': 0,
|
||||||
|
'no-return-assign': 0
|
||||||
|
}
|
||||||
|
}
|
@ -25,13 +25,13 @@ function ipSchedule() {
|
|||||||
getIpInfo()
|
getIpInfo()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 每日凌晨两点整,刷新ip信息(兼容动态ip服务器)
|
// 每日凌晨两点整,刷新ip信息
|
||||||
let rule2 = new schedule.RecurrenceRule()
|
let rule2 = new schedule.RecurrenceRule()
|
||||||
rule2.hour = 2
|
rule2.hour = 2
|
||||||
rule2.minute = 0
|
rule2.minute = 0
|
||||||
rule2.second = 0
|
rule2.second = 0
|
||||||
schedule.scheduleJob(rule2, () => {
|
schedule.scheduleJob(rule2, () => {
|
||||||
console.log('Task: refresh ip info', new Date())
|
console.log('Task: refresh ip info: ', new Date())
|
||||||
getIpInfo()
|
getIpInfo()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
|
"eslint": "^8.56.0",
|
||||||
"koa": "^2.15.3",
|
"koa": "^2.15.3",
|
||||||
"node-os-utils": "^1.3.7",
|
"node-os-utils": "^1.3.7",
|
||||||
"node-schedule": "^2.1.1",
|
"node-schedule": "^2.1.1",
|
||||||
|
@ -3,7 +3,7 @@ const { getNetIPInfo, readKey, writeKey, RSADecryptSync, AESEncryptSync, SHA1Enc
|
|||||||
|
|
||||||
const getpublicKey = async ({ res }) => {
|
const getpublicKey = async ({ res }) => {
|
||||||
let { publicKey: data } = await readKey()
|
let { publicKey: data } = await readKey()
|
||||||
if(!data) return res.fail({ msg: 'publicKey not found, Try to restart the server', status: 500 })
|
if (!data) return res.fail({ msg: 'publicKey not found, Try to restart the server', status: 500 })
|
||||||
res.success({ data })
|
res.success({ data })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,18 +16,16 @@ let loginCountDown = forbidTimer
|
|||||||
let forbidLogin = false
|
let forbidLogin = false
|
||||||
|
|
||||||
const login = async ({ res, request }) => {
|
const login = async ({ res, request }) => {
|
||||||
let { body: { ciphertext, jwtExpires }, ip: clientIp } = request
|
let { body: { loginName, ciphertext, jwtExpires }, ip: clientIp } = request
|
||||||
if(!ciphertext) return res.fail({ msg: '参数错误' })
|
if (!loginName && !ciphertext) return res.fail({ msg: '请求非法!' })
|
||||||
|
if (forbidLogin) return res.fail({ msg: `禁止登录! 倒计时[${ loginCountDown }s]后尝试登录或重启面板服务` })
|
||||||
if(forbidLogin) return res.fail({ msg: `禁止登录! 倒计时[${ loginCountDown }s]后尝试登录或重启面板服务` })
|
|
||||||
|
|
||||||
loginErrCount++
|
loginErrCount++
|
||||||
loginErrTotal++
|
loginErrTotal++
|
||||||
if(loginErrCount >= allowErrCount) {
|
if (loginErrCount >= allowErrCount) {
|
||||||
const { ip, country, city } = await getNetIPInfo(clientIp)
|
const { ip, country, city } = await getNetIPInfo(clientIp)
|
||||||
// 发送通知&禁止登录
|
// 发送通知&禁止登录
|
||||||
let sw = getNotifySwByType('err_login')
|
let sw = getNotifySwByType('err_login')
|
||||||
if(sw) sendEmailToConfList('登录错误提醒', `重新登录次数: ${ loginErrTotal }<br/>地点:${ country + city }<br/>IP: ${ ip }`)
|
if (sw) sendEmailToConfList('登录错误提醒', `重新登录次数: ${ loginErrTotal }<br/>地点:${ country + city }<br/>IP: ${ ip }`)
|
||||||
forbidLogin = true
|
forbidLogin = true
|
||||||
loginErrCount = 0
|
loginErrCount = 0
|
||||||
|
|
||||||
@ -38,8 +36,9 @@ const login = async ({ res, request }) => {
|
|||||||
|
|
||||||
// 计算登录倒计时
|
// 计算登录倒计时
|
||||||
timer = setInterval(() => {
|
timer = setInterval(() => {
|
||||||
if(loginCountDown <= 0){
|
if (loginCountDown <= 0) {
|
||||||
clearInterval(timer)
|
clearInterval(timer)
|
||||||
|
timer = null
|
||||||
loginCountDown = forbidTimer
|
loginCountDown = forbidTimer
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -50,15 +49,15 @@ const login = async ({ res, request }) => {
|
|||||||
// 登录流程
|
// 登录流程
|
||||||
try {
|
try {
|
||||||
// console.log('ciphertext', ciphertext)
|
// console.log('ciphertext', ciphertext)
|
||||||
let password = await RSADecryptSync(ciphertext)
|
let loginPwd = await RSADecryptSync(ciphertext)
|
||||||
console.log('Decrypt解密password:', password)
|
// console.log('Decrypt解密password:', loginPwd)
|
||||||
let { pwd } = await readKey()
|
let { user, pwd } = await readKey()
|
||||||
if(password === 'admin' && pwd === 'admin') {
|
if (loginName === user && loginPwd === 'admin' && pwd === 'admin') {
|
||||||
const token = await beforeLoginHandler(clientIp, jwtExpires)
|
const token = await beforeLoginHandler(clientIp, jwtExpires)
|
||||||
return res.success({ data: { token, jwtExpires }, msg: '登录成功,请及时修改默认密码' })
|
return res.success({ data: { token, jwtExpires }, msg: '登录成功,请及时修改默认用户名和密码' })
|
||||||
}
|
}
|
||||||
password = SHA1Encrypt(password)
|
loginPwd = SHA1Encrypt(loginPwd)
|
||||||
if(password !== pwd) return res.fail({ msg: '密码错误' })
|
if (loginName !== user || loginPwd !== pwd) return res.fail({ msg: `用户名或密码错误 ${ loginErrTotal }/${ allowErrCount }` })
|
||||||
const token = await beforeLoginHandler(clientIp, jwtExpires)
|
const token = await beforeLoginHandler(clientIp, jwtExpires)
|
||||||
return res.success({ data: { token, jwtExpires }, msg: '登录成功' })
|
return res.success({ data: { token, jwtExpires }, msg: '登录成功' })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -83,26 +82,28 @@ const beforeLoginHandler = async (clientIp, jwtExpires) => {
|
|||||||
|
|
||||||
// 邮件登录通知
|
// 邮件登录通知
|
||||||
let sw = getNotifySwByType('login')
|
let sw = getNotifySwByType('login')
|
||||||
if(sw) sendEmailToConfList('登录提醒', `地点:${ country + city }<br/>IP: ${ ip }`)
|
if (sw) sendEmailToConfList('登录提醒', `地点:${ country + city }<br/>IP: ${ ip }`)
|
||||||
|
|
||||||
global.loginRecord.unshift(clientIPInfo)
|
global.loginRecord.unshift(clientIPInfo)
|
||||||
if(global.loginRecord.length > 10) global.loginRecord = global.loginRecord.slice(0, 10)
|
if (global.loginRecord.length > 10) global.loginRecord = global.loginRecord.slice(0, 10)
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatePwd = async ({ res, request }) => {
|
const updatePwd = async ({ res, request }) => {
|
||||||
let { body: { oldPwd, newPwd } } = request
|
let { body: { oldLoginName, oldPwd, newLoginName, newPwd } } = request
|
||||||
let rsaOldPwd = await RSADecryptSync(oldPwd)
|
let rsaOldPwd = await RSADecryptSync(oldPwd)
|
||||||
oldPwd = rsaOldPwd === 'admin' ? 'admin' : SHA1Encrypt(rsaOldPwd)
|
oldPwd = rsaOldPwd === 'admin' ? 'admin' : SHA1Encrypt(rsaOldPwd)
|
||||||
let keyObj = await readKey()
|
let keyObj = await readKey()
|
||||||
if(oldPwd !== keyObj.pwd) return res.fail({ data: false, msg: '旧密码校验失败' })
|
let { user, pwd } = keyObj
|
||||||
|
if (oldLoginName !== user || oldPwd !== pwd) return res.fail({ data: false, msg: '原用户名或密码校验失败' })
|
||||||
// 旧密钥校验通过,加密保存新密码
|
// 旧密钥校验通过,加密保存新密码
|
||||||
newPwd = await RSADecryptSync(newPwd) === 'admin' ? 'admin' : SHA1Encrypt(await RSADecryptSync(newPwd))
|
newPwd = await RSADecryptSync(newPwd) === 'admin' ? 'admin' : SHA1Encrypt(await RSADecryptSync(newPwd))
|
||||||
|
keyObj.user = newLoginName
|
||||||
keyObj.pwd = newPwd
|
keyObj.pwd = newPwd
|
||||||
await writeKey(keyObj)
|
await writeKey(keyObj)
|
||||||
|
|
||||||
let sw = getNotifySwByType('updatePwd')
|
let sw = getNotifySwByType('updatePwd')
|
||||||
if(sw) sendEmailToConfList('密码修改提醒', '面板登录密码已更改')
|
if (sw) sendEmailToConfList(`登录信息修改提醒, 新用户名: ${ newLoginName }`)
|
||||||
|
|
||||||
res.success({ data: true, msg: 'success' })
|
res.success({ data: true, msg: 'success' })
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ function initKeyDB() {
|
|||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
consola.log('初始化keyDB✔')
|
consola.log('初始化keyDB✔')
|
||||||
const defaultData = {
|
const defaultData = {
|
||||||
|
user: 'admin',
|
||||||
pwd: 'admin',
|
pwd: 'admin',
|
||||||
commonKey: '',
|
commonKey: '',
|
||||||
publicKey: '',
|
publicKey: '',
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
const consola = require('consola')
|
const consola = require('consola')
|
||||||
global.consola = consola
|
global.consola = consola
|
||||||
const { httpServer, clientHttpServer } = require('./server')
|
const { httpServer } = require('./server')
|
||||||
const initDB = require('./db')
|
const initDB = require('./db')
|
||||||
const initEncryptConf = require('./init')
|
const initEncryptConf = require('./init')
|
||||||
// const scheduleJob = require('./schedule')
|
const scheduleJob = require('./schedule')
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
await initDB()
|
await initDB()
|
||||||
await initEncryptConf()
|
await initEncryptConf()
|
||||||
httpServer()
|
httpServer()
|
||||||
clientHttpServer()
|
scheduleJob()
|
||||||
// scheduleJob()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
@ -25,5 +25,6 @@ const expiredNotifyJob = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = () => {
|
module.exports = () => {
|
||||||
|
// 每天中午12点执行一次。
|
||||||
schedule.scheduleJob('0 0 12 1/1 * ?', expiredNotifyJob)
|
schedule.scheduleJob('0 0 12 1/1 * ?', expiredNotifyJob)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
const offlineInspect = require('./offline-inspect')
|
|
||||||
const expiredNotify = require('./expired-notify')
|
const expiredNotify = require('./expired-notify')
|
||||||
|
|
||||||
module.exports = () => {
|
module.exports = () => {
|
||||||
offlineInspect()
|
|
||||||
expiredNotify()
|
expiredNotify()
|
||||||
}
|
}
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
const schedule = require('node-schedule')
|
|
||||||
const { clientPort } = require('../config')
|
|
||||||
const { readHostList, sendEmailToConfList, getNotifySwByType, formatTimestamp, isProd } = require('../utils')
|
|
||||||
const testConnectAsync = require('../utils/test-connect')
|
|
||||||
|
|
||||||
let sendNotifyRecord = new Map()
|
|
||||||
const offlineJob = async () => {
|
|
||||||
let sw = getNotifySwByType('host_offline')
|
|
||||||
if(!sw) return
|
|
||||||
consola.info('=====开始检测服务器状态=====', new Date())
|
|
||||||
const hostList = await readHostList()
|
|
||||||
for (const item of hostList) {
|
|
||||||
const { host, name } = item
|
|
||||||
// consola.info('start inpect:', host, name )
|
|
||||||
testConnectAsync({
|
|
||||||
port: clientPort ,
|
|
||||||
host: `http://${ host }`,
|
|
||||||
timeout: 3000,
|
|
||||||
retryTimes: 20 // 尝试重连次数
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
// consola.success('测试连接成功:', host, name)
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
consola.error('测试连接失败: ', host, name)
|
|
||||||
// 当前小时是否发送过通知
|
|
||||||
let curHourIsSend = sendNotifyRecord.has(host) && (sendNotifyRecord.get(host).sendTime === formatTimestamp(Date.now(), 'hour'))
|
|
||||||
if(curHourIsSend) return consola.info('当前小时已发送过通知: ', sendNotifyRecord.get(host).sendTime)
|
|
||||||
sendEmailToConfList('服务器离线提醒', `别名: ${ name }<br/>IP: ${ host }<br/>错误信息:${ error.message }`)
|
|
||||||
.then(() => {
|
|
||||||
sendNotifyRecord.set(host, { 'sendTime': formatTimestamp(Date.now(), 'hour') })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = () => {
|
|
||||||
if(!isProd()) return consola.info('本地开发不检测服务器离线状态')
|
|
||||||
schedule.scheduleJob('0 0/5 12 1/1 * ?', offlineJob)
|
|
||||||
}
|
|
@ -1,10 +1,8 @@
|
|||||||
const Koa = require('koa')
|
const Koa = require('koa')
|
||||||
const compose = require('koa-compose') // 组合中间件,简化写法
|
const compose = require('koa-compose') // 组合中间件,简化写法
|
||||||
const http = require('http')
|
const http = require('http')
|
||||||
const { clientPort } = require('./config')
|
|
||||||
const { httpPort } = require('./config')
|
const { httpPort } = require('./config')
|
||||||
const middlewares = require('./middlewares')
|
const middlewares = require('./middlewares')
|
||||||
const wsMonitorOsInfo = require('./socket/monitor')
|
|
||||||
const wsTerminal = require('./socket/terminal')
|
const wsTerminal = require('./socket/terminal')
|
||||||
const wsSftp = require('./socket/sftp')
|
const wsSftp = require('./socket/sftp')
|
||||||
const wsHostStatus = require('./socket/host-status')
|
const wsHostStatus = require('./socket/host-status')
|
||||||
@ -21,15 +19,6 @@ const httpServer = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const clientHttpServer = () => {
|
|
||||||
const app = new Koa()
|
|
||||||
const server = http.createServer(app.callback())
|
|
||||||
wsMonitorOsInfo(server) // 监控本机信息
|
|
||||||
server.listen(clientPort, () => {
|
|
||||||
consola.success(`Client(http) is running on: http://localhost:${ clientPort }`)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 服务
|
// 服务
|
||||||
function serverHandler(app, server) {
|
function serverHandler(app, server) {
|
||||||
app.proxy = true // 用于nginx反代时获取真实客户端ip
|
app.proxy = true // 用于nginx反代时获取真实客户端ip
|
||||||
@ -50,6 +39,5 @@ function serverHandler(app, server) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
httpServer,
|
httpServer
|
||||||
clientHttpServer
|
|
||||||
}
|
}
|
@ -1,71 +0,0 @@
|
|||||||
const { Server } = require('socket.io')
|
|
||||||
const schedule = require('node-schedule')
|
|
||||||
const axios = require('axios')
|
|
||||||
let getOsData = require('../utils/os-data')
|
|
||||||
const consola = require('consola')
|
|
||||||
|
|
||||||
let serverSockets = {}, ipInfo = {}, osData = {}
|
|
||||||
|
|
||||||
async function getIpInfo() {
|
|
||||||
try {
|
|
||||||
let { data } = await axios.get('http://ip-api.com/json?lang=zh-CN')
|
|
||||||
consola.success('getIpInfo Success: ', new Date())
|
|
||||||
ipInfo = data
|
|
||||||
} catch (error) {
|
|
||||||
consola.error('getIpInfo Error: ', new Date(), error.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function ipSchedule() {
|
|
||||||
let rule1 = new schedule.RecurrenceRule()
|
|
||||||
rule1.second = [0]
|
|
||||||
schedule.scheduleJob(rule1, () => {
|
|
||||||
let { query, country, city } = ipInfo || {}
|
|
||||||
if(query && country && city) return
|
|
||||||
consola.success('Task: start getIpInfo', new Date())
|
|
||||||
getIpInfo()
|
|
||||||
})
|
|
||||||
|
|
||||||
// 每日凌晨两点整,刷新ip信息(兼容动态ip服务器)
|
|
||||||
let rule2 = new schedule.RecurrenceRule()
|
|
||||||
rule2.hour = 2
|
|
||||||
rule2.minute = 0
|
|
||||||
rule2.second = 0
|
|
||||||
schedule.scheduleJob(rule2, () => {
|
|
||||||
consola.info('Task: refresh ip info', new Date())
|
|
||||||
getIpInfo()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
ipSchedule()
|
|
||||||
|
|
||||||
module.exports = (httpServer) => {
|
|
||||||
const serverIo = new Server(httpServer, {
|
|
||||||
path: '/client/os-info',
|
|
||||||
cors: {
|
|
||||||
origin: '*'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
serverIo.on('connection', (socket) => {
|
|
||||||
// 存储对应websocket连接的定时器
|
|
||||||
serverSockets[socket.id] = setInterval(async () => {
|
|
||||||
try {
|
|
||||||
osData = await getOsData()
|
|
||||||
socket && socket.emit('client_data', Object.assign(osData, { ipInfo }))
|
|
||||||
} catch (error) {
|
|
||||||
consola.error('客户端错误:', error)
|
|
||||||
socket && socket.emit('client_error', { error })
|
|
||||||
}
|
|
||||||
}, 1000)
|
|
||||||
|
|
||||||
socket.on('disconnect', () => {
|
|
||||||
// 断开时清除对应的websocket连接
|
|
||||||
if(serverSockets[socket.id]) clearInterval(serverSockets[socket.id])
|
|
||||||
delete serverSockets[socket.id]
|
|
||||||
socket.close && socket.close()
|
|
||||||
socket = null
|
|
||||||
// console.log('断开socketId: ', socket.id, '剩余链接数: ', Object.keys(serverSockets).length)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
0
server/app/socket/sftp-cache/.gitkeep
Normal file
0
server/app/socket/sftp-cache/.gitkeep
Normal file
@ -29,12 +29,16 @@ html, body, div, ul, section, textarea {
|
|||||||
|
|
||||||
// 全局背景
|
// 全局背景
|
||||||
body {
|
body {
|
||||||
// background-color: #f4f6f9;
|
// background-position: center center;
|
||||||
background-position: center center;
|
// background-attachment: fixed;
|
||||||
background-attachment: fixed;
|
// background-size: cover;
|
||||||
background-size: cover;
|
// background-repeat: no-repeat;
|
||||||
|
// // background-image: url(../bg.jpg), linear-gradient(to bottom, #010179, #F5C4C1, #151799);
|
||||||
|
background-color: #f4f6f9;
|
||||||
|
background-image: url(https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-image: url(../bg.jpg), linear-gradient(to bottom, #010179, #F5C4C1, #151799);
|
background-position: center 110px;
|
||||||
|
background-size: 58%;
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
|
@ -7,29 +7,41 @@
|
|||||||
:hide-required-asterisk="true"
|
:hide-required-asterisk="true"
|
||||||
label-suffix=":"
|
label-suffix=":"
|
||||||
label-width="90px"
|
label-width="90px"
|
||||||
|
:show-message="false"
|
||||||
>
|
>
|
||||||
<el-form-item label="旧密码" prop="oldPwd">
|
<el-form-item label="原用户名" prop="oldLoginName">
|
||||||
|
<el-input
|
||||||
|
v-model.trim="formData.oldLoginName"
|
||||||
|
clearable
|
||||||
|
placeholder=""
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="原密码" prop="oldPwd">
|
||||||
<el-input
|
<el-input
|
||||||
v-model.trim="formData.oldPwd"
|
v-model.trim="formData.oldPwd"
|
||||||
|
type="password"
|
||||||
clearable
|
clearable
|
||||||
placeholder="旧密码"
|
show-password
|
||||||
|
placeholder=""
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="新用户名" prop="oldPwd">
|
||||||
|
<el-input
|
||||||
|
v-model.trim="formData.newLoginName"
|
||||||
|
clearable
|
||||||
|
placeholder=""
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="新密码" prop="newPwd">
|
<el-form-item label="新密码" prop="newPwd">
|
||||||
<el-input
|
<el-input
|
||||||
v-model.trim="formData.newPwd"
|
v-model.trim="formData.newPwd"
|
||||||
|
type="password"
|
||||||
|
show-password
|
||||||
clearable
|
clearable
|
||||||
placeholder="新密码"
|
placeholder=""
|
||||||
autocomplete="off"
|
|
||||||
@keyup.enter="handleUpdate"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="确认密码" prop="confirmPwd">
|
|
||||||
<el-input
|
|
||||||
v-model.trim="formData.confirmPwd"
|
|
||||||
clearable
|
|
||||||
placeholder="确认密码"
|
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
@keyup.enter="handleUpdate"
|
@keyup.enter="handleUpdate"
|
||||||
/>
|
/>
|
||||||
@ -49,28 +61,30 @@ const { proxy: { $api, $message } } = getCurrentInstance()
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const formRef = ref(null)
|
const formRef = ref(null)
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
|
oldLoginName: '',
|
||||||
oldPwd: '',
|
oldPwd: '',
|
||||||
newPwd: '',
|
newLoginName: '',
|
||||||
confirmPwd: ''
|
newPwd: ''
|
||||||
})
|
})
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
oldPwd: { required: true, message: '输入旧密码', trigger: 'change' },
|
oldLoginName: { required: true, message: '输入原用户名', trigger: 'change' },
|
||||||
newPwd: { required: true, message: '输入新密码', trigger: 'change' },
|
oldPwd: { required: true, message: '输入原密码', trigger: 'change' },
|
||||||
confirmPwd: { required: true, message: '输入确认密码', trigger: 'change' }
|
newLoginName: { required: true, message: '输入新用户名', trigger: 'change' },
|
||||||
|
newPwd: { required: true, message: '输入新密码', trigger: 'change' }
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleUpdate = () => {
|
const handleUpdate = () => {
|
||||||
formRef.value.validate()
|
formRef.value.validate()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
let { oldPwd, newPwd, confirmPwd } = formData
|
let { oldLoginName, oldPwd, newLoginName, newPwd } = formData
|
||||||
if(newPwd !== confirmPwd) return $message.error({ center: true, message: '两次密码输入不一致' })
|
|
||||||
oldPwd = RSAEncrypt(oldPwd)
|
oldPwd = RSAEncrypt(oldPwd)
|
||||||
newPwd = RSAEncrypt(newPwd)
|
newPwd = RSAEncrypt(newPwd)
|
||||||
let { msg } = await $api.updatePwd({ oldPwd, newPwd })
|
let { msg } = await $api.updatePwd({ oldLoginName, oldPwd, newLoginName, newPwd })
|
||||||
$message({ type: 'success', center: true, message: msg })
|
$message({ type: 'success', center: true, message: msg })
|
||||||
|
formData.oldLoginName = ''
|
||||||
formData.oldPwd = ''
|
formData.oldPwd = ''
|
||||||
|
formData.newLoginName = ''
|
||||||
formData.newPwd = ''
|
formData.newPwd = ''
|
||||||
formData.confirmPwd = ''
|
|
||||||
formRef.value.resetFields()
|
formRef.value.resetFields()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,70 +1,78 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<div class="login_container">
|
||||||
v-model="visible"
|
<div class="login_box">
|
||||||
width="500px"
|
<div>
|
||||||
:top="'30vh'"
|
<h2>EasyNode</h2>
|
||||||
destroy-on-close
|
</div>
|
||||||
:close-on-click-modal="false"
|
<div v-if="notKey">
|
||||||
:close-on-press-escape="false"
|
<el-alert title="Error: 用于加密的公钥获取失败,请尝试重新启动或部署服务" type="error" show-icon />
|
||||||
:show-close="false"
|
</div>
|
||||||
center
|
<div v-else>
|
||||||
>
|
<el-form
|
||||||
<template #header>
|
ref="loginFormRefs"
|
||||||
<h2 v-if="notKey" style="color: #f56c6c;"> Error </h2>
|
:model="loginForm"
|
||||||
<h2 v-else style="color: #409eff;"> LOGIN </h2>
|
:rules="rules"
|
||||||
</template>
|
:hide-required-asterisk="true"
|
||||||
<div v-if="notKey">
|
label-suffix=":"
|
||||||
<el-alert title="Error: 用于加密的公钥获取失败,请尝试重新启动或部署服务" type="error" show-icon />
|
label-width="90px"
|
||||||
</div>
|
:show-message="false"
|
||||||
<div v-else>
|
>
|
||||||
<el-form
|
<el-form-item prop="loginName" label="用户名">
|
||||||
ref="loginFormRefs"
|
<el-input
|
||||||
:model="loginForm"
|
v-model.trim="loginForm.loginName"
|
||||||
:rules="rules"
|
type="text"
|
||||||
:hide-required-asterisk="true"
|
placeholder=""
|
||||||
label-suffix=":"
|
autocomplete="off"
|
||||||
label-width="90px"
|
:trigger-on-focus="false"
|
||||||
>
|
clearable
|
||||||
<el-form-item prop="pwd" label="密码">
|
|
||||||
<el-input
|
|
||||||
v-model.trim="loginForm.pwd"
|
|
||||||
type="password"
|
|
||||||
placeholder="Please input password"
|
|
||||||
autocomplete="off"
|
|
||||||
:trigger-on-focus="false"
|
|
||||||
clearable
|
|
||||||
show-password
|
|
||||||
@keyup.enter="handleLogin"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item v-show="false" prop="pwd" label="密码">
|
|
||||||
<el-input v-model.trim="loginForm.pwd" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item prop="jwtExpires" label="有效期">
|
|
||||||
<el-radio-group v-model="isSession" class="login-indate">
|
|
||||||
<el-radio :value="true">一次性会话</el-radio>
|
|
||||||
<el-radio :value="false">自定义(小时)</el-radio>
|
|
||||||
<el-input-number
|
|
||||||
v-model="loginForm.jwtExpires"
|
|
||||||
:disabled="isSession"
|
|
||||||
placeholder="单位:小时"
|
|
||||||
class="input"
|
|
||||||
:min="1"
|
|
||||||
:max="72"
|
|
||||||
value-on-clear="min"
|
|
||||||
size="small"
|
|
||||||
controls-position="right"
|
|
||||||
/>
|
/>
|
||||||
</el-radio-group>
|
</el-form-item>
|
||||||
</el-form-item>
|
<el-form-item prop="pwd" label="密码">
|
||||||
</el-form>
|
<el-input
|
||||||
|
v-model.trim="loginForm.pwd"
|
||||||
|
type="password"
|
||||||
|
placeholder=""
|
||||||
|
autocomplete="off"
|
||||||
|
:trigger-on-focus="false"
|
||||||
|
clearable
|
||||||
|
show-password
|
||||||
|
@keyup.enter="handleLogin"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-show="false" prop="pwd" label="密码">
|
||||||
|
<el-input v-model.trim="loginForm.pwd" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item prop="jwtExpires" label="有效期">
|
||||||
|
<el-radio-group v-model="isSession" class="login-indate">
|
||||||
|
<el-radio :value="true">一次性会话</el-radio>
|
||||||
|
<el-radio :value="false">自定义(小时)</el-radio>
|
||||||
|
<el-input-number
|
||||||
|
v-model="loginForm.jwtExpires"
|
||||||
|
:disabled="isSession"
|
||||||
|
placeholder="单位:小时"
|
||||||
|
class="input"
|
||||||
|
:min="1"
|
||||||
|
:max="72"
|
||||||
|
value-on-clear="min"
|
||||||
|
size="small"
|
||||||
|
controls-position="right"
|
||||||
|
/>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<div class="footer_btns">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
class="login_btn"
|
||||||
|
:loading="loading"
|
||||||
|
@click="handleLogin"
|
||||||
|
>
|
||||||
|
登录
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
</div>
|
||||||
<span class="dialog-footer">
|
|
||||||
<el-button type="primary" :loading="loading" @click="handleLogin">登录</el-button>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -82,23 +90,26 @@ const visible = ref(true)
|
|||||||
const notKey = ref(false)
|
const notKey = ref(false)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const loginForm = reactive({
|
const loginForm = reactive({
|
||||||
|
loginName: '',
|
||||||
pwd: '',
|
pwd: '',
|
||||||
jwtExpires: 8
|
jwtExpires: 8
|
||||||
})
|
})
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
|
loginName: { required: true, message: '需输入用户名', trigger: 'change' },
|
||||||
pwd: { required: true, message: '需输入密码', trigger: 'change' }
|
pwd: { required: true, message: '需输入密码', trigger: 'change' }
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleLogin = () => {
|
const handleLogin = () => {
|
||||||
loginFormRefs.value.validate().then(() => {
|
loginFormRefs.value.validate().then(() => {
|
||||||
let jwtExpires = isSession.value ? '12h' : `${ loginForm.jwtExpires }h`
|
let { jwtExpires, loginName, pwd } = loginForm
|
||||||
|
jwtExpires = isSession.value ? '12h' : `${ jwtExpires }h`
|
||||||
if (!isSession.value) {
|
if (!isSession.value) {
|
||||||
localStorage.setItem('jwtExpires', loginForm.jwtExpires)
|
localStorage.setItem('jwtExpires', jwtExpires)
|
||||||
}
|
}
|
||||||
const ciphertext = RSAEncrypt(loginForm.pwd)
|
const ciphertext = RSAEncrypt(pwd)
|
||||||
if (ciphertext === -1) return $message.error({ message: '公钥加载失败', center: true })
|
if (ciphertext === -1) return $message.error({ message: '公钥加载失败', center: true })
|
||||||
loading.value = true
|
loading.value = true
|
||||||
$api.login({ ciphertext, jwtExpires })
|
$api.login({ loginName, ciphertext, jwtExpires })
|
||||||
.then(({ data, msg }) => {
|
.then(({ data, msg }) => {
|
||||||
const { token } = data
|
const { token } = data
|
||||||
$store.setJwtToken(token, isSession.value)
|
$store.setJwtToken(token, isSession.value)
|
||||||
@ -120,6 +131,43 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.login_container {
|
||||||
|
// min-height: 600px;
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(171, 181, 196, 0.3); // #f0f2f5;
|
||||||
|
padding-top: 70px;
|
||||||
|
|
||||||
|
.login_box {
|
||||||
|
width: 500px;
|
||||||
|
min-height: 250px;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #ebedef;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-align: center;
|
||||||
|
margin: 25px;
|
||||||
|
color: #409eff;
|
||||||
|
font-size: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer_btns {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
.login_btn {
|
||||||
|
width: 88px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.login-indate {
|
.login-indate {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
|
129
yarn.lock
129
yarn.lock
@ -653,20 +653,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
eslint-visitor-keys "^3.3.0"
|
eslint-visitor-keys "^3.3.0"
|
||||||
|
|
||||||
"@eslint-community/regexpp@^4.11.0", "@eslint-community/regexpp@^4.6.1":
|
"@eslint-community/regexpp@^4.6.1":
|
||||||
version "4.11.0"
|
version "4.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae"
|
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae"
|
||||||
integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==
|
integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==
|
||||||
|
|
||||||
"@eslint/config-array@^0.17.0":
|
|
||||||
version "0.17.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.17.0.tgz#ff305e1ee618a00e6e5d0485454c8d92d94a860d"
|
|
||||||
integrity sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==
|
|
||||||
dependencies:
|
|
||||||
"@eslint/object-schema" "^2.1.4"
|
|
||||||
debug "^4.3.1"
|
|
||||||
minimatch "^3.1.2"
|
|
||||||
|
|
||||||
"@eslint/eslintrc@^2.1.4":
|
"@eslint/eslintrc@^2.1.4":
|
||||||
version "2.1.4"
|
version "2.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad"
|
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad"
|
||||||
@ -682,36 +673,11 @@
|
|||||||
minimatch "^3.1.2"
|
minimatch "^3.1.2"
|
||||||
strip-json-comments "^3.1.1"
|
strip-json-comments "^3.1.1"
|
||||||
|
|
||||||
"@eslint/eslintrc@^3.1.0":
|
|
||||||
version "3.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6"
|
|
||||||
integrity sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==
|
|
||||||
dependencies:
|
|
||||||
ajv "^6.12.4"
|
|
||||||
debug "^4.3.2"
|
|
||||||
espree "^10.0.1"
|
|
||||||
globals "^14.0.0"
|
|
||||||
ignore "^5.2.0"
|
|
||||||
import-fresh "^3.2.1"
|
|
||||||
js-yaml "^4.1.0"
|
|
||||||
minimatch "^3.1.2"
|
|
||||||
strip-json-comments "^3.1.1"
|
|
||||||
|
|
||||||
"@eslint/js@8.57.0":
|
"@eslint/js@8.57.0":
|
||||||
version "8.57.0"
|
version "8.57.0"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f"
|
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f"
|
||||||
integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==
|
integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==
|
||||||
|
|
||||||
"@eslint/js@9.7.0":
|
|
||||||
version "9.7.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.7.0.tgz#b712d802582f02b11cfdf83a85040a296afec3f0"
|
|
||||||
integrity sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==
|
|
||||||
|
|
||||||
"@eslint/object-schema@^2.1.4":
|
|
||||||
version "2.1.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843"
|
|
||||||
integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==
|
|
||||||
|
|
||||||
"@floating-ui/core@^1.6.0":
|
"@floating-ui/core@^1.6.0":
|
||||||
version "1.6.4"
|
version "1.6.4"
|
||||||
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.4.tgz#0140cf5091c8dee602bff9da5ab330840ff91df6"
|
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.4.tgz#0140cf5091c8dee602bff9da5ab330840ff91df6"
|
||||||
@ -756,11 +722,6 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
|
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
|
||||||
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
|
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
|
||||||
|
|
||||||
"@humanwhocodes/retry@^0.3.0":
|
|
||||||
version "0.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.0.tgz#6d86b8cb322660f03d3f0aa94b99bdd8e172d570"
|
|
||||||
integrity sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==
|
|
||||||
|
|
||||||
"@isaacs/cliui@^8.0.2":
|
"@isaacs/cliui@^8.0.2":
|
||||||
version "8.0.2"
|
version "8.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
|
resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
|
||||||
@ -1412,7 +1373,7 @@ acorn-jsx@^5.3.2:
|
|||||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
||||||
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||||
|
|
||||||
acorn@^8.11.3, acorn@^8.12.0, acorn@^8.12.1, acorn@^8.9.0:
|
acorn@^8.11.3, acorn@^8.12.1, acorn@^8.9.0:
|
||||||
version "8.12.1"
|
version "8.12.1"
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248"
|
||||||
integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
|
integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
|
||||||
@ -2260,24 +2221,11 @@ eslint-scope@^7.1.1, eslint-scope@^7.2.2:
|
|||||||
esrecurse "^4.3.0"
|
esrecurse "^4.3.0"
|
||||||
estraverse "^5.2.0"
|
estraverse "^5.2.0"
|
||||||
|
|
||||||
eslint-scope@^8.0.2:
|
|
||||||
version "8.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.0.2.tgz#5cbb33d4384c9136083a71190d548158fe128f94"
|
|
||||||
integrity sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==
|
|
||||||
dependencies:
|
|
||||||
esrecurse "^4.3.0"
|
|
||||||
estraverse "^5.2.0"
|
|
||||||
|
|
||||||
eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
|
eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
|
||||||
version "3.4.3"
|
version "3.4.3"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
|
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
|
||||||
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
|
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
|
||||||
|
|
||||||
eslint-visitor-keys@^4.0.0:
|
|
||||||
version "4.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb"
|
|
||||||
integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==
|
|
||||||
|
|
||||||
eslint@^8.56.0:
|
eslint@^8.56.0:
|
||||||
version "8.57.0"
|
version "8.57.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668"
|
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668"
|
||||||
@ -2322,55 +2270,6 @@ eslint@^8.56.0:
|
|||||||
strip-ansi "^6.0.1"
|
strip-ansi "^6.0.1"
|
||||||
text-table "^0.2.0"
|
text-table "^0.2.0"
|
||||||
|
|
||||||
eslint@^9.6.0:
|
|
||||||
version "9.7.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.7.0.tgz#bedb48e1cdc2362a0caaa106a4c6ed943e8b09e4"
|
|
||||||
integrity sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==
|
|
||||||
dependencies:
|
|
||||||
"@eslint-community/eslint-utils" "^4.2.0"
|
|
||||||
"@eslint-community/regexpp" "^4.11.0"
|
|
||||||
"@eslint/config-array" "^0.17.0"
|
|
||||||
"@eslint/eslintrc" "^3.1.0"
|
|
||||||
"@eslint/js" "9.7.0"
|
|
||||||
"@humanwhocodes/module-importer" "^1.0.1"
|
|
||||||
"@humanwhocodes/retry" "^0.3.0"
|
|
||||||
"@nodelib/fs.walk" "^1.2.8"
|
|
||||||
ajv "^6.12.4"
|
|
||||||
chalk "^4.0.0"
|
|
||||||
cross-spawn "^7.0.2"
|
|
||||||
debug "^4.3.2"
|
|
||||||
escape-string-regexp "^4.0.0"
|
|
||||||
eslint-scope "^8.0.2"
|
|
||||||
eslint-visitor-keys "^4.0.0"
|
|
||||||
espree "^10.1.0"
|
|
||||||
esquery "^1.5.0"
|
|
||||||
esutils "^2.0.2"
|
|
||||||
fast-deep-equal "^3.1.3"
|
|
||||||
file-entry-cache "^8.0.0"
|
|
||||||
find-up "^5.0.0"
|
|
||||||
glob-parent "^6.0.2"
|
|
||||||
ignore "^5.2.0"
|
|
||||||
imurmurhash "^0.1.4"
|
|
||||||
is-glob "^4.0.0"
|
|
||||||
is-path-inside "^3.0.3"
|
|
||||||
json-stable-stringify-without-jsonify "^1.0.1"
|
|
||||||
levn "^0.4.1"
|
|
||||||
lodash.merge "^4.6.2"
|
|
||||||
minimatch "^3.1.2"
|
|
||||||
natural-compare "^1.4.0"
|
|
||||||
optionator "^0.9.3"
|
|
||||||
strip-ansi "^6.0.1"
|
|
||||||
text-table "^0.2.0"
|
|
||||||
|
|
||||||
espree@^10.0.1, espree@^10.1.0:
|
|
||||||
version "10.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/espree/-/espree-10.1.0.tgz#8788dae611574c0f070691f522e4116c5a11fc56"
|
|
||||||
integrity sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==
|
|
||||||
dependencies:
|
|
||||||
acorn "^8.12.0"
|
|
||||||
acorn-jsx "^5.3.2"
|
|
||||||
eslint-visitor-keys "^4.0.0"
|
|
||||||
|
|
||||||
espree@^9.3.1, espree@^9.6.0, espree@^9.6.1:
|
espree@^9.3.1, espree@^9.6.0, espree@^9.6.1:
|
||||||
version "9.6.1"
|
version "9.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
|
resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
|
||||||
@ -2380,7 +2279,7 @@ espree@^9.3.1, espree@^9.6.0, espree@^9.6.1:
|
|||||||
acorn-jsx "^5.3.2"
|
acorn-jsx "^5.3.2"
|
||||||
eslint-visitor-keys "^3.4.1"
|
eslint-visitor-keys "^3.4.1"
|
||||||
|
|
||||||
esquery@^1.4.0, esquery@^1.4.2, esquery@^1.5.0:
|
esquery@^1.4.0, esquery@^1.4.2:
|
||||||
version "1.6.0"
|
version "1.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7"
|
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7"
|
||||||
integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==
|
integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==
|
||||||
@ -2461,13 +2360,6 @@ file-entry-cache@^6.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
flat-cache "^3.0.4"
|
flat-cache "^3.0.4"
|
||||||
|
|
||||||
file-entry-cache@^8.0.0:
|
|
||||||
version "8.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f"
|
|
||||||
integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==
|
|
||||||
dependencies:
|
|
||||||
flat-cache "^4.0.0"
|
|
||||||
|
|
||||||
fill-range@^7.1.1:
|
fill-range@^7.1.1:
|
||||||
version "7.1.1"
|
version "7.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
|
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
|
||||||
@ -2492,14 +2384,6 @@ flat-cache@^3.0.4:
|
|||||||
keyv "^4.5.3"
|
keyv "^4.5.3"
|
||||||
rimraf "^3.0.2"
|
rimraf "^3.0.2"
|
||||||
|
|
||||||
flat-cache@^4.0.0:
|
|
||||||
version "4.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c"
|
|
||||||
integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==
|
|
||||||
dependencies:
|
|
||||||
flatted "^3.2.9"
|
|
||||||
keyv "^4.5.4"
|
|
||||||
|
|
||||||
flatted@^3.2.7, flatted@^3.2.9:
|
flatted@^3.2.7, flatted@^3.2.9:
|
||||||
version "3.3.1"
|
version "3.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a"
|
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a"
|
||||||
@ -2689,11 +2573,6 @@ globals@^13.19.0, globals@^13.24.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
type-fest "^0.20.2"
|
type-fest "^0.20.2"
|
||||||
|
|
||||||
globals@^14.0.0:
|
|
||||||
version "14.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e"
|
|
||||||
integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==
|
|
||||||
|
|
||||||
globby@^11.1.0:
|
globby@^11.1.0:
|
||||||
version "11.1.0"
|
version "11.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
|
resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
|
||||||
@ -3110,7 +2989,7 @@ keygrip@~1.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
tsscmp "1.0.6"
|
tsscmp "1.0.6"
|
||||||
|
|
||||||
keyv@^4.5.3, keyv@^4.5.4:
|
keyv@^4.5.3:
|
||||||
version "4.5.4"
|
version "4.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
|
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
|
||||||
integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
|
integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
|
||||||
|
Loading…
x
Reference in New Issue
Block a user