Files
chuanqi-qycq-web/module/server/utils.js
艾贤凌 6d4a72161f inint
2026-03-16 12:05:55 +08:00

118 lines
3.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import dayjs from 'dayjs'
import crypto from 'crypto'
// 密码加密 key与原 PHP 保持一致)
const PASSWORD_KEY = process.env.PASSWORD_KEY || 'WVImV8mIMnpY9Lrmh3yoaJ2yRLNACBfg'
/**
* 格式化时间
*/
export function time(date) {
return dayjs(date).format('YYYY-MM-DD HH:mm:ss')
}
/**
* 当前 Unix 时间戳(秒)
*/
export function unixTime() {
return Math.floor(Date.now() / 1000)
}
/**
* md5 加密(兼容 PHP md5()
*/
export function md5(str) {
return crypto.createHash('md5').update(str).digest('hex')
}
/**
* 对密码做 md5+key 加密(与原 PHP PASSWORD_KEY 逻辑一致)
*/
export function encryptPassword(password) {
return md5(password + PASSWORD_KEY)
}
/**
* 生成随机验证码
* @param {number} length 长度
* @param {'NUMBER'|'CHAR'|'ALL'} type 类型
*/
export function generateCode(length = 6, type = 'NUMBER') {
const chars = {
NUMBER: '0123456789',
CHAR: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
ALL: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
}
const pool = chars[type] || chars.NUMBER
let code = ''
for (let i = 0; i < length; i++) {
code += pool[Math.floor(Math.random() * pool.length)]
}
return code
}
/**
* 获取客户端真实 IP支持代理
*/
export function getClientIp(ctx) {
return (
ctx.request.headers['x-forwarded-for']?.split(',')[0]?.trim() ||
ctx.request.headers['x-real-ip'] ||
ctx.ip
)
}
/**
* 校验账号格式6-16位字母数字下划线
*/
export function isValidAccount(account) {
return /^[a-zA-Z0-9_]{6,16}$/.test(account)
}
/**
* 校验邮箱格式
*/
export function isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
}
/**
* 从 User-Agent 解析设备/系统/浏览器信息(与 PHP function.php 保持一致)
* @param {string} ua
* @returns {{ device: string, os: string, browse: string }}
*/
export function getDeviceInfo(ua = '') {
const uaLower = ua.toLowerCase()
// 设备类型
let device = 'pc'
if (/mobile|android|iphone|ipad|ipod|windows phone/i.test(ua)) device = 'mobile'
// 操作系统
let os = 'Other'
if (/windows nt 10/i.test(ua)) os = 'Windows 10'
else if (/windows nt 6\.3/i.test(ua)) os = 'Windows 8.1'
else if (/windows nt 6\.2/i.test(ua)) os = 'Windows 8'
else if (/windows nt 6\.1/i.test(ua)) os = 'Windows 7'
else if (/windows nt 6\.0/i.test(ua)) os = 'Windows Vista'
else if (/windows nt 5\.1/i.test(ua)) os = 'Windows XP'
else if (/windows/i.test(ua)) os = 'Windows'
else if (/android (\d+\.\d+)/i.test(ua)) os = 'Android ' + ua.match(/android (\d+\.\d+)/i)[1]
else if (/iphone os (\d+_\d+)/i.test(ua)) os = 'iOS ' + ua.match(/iphone os (\d+_\d+)/i)[1].replace('_', '.')
else if (/ipad.*os (\d+_\d+)/i.test(ua)) os = 'iPadOS ' + ua.match(/ipad.*os (\d+_\d+)/i)[1].replace('_', '.')
else if (/mac os x/i.test(ua)) os = 'macOS'
else if (/linux/i.test(ua)) os = 'Linux'
// 浏览器
let browse = 'Other'
if (/edg\//i.test(ua)) browse = 'Edge'
else if (/opr\//i.test(ua) || /opera/i.test(ua)) browse = 'Opera'
else if (/chrome\/(\d+)/i.test(ua)) browse = 'Chrome ' + ua.match(/chrome\/(\d+)/i)[1]
else if (/firefox\/(\d+)/i.test(ua)) browse = 'Firefox ' + ua.match(/firefox\/(\d+)/i)[1]
else if (/safari\/(\d+)/i.test(ua) && !/chrome/i.test(ua)) browse = 'Safari'
else if (/micromessenger/i.test(ua)) browse = '微信'
else if (/mqqbrowser/i.test(ua)) browse = 'QQ浏览器'
return { device, os, browse }
}