Автоопределение 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
+21 -1
View File
@@ -1,3 +1,6 @@
import { resolveShineServerByServerLogin } from './shine-server-resolver.js';
import { SOLANA_ENDPOINT_DEFAULT } from '../solana-programs.js';
function normalizeUrl(value) {
return String(value || '').trim();
}
@@ -103,10 +106,28 @@ function checkShineWs(url, timeoutMs = 7000) {
});
}
export async function resolveAndCheckShineServerLogin(serverLogin, solanaEndpoint) {
const resolved = await resolveShineServerByServerLogin({
serverLogin,
solanaEndpoint,
});
const available = await checkShineWs(resolved.wsUrl);
return {
status: available ? 'available' : 'unavailable',
serverLogin: resolved.serverLogin,
serverAddress: resolved.serverAddress,
httpBase: resolved.httpBase,
wsUrl: resolved.wsUrl,
};
}
export async function checkServerAvailabilityByKey(key, url) {
if (key === 'solanaServer') {
return (await checkSolanaRpc(url)) ? 'available' : 'unavailable';
}
if (key === 'shineServerLogin') {
return (await resolveAndCheckShineServerLogin(url, SOLANA_ENDPOINT_DEFAULT)).status;
}
if (key === 'shineServer') {
return (await checkShineWs(url)) ? 'available' : 'unavailable';
}
@@ -115,4 +136,3 @@ export async function checkServerAvailabilityByKey(key, url) {
}
return 'unavailable';
}
@@ -0,0 +1,81 @@
import { readShineUserPda } from './shine-user-pda-service.js';
export const DEFAULT_SHINE_SERVER_LOGIN = 'shineupme';
export const DEFAULT_SHINE_SERVER_ADDRESS = 'shineup.me';
export const DEFAULT_SHINE_SERVER_HTTP = `https://${DEFAULT_SHINE_SERVER_ADDRESS}`;
export const DEFAULT_SHINE_SERVER_WS = `wss://${DEFAULT_SHINE_SERVER_ADDRESS}/ws`;
function normalizeHostLike(value) {
const raw = String(value || '').trim();
if (!raw) return '';
try {
const withScheme = /^[a-z]+:\/\//i.test(raw) ? raw : `https://${raw}`;
const parsed = new URL(withScheme);
return String(parsed.host || '').trim().toLowerCase();
} catch {
return raw.replace(/^https?:\/\//i, '').replace(/^wss?:\/\//i, '').replace(/\/.*$/, '').trim().toLowerCase();
}
}
export function normalizeShineServerLogin(value) {
return String(value || '').trim().toLowerCase();
}
export function buildShineHttpUrlFromAddress(address) {
const host = normalizeHostLike(address);
if (!host) return DEFAULT_SHINE_SERVER_HTTP;
return `https://${host}`;
}
export function buildShineWsUrlFromAddress(address) {
const host = normalizeHostLike(address);
if (!host) return DEFAULT_SHINE_SERVER_WS;
return `wss://${host}/ws`;
}
export async function resolveShineServerByServerLogin({ serverLogin, solanaEndpoint }) {
const cleanServerLogin = normalizeShineServerLogin(serverLogin) || DEFAULT_SHINE_SERVER_LOGIN;
const parsed = await readShineUserPda({
login: cleanServerLogin,
solanaEndpoint,
});
if (!parsed?.isServer) {
throw new Error(`Логин @${cleanServerLogin} не опубликован как сервер SHiNE.`);
}
const serverAddress = normalizeHostLike(parsed?.serverAddress || '');
if (!serverAddress) {
throw new Error(`У server PDA пользователя @${cleanServerLogin} не задан server_address.`);
}
return {
serverLogin: cleanServerLogin,
serverAddress,
httpBase: buildShineHttpUrlFromAddress(serverAddress),
wsUrl: buildShineWsUrlFromAddress(serverAddress),
userPda: parsed,
};
}
export async function resolveShineServerForUserLogin({ login, solanaEndpoint, fallbackServerLogin = '' }) {
const cleanLogin = normalizeShineServerLogin(login);
if (!cleanLogin) throw new Error('Не указан логин пользователя для поиска сервера доступа.');
const parsedUser = await readShineUserPda({
login: cleanLogin,
solanaEndpoint,
});
const accessServers = Array.isArray(parsedUser?.accessServers)
? parsedUser.accessServers.map((value) => normalizeShineServerLogin(value)).filter(Boolean)
: [];
const pickedServerLogin = accessServers[0] || normalizeShineServerLogin(fallbackServerLogin);
if (!pickedServerLogin) {
throw new Error(`У пользователя @${cleanLogin} в PDA не указан access server.`);
}
const resolved = await resolveShineServerByServerLogin({
serverLogin: pickedServerLogin,
solanaEndpoint,
});
return {
...resolved,
accessServerLogin: pickedServerLogin,
userPda: parsedUser,
};
}