Spaces:
Running
Running
Download BeadQR/miniprogram/utils/security.js from Ethscriptions/cut: direct link, hf CLI and curl.
- Browser
- Download file 4.93 kB
-
https://huggingface.co/spaces/Ethscriptions/cut/resolve/main/BeadQR/miniprogram/utils/security.js
- Command line
-
hf download hf://spaces/Ethscriptions/cut/BeadQR/miniprogram/utils/security.js
-
curl -L -o security.js https://huggingface.co/spaces/Ethscriptions/cut/resolve/main/BeadQR/miniprogram/utils/security.js
4.93 kB
| // 内容安全链路(微信平台规范) | |
| // 文本:security.msgSecCheck v2(二维码内容 / 扫码导入内容 / 水印) | |
| // 图片:先按官方建议压缩到 750×1334 内、1MB 内的 JPG,再上传云存储由云函数调用 security.imgSecCheck | |
| // 约定返回:{ status: 'pass' | 'risky' | 'error' } | |
| // - risky:命中违规,必须拦截 | |
| // - error:检测服务不可用,同样拦截(禁止静默放行) | |
| // 通过结果按内容缓存,导出时无需重复请求,显著提升导出速度 | |
| const FUNCTION_NAME = 'quickstartFunctions' | |
| const MAX_W = 750 | |
| const MAX_H = 1334 | |
| const textCache = new Map() // value -> 'pass' | 'risky' | |
| const pending = new Map() // value -> Promise | |
| function callFunction(data) { | |
| return new Promise((resolve) => { | |
| if (!wx.cloud) { resolve(null); return } | |
| wx.cloud.callFunction({ | |
| name: FUNCTION_NAME, | |
| data, | |
| success: (res) => resolve((res && res.result) || null), | |
| fail: () => resolve(null), | |
| }) | |
| }) | |
| } | |
| function normalize(result) { | |
| if (!result) return 'error' | |
| if (result.risk) return 'risky' | |
| if (result.skipped) return 'error' | |
| return 'pass' | |
| } | |
| function checkText(content, scene = 2) { | |
| const value = String(content || '').trim().slice(0, 2500) | |
| if (!value) return Promise.resolve({ status: 'pass' }) | |
| const cached = textCache.get(value) | |
| if (cached) return Promise.resolve({ status: cached, cached: true }) | |
| if (pending.has(value)) return pending.get(value) | |
| const task = callFunction({ type: 'checkMessage', content: value, scene }).then((result) => { | |
| const status = normalize(result) | |
| if (status !== 'error') textCache.set(value, status) | |
| pending.delete(value) | |
| return { status } | |
| }) | |
| pending.set(value, task) | |
| return task | |
| } | |
| // 导出前一次性校验多段文本(已缓存的直接复用,未缓存的合并为一次云函数调用) | |
| function checkTexts(items) { | |
| const list = items.map((it) => ({ value: String(it.value || '').trim().slice(0, 2500), scene: it.scene || 2 })).filter((it) => it.value) | |
| const results = list.map((it) => textCache.get(it.value)) | |
| if (results.includes('risky')) return Promise.resolve({ status: 'risky' }) | |
| const missing = list.filter((it, i) => !results[i]) | |
| if (!missing.length) return Promise.resolve({ status: 'pass' }) | |
| return callFunction({ type: 'checkMessages', items: missing }).then((res) => { | |
| if (!res || !Array.isArray(res.results)) return { status: 'error' } | |
| let status = 'pass' | |
| res.results.forEach((r, i) => { | |
| const s = normalize(r) | |
| if (s !== 'error') textCache.set(missing[i].value, s) | |
| if (s === 'risky') status = 'risky' | |
| else if (s === 'error' && status === 'pass') status = 'error' | |
| }) | |
| return { status } | |
| }) | |
| } | |
| function isTextPassed(value) { | |
| const v = String(value || '').trim() | |
| return !v || textCache.get(v) === 'pass' | |
| } | |
| // 按官方尺寸建议压缩(同时加快上传) | |
| function compressForCheck(canvas, image, path) { | |
| return new Promise((resolve) => { | |
| if (!canvas || !image) { resolve(path); return } | |
| try { | |
| const w0 = image.width || image._w | |
| const h0 = image.height || image._h | |
| const scale = Math.min(1, MAX_W / w0, MAX_H / h0) | |
| canvas.width = Math.max(1, Math.round(w0 * scale)) | |
| canvas.height = Math.max(1, Math.round(h0 * scale)) | |
| const ctx = canvas.getContext('2d') | |
| ctx.fillStyle = '#ffffff' | |
| ctx.fillRect(0, 0, canvas.width, canvas.height) | |
| ctx.drawImage(image, 0, 0, canvas.width, canvas.height) | |
| wx.canvasToTempFilePath({ | |
| canvas, | |
| fileType: 'jpg', | |
| quality: 0.82, | |
| destWidth: canvas.width, | |
| destHeight: canvas.height, | |
| success: (res) => resolve(res.tempFilePath), | |
| fail: () => resolve(path), | |
| }) | |
| } catch (e) { | |
| resolve(path) | |
| } | |
| }) | |
| } | |
| function upload(filePath) { | |
| return new Promise((resolve) => { | |
| if (!wx.cloud) { resolve(''); return } | |
| wx.cloud.uploadFile({ | |
| cloudPath: `sec-check/${Date.now()}-${Math.random().toString(36).slice(2, 10)}.jpg`, | |
| filePath, | |
| success: (res) => resolve((res && res.fileID) || ''), | |
| fail: () => resolve(''), | |
| }) | |
| }) | |
| } | |
| // image:已加载的 CanvasImage(用于压缩);path:本地临时路径 | |
| function checkImage(path, image, canvas) { | |
| if (!path) return Promise.resolve({ status: 'error' }) | |
| return compressForCheck(canvas, image, path) | |
| .then(upload) | |
| .then((fileID) => { | |
| if (!fileID) return { status: 'error' } | |
| // 云函数检测完毕后会立即删除该临时文件 | |
| return callFunction({ type: 'checkImage', fileID }).then((result) => ({ status: normalize(result) })) | |
| }) | |
| .catch(() => ({ status: 'error' })) | |
| } | |
| const MESSAGES = { | |
| risky: '内容含违规信息,请修改后再试', | |
| error: '安全检测暂不可用,请检查网络后重试', | |
| } | |
| module.exports = { checkText, checkTexts, isTextPassed, checkImage, MESSAGES } | |