Автоопределение SHiNE-сервера по логину через PDA
This commit is contained in:
parent
3efa8bb7ee
commit
f8a76bcd7f
@ -0,0 +1,16 @@
|
||||
# Автоопределение SHiNE-сервера по PDA
|
||||
|
||||
- краткое описание:
|
||||
в основном UI и в `SHiNE-browser-plugin-wallet` ручной ввод адреса SHiNE-сервера заменён на ввод логина серверного аккаунта. Клиент читает `server_address` из PDA сервера и сам строит `https://...` и `wss://...`.
|
||||
|
||||
- что проверять:
|
||||
1. В `shine-UI` на экранах настроек входа и серверов в поле SHiNE вводится логин `shineupme`, а статус показывает точный адрес `https://shineup.me`.
|
||||
2. После сохранения настроек обычный вход и login через другое устройство продолжают работать.
|
||||
3. В `SHiNE-browser-plugin-wallet` поле сервера принимает логин `shineupme`, а popup показывает `Текущий адрес: https://shineup.me`.
|
||||
4. В plugin pairing и повторное восстановление wallet-session продолжают работать через авторазрешённый адрес.
|
||||
|
||||
- ожидаемый результат:
|
||||
пользователь больше не вводит вручную `wss://...`; внутренний WS-адрес строится автоматически из PDA серверного аккаунта.
|
||||
|
||||
- статус:
|
||||
pending
|
||||
@ -11,6 +11,7 @@ Chrome-compatible Manifest V3 plugin for SHiNE wallet-session login.
|
||||
- сохранить `sessionPriv/sessionKey/sessionId` в локальном хранилище plugin;
|
||||
- восстанавливать session через `SessionChallenge -> SessionLogin`;
|
||||
- держать wallet-state в `background service worker`, а popup использовать как UI.
|
||||
- принимать не адрес сервера, а логин серверного аккаунта SHiNE и находить точный `https://...` / `wss://...` адрес через его PDA.
|
||||
|
||||
## Как загрузить локально
|
||||
|
||||
@ -35,4 +36,5 @@ Chrome-compatible Manifest V3 plugin for SHiNE wallet-session login.
|
||||
```bash
|
||||
npm install
|
||||
npx esbuild js/lib/vendor/noble-ed25519-entry.js --bundle --format=esm --platform=browser --outfile=js/lib/vendor/noble-ed25519-bundle.js
|
||||
npx esbuild js/lib/vendor/solana-publickey-entry.js --bundle --format=esm --platform=browser --outfile=js/lib/vendor/solana-publickey-bundle.js
|
||||
```
|
||||
|
||||
@ -1,10 +1,18 @@
|
||||
import { createRequesterPairingMaterial, decryptPairingPayloadFromEnvelope, deriveEspPairingPasswordHash } from './js/lib/device-pairing.js';
|
||||
import { loadPluginSettings, loadSessionMaterial, savePluginSettings, saveSessionMaterial, clearSessionMaterial } from './js/lib/session-store.js';
|
||||
import { ShineApiClient } from './js/lib/shine-api.js';
|
||||
import {
|
||||
DEFAULT_SHINE_SERVER_LOGIN,
|
||||
buildHttpBase,
|
||||
normalizeServerLogin,
|
||||
resolveShineServerByServerLogin,
|
||||
} from './js/lib/shine-server-resolver.js';
|
||||
|
||||
const state = {
|
||||
api: null,
|
||||
settings: {
|
||||
serverLogin: DEFAULT_SHINE_SERVER_LOGIN,
|
||||
serverHttp: buildHttpBase('shineup.me'),
|
||||
serverUrl: 'wss://shineup.me/ws',
|
||||
login: '',
|
||||
},
|
||||
@ -50,19 +58,36 @@ function ensureApi(serverUrl = state.settings.serverUrl) {
|
||||
return state.api;
|
||||
}
|
||||
|
||||
async function resolveSettingsServer(nextSettings = {}) {
|
||||
const serverLogin = normalizeServerLogin(nextSettings?.serverLogin || state.settings.serverLogin || DEFAULT_SHINE_SERVER_LOGIN)
|
||||
|| DEFAULT_SHINE_SERVER_LOGIN;
|
||||
const resolved = await resolveShineServerByServerLogin(serverLogin);
|
||||
return {
|
||||
serverLogin: resolved.serverLogin,
|
||||
serverHttp: resolved.serverHttp,
|
||||
serverUrl: resolved.serverUrl,
|
||||
};
|
||||
}
|
||||
|
||||
async function loadStateFromStorage() {
|
||||
const settings = await loadPluginSettings();
|
||||
const storedServerLogin = normalizeServerLogin(settings?.serverLogin || state.settings.serverLogin || DEFAULT_SHINE_SERVER_LOGIN)
|
||||
|| DEFAULT_SHINE_SERVER_LOGIN;
|
||||
state.settings = {
|
||||
serverUrl: String(settings?.serverUrl || 'wss://shineup.me/ws').trim() || 'wss://shineup.me/ws',
|
||||
serverLogin: storedServerLogin,
|
||||
serverHttp: String(settings?.serverHttp || state.settings.serverHttp || buildHttpBase('shineup.me')).trim() || buildHttpBase('shineup.me'),
|
||||
serverUrl: String(settings?.serverUrl || state.settings.serverUrl || 'wss://shineup.me/ws').trim() || 'wss://shineup.me/ws',
|
||||
login: String(settings?.login || '').trim(),
|
||||
};
|
||||
state.activeSession = await loadSessionMaterial();
|
||||
}
|
||||
|
||||
async function persistSettings(nextSettings = {}) {
|
||||
const resolved = await resolveSettingsServer(nextSettings);
|
||||
state.settings = {
|
||||
...state.settings,
|
||||
...nextSettings,
|
||||
...resolved,
|
||||
};
|
||||
await savePluginSettings(state.settings);
|
||||
return state.settings;
|
||||
@ -79,6 +104,8 @@ async function resumeActiveSession() {
|
||||
|
||||
try {
|
||||
await persistSettings({
|
||||
serverLogin: String(sessionRecord?.serverLogin || state.settings.serverLogin || DEFAULT_SHINE_SERVER_LOGIN).trim(),
|
||||
serverHttp: String(sessionRecord?.serverHttp || state.settings.serverHttp || buildHttpBase('shineup.me')).trim(),
|
||||
serverUrl: String(sessionRecord?.serverUrl || state.settings.serverUrl || 'wss://shineup.me/ws').trim(),
|
||||
login: String(sessionRecord?.login || state.settings.login || '').trim(),
|
||||
});
|
||||
@ -106,6 +133,8 @@ async function attachApprovedSession(payload) {
|
||||
sessionKey: state.requesterMaterial?.sessionKey || '',
|
||||
sessionPrivPkcs8: state.requesterMaterial?.sessionPrivPkcs8 || '',
|
||||
sessionType: Number(approvedSession?.sessionType || 50) || 50,
|
||||
serverLogin: state.settings.serverLogin,
|
||||
serverHttp: state.settings.serverHttp,
|
||||
serverUrl: state.settings.serverUrl,
|
||||
};
|
||||
if (!sessionRecord.login || !sessionRecord.sessionId || !sessionRecord.sessionKey || !sessionRecord.sessionPrivPkcs8) {
|
||||
@ -159,14 +188,14 @@ async function pollPairingStatus() {
|
||||
}
|
||||
}
|
||||
|
||||
async function startPairing({ login, usePassword, password, serverUrl }) {
|
||||
async function startPairing({ login, usePassword, password, serverLogin }) {
|
||||
const cleanLogin = String(login || '').trim();
|
||||
if (!cleanLogin) {
|
||||
throw new Error('Введите логин.');
|
||||
}
|
||||
|
||||
await persistSettings({
|
||||
serverUrl: String(serverUrl || state.settings.serverUrl || 'wss://shineup.me/ws').trim() || 'wss://shineup.me/ws',
|
||||
serverLogin: String(serverLogin || state.settings.serverLogin || DEFAULT_SHINE_SERVER_LOGIN).trim(),
|
||||
login: cleanLogin,
|
||||
});
|
||||
clearPairingState();
|
||||
|
||||
186
SHiNE-browser-plugin-wallet/js/lib/shine-server-resolver.js
Normal file
186
SHiNE-browser-plugin-wallet/js/lib/shine-server-resolver.js
Normal file
@ -0,0 +1,186 @@
|
||||
import { base64ToBytes } from './crypto-utils.js';
|
||||
import { PublicKey } from './vendor/solana-publickey-bundle.js';
|
||||
|
||||
const SOLANA_ENDPOINT_DEFAULT = 'https://api.devnet.solana.com';
|
||||
const SHINE_USERS_PROGRAM_ID = 'FZS1YctoeEhCkZ5VTjsysUFAXR8CqxYztcLboXcg2Rpm';
|
||||
const SHINE_USERS_USER_PDA_SEED_PREFIX = 'user_login=';
|
||||
const DEFAULT_SHINE_SERVER_LOGIN = 'shineupme';
|
||||
const DEFAULT_SHINE_SERVER_ADDRESS = 'shineup.me';
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeServerLogin(value) {
|
||||
return String(value || '').trim().toLowerCase();
|
||||
}
|
||||
|
||||
function buildHttpBase(address) {
|
||||
const host = normalizeHostLike(address) || DEFAULT_SHINE_SERVER_ADDRESS;
|
||||
return `https://${host}`;
|
||||
}
|
||||
|
||||
function buildWsUrl(address) {
|
||||
const host = normalizeHostLike(address) || DEFAULT_SHINE_SERVER_ADDRESS;
|
||||
return `wss://${host}/ws`;
|
||||
}
|
||||
|
||||
function readU8(bytes, cursorRef) {
|
||||
if (cursorRef.value >= bytes.length) throw new Error('Повреждённый формат PDA');
|
||||
return bytes[cursorRef.value++];
|
||||
}
|
||||
|
||||
function readBytes(bytes, cursorRef, length) {
|
||||
if (cursorRef.value + length > bytes.length) throw new Error('Повреждённый формат PDA');
|
||||
const out = bytes.slice(cursorRef.value, cursorRef.value + length);
|
||||
cursorRef.value += length;
|
||||
return out;
|
||||
}
|
||||
|
||||
function readStrU8(bytes, cursorRef) {
|
||||
const length = readU8(bytes, cursorRef);
|
||||
return new TextDecoder().decode(readBytes(bytes, cursorRef, length));
|
||||
}
|
||||
|
||||
function parseServerFieldsFromUserPda(dataBytes) {
|
||||
const bytes = dataBytes instanceof Uint8Array ? dataBytes : new Uint8Array(dataBytes || []);
|
||||
if (bytes.length < 5) throw new Error('Некорректный формат PDA');
|
||||
const cursorRef = { value: 0 };
|
||||
const magic = new TextDecoder().decode(readBytes(bytes, cursorRef, 5));
|
||||
if (magic !== 'SHiNE') throw new Error('Некорректный формат PDA');
|
||||
cursorRef.value += 1; // format_major
|
||||
cursorRef.value += 1; // format_minor
|
||||
cursorRef.value += 2; // record_len
|
||||
cursorRef.value += 8; // created_at_ms
|
||||
cursorRef.value += 8; // updated_at_ms
|
||||
cursorRef.value += 4; // record_number
|
||||
cursorRef.value += 32; // prev_record_hash
|
||||
readStrU8(bytes, cursorRef); // login
|
||||
const blocksCount = readU8(bytes, cursorRef);
|
||||
|
||||
let isServer = false;
|
||||
let serverAddress = '';
|
||||
let accessServers = [];
|
||||
|
||||
for (let i = 0; i < blocksCount; i += 1) {
|
||||
const blockType = readU8(bytes, cursorRef);
|
||||
cursorRef.value += 1; // block_version
|
||||
|
||||
if (blockType === 1 || blockType === 2) {
|
||||
cursorRef.value += 32;
|
||||
continue;
|
||||
}
|
||||
if (blockType === 3) {
|
||||
const count = readU8(bytes, cursorRef);
|
||||
for (let j = 0; j < count; j += 1) {
|
||||
cursorRef.value += 1;
|
||||
readStrU8(bytes, cursorRef);
|
||||
cursorRef.value += 32;
|
||||
cursorRef.value += 8 + 8 + 4 + 32 + 64;
|
||||
const arPresent = readU8(bytes, cursorRef);
|
||||
if (arPresent === 1) readStrU8(bytes, cursorRef);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (blockType === 30) {
|
||||
isServer = readU8(bytes, cursorRef) === 1;
|
||||
if (isServer) {
|
||||
cursorRef.value += 1; // address_format_type
|
||||
cursorRef.value += 1; // address_format_version
|
||||
serverAddress = readStrU8(bytes, cursorRef);
|
||||
const syncCount = readU8(bytes, cursorRef);
|
||||
for (let j = 0; j < syncCount; j += 1) readStrU8(bytes, cursorRef);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (blockType === 40) {
|
||||
const accessCount = readU8(bytes, cursorRef);
|
||||
accessServers = [];
|
||||
for (let j = 0; j < accessCount; j += 1) accessServers.push(readStrU8(bytes, cursorRef));
|
||||
continue;
|
||||
}
|
||||
if (blockType === 50) {
|
||||
cursorRef.value += 1;
|
||||
const sessionsCount = readU8(bytes, cursorRef);
|
||||
for (let j = 0; j < sessionsCount; j += 1) {
|
||||
cursorRef.value += 1 + 1;
|
||||
readStrU8(bytes, cursorRef);
|
||||
cursorRef.value += 32;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (blockType === 70) {
|
||||
cursorRef.value += 1;
|
||||
continue;
|
||||
}
|
||||
throw new Error(`Неизвестный блок PDA: ${blockType}`);
|
||||
}
|
||||
|
||||
return {
|
||||
isServer,
|
||||
serverAddress: normalizeHostLike(serverAddress),
|
||||
accessServers: accessServers.map((value) => normalizeServerLogin(value)).filter(Boolean),
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchUserPda(login, solanaEndpoint = SOLANA_ENDPOINT_DEFAULT) {
|
||||
const cleanLogin = normalizeServerLogin(login);
|
||||
if (!cleanLogin) throw new Error('Не указан логин для чтения PDA.');
|
||||
const usersProgram = new PublicKey(SHINE_USERS_PROGRAM_ID);
|
||||
const enc = new TextEncoder();
|
||||
const [userPda] = PublicKey.findProgramAddressSync(
|
||||
[enc.encode(SHINE_USERS_USER_PDA_SEED_PREFIX), enc.encode(cleanLogin)],
|
||||
usersProgram,
|
||||
);
|
||||
|
||||
const response = await fetch(String(solanaEndpoint || SOLANA_ENDPOINT_DEFAULT), {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
cache: 'no-store',
|
||||
body: JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
id: 1,
|
||||
method: 'getAccountInfo',
|
||||
params: [userPda.toBase58(), { encoding: 'base64', commitment: 'confirmed' }],
|
||||
}),
|
||||
});
|
||||
if (!response.ok) throw new Error('Не удалось прочитать Solana RPC.');
|
||||
const json = await response.json();
|
||||
const dataB64 = json?.result?.value?.data?.[0];
|
||||
if (!dataB64) throw new Error(`PDA не найдена для логина @${cleanLogin}.`);
|
||||
return parseServerFieldsFromUserPda(base64ToBytes(dataB64));
|
||||
}
|
||||
|
||||
export async function resolveShineServerByServerLogin(serverLogin, solanaEndpoint = SOLANA_ENDPOINT_DEFAULT) {
|
||||
const cleanServerLogin = normalizeServerLogin(serverLogin) || DEFAULT_SHINE_SERVER_LOGIN;
|
||||
const parsed = await fetchUserPda(cleanServerLogin, solanaEndpoint);
|
||||
if (!parsed.isServer) {
|
||||
throw new Error(`Логин @${cleanServerLogin} не опубликован как сервер SHiNE.`);
|
||||
}
|
||||
if (!parsed.serverAddress) {
|
||||
throw new Error(`У server PDA пользователя @${cleanServerLogin} не задан server_address.`);
|
||||
}
|
||||
return {
|
||||
serverLogin: cleanServerLogin,
|
||||
serverAddress: parsed.serverAddress,
|
||||
serverHttp: buildHttpBase(parsed.serverAddress),
|
||||
serverUrl: buildWsUrl(parsed.serverAddress),
|
||||
};
|
||||
}
|
||||
|
||||
export {
|
||||
DEFAULT_SHINE_SERVER_ADDRESS,
|
||||
DEFAULT_SHINE_SERVER_LOGIN,
|
||||
SOLANA_ENDPOINT_DEFAULT,
|
||||
buildHttpBase,
|
||||
buildWsUrl,
|
||||
normalizeServerLogin,
|
||||
};
|
||||
16323
SHiNE-browser-plugin-wallet/js/lib/vendor/solana-publickey-bundle.js
vendored
Normal file
16323
SHiNE-browser-plugin-wallet/js/lib/vendor/solana-publickey-bundle.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
3
SHiNE-browser-plugin-wallet/js/lib/vendor/solana-publickey-entry.js
vendored
Normal file
3
SHiNE-browser-plugin-wallet/js/lib/vendor/solana-publickey-entry.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
|
||||
export { PublicKey };
|
||||
652
SHiNE-browser-plugin-wallet/package-lock.json
generated
652
SHiNE-browser-plugin-wallet/package-lock.json
generated
@ -9,12 +9,22 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@noble/curves": "^1.5.0"
|
||||
"@noble/curves": "^1.5.0",
|
||||
"@solana/web3.js": "^1.98.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.28.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz",
|
||||
"integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
|
||||
@ -481,6 +491,295 @@
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/hashes": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
|
||||
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^14.21.3 || >=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@solana/buffer-layout": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz",
|
||||
"integrity": "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer": "~6.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=5.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@solana/codecs-core": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@solana/codecs-core/-/codecs-core-2.3.0.tgz",
|
||||
"integrity": "sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@solana/errors": "2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.18.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=5.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@solana/codecs-numbers": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@solana/codecs-numbers/-/codecs-numbers-2.3.0.tgz",
|
||||
"integrity": "sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@solana/codecs-core": "2.3.0",
|
||||
"@solana/errors": "2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.18.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=5.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@solana/errors": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@solana/errors/-/errors-2.3.0.tgz",
|
||||
"integrity": "sha512-66RI9MAbwYV0UtP7kGcTBVLxJgUxoZGm8Fbc0ah+lGiAw17Gugco6+9GrJCV83VyF2mDWyYnYM9qdI3yjgpnaQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^5.4.1",
|
||||
"commander": "^14.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"errors": "bin/cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.18.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=5.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@solana/web3.js": {
|
||||
"version": "1.98.4",
|
||||
"resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.98.4.tgz",
|
||||
"integrity": "sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.25.0",
|
||||
"@noble/curves": "^1.4.2",
|
||||
"@noble/hashes": "^1.4.0",
|
||||
"@solana/buffer-layout": "^4.0.1",
|
||||
"@solana/codecs-numbers": "^2.1.0",
|
||||
"agentkeepalive": "^4.5.0",
|
||||
"bn.js": "^5.2.1",
|
||||
"borsh": "^0.7.0",
|
||||
"bs58": "^4.0.1",
|
||||
"buffer": "6.0.3",
|
||||
"fast-stable-stringify": "^1.0.0",
|
||||
"jayson": "^4.1.1",
|
||||
"node-fetch": "^2.7.0",
|
||||
"rpc-websockets": "^9.0.2",
|
||||
"superstruct": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.23",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz",
|
||||
"integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/connect": {
|
||||
"version": "3.4.38",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
|
||||
"integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "12.20.55",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
|
||||
"integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
|
||||
"integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "7.4.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz",
|
||||
"integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/agentkeepalive": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz",
|
||||
"integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"humanize-ms": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/base-x": {
|
||||
"version": "3.0.11",
|
||||
"resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz",
|
||||
"integrity": "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bn.js": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.3.tgz",
|
||||
"integrity": "sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/borsh": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz",
|
||||
"integrity": "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"bn.js": "^5.2.0",
|
||||
"bs58": "^4.0.0",
|
||||
"text-encoding-utf-8": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/bs58": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
|
||||
"integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base-x": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/bufferutil": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.1.0.tgz",
|
||||
"integrity": "sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
|
||||
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "14.0.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
|
||||
"integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/delay": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz",
|
||||
"integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-promise": {
|
||||
"version": "4.2.8",
|
||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
|
||||
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/es6-promisify": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
|
||||
"integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es6-promise": "^4.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
|
||||
@ -522,6 +821,357 @@
|
||||
"@esbuild/win32-ia32": "0.28.1",
|
||||
"@esbuild/win32-x64": "0.28.1"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
|
||||
"integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/eyes": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
|
||||
"integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==",
|
||||
"engines": {
|
||||
"node": "> 0.1.90"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-stable-stringify": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz",
|
||||
"integrity": "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/humanize-ms": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
|
||||
"integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/isomorphic-ws": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz",
|
||||
"integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"ws": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/jayson": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/jayson/-/jayson-4.3.0.tgz",
|
||||
"integrity": "sha512-AauzHcUcqs8OBnCHOkJY280VaTiCm57AbuO7lqzcw7JapGj50BisE3xhksye4zlTSR1+1tAz67wLTl8tEH1obQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/connect": "^3.4.33",
|
||||
"@types/node": "^12.12.54",
|
||||
"@types/ws": "^7.4.4",
|
||||
"commander": "^2.20.3",
|
||||
"delay": "^5.0.0",
|
||||
"es6-promisify": "^5.0.0",
|
||||
"eyes": "^0.1.8",
|
||||
"isomorphic-ws": "^4.0.1",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"stream-json": "^1.9.1",
|
||||
"uuid": "^8.3.2",
|
||||
"ws": "^7.5.10"
|
||||
},
|
||||
"bin": {
|
||||
"jayson": "bin/jayson.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/jayson/node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp-build": {
|
||||
"version": "4.8.4",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
|
||||
"integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"bin": {
|
||||
"node-gyp-build": "bin.js",
|
||||
"node-gyp-build-optional": "optional.js",
|
||||
"node-gyp-build-test": "build-test.js"
|
||||
}
|
||||
},
|
||||
"node_modules/rpc-websockets": {
|
||||
"version": "9.3.9",
|
||||
"resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-9.3.9.tgz",
|
||||
"integrity": "sha512-2iQDaTB4g5fDB2ihrTFSJSibCEuxaRi1q7qTW7ZO9/M5/TC+ToHA4D9/ffNLEbAoHNNrcdeP05oATNk44SKZXA==",
|
||||
"license": "LGPL-3.0-only",
|
||||
"dependencies": {
|
||||
"@swc/helpers": "^0.5.11",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@types/ws": "^8.2.2",
|
||||
"buffer": "^6.0.3",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"uuid": "^14.0.0",
|
||||
"ws": "^8.5.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "paypal",
|
||||
"url": "https://paypal.me/kozjak"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rpc-websockets/node_modules/@types/ws": {
|
||||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
||||
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/rpc-websockets/node_modules/utf-8-validate": {
|
||||
"version": "6.0.6",
|
||||
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.6.tgz",
|
||||
"integrity": "sha512-q3l3P9UtEEiAHcsgsqTgf9PPjctrDWoIXW3NpOHFdRDbLvu4DLIcxHangJ4RLrWkBcKjmcs/6NkerI8T/rE4LA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/rpc-websockets/node_modules/uuid": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz",
|
||||
"integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist-node/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/rpc-websockets/node_modules/ws": {
|
||||
"version": "8.21.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
|
||||
"integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/stream-chain": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz",
|
||||
"integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/stream-json": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.9.1.tgz",
|
||||
"integrity": "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"stream-chain": "^2.2.5"
|
||||
}
|
||||
},
|
||||
"node_modules/superstruct": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/superstruct/-/superstruct-2.0.2.tgz",
|
||||
"integrity": "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/text-encoding-utf-8": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz",
|
||||
"integrity": "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg=="
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
|
||||
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/utf-8-validate": {
|
||||
"version": "5.0.10",
|
||||
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz",
|
||||
"integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "7.5.11",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz",
|
||||
"integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,8 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@noble/curves": "^1.5.0"
|
||||
"@noble/curves": "^1.5.0",
|
||||
"@solana/web3.js": "^1.98.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.28.1"
|
||||
|
||||
@ -18,9 +18,10 @@
|
||||
</div>
|
||||
|
||||
<label class="field">
|
||||
<span>Shine server</span>
|
||||
<input id="server-url" type="text" placeholder="wss://shineup.me/ws" />
|
||||
<span>Логин сервера SHiNE</span>
|
||||
<input id="server-url" type="text" placeholder="shineupme" />
|
||||
</label>
|
||||
<p id="server-address" class="muted small">Текущий адрес: https://shineup.me</p>
|
||||
|
||||
<div id="session-card" class="card hidden">
|
||||
<div class="card-title">Подключённая wallet-session</div>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
const els = {
|
||||
serverUrl: document.querySelector('#server-url'),
|
||||
serverAddress: document.querySelector('#server-address'),
|
||||
loginInput: document.querySelector('#login-input'),
|
||||
usePassword: document.querySelector('#use-password'),
|
||||
passwordField: document.querySelector('#password-field'),
|
||||
@ -22,6 +23,8 @@ const els = {
|
||||
|
||||
let state = {
|
||||
settings: {
|
||||
serverLogin: 'shineupme',
|
||||
serverHttp: 'https://shineup.me',
|
||||
serverUrl: 'wss://shineup.me/ws',
|
||||
login: '',
|
||||
},
|
||||
@ -61,11 +64,13 @@ function formatRemaining(ms) {
|
||||
|
||||
function applyState(nextState) {
|
||||
state = nextState || state;
|
||||
const serverValue = String(state?.settings?.serverUrl || 'wss://shineup.me/ws');
|
||||
const serverValue = String(state?.settings?.serverLogin || 'shineupme');
|
||||
const serverAddressValue = String(state?.settings?.serverHttp || 'https://shineup.me');
|
||||
const loginValue = String(state?.settings?.login || '');
|
||||
if (document.activeElement !== els.serverUrl) {
|
||||
els.serverUrl.value = serverValue;
|
||||
}
|
||||
els.serverAddress.textContent = `Текущий адрес: ${serverAddressValue}`;
|
||||
if (document.activeElement !== els.loginInput) {
|
||||
els.loginInput.value = loginValue;
|
||||
}
|
||||
@ -134,7 +139,7 @@ async function refreshState() {
|
||||
|
||||
async function saveSettings() {
|
||||
await sendMessage('wallet:saveSettings', {
|
||||
serverUrl: String(els.serverUrl.value || '').trim(),
|
||||
serverLogin: String(els.serverUrl.value || '').trim(),
|
||||
login: String(els.loginInput.value || '').trim(),
|
||||
});
|
||||
}
|
||||
@ -162,7 +167,7 @@ async function startPairing() {
|
||||
login,
|
||||
usePassword: !!els.usePassword.checked,
|
||||
password: String(els.passwordInput.value || ''),
|
||||
serverUrl: String(els.serverUrl.value || '').trim(),
|
||||
serverLogin: String(els.serverUrl.value || '').trim(),
|
||||
});
|
||||
applyState(response.state);
|
||||
} catch (error) {
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
client.version=1.2.205
|
||||
server.version=1.2.194
|
||||
client.version=1.2.206
|
||||
server.version=1.2.195
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<link rel="manifest" href="./manifest.webmanifest" />
|
||||
<title>Shine UI Demo</title>
|
||||
<script>
|
||||
window.__SHINE_BUILD_HASH__ = '20260616091500';
|
||||
window.__SHINE_BUILD_HASH__ = '20260616124000';
|
||||
window.__SHINE_CLIENT_VERSION__ = '1.2.8';
|
||||
</script>
|
||||
<script>
|
||||
|
||||
@ -33,7 +33,7 @@ import {
|
||||
} from './state.js';
|
||||
|
||||
import * as startView from './pages/start-view.js?v=202606142105';
|
||||
import * as entrySettingsView from './pages/entry-settings-view.js';
|
||||
import * as entrySettingsView from './pages/entry-settings-view.js?v=202606161240';
|
||||
import * as registerView from './pages/register-view.js';
|
||||
import * as registrationPaymentView from './pages/registration-payment-view.js';
|
||||
import * as registrationKeysView from './pages/registration-keys-view.js';
|
||||
@ -51,7 +51,7 @@ import * as profileEditView from './pages/profile-edit-view.js';
|
||||
import * as walletView from './pages/wallet-view.js?v=202605300007';
|
||||
import * as settingsView from './pages/settings-view.js';
|
||||
import * as developerSettingsView from './pages/developer-settings-view.js';
|
||||
import * as serverSettingsView from './pages/server-settings-view.js';
|
||||
import * as serverSettingsView from './pages/server-settings-view.js?v=202606161240';
|
||||
import * as toolsSettingsView from './pages/tools-settings-view.js';
|
||||
import * as deviceView from './pages/device-view.js?v=202606131435';
|
||||
import * as connectDeviceView from './pages/connect-device-view.js?v=202606142055';
|
||||
|
||||
@ -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} сек.`);
|
||||
|
||||
@ -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 {
|
||||
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);
|
||||
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-канала автоматически.',
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@ -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 {
|
||||
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);
|
||||
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-канала автоматически.',
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@ -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';
|
||||
}
|
||||
|
||||
|
||||
81
shine-UI/js/services/shine-server-resolver.js
Normal file
81
shine-UI/js/services/shine-server-resolver.js
Normal file
@ -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,
|
||||
};
|
||||
}
|
||||
@ -1,6 +1,12 @@
|
||||
import { AuthService } from './services/auth-service.js';
|
||||
import { listStoredMessages, putStoredMessage, clearStoredMessages } from './services/message-store.js';
|
||||
import { SOLANA_ENDPOINT_DEFAULT } from './solana-programs.js';
|
||||
import {
|
||||
DEFAULT_SHINE_SERVER_HTTP,
|
||||
DEFAULT_SHINE_SERVER_LOGIN,
|
||||
DEFAULT_SHINE_SERVER_WS,
|
||||
resolveShineServerByServerLogin,
|
||||
} from './services/shine-server-resolver.js';
|
||||
|
||||
const clone = (value) => JSON.parse(JSON.stringify(value));
|
||||
const SESSION_STORAGE_KEY = 'shine-ui-current-session-v1';
|
||||
@ -78,7 +84,9 @@ function inferTunnelWsUrl() {
|
||||
|
||||
const LOCAL_WS_OVERRIDE_URL = readLocalWsOverrideUrl() || inferTunnelWsUrl();
|
||||
const DEFAULT_SOLANA_SERVER = SOLANA_ENDPOINT_DEFAULT;
|
||||
const DEFAULT_SHINE_SERVER = 'wss://shineup.me/ws';
|
||||
const DEFAULT_SHINE_SERVER = DEFAULT_SHINE_SERVER_WS;
|
||||
const DEFAULT_SHINE_SERVER_LOGIN_VALUE = DEFAULT_SHINE_SERVER_LOGIN;
|
||||
const DEFAULT_SHINE_SERVER_HTTP_VALUE = DEFAULT_SHINE_SERVER_HTTP;
|
||||
const DEFAULT_ARWEAVE_SERVER = 'https://arweave.net';
|
||||
const DEFAULT_CALL_PREFLIGHT_TIMEOUT_MS = 6000;
|
||||
const DEFAULT_OPENAI_BASE_URL = 'https://api.openai.com/v1';
|
||||
@ -171,11 +179,13 @@ function persistEntrySettings(settings) {
|
||||
language: String(settings?.language || 'ru'),
|
||||
solanaServer: String(settings?.solanaServer || DEFAULT_SOLANA_SERVER),
|
||||
shineServer: String(settings?.shineServer || DEFAULT_SHINE_SERVER),
|
||||
shineServerLogin: String(settings?.shineServerLogin || DEFAULT_SHINE_SERVER_LOGIN_VALUE),
|
||||
shineServerHttp: String(settings?.shineServerHttp || DEFAULT_SHINE_SERVER_HTTP_VALUE),
|
||||
arweaveServer: String(settings?.arweaveServer || DEFAULT_ARWEAVE_SERVER),
|
||||
callPreflightTimeoutMs: Math.max(1000, Math.min(20000, Number(settings?.callPreflightTimeoutMs || DEFAULT_CALL_PREFLIGHT_TIMEOUT_MS) || DEFAULT_CALL_PREFLIGHT_TIMEOUT_MS)),
|
||||
statuses: {
|
||||
solanaServer: String(settings?.statuses?.solanaServer || 'idle'),
|
||||
shineServer: String(settings?.statuses?.shineServer || 'idle'),
|
||||
shineServerLogin: String(settings?.statuses?.shineServerLogin || settings?.statuses?.shineServer || 'idle'),
|
||||
arweaveServer: String(settings?.statuses?.arweaveServer || 'idle'),
|
||||
},
|
||||
tools: normalizeToolsSettings(settings?.tools),
|
||||
@ -235,11 +245,13 @@ function createInitialState({ withStoredSession = true } = {}) {
|
||||
language: String(storedEntrySettings?.language || 'ru'),
|
||||
solanaServer: String(storedEntrySettings?.solanaServer || DEFAULT_SOLANA_SERVER),
|
||||
shineServer: String(LOCAL_WS_OVERRIDE_URL || storedEntrySettings?.shineServer || initialShineServer),
|
||||
shineServerLogin: String(storedEntrySettings?.shineServerLogin || DEFAULT_SHINE_SERVER_LOGIN_VALUE),
|
||||
shineServerHttp: String(storedEntrySettings?.shineServerHttp || DEFAULT_SHINE_SERVER_HTTP_VALUE),
|
||||
arweaveServer: String(storedEntrySettings?.arweaveServer || DEFAULT_ARWEAVE_SERVER),
|
||||
callPreflightTimeoutMs: Math.max(1000, Math.min(20000, Number(storedEntrySettings?.callPreflightTimeoutMs || DEFAULT_CALL_PREFLIGHT_TIMEOUT_MS) || DEFAULT_CALL_PREFLIGHT_TIMEOUT_MS)),
|
||||
statuses: {
|
||||
solanaServer: String(storedEntrySettings?.statuses?.solanaServer || 'idle'),
|
||||
shineServer: String(storedEntrySettings?.statuses?.shineServer || 'idle'),
|
||||
shineServerLogin: String(storedEntrySettings?.statuses?.shineServerLogin || storedEntrySettings?.statuses?.shineServer || 'idle'),
|
||||
arweaveServer: String(storedEntrySettings?.statuses?.arweaveServer || 'idle'),
|
||||
},
|
||||
tools: normalizeToolsSettings(storedEntrySettings?.tools),
|
||||
@ -649,11 +661,28 @@ export function checkServerAvailability(address) {
|
||||
}
|
||||
|
||||
export async function saveEntrySettings(nextSettings) {
|
||||
const forcedShineServer = LOCAL_WS_OVERRIDE_URL || nextSettings.shineServer;
|
||||
const nextSolanaServer = String(nextSettings?.solanaServer || state.entrySettings.solanaServer || DEFAULT_SOLANA_SERVER);
|
||||
const nextShineServerLogin = String(nextSettings?.shineServerLogin || state.entrySettings.shineServerLogin || DEFAULT_SHINE_SERVER_LOGIN_VALUE).trim().toLowerCase()
|
||||
|| DEFAULT_SHINE_SERVER_LOGIN_VALUE;
|
||||
let forcedShineServer = LOCAL_WS_OVERRIDE_URL || '';
|
||||
let resolvedShineServerHttp = String(state.entrySettings.shineServerHttp || DEFAULT_SHINE_SERVER_HTTP_VALUE).trim() || DEFAULT_SHINE_SERVER_HTTP_VALUE;
|
||||
|
||||
if (!forcedShineServer) {
|
||||
const resolved = await resolveShineServerByServerLogin({
|
||||
serverLogin: nextShineServerLogin,
|
||||
solanaEndpoint: nextSolanaServer,
|
||||
});
|
||||
forcedShineServer = resolved.wsUrl;
|
||||
resolvedShineServerHttp = resolved.httpBase;
|
||||
}
|
||||
|
||||
state.entrySettings = {
|
||||
...state.entrySettings,
|
||||
...nextSettings,
|
||||
solanaServer: nextSolanaServer,
|
||||
shineServer: forcedShineServer,
|
||||
shineServerLogin: nextShineServerLogin,
|
||||
shineServerHttp: resolvedShineServerHttp,
|
||||
statuses: {
|
||||
...state.entrySettings.statuses,
|
||||
...(nextSettings.statuses || {}),
|
||||
@ -662,7 +691,7 @@ export async function saveEntrySettings(nextSettings) {
|
||||
};
|
||||
persistEntrySettings(state.entrySettings);
|
||||
await authService.reconnect(state.entrySettings.shineServer);
|
||||
state.startHint = 'Настройки входа сохранены, адреса серверов обновлены.';
|
||||
state.startHint = `Настройки входа сохранены. SHiNE: ${state.entrySettings.shineServerHttp}`;
|
||||
}
|
||||
|
||||
export function clearStartHint() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user