Files
jiapuapp/utils/auth-sms-cooldown.js
T
2026-07-27 06:50:23 +08:00

67 lines
1.8 KiB
JavaScript

const DEFAULT_COOLDOWN_SECONDS = 60;
const sceneExpiresAt = new Map();
const requireFunction = (value, name) => {
if (typeof value !== "function") {
throw new TypeError(`${name} must be a function`);
}
return value;
};
export const createAuthSmsCooldown = ({
operationCode,
onChange,
now = Date.now,
setIntervalFn = setInterval,
clearIntervalFn = clearInterval,
}) => {
if (typeof operationCode !== "string" || !operationCode.trim()) {
throw new TypeError("operationCode must be a non-empty string");
}
const normalizedOperationCode = operationCode.trim();
const publish = requireFunction(onChange, "onChange");
const readNow = requireFunction(now, "now");
const scheduleInterval = requireFunction(setIntervalFn, "setIntervalFn");
const cancelInterval = requireFunction(clearIntervalFn, "clearIntervalFn");
let timer = null;
const stopTimer = () => {
if (timer === null) return;
cancelInterval(timer);
timer = null;
};
const sync = () => {
const expiresAt = sceneExpiresAt.get(normalizedOperationCode) || 0;
const remaining = Math.max(
0,
Math.ceil((expiresAt - Number(readNow())) / 1000),
);
if (remaining === 0) {
sceneExpiresAt.delete(normalizedOperationCode);
stopTimer();
} else if (timer === null) {
timer = scheduleInterval(sync, 1000);
}
publish(remaining);
return remaining;
};
const start = (seconds = DEFAULT_COOLDOWN_SECONDS) => {
if (!Number.isInteger(seconds) || seconds < 1 || seconds > 300) {
throw new TypeError("cooldown seconds must be an integer from 1 to 300");
}
sceneExpiresAt.set(
normalizedOperationCode,
Number(readNow()) + seconds * 1000,
);
return sync();
};
return Object.freeze({
start,
sync,
dispose: stopTimer,
});
};