Реализована интеграция с СМС провайдером
This commit is contained in:
@@ -20,5 +20,6 @@ verifyOtp=Подтвердить
|
||||
mockMode=Тестовый режим отправки кода
|
||||
phoneInvalid=Проверьте формат номера телефона.
|
||||
otpInvalid=Код неверен, истёк или уже использован.
|
||||
otpCooldown=Повторно отправить СМС можно после обнуления таймера.
|
||||
otpLimited=Слишком много попыток. Повторите позже.
|
||||
otpUnavailable=Сервис подтверждения временно недоступен. Повторите позже.
|
||||
|
||||
@@ -16,8 +16,15 @@
|
||||
|
||||
<form id="kc-otp-form" action="${url.loginAction}" method="post">
|
||||
<input id="otp" name="otp" type="hidden" value=""/>
|
||||
<div id="han-otp-inputs" class="han-otp-inputs <#if message?has_content>han-shake</#if>">
|
||||
<#list 0..5 as index>
|
||||
<input type="hidden" name="han_device_id" class="han-device-id" value="${hanDeviceId!""}"/>
|
||||
<input type="hidden" name="han_fingerprint" class="han-fingerprint" value="${hanFingerprint!""}"/>
|
||||
<input type="hidden" name="han_platform" value="${hanPlatform!"web"}"/>
|
||||
<input type="hidden" name="han_os_name" class="han-os-name" value="${hanOsName!""}"/>
|
||||
<input type="hidden" name="han_os_version" class="han-os-version" value="${hanOsVersion!""}"/>
|
||||
<input type="hidden" name="han_app_version" class="han-app-version" value="${hanAppVersion!""}"/>
|
||||
<div id="han-otp-inputs" class="han-otp-inputs <#if message?has_content>han-shake</#if>"
|
||||
style="grid-template-columns: repeat(${otpCodeLength!6}, minmax(0, 1fr));">
|
||||
<#list 0..((otpCodeLength!6) - 1) as index>
|
||||
<input class="han-otp-digit" type="text" inputmode="numeric" maxlength="1"
|
||||
aria-label="${msg("otpDigit", index + 1)}"
|
||||
<#if index == 0>autocomplete="one-time-code" autofocus</#if>
|
||||
@@ -33,8 +40,10 @@
|
||||
</#if>
|
||||
|
||||
<div class="han-resend">
|
||||
<p id="han-resend-countdown">${msg("otpResendCountdown")} <strong>0:59</strong></p>
|
||||
<button id="han-resend-button" type="button" hidden onclick="window.history.back()">
|
||||
<p id="han-resend-countdown" data-expires-at="${(otpExpiresAt!0)?c}">
|
||||
${msg("otpResendCountdown")} <strong>—</strong>
|
||||
</p>
|
||||
<button id="han-resend-button" type="submit" name="otp_action" value="resend" hidden>
|
||||
<span aria-hidden="true">↻</span>
|
||||
<span>${msg("otpResend")}</span>
|
||||
</button>
|
||||
@@ -45,6 +54,6 @@
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<script src="${url.resourcesPath}/js/han-login.js?v=3"></script>
|
||||
<script src="${url.resourcesPath}/js/han-login.js?v=4"></script>
|
||||
</#if>
|
||||
</@layout.registrationLayout>
|
||||
|
||||
@@ -17,6 +17,12 @@
|
||||
</div>
|
||||
|
||||
<form id="kc-phone-form" action="${url.loginAction}" method="post">
|
||||
<input type="hidden" name="han_device_id" class="han-device-id" value="${hanDeviceId!""}"/>
|
||||
<input type="hidden" name="han_fingerprint" class="han-fingerprint" value="${hanFingerprint!""}"/>
|
||||
<input type="hidden" name="han_platform" value="${hanPlatform!"web"}"/>
|
||||
<input type="hidden" name="han_os_name" class="han-os-name" value="${hanOsName!""}"/>
|
||||
<input type="hidden" name="han_os_version" class="han-os-version" value="${hanOsVersion!""}"/>
|
||||
<input type="hidden" name="han_app_version" class="han-app-version" value="${hanAppVersion!""}"/>
|
||||
<div class="han-field">
|
||||
<label for="phone">${msg("phoneLabel")}</label>
|
||||
<input id="phone" name="phone" type="tel" inputmode="numeric" autocomplete="tel"
|
||||
@@ -46,6 +52,6 @@
|
||||
<span>${msg("privacyPolicy")}</span>
|
||||
</p>
|
||||
</div>
|
||||
<script src="${url.resourcesPath}/js/han-login.js?v=3"></script>
|
||||
<script src="${url.resourcesPath}/js/han-login.js?v=4"></script>
|
||||
</#if>
|
||||
</@layout.registrationLayout>
|
||||
|
||||
@@ -1,4 +1,30 @@
|
||||
(function () {
|
||||
function randomId() {
|
||||
if (window.crypto && window.crypto.randomUUID) return window.crypto.randomUUID();
|
||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (char) {
|
||||
var value = Math.random() * 16 | 0;
|
||||
return (char === "x" ? value : (value & 3 | 8)).toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
function initDeviceMetadata() {
|
||||
var deviceId = window.localStorage.getItem("han_device_id") || randomId();
|
||||
var fingerprint = window.localStorage.getItem("han_fingerprint") || randomId();
|
||||
window.localStorage.setItem("han_device_id", deviceId);
|
||||
window.localStorage.setItem("han_fingerprint", fingerprint);
|
||||
document.querySelectorAll(".han-device-id").forEach(function (input) {
|
||||
if (!input.value) input.value = deviceId;
|
||||
});
|
||||
document.querySelectorAll(".han-fingerprint").forEach(function (input) {
|
||||
if (!input.value) input.value = fingerprint;
|
||||
});
|
||||
document.querySelectorAll(".han-os-name").forEach(function (input) {
|
||||
if (!input.value) {
|
||||
input.value = (navigator.userAgentData && navigator.userAgentData.platform) || navigator.platform || "";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initPhoneForm() {
|
||||
var input = document.getElementById("phone");
|
||||
var submit = document.getElementById("han-phone-submit");
|
||||
@@ -81,23 +107,31 @@
|
||||
|
||||
syncOtp();
|
||||
|
||||
var seconds = 59;
|
||||
var countdown = document.getElementById("han-resend-countdown");
|
||||
var countdownValue = countdown && countdown.querySelector("strong");
|
||||
var resend = document.getElementById("han-resend-button");
|
||||
if (!countdown || !countdownValue || !resend) return;
|
||||
|
||||
var timer = window.setInterval(function () {
|
||||
seconds -= 1;
|
||||
countdownValue.textContent = "0:" + String(seconds).padStart(2, "0");
|
||||
var expiresAt = Number(countdown.getAttribute("data-expires-at"));
|
||||
if (!Number.isFinite(expiresAt)) expiresAt = Date.now();
|
||||
var timer;
|
||||
function updateCountdown() {
|
||||
var seconds = Math.max(0, Math.ceil((expiresAt - Date.now()) / 1000));
|
||||
countdownValue.textContent = Math.floor(seconds / 60) + ":" + String(seconds % 60).padStart(2, "0");
|
||||
if (seconds <= 0) {
|
||||
window.clearInterval(timer);
|
||||
if (timer) window.clearInterval(timer);
|
||||
countdown.hidden = true;
|
||||
resend.hidden = false;
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
updateCountdown();
|
||||
if (expiresAt > Date.now()) timer = window.setInterval(updateCountdown, 1000);
|
||||
resend.addEventListener("click", function () {
|
||||
window.setTimeout(function () { resend.disabled = true; }, 0);
|
||||
});
|
||||
}
|
||||
|
||||
initDeviceMetadata();
|
||||
initPhoneForm();
|
||||
initOtpForm();
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user