const axios = require("axios"); const crypto = require("crypto"); const logger = require("../utils/logger"); const { defaults } = require("./config"); const DEFAULT_TIMEOUT = defaults.timeout; function getBaseUrl(overrideUrl) { const baseUrl = overrideUrl || defaults.baseUrl; if (!baseUrl) { throw new Error("短信网关地址未配置: 请设置 CONFIG_SMS_BASE_URL 环境变量"); } return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}`; } function encodePayload(payload) { const json = JSON.stringify(payload); return Buffer.from(json, "utf8").toString("base64"); } function tryDecodeResponse(data) { if (data === null || data === undefined) return data; if (typeof data !== "string") return data; const trimmed = data.trim(); if (!trimmed) return ""; try { const decoded = Buffer.from(trimmed, "base64").toString("utf8"); const normalized = decoded.trim(); if (!normalized) return decoded; if (normalized.startsWith("{") || normalized.startsWith("[")) { return JSON.parse(decoded); } return decoded; } catch (err) { return data; } } function buildMac(parts) { const raw = parts.join(""); return crypto.createHash("md5").update(raw, "utf8").digest("hex"); } async function post(pathname, payload, options = {}) { const baseUrl = getBaseUrl(options.baseUrl); const url = new URL(pathname, baseUrl).toString(); const encodedPayload = encodePayload(payload); const timeout = options.timeout || DEFAULT_TIMEOUT; const headers = { "Content-Type": "application/json", ...(options.headers || {}), }; const sanitizedPayload = { ecName: payload.ecName, apId: payload.apId, mobiles: payload.mobiles, templateId: payload.templateId, addSerial: payload.addSerial, contentBytes: typeof payload.content === "string" ? Buffer.byteLength(payload.content, "utf8") : undefined, }; logger.info({ scope: "sms-request", url, payload: sanitizedPayload }); try { const response = await axios.post(url, encodedPayload, { timeout, headers, }); const decoded = tryDecodeResponse(response.data); logger.info({ scope: "sms-response", url, data: decoded }); return decoded; } catch (error) { logger.error({ scope: "sms-request-error", url, message: error.message, }); throw error; } } module.exports = { post, buildMac, tryDecodeResponse, };