Автоопределение SHiNE-сервера по логину через PDA

This commit is contained in:
AidarKC
2026-06-16 16:32:33 +04:00
parent 3efa8bb7ee
commit f8a76bcd7f
19 changed files with 17426 additions and 48 deletions
+3 -3
View File
@@ -173,7 +173,7 @@ export function render({ navigate }) {
let cleanupEvent = () => {};
let disposed = false;
let settingsBusy = false;
let pairingPasswordConfigured = loadLocalPairingPasswordState(state.session.login, state.entrySettings.shineServer);
let pairingPasswordConfigured = loadLocalPairingPasswordState(state.session.login, state.entrySettings.shineServerLogin || state.entrySettings.shineServerHttp);
let dialogMode = '';
screen.append(
@@ -357,7 +357,7 @@ export function render({ navigate }) {
ttlSeconds: 180,
}));
pairingPasswordConfigured = false;
saveLocalPairingPasswordState(state.session.login, state.entrySettings.shineServer, false);
saveLocalPairingPasswordState(state.session.login, state.entrySettings.shineServerLogin || state.entrySettings.shineServerHttp, false);
setAuthInfo(`Подключение по коду без дополнительного пароля включено. TTL: ${payload?.ttlSeconds || 180} сек.`);
setStatus(status, 'Дополнительный пароль убран. Подключение по коду теперь работает без него.', 'info');
};
@@ -598,7 +598,7 @@ export function render({ navigate }) {
ttlSeconds: 180,
}));
pairingPasswordConfigured = true;
saveLocalPairingPasswordState(state.session.login, state.entrySettings.shineServer, true);
saveLocalPairingPasswordState(state.session.login, state.entrySettings.shineServerLogin || state.entrySettings.shineServerHttp, true);
closePasswordDialog();
renderSettingsCard();
setAuthInfo(`Подключение по коду включено с дополнительным паролем. TTL: ${payload?.ttlSeconds || 180} сек.`);
+28 -12
View File
@@ -1,12 +1,12 @@
import { renderHeader } from '../components/header.js';
import { saveEntrySettings, state } from '../state.js';
import { checkServerAvailabilityByKey } from '../services/server-health-service.js';
import { checkServerAvailabilityByKey, resolveAndCheckShineServerLogin } from '../services/server-health-service.js';
export const pageMeta = { id: 'entry-settings-view', title: 'Настройки входа', showAppChrome: false };
const SERVER_FIELDS = [
{ key: 'solanaServer', label: 'Адрес Solana сервера' },
{ key: 'shineServer', label: 'Адрес сервера Сияние' },
{ key: 'shineServerLogin', label: 'Логин сервера Сияние' },
{ key: 'arweaveServer', label: 'Адрес сервера Arweave' },
];
@@ -17,7 +17,8 @@ export function render({ navigate }) {
const draft = {
language: state.entrySettings.language,
solanaServer: state.entrySettings.solanaServer,
shineServer: state.entrySettings.shineServer,
shineServerLogin: state.entrySettings.shineServerLogin,
shineServerHttp: state.entrySettings.shineServerHttp,
arweaveServer: state.entrySettings.arweaveServer,
statuses: { ...state.entrySettings.statuses },
};
@@ -69,13 +70,15 @@ export function render({ navigate }) {
const status = document.createElement('span');
status.className = 'status-line';
const applyStatus = (value) => {
const applyStatus = (value, exactAddress = '') => {
draft.statuses[field.key] = value;
checkButton.classList.remove('is-available', 'is-unavailable');
status.classList.remove('is-available', 'is-unavailable');
if (value === 'available') {
status.textContent = 'Доступен';
status.textContent = field.key === 'shineServerLogin' && exactAddress
? `Доступен: ${exactAddress}`
: 'Доступен';
checkButton.classList.add('is-available');
status.classList.add('is-available');
} else if (value === 'unavailable') {
@@ -83,7 +86,9 @@ export function render({ navigate }) {
checkButton.classList.add('is-unavailable');
status.classList.add('is-unavailable');
} else {
status.textContent = 'Статус не проверен';
status.textContent = field.key === 'shineServerLogin' && draft.shineServerHttp
? `Текущий адрес: ${draft.shineServerHttp}`
: 'Статус не проверен';
}
};
@@ -92,8 +97,15 @@ export function render({ navigate }) {
checkButton.disabled = true;
checkButton.textContent = 'Проверка...';
try {
const next = await checkServerAvailabilityByKey(field.key, input.value);
applyStatus(next);
if (field.key === 'shineServerLogin') {
const resolved = await resolveAndCheckShineServerLogin(input.value, draft.solanaServer);
draft.shineServerHttp = resolved.httpBase;
draft.shineServer = resolved.wsUrl;
applyStatus(resolved.status, resolved.httpBase);
} else {
const next = await checkServerAvailabilityByKey(field.key, input.value);
applyStatus(next);
}
} finally {
checkButton.disabled = false;
checkButton.textContent = 'Проверить';
@@ -148,9 +160,13 @@ export function render({ navigate }) {
saveButton.className = 'primary-btn';
saveButton.type = 'button';
saveButton.textContent = 'Сохранить';
saveButton.addEventListener('click', () => {
saveEntrySettings(draft);
navigate('start-view');
saveButton.addEventListener('click', async () => {
try {
await saveEntrySettings(draft);
navigate('start-view');
} catch (error) {
window.alert(error?.message || 'Не удалось сохранить настройки входа.');
}
});
actions.append(serverUiButton, cancelButton, saveButton);
@@ -161,7 +177,7 @@ export function render({ navigate }) {
help.textContent = '?';
help.addEventListener('click', () => {
window.alert(
'Текст для разработчиков: после ввода адреса любого сервера автопроверка запускается по кнопке "Проверить", после перехода в другое поле или если подождать больше 3 секунд. Зелёный статус означает доступность, красный — недоступность.',
'Текст для разработчиков: для SHiNE вводится логин серверного аккаунта. Клиент читает его PDA, берёт server_address, показывает точный https-адрес и проверяет доступность WS-канала автоматически.',
);
});
+28 -12
View File
@@ -1,12 +1,12 @@
import { renderHeader } from '../components/header.js';
import { saveEntrySettings, state } from '../state.js';
import { checkServerAvailabilityByKey } from '../services/server-health-service.js';
import { checkServerAvailabilityByKey, resolveAndCheckShineServerLogin } from '../services/server-health-service.js';
export const pageMeta = { id: 'server-settings-view', title: 'Настройки серверов' };
const SERVER_FIELDS = [
{ key: 'solanaServer', label: 'Адрес Solana сервера' },
{ key: 'shineServer', label: 'Адрес сервера Сияние' },
{ key: 'shineServerLogin', label: 'Логин сервера Сияние' },
{ key: 'arweaveServer', label: 'Адрес сервера Arweave' },
];
@@ -17,7 +17,8 @@ export function render({ navigate }) {
const draft = {
language: state.entrySettings.language,
solanaServer: state.entrySettings.solanaServer,
shineServer: state.entrySettings.shineServer,
shineServerLogin: state.entrySettings.shineServerLogin,
shineServerHttp: state.entrySettings.shineServerHttp,
arweaveServer: state.entrySettings.arweaveServer,
callPreflightTimeoutMs: Number(state.entrySettings.callPreflightTimeoutMs || 6000),
statuses: { ...state.entrySettings.statuses },
@@ -52,13 +53,15 @@ export function render({ navigate }) {
const status = document.createElement('span');
status.className = 'status-line';
const applyStatus = (value) => {
const applyStatus = (value, exactAddress = '') => {
draft.statuses[field.key] = value;
checkButton.classList.remove('is-available', 'is-unavailable');
status.classList.remove('is-available', 'is-unavailable');
if (value === 'available') {
status.textContent = 'Доступен';
status.textContent = field.key === 'shineServerLogin' && exactAddress
? `Доступен: ${exactAddress}`
: 'Доступен';
checkButton.classList.add('is-available');
status.classList.add('is-available');
} else if (value === 'unavailable') {
@@ -66,7 +69,9 @@ export function render({ navigate }) {
checkButton.classList.add('is-unavailable');
status.classList.add('is-unavailable');
} else {
status.textContent = 'Статус не проверен';
status.textContent = field.key === 'shineServerLogin' && draft.shineServerHttp
? `Текущий адрес: ${draft.shineServerHttp}`
: 'Статус не проверен';
}
};
@@ -75,8 +80,15 @@ export function render({ navigate }) {
checkButton.disabled = true;
checkButton.textContent = 'Проверка...';
try {
const next = await checkServerAvailabilityByKey(field.key, input.value);
applyStatus(next);
if (field.key === 'shineServerLogin') {
const resolved = await resolveAndCheckShineServerLogin(input.value, draft.solanaServer);
draft.shineServerHttp = resolved.httpBase;
draft.shineServer = resolved.wsUrl;
applyStatus(resolved.status, resolved.httpBase);
} else {
const next = await checkServerAvailabilityByKey(field.key, input.value);
applyStatus(next);
}
} finally {
checkButton.disabled = false;
checkButton.textContent = 'Проверить';
@@ -149,9 +161,13 @@ export function render({ navigate }) {
saveButton.className = 'primary-btn';
saveButton.type = 'button';
saveButton.textContent = 'Сохранить';
saveButton.addEventListener('click', () => {
saveEntrySettings(draft);
navigate('settings-view');
saveButton.addEventListener('click', async () => {
try {
await saveEntrySettings(draft);
navigate('settings-view');
} catch (error) {
window.alert(error?.message || 'Не удалось сохранить настройки серверов.');
}
});
actions.append(cancelButton, saveButton);
@@ -162,7 +178,7 @@ export function render({ navigate }) {
help.textContent = '?';
help.addEventListener('click', () => {
window.alert(
'Текст для разработчиков: после ввода адреса любого сервера автопроверка запускается по кнопке "Проверить", после перехода в другое поле или если подождать больше 3 секунд. Зелёный статус означает доступность, красный — недоступность.',
'Текст для разработчиков: для SHiNE вводится логин серверного аккаунта. Клиент читает его PDA, берёт server_address, показывает точный https-адрес и проверяет доступность WS-канала автоматически.',
);
});