hn-hlw-service/sms/client.js

91 lines
2.4 KiB
JavaScript
Raw Permalink Normal View History

2026-07-27 11:28:33 +08:00
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,
};