const db = uniCloud.database(); const _ = db.command; const crypto = require('crypto'); let obj = { isValidIP(ip) { // ------------------------- IPv4 验证 ------------------------- if (ip.includes('.')) { const ipv4Regex = /^(?:(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])$/; return ipv4Regex.test(ip); // 直接通过正则匹配[1,5](@ref) } // ------------------------- IPv6 验证 ------------------------- if (ip.includes(':')) { const parts = ip.split(':'); // 处理压缩的::符号(如::1或2001:db8::1) const hasDoubleColon = ip.includes('::'); if (hasDoubleColon && ip.indexOf('::') !== ip.lastIndexOf('::')) return false; // 禁止多个::[6](@ref) // 分割后的段数应在1到8之间(允许压缩) if (parts.length < 3 || parts.length > 8) return false; // 展开压缩段(如将::替换为填充段) let expandedParts = []; let fillZeros = 8 - parts.filter(p => p !== '').length; for (let part of parts) { if (part === '') { while (fillZeros-- > 0) expandedParts.push('0'); } else { expandedParts.push(part); } } if (expandedParts.length !== 8) return false; // 验证每段是否为1-4位十六进制字符 const hexRegex = /^[0-9a-fA-F]{1,4}$/; return expandedParts.every(p => hexRegex.test(p)); // 每段必须符合规则[6,7](@ref) } return false; }, isReservedOrLocalIP(ip) { if (ip.includes('.')) { const parts = ip.split('.').map(Number); // 格式验证 if (parts.length !== 4 || parts.some(n => n < 0 || n > 255)) return false; // 1. 局域网地址(私有地址) if ( parts[0] === 10 || // 10.0.0.0/8 [1,2,4](@ref) (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) || // 172.16.0.0/12 [1,2,4](@ref) (parts[0] === 192 && parts[1] === 168) // 192.168.0.0/16 [1,2,4](@ref) ) return true; // 2. 保留地址 return ( parts[0] === 127 || (parts[0] === 169 && parts[1] === 254) || (parts[0] >= 224 && parts[0] <= 239) || ip === '0.0.0.0' || ip === '255.255.255.255' ); } // ------------------------- IPv6 处理逻辑 ------------------------- if (ip.includes(':')) { // 规范化IPv6(展开缩写) const normalized = ip.replace(/(^:|:$)/g, '::').replace(/::/g, ':0:').split(':') .map(seg => seg.padStart(4, '0')).join(':').padStart(39, '0:'); if (normalized.startsWith('fc00:') || normalized.startsWith('fd00:')) return true; // 2. 保留地址 return ( normalized === '0000:0000:0000:0000:0000:0000:0000:0001' || normalized.startsWith('fe80:') || normalized.startsWith('ff00:') || normalized.startsWith('2001:0db8:') || normalized === '0000:0000:0000:0000:0000:0000:0000:0000' ); } return false; }, async getIPAddress(ip) { if (!obj.isValidIP(ip)) return "未知"; if (obj.isReservedOrLocalIP(ip)) return "未知"; let address = ""; const res = await db.collection("ip").where({ ip: ip }) .field({ ip:true, country: true, economize: true, market: true, }) .get(); console.log(res); if (res.data.length > 0) { address = res.data.city; } let key = "IPUBZ-ZWF6I-PQ4GR-56NKD-BLZYJ-YHFIY"; let requests = "/ws/location/v1/ip?ip=" + ip + "&key="+ key+ "&output=json"; let sk = "1YTD18oU9wjLtVTVJWYupigJjuTPGvae"; const sig = crypto.createHash('md5') .update(requests+sk) .digest("hex"); console.log(sig) let url= "https://apis.map.qq.com"+ requests + "&sig=" + sig; console.log(url); const response = await uniCloud.httpclient.request(url, { method: 'GET', }); console.log(response); console.log(response.data) if (response.status === 200) { let json = JSON.parse(response.data.toString('utf8')); if (json.message==="Success") { await db.collection("ip").add({...json.result}); address = json.result.ad_info.province + " " + json.result.ad_info.city; console.log(address) return address; } } return "未知"; } } module.exports = { _before: function () { // 通用预处理器 }, async get_user_info(query) { try { let uid = query.uid; console.log(uid); if (!uid) throw new Error("uid不存在"); console.log(uid); let res = await db.collection("uni-id-users") .doc(uid) .field({ _id: 1, nickname: 1, avatar_file: 1, last_login_ip:1, register_date: 1, last_login_date:1, personal_profile: 1, gender: 1 }) .get(); console.log(res.data[0].last_login_ip) res.data[0].last_login_ip = await obj.getIPAddress(res.data[0].last_login_ip); console.log(res) return { code: 200, data: res.data[0] } } catch (e) { console.log(e); return { code: 500, msg: e.message } } } }