🚩 移除服务端监控服务&新增用户名
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()
|
||||
})
|
||||
|
||||
// 每日凌晨两点整,刷新ip信息(兼容动态ip服务器)
|
||||
// 每日凌晨两点整,刷新ip信息
|
||||
let rule2 = new schedule.RecurrenceRule()
|
||||
rule2.hour = 2
|
||||
rule2.minute = 0
|
||||
rule2.second = 0
|
||||
schedule.scheduleJob(rule2, () => {
|
||||
console.log('Task: refresh ip info', new Date())
|
||||
console.log('Task: refresh ip info: ', new Date())
|
||||
getIpInfo()
|
||||
})
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.7.2",
|
||||
"eslint": "^8.56.0",
|
||||
"koa": "^2.15.3",
|
||||
"node-os-utils": "^1.3.7",
|
||||
"node-schedule": "^2.1.1",
|
||||
|
@ -3,7 +3,7 @@ const { getNetIPInfo, readKey, writeKey, RSADecryptSync, AESEncryptSync, SHA1Enc
|
||||
|
||||
const getpublicKey = async ({ res }) => {
|
||||
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 })
|
||||
}
|
||||
|
||||
@ -16,18 +16,16 @@ let loginCountDown = forbidTimer
|
||||
let forbidLogin = false
|
||||
|
||||
const login = async ({ res, request }) => {
|
||||
let { body: { ciphertext, jwtExpires }, ip: clientIp } = request
|
||||
if(!ciphertext) return res.fail({ msg: '参数错误' })
|
||||
|
||||
if(forbidLogin) return res.fail({ msg: `禁止登录! 倒计时[${ loginCountDown }s]后尝试登录或重启面板服务` })
|
||||
|
||||
let { body: { loginName, ciphertext, jwtExpires }, ip: clientIp } = request
|
||||
if (!loginName && !ciphertext) return res.fail({ msg: '请求非法!' })
|
||||
if (forbidLogin) return res.fail({ msg: `禁止登录! 倒计时[${ loginCountDown }s]后尝试登录或重启面板服务` })
|
||||
loginErrCount++
|
||||
loginErrTotal++
|
||||
if(loginErrCount >= allowErrCount) {
|
||||
if (loginErrCount >= allowErrCount) {
|
||||
const { ip, country, city } = await getNetIPInfo(clientIp)
|
||||
// 发送通知&禁止登录
|
||||
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
|
||||
loginErrCount = 0
|
||||
|
||||
@ -38,8 +36,9 @@ const login = async ({ res, request }) => {
|
||||
|
||||
// 计算登录倒计时
|
||||
timer = setInterval(() => {
|
||||
if(loginCountDown <= 0){
|
||||
if (loginCountDown <= 0) {
|
||||
clearInterval(timer)
|
||||
timer = null
|
||||
loginCountDown = forbidTimer
|
||||
return
|
||||
}
|
||||
@ -50,15 +49,15 @@ const login = async ({ res, request }) => {
|
||||
// 登录流程
|
||||
try {
|
||||
// console.log('ciphertext', ciphertext)
|
||||
let password = await RSADecryptSync(ciphertext)
|
||||
console.log('Decrypt解密password:', password)
|
||||
let { pwd } = await readKey()
|
||||
if(password === 'admin' && pwd === 'admin') {
|
||||
let loginPwd = await RSADecryptSync(ciphertext)
|
||||
// console.log('Decrypt解密password:', loginPwd)
|
||||
let { user, pwd } = await readKey()
|
||||
if (loginName === user && loginPwd === 'admin' && pwd === 'admin') {
|
||||
const token = await beforeLoginHandler(clientIp, jwtExpires)
|
||||
return res.success({ data: { token, jwtExpires }, msg: '登录成功,请及时修改默认密码' })
|
||||
return res.success({ data: { token, jwtExpires }, msg: '登录成功,请及时修改默认用户名和密码' })
|
||||
}
|
||||
password = SHA1Encrypt(password)
|
||||
if(password !== pwd) return res.fail({ msg: '密码错误' })
|
||||
loginPwd = SHA1Encrypt(loginPwd)
|
||||
if (loginName !== user || loginPwd !== pwd) return res.fail({ msg: `用户名或密码错误 ${ loginErrTotal }/${ allowErrCount }` })
|
||||
const token = await beforeLoginHandler(clientIp, jwtExpires)
|
||||
return res.success({ data: { token, jwtExpires }, msg: '登录成功' })
|
||||
} catch (error) {
|
||||
@ -83,26 +82,28 @@ const beforeLoginHandler = async (clientIp, jwtExpires) => {
|
||||
|
||||
// 邮件登录通知
|
||||
let sw = getNotifySwByType('login')
|
||||
if(sw) sendEmailToConfList('登录提醒', `地点:${ country + city }<br/>IP: ${ ip }`)
|
||||
if (sw) sendEmailToConfList('登录提醒', `地点:${ country + city }<br/>IP: ${ ip }`)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
const updatePwd = async ({ res, request }) => {
|
||||
let { body: { oldPwd, newPwd } } = request
|
||||
let { body: { oldLoginName, oldPwd, newLoginName, newPwd } } = request
|
||||
let rsaOldPwd = await RSADecryptSync(oldPwd)
|
||||
oldPwd = rsaOldPwd === 'admin' ? 'admin' : SHA1Encrypt(rsaOldPwd)
|
||||
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))
|
||||
keyObj.user = newLoginName
|
||||
keyObj.pwd = newPwd
|
||||
await writeKey(keyObj)
|
||||
|
||||
let sw = getNotifySwByType('updatePwd')
|
||||
if(sw) sendEmailToConfList('密码修改提醒', '面板登录密码已更改')
|
||||
if (sw) sendEmailToConfList(`登录信息修改提醒, 新用户名: ${ newLoginName }`)
|
||||
|
||||
res.success({ data: true, msg: 'success' })
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ function initKeyDB() {
|
||||
if (count === 0) {
|
||||
consola.log('初始化keyDB✔')
|
||||
const defaultData = {
|
||||
user: 'admin',
|
||||
pwd: 'admin',
|
||||
commonKey: '',
|
||||
publicKey: '',
|
||||
|
@ -1,16 +1,15 @@
|
||||
const consola = require('consola')
|
||||
global.consola = consola
|
||||
const { httpServer, clientHttpServer } = require('./server')
|
||||
const { httpServer } = require('./server')
|
||||
const initDB = require('./db')
|
||||
const initEncryptConf = require('./init')
|
||||
// const scheduleJob = require('./schedule')
|
||||
const scheduleJob = require('./schedule')
|
||||
|
||||
async function main() {
|
||||
await initDB()
|
||||
await initEncryptConf()
|
||||
httpServer()
|
||||
clientHttpServer()
|
||||
// scheduleJob()
|
||||
scheduleJob()
|
||||
}
|
||||
|
||||
main()
|
||||
|
@ -25,5 +25,6 @@ const expiredNotifyJob = async () => {
|
||||
}
|
||||
|
||||
module.exports = () => {
|
||||
// 每天中午12点执行一次。
|
||||
schedule.scheduleJob('0 0 12 1/1 * ?', expiredNotifyJob)
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
const offlineInspect = require('./offline-inspect')
|
||||
const expiredNotify = require('./expired-notify')
|
||||
|
||||
module.exports = () => {
|
||||
offlineInspect()
|
||||
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 compose = require('koa-compose') // 组合中间件,简化写法
|
||||
const http = require('http')
|
||||
const { clientPort } = require('./config')
|
||||
const { httpPort } = require('./config')
|
||||
const middlewares = require('./middlewares')
|
||||
const wsMonitorOsInfo = require('./socket/monitor')
|
||||
const wsTerminal = require('./socket/terminal')
|
||||
const wsSftp = require('./socket/sftp')
|
||||
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) {
|
||||
app.proxy = true // 用于nginx反代时获取真实客户端ip
|
||||
@ -50,6 +39,5 @@ function serverHandler(app, server) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
httpServer,
|
||||
clientHttpServer
|
||||
httpServer
|
||||
}
|
@ -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 {
|
||||
// background-color: #f4f6f9;
|
||||
background-position: center center;
|
||||
background-attachment: fixed;
|
||||
background-size: cover;
|
||||
// background-position: center center;
|
||||
// background-attachment: fixed;
|
||||
// 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-image: url(../bg.jpg), linear-gradient(to bottom, #010179, #F5C4C1, #151799);
|
||||
background-position: center 110px;
|
||||
background-size: 58%;
|
||||
}
|
||||
|
||||
html, body {
|
||||
|
@ -7,29 +7,41 @@
|
||||
:hide-required-asterisk="true"
|
||||
label-suffix=":"
|
||||
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
|
||||
v-model.trim="formData.oldPwd"
|
||||
type="password"
|
||||
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"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码" prop="newPwd">
|
||||
<el-input
|
||||
v-model.trim="formData.newPwd"
|
||||
type="password"
|
||||
show-password
|
||||
clearable
|
||||
placeholder="新密码"
|
||||
autocomplete="off"
|
||||
@keyup.enter="handleUpdate"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" prop="confirmPwd">
|
||||
<el-input
|
||||
v-model.trim="formData.confirmPwd"
|
||||
clearable
|
||||
placeholder="确认密码"
|
||||
placeholder=""
|
||||
autocomplete="off"
|
||||
@keyup.enter="handleUpdate"
|
||||
/>
|
||||
@ -49,28 +61,30 @@ const { proxy: { $api, $message } } = getCurrentInstance()
|
||||
const loading = ref(false)
|
||||
const formRef = ref(null)
|
||||
const formData = reactive({
|
||||
oldLoginName: '',
|
||||
oldPwd: '',
|
||||
newPwd: '',
|
||||
confirmPwd: ''
|
||||
newLoginName: '',
|
||||
newPwd: ''
|
||||
})
|
||||
const rules = reactive({
|
||||
oldPwd: { required: true, message: '输入旧密码', trigger: 'change' },
|
||||
newPwd: { required: true, message: '输入新密码', trigger: 'change' },
|
||||
confirmPwd: { required: true, message: '输入确认密码', trigger: 'change' }
|
||||
oldLoginName: { required: true, message: '输入原用户名', trigger: 'change' },
|
||||
oldPwd: { required: true, message: '输入原密码', trigger: 'change' },
|
||||
newLoginName: { required: true, message: '输入新用户名', trigger: 'change' },
|
||||
newPwd: { required: true, message: '输入新密码', trigger: 'change' }
|
||||
})
|
||||
|
||||
const handleUpdate = () => {
|
||||
formRef.value.validate()
|
||||
.then(async () => {
|
||||
let { oldPwd, newPwd, confirmPwd } = formData
|
||||
if(newPwd !== confirmPwd) return $message.error({ center: true, message: '两次密码输入不一致' })
|
||||
let { oldLoginName, oldPwd, newLoginName, newPwd } = formData
|
||||
oldPwd = RSAEncrypt(oldPwd)
|
||||
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 })
|
||||
formData.oldLoginName = ''
|
||||
formData.oldPwd = ''
|
||||
formData.newLoginName = ''
|
||||
formData.newPwd = ''
|
||||
formData.confirmPwd = ''
|
||||
formRef.value.resetFields()
|
||||
})
|
||||
}
|
||||
|
@ -1,70 +1,78 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
width="500px"
|
||||
:top="'30vh'"
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:show-close="false"
|
||||
center
|
||||
>
|
||||
<template #header>
|
||||
<h2 v-if="notKey" style="color: #f56c6c;"> Error </h2>
|
||||
<h2 v-else style="color: #409eff;"> LOGIN </h2>
|
||||
</template>
|
||||
<div v-if="notKey">
|
||||
<el-alert title="Error: 用于加密的公钥获取失败,请尝试重新启动或部署服务" type="error" show-icon />
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-form
|
||||
ref="loginFormRefs"
|
||||
:model="loginForm"
|
||||
:rules="rules"
|
||||
:hide-required-asterisk="true"
|
||||
label-suffix=":"
|
||||
label-width="90px"
|
||||
>
|
||||
<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"
|
||||
<div class="login_container">
|
||||
<div class="login_box">
|
||||
<div>
|
||||
<h2>EasyNode</h2>
|
||||
</div>
|
||||
<div v-if="notKey">
|
||||
<el-alert title="Error: 用于加密的公钥获取失败,请尝试重新启动或部署服务" type="error" show-icon />
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-form
|
||||
ref="loginFormRefs"
|
||||
:model="loginForm"
|
||||
:rules="rules"
|
||||
:hide-required-asterisk="true"
|
||||
label-suffix=":"
|
||||
label-width="90px"
|
||||
:show-message="false"
|
||||
>
|
||||
<el-form-item prop="loginName" label="用户名">
|
||||
<el-input
|
||||
v-model.trim="loginForm.loginName"
|
||||
type="text"
|
||||
placeholder=""
|
||||
autocomplete="off"
|
||||
:trigger-on-focus="false"
|
||||
clearable
|
||||
/>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-form-item>
|
||||
<el-form-item prop="pwd" label="密码">
|
||||
<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>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button type="primary" :loading="loading" @click="handleLogin">登录</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@ -82,23 +90,26 @@ const visible = ref(true)
|
||||
const notKey = ref(false)
|
||||
const loading = ref(false)
|
||||
const loginForm = reactive({
|
||||
loginName: '',
|
||||
pwd: '',
|
||||
jwtExpires: 8
|
||||
})
|
||||
const rules = reactive({
|
||||
loginName: { required: true, message: '需输入用户名', trigger: 'change' },
|
||||
pwd: { required: true, message: '需输入密码', trigger: 'change' }
|
||||
})
|
||||
|
||||
const handleLogin = () => {
|
||||
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) {
|
||||
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 })
|
||||
loading.value = true
|
||||
$api.login({ ciphertext, jwtExpires })
|
||||
$api.login({ loginName, ciphertext, jwtExpires })
|
||||
.then(({ data, msg }) => {
|
||||
const { token } = data
|
||||
$store.setJwtToken(token, isSession.value)
|
||||
@ -120,6 +131,43 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<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 {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
|
129
yarn.lock
129
yarn.lock
@ -653,20 +653,11 @@
|
||||
dependencies:
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae"
|
||||
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":
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad"
|
||||
@ -682,36 +673,11 @@
|
||||
minimatch "^3.1.2"
|
||||
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":
|
||||
version "8.57.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f"
|
||||
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":
|
||||
version "1.6.4"
|
||||
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"
|
||||
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":
|
||||
version "8.0.2"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248"
|
||||
integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
|
||||
@ -2260,24 +2221,11 @@ eslint-scope@^7.1.1, eslint-scope@^7.2.2:
|
||||
esrecurse "^4.3.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:
|
||||
version "3.4.3"
|
||||
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==
|
||||
|
||||
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:
|
||||
version "8.57.0"
|
||||
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"
|
||||
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:
|
||||
version "9.6.1"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7"
|
||||
integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==
|
||||
@ -2461,13 +2360,6 @@ file-entry-cache@^6.0.1:
|
||||
dependencies:
|
||||
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:
|
||||
version "7.1.1"
|
||||
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"
|
||||
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:
|
||||
version "3.3.1"
|
||||
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:
|
||||
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:
|
||||
version "11.1.0"
|
||||
resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
|
||||
@ -3110,7 +2989,7 @@ keygrip@~1.1.0:
|
||||
dependencies:
|
||||
tsscmp "1.0.6"
|
||||
|
||||
keyv@^4.5.3, keyv@^4.5.4:
|
||||
keyv@^4.5.3:
|
||||
version "4.5.4"
|
||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
|
||||
integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
|
||||
|
Loading…
x
Reference in New Issue
Block a user