SHA256
Синхронизировать изменения проекта
This commit is contained in:
@@ -13,10 +13,39 @@ import {
|
||||
PASSWORD_MAX_LENGTH,
|
||||
PASSWORD_WORDS_COUNT,
|
||||
} from '../services/password-words.js';
|
||||
import { sha256Text } from '../services/crypto-utils.js';
|
||||
import { openRegistrationFaq } from './registration-faq-view.js';
|
||||
import { defaultServerHttp } from '../deploy-config.js';
|
||||
|
||||
export const pageMeta = { id: 'register-view', title: 'Зарегистрироваться', showAppChrome: false };
|
||||
// ВРЕМЕННАЯ UI-ЗАГЛУШКА:
|
||||
// пока полноценная логика продажи/выдачи коротких имён не внедрена on-chain,
|
||||
// UI пускает логины 5..7 символов только по временному коду.
|
||||
const TEMP_MIN_LOGIN_WITHOUT_PROMO = 8;
|
||||
const TEMP_ABSOLUTE_MIN_LOGIN_LEN = 5;
|
||||
const TEMP_PROMO_HASH_HEX = 'a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3';
|
||||
|
||||
function bytesToHex(bytes) {
|
||||
return Array.from(bytes || [], (b) => b.toString(16).padStart(2, '0')).join('');
|
||||
}
|
||||
|
||||
async function isTemporaryPromoAccepted(value) {
|
||||
const digest = await sha256Text(String(value || ''));
|
||||
return bytesToHex(digest) === TEMP_PROMO_HASH_HEX;
|
||||
}
|
||||
|
||||
function normalizeLoginForTemporaryUiGuard(login) {
|
||||
const source = String(login || '').trim();
|
||||
if (!source || source.length > 20) return null;
|
||||
let normalized = '';
|
||||
for (const ch of source) {
|
||||
if (ch === '_') continue;
|
||||
if (!/[0-9A-Za-z]/.test(ch)) return null;
|
||||
normalized += ch.toLowerCase();
|
||||
}
|
||||
if (!normalized || normalized.length > 20) return null;
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function createWordsLayout({ words, onInput }) {
|
||||
const section = document.createElement('div');
|
||||
@@ -148,7 +177,7 @@ export function render({ navigate }) {
|
||||
|
||||
const promoHint = document.createElement('p');
|
||||
promoHint.className = 'meta-muted';
|
||||
promoHint.textContent = 'Если промокод включён, обычная проверка premium/trademark логина пропускается.';
|
||||
promoHint.textContent = 'Временный режим: логины длиной 5-7 символов доступны только по специальному коду. Любое другое значение считается неверным промокодом.';
|
||||
|
||||
promoField.append(promoFieldLabel, promoInput, promoHint);
|
||||
|
||||
@@ -247,26 +276,58 @@ export function render({ navigate }) {
|
||||
|
||||
async function runAvailabilityCheck() {
|
||||
const login = loginInput.value.trim();
|
||||
const promoCode = String(promoInput.value || '').trim();
|
||||
if (!login) {
|
||||
statusText.textContent = 'Введите логин';
|
||||
formError.style.display = 'none';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (usePromoCode) {
|
||||
lastCheckedLogin = login;
|
||||
lastCheckedFree = true;
|
||||
lastCheckedClassName = 'promo';
|
||||
statusText.textContent = 'Промокод включён: обычная проверка premium/trademark логина пропущена';
|
||||
statusText.className = 'status-line is-available';
|
||||
const normalizedLogin = normalizeLoginForTemporaryUiGuard(login);
|
||||
if (!normalizedLogin) {
|
||||
statusText.textContent = 'Логин содержит недопустимые символы или имеет неверную длину ❌';
|
||||
statusText.className = 'status-line is-unavailable';
|
||||
formError.style.display = 'none';
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (normalizedLogin.length < TEMP_ABSOLUTE_MIN_LOGIN_LEN) {
|
||||
statusText.textContent = `Логин должен быть не короче ${TEMP_ABSOLUTE_MIN_LOGIN_LEN} символов ❌`;
|
||||
statusText.className = 'status-line is-unavailable';
|
||||
formError.style.display = 'none';
|
||||
return false;
|
||||
}
|
||||
|
||||
const wantsShortLogin = normalizedLogin.length < TEMP_MIN_LOGIN_WITHOUT_PROMO;
|
||||
let promoAccepted = false;
|
||||
if (usePromoCode) {
|
||||
if (!promoCode) {
|
||||
statusText.textContent = 'Введите промокод ❌';
|
||||
statusText.className = 'status-line is-unavailable';
|
||||
formError.style.display = 'none';
|
||||
return false;
|
||||
}
|
||||
promoAccepted = await isTemporaryPromoAccepted(promoCode);
|
||||
if (!promoAccepted) {
|
||||
statusText.textContent = 'Неверный промокод ❌';
|
||||
statusText.className = 'status-line is-unavailable';
|
||||
formError.style.display = 'none';
|
||||
return false;
|
||||
}
|
||||
} else if (wantsShortLogin) {
|
||||
statusText.textContent = `Логины короче ${TEMP_MIN_LOGIN_WITHOUT_PROMO} символов временно доступны только по специальному коду ❌`;
|
||||
statusText.className = 'status-line is-unavailable';
|
||||
formError.style.display = 'none';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (login === lastCheckedLogin) {
|
||||
if (!lastCheckedFree) {
|
||||
statusText.textContent = 'Логин уже занят ❌';
|
||||
statusText.className = 'status-line is-unavailable';
|
||||
} else if (lastCheckedClassName === 'promo') {
|
||||
statusText.textContent = 'Логин свободен ✅ Временный код принят';
|
||||
statusText.className = 'status-line is-available';
|
||||
} else if (lastCheckedClassName === 'free') {
|
||||
statusText.textContent = 'Логин свободен ✅';
|
||||
statusText.className = 'status-line is-available';
|
||||
@@ -281,7 +342,7 @@ export function render({ navigate }) {
|
||||
statusText.className = 'status-line is-unavailable';
|
||||
}
|
||||
formError.style.display = 'none';
|
||||
return lastCheckedFree && lastCheckedClassName === 'free';
|
||||
return lastCheckedFree && (lastCheckedClassName === 'free' || lastCheckedClassName === 'promo');
|
||||
}
|
||||
|
||||
checkButton.disabled = true;
|
||||
@@ -295,15 +356,19 @@ export function render({ navigate }) {
|
||||
let className = '';
|
||||
let precheckWarning = '';
|
||||
if (isFree) {
|
||||
try {
|
||||
const precheck = await precheckLoginClassOnSolana({
|
||||
login,
|
||||
solanaEndpoint: state.entrySettings.solanaServer,
|
||||
});
|
||||
className = precheck.className;
|
||||
} catch (precheckError) {
|
||||
className = 'free';
|
||||
precheckWarning = formatSolanaErrorDetails(precheckError);
|
||||
if (promoAccepted) {
|
||||
className = 'promo';
|
||||
} else {
|
||||
try {
|
||||
const precheck = await precheckLoginClassOnSolana({
|
||||
login,
|
||||
solanaEndpoint: state.entrySettings.solanaServer,
|
||||
});
|
||||
className = precheck.className;
|
||||
} catch (precheckError) {
|
||||
className = 'free';
|
||||
precheckWarning = formatSolanaErrorDetails(precheckError);
|
||||
}
|
||||
}
|
||||
}
|
||||
lastCheckedLogin = login;
|
||||
@@ -312,6 +377,9 @@ export function render({ navigate }) {
|
||||
if (!isFree) {
|
||||
statusText.textContent = 'Логин уже занят ❌';
|
||||
statusText.className = 'status-line is-unavailable';
|
||||
} else if (className === 'promo') {
|
||||
statusText.textContent = 'Логин свободен ✅ Временный код принят';
|
||||
statusText.className = 'status-line is-available';
|
||||
} else if (className === 'free') {
|
||||
statusText.textContent = precheckWarning
|
||||
? `Логин свободен ✅ (предпроверка Solana недоступна: ${precheckWarning})`
|
||||
@@ -328,7 +396,7 @@ export function render({ navigate }) {
|
||||
statusText.className = 'status-line is-unavailable';
|
||||
}
|
||||
formError.style.display = 'none';
|
||||
return isFree && className === 'free';
|
||||
return isFree && (className === 'free' || className === 'promo');
|
||||
} catch (error) {
|
||||
const base = toUserMessage(error, 'Не удалось проверить логин');
|
||||
const details = formatSolanaErrorDetails(error);
|
||||
@@ -379,7 +447,7 @@ export function render({ navigate }) {
|
||||
updatePromoVisibility();
|
||||
syncDraftState();
|
||||
if (usePromoCode) {
|
||||
statusText.textContent = 'Промокод включён: проверка логина будет пропущена';
|
||||
statusText.textContent = 'Временный код включён: для логинов 5-7 символов будет локальная проверка';
|
||||
statusText.className = 'status-line is-available';
|
||||
} else {
|
||||
statusText.textContent = 'Проверка логина: не выполнена';
|
||||
@@ -388,6 +456,9 @@ export function render({ navigate }) {
|
||||
});
|
||||
|
||||
promoInput.addEventListener('input', () => {
|
||||
lastCheckedLogin = '';
|
||||
lastCheckedFree = false;
|
||||
lastCheckedClassName = '';
|
||||
syncDraftState();
|
||||
});
|
||||
|
||||
@@ -395,7 +466,7 @@ export function render({ navigate }) {
|
||||
formError.style.display = 'none';
|
||||
const promoCode = String(promoInput.value || '').trim();
|
||||
if (usePromoCode && !promoCode) {
|
||||
formError.textContent = 'Если включён промокод, поле промокода должно быть заполнено.';
|
||||
formError.textContent = 'Если включён временный код, поле должно быть заполнено.';
|
||||
formError.style.display = '';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ export function render({ navigate }) {
|
||||
keyBundle,
|
||||
solanaEndpoint: state.entrySettings.solanaServer,
|
||||
accessServers: [state.entrySettings.shineServerLogin || defaultServerLogin],
|
||||
promoCode: state.registrationDraft.usePromoCode ? state.registrationDraft.promoCode : '',
|
||||
promoCode: '',
|
||||
});
|
||||
} catch (solanaError) {
|
||||
const solanaMsg = formatSolanaErrorDetails(solanaError);
|
||||
|
||||
Reference in New Issue
Block a user