189 lines
6.1 KiB
JavaScript
189 lines
6.1 KiB
JavaScript
const fs = require('fs')
|
||
const CryptoJS = require('crypto-js')
|
||
const rawCrypto = require('crypto')
|
||
const NodeRSA = require('node-rsa')
|
||
const jwt = require('jsonwebtoken')
|
||
const axios = require('axios')
|
||
const request = axios.create({ timeout: 3000 })
|
||
|
||
const { sshRecordPath, hostListPath, keyPath } = require('../config')
|
||
|
||
const readSSHRecord = () => {
|
||
let list
|
||
try {
|
||
list = JSON.parse(fs.readFileSync(sshRecordPath, 'utf8'))
|
||
} catch (error) {
|
||
console.log('读取ssh-record错误, 即将重置ssh列表: ', error)
|
||
writeSSHRecord([])
|
||
}
|
||
return list || []
|
||
}
|
||
|
||
const writeSSHRecord = (record = []) => {
|
||
fs.writeFileSync(sshRecordPath, JSON.stringify(record, null, 2))
|
||
}
|
||
|
||
const readHostList = () => {
|
||
let list
|
||
try {
|
||
list = JSON.parse(fs.readFileSync(hostListPath, 'utf8'))
|
||
} catch (error) {
|
||
console.log('读取host-list错误, 即将重置host列表: ', error)
|
||
writeHostList([])
|
||
}
|
||
return list || []
|
||
}
|
||
|
||
const writeHostList = (record = []) => {
|
||
fs.writeFileSync(hostListPath, JSON.stringify(record, null, 2))
|
||
}
|
||
|
||
const readKey = () => {
|
||
let keyObj = JSON.parse(fs.readFileSync(keyPath, 'utf8'))
|
||
return keyObj
|
||
}
|
||
|
||
const writeKey = (keyObj = {}) => {
|
||
fs.writeFileSync(keyPath, JSON.stringify(keyObj, null, 2))
|
||
}
|
||
|
||
// 为空时请求本地IP
|
||
const getNetIPInfo = async (ip = '') => {
|
||
try {
|
||
let date = getUTCDate(8)
|
||
let ipUrls = [`http://ip-api.com/json/${ ip }?lang=zh-CN`, `http://whois.pconline.com.cn/ipJson.jsp?json=true&ip=${ ip }`]
|
||
let result = await Promise.allSettled(ipUrls.map(url => request.get(url)))
|
||
let [ipApi, pconline] = result
|
||
if(ipApi.status === 'fulfilled') {
|
||
let { query: ip, country, regionName, city } = ipApi.value.data
|
||
// console.log({ ip, country, city: regionName + city })
|
||
return { ip, country, city: regionName + city, date }
|
||
}
|
||
if(pconline.status === 'fulfilled') {
|
||
let { ip, pro, city, addr } = pconline.value.data
|
||
// console.log({ ip, country: pro || addr, city })
|
||
return { ip, country: pro || addr, city, date }
|
||
}
|
||
throw Error('获取IP信息API出错,请排查或更新API')
|
||
} catch (error) {
|
||
console.error('getIpInfo Error: ', error)
|
||
return {
|
||
ip: '未知',
|
||
country: '未知',
|
||
city: '未知',
|
||
error
|
||
}
|
||
}
|
||
}
|
||
|
||
const throwError = ({ status = 500, msg = 'defalut error' } = {}) => {
|
||
const err = new Error(msg)
|
||
err.status = status // 主动抛错
|
||
throw err
|
||
}
|
||
|
||
const isIP = (ip = '') => {
|
||
const isIPv4 = /^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/
|
||
const isIPv6 = /^([\da-fA-F]{1,4}:){7}[\da-fA-F]{1,4}|:((:[\da−fA−F]1,4)1,6|:)|:((:[\da−fA−F]1,4)1,6|:)|^[\da-fA-F]{1,4}:((:[\da-fA-F]{1,4}){1,5}|:)|([\da−fA−F]1,4:)2((:[\da−fA−F]1,4)1,4|:)|([\da−fA−F]1,4:)2((:[\da−fA−F]1,4)1,4|:)|^([\da-fA-F]{1,4}:){3}((:[\da-fA-F]{1,4}){1,3}|:)|([\da−fA−F]1,4:)4((:[\da−fA−F]1,4)1,2|:)|([\da−fA−F]1,4:)4((:[\da−fA−F]1,4)1,2|:)|^([\da-fA-F]{1,4}:){5}:([\da-fA-F]{1,4})?|([\da−fA−F]1,4:)6:|([\da−fA−F]1,4:)6:/
|
||
return isIPv4.test(ip) || isIPv6.test(ip)
|
||
}
|
||
|
||
const randomStr = (e) =>{
|
||
e = e || 16
|
||
let str = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678',
|
||
a = str.length,
|
||
res = ''
|
||
for (let i = 0; i < e; i++) res += str.charAt(Math.floor(Math.random() * a))
|
||
return res
|
||
}
|
||
|
||
// 校验token与登录IP
|
||
const verifyAuth = (token, clientIp) =>{
|
||
token = AESDecrypt(token) // 先aes解密
|
||
const { commonKey } = readKey()
|
||
try {
|
||
const { exp } = jwt.verify(token, commonKey)
|
||
// console.log('校验token:', new Date(), '---', new Date(exp * 1000))
|
||
if(Date.now() > (exp * 1000)) return { code: -1, msg: 'token expires' } // 过期
|
||
|
||
let lastLoginIp = global.loginRecord[0] ? global.loginRecord[0].ip : ''
|
||
console.log('校验客户端IP:', clientIp)
|
||
console.log('最后登录的IP:', lastLoginIp)
|
||
// 判断: (生产环境)clientIp与上次登录成功IP不一致
|
||
if(isProd() && (!lastLoginIp || !clientIp || !clientIp.includes(lastLoginIp))) {
|
||
return { code: -1, msg: '登录IP发生变化, 需重新登录' } // IP与上次登录访问的不一致
|
||
}
|
||
// console.log('token验证成功')
|
||
return { code: 1, msg: 'success' } // 验证成功
|
||
} catch (error) {
|
||
// console.log('token校验错误:', error)
|
||
return { code: -2, msg: error } // token错误, 验证失败
|
||
}
|
||
}
|
||
|
||
const isProd = () => {
|
||
const EXEC_ENV = process.env.EXEC_ENV || 'production'
|
||
return EXEC_ENV === 'production'
|
||
}
|
||
|
||
// rsa非对称 私钥解密
|
||
const RSADecrypt = (ciphertext) => {
|
||
if(!ciphertext) return
|
||
let { privateKey } = readKey()
|
||
privateKey = AESDecrypt(privateKey) // 先解密私钥
|
||
const rsakey = new NodeRSA(privateKey)
|
||
rsakey.setOptions({ encryptionScheme: 'pkcs1' }) // Must Set It When Frontend Use jsencrypt
|
||
const plaintext = rsakey.decrypt(ciphertext, 'utf8')
|
||
return plaintext
|
||
}
|
||
|
||
// aes对称 加密(default commonKey)
|
||
const AESEncrypt = (text, key) => {
|
||
if(!text) return
|
||
let { commonKey } = readKey()
|
||
let ciphertext = CryptoJS.AES.encrypt(text, key || commonKey).toString()
|
||
return ciphertext
|
||
}
|
||
|
||
// aes对称 解密(default commonKey)
|
||
const AESDecrypt = (ciphertext, key) => {
|
||
if(!ciphertext) return
|
||
let { commonKey } = readKey()
|
||
let bytes = CryptoJS.AES.decrypt(ciphertext, key || commonKey)
|
||
let originalText = bytes.toString(CryptoJS.enc.Utf8)
|
||
return originalText
|
||
}
|
||
|
||
// sha1 加密(不可逆)
|
||
const SHA1Encrypt = (clearText) => {
|
||
return rawCrypto.createHash('sha1').update(clearText).digest('hex')
|
||
}
|
||
|
||
// 获取UTC-x时间
|
||
const getUTCDate = (num = 8) => {
|
||
let date = new Date()
|
||
let now_utc = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(),
|
||
date.getUTCDate(), date.getUTCHours() + num,
|
||
date.getUTCMinutes(), date.getUTCSeconds())
|
||
return new Date(now_utc)
|
||
}
|
||
|
||
module.exports = {
|
||
readSSHRecord,
|
||
writeSSHRecord,
|
||
readHostList,
|
||
writeHostList,
|
||
getNetIPInfo,
|
||
throwError,
|
||
isIP,
|
||
readKey,
|
||
writeKey,
|
||
randomStr,
|
||
verifyAuth,
|
||
isProd,
|
||
RSADecrypt,
|
||
AESEncrypt,
|
||
AESDecrypt,
|
||
SHA1Encrypt,
|
||
getUTCDate
|
||
} |