feat(front): убрана мёртвая кнопка /close, события ставки по WebSocket

- блок «Закрытие (POST /close)» -> «Ставка: ход игры»: № ставки, состояние, вход/выход
- ручка /close удалена на сервере (404) -> кнопка и gwClose() убраны
- ws.onmessage разбирает bet_open / bet_closed: ставка показывается и
  рассчитывается сама, сервер закрывает её без участия клиента
This commit is contained in:
Caffeine
2026-09-13 18:49:32 +03:00
parent 707668b178
commit f9daee67bf
+42 -19
View File
@@ -100,12 +100,18 @@
</fieldset>
<fieldset>
<legend>Закрытие (POST /close)</legend>
<legend>Ставка: ход игры (сервер закрывает сам)</legend>
<div class="row">
<label>id ставки</label><input id="closeId" size="12" value=""/>
<button onclick="gwClose()">Закрыть ставку</button>
<span class="hint">id заполнится после ставки, либо введите вручную</span>
<span>№ ставки: <b id="liveBetId">—</b></span>
<span>состояние: <b id="liveBetState">нет ставки</b></span>
</div>
<div class="row">
<span>вход: <b id="liveBetEntry">—</b></span>
<span>выход: <b id="liveBetExit">—</b></span>
</div>
<div class="hint">Ставку открывает кнопка «Сделать ставку» (HTTP). Итог приходит сам по WebSocket:
<code>bet_open</code> — ставка принята, <code>bet_closed</code> — рассчитана (примерно через 15 с).
Пока ставка открыта, вторую на этот адрес сервер не примет.</div>
</fieldset>
<fieldset>
@@ -225,7 +231,11 @@ function useWalletAddr(){
log.info('Адрес «'+w+'» подставлен в поле «Адрес» (для ставки и баланса)');
}
let lastBetId = '';
let liveBetId = '';
function setLiveBet(field, value){
const el = document.getElementById(field);
if(el) el.textContent = value;
}
async function gwBet(){
try{
const side = document.getElementById('betSide').value;
@@ -239,24 +249,33 @@ async function gwBet(){
const address = ADDR();
log.info('POST /bet side='+side+', amountUnits='+units.toString()+(address?' (address='+address+')':' (адрес пуст — шлюз сгенерит новый кошелёк)'));
const j = await gwFetch('/bet', {side:side, amountUnits:units.toString(), address:address});
lastBetId = String(j.id);
document.getElementById('closeId').value = lastBetId;
liveBetId = String(j.id);
setLiveBet('liveBetId', liveBetId);
setLiveBet('liveBetState', 'открыта, ждём закрытия (~15 с)');
setLiveBet('liveBetEntry', String(j.entryPrice));
setLiveBet('liveBetExit', '—');
log.ok('Ставка принята: id='+j.id+', bettor='+j.bettor+', entryPrice='+j.entryPrice+(j.tx?' tx='+j.tx:''));
if(!address) log.info('кошелёк шлюза: '+j.bettor+' (скопируйте в поле «Адрес» для дальнейших ставок)');
}catch(e){ log.err('bet: '+e.message); }
}
async function gwClose(){
try{
const id = document.getElementById('closeId').value.trim();
if(!id){ log.err('close: нет id ставки (сначала сделайте ставку или введите id вручную)'); return; }
const j = await gwFetch('/close', {id:id});
const label = {payout_done:'ВЫИГРАЛ (payout_done)', house_won:'ПРОИГРАЛ (house_won)', open:'открыт (open)'}[j.statusName] || ('status='+j.statusName);
const detail = 'exitPrice='+j.exitPrice+(j.note?' — '+j.note:'');
if(j.statusName==='payout_done') log.ok('Закрыт #'+id+': '+label+', '+detail);
else if(j.statusName==='house_won') log.err('Закрыт #'+id+': '+label+', '+detail);
else log.info('Закрыт #'+id+': '+label+', '+detail);
}catch(e){ log.err('close: '+e.message); }
/** Событие WS: ставка открыта. */
function onBetOpen(m){
setLiveBet('liveBetId', String(m.id));
setLiveBet('liveBetState', 'открыта ('+m.side+'), ждём закрытия');
setLiveBet('liveBetEntry', String(m.entryPrice));
setLiveBet('liveBetExit', '—');
log.info('bet_open: #'+m.id+' '+m.side+', сумма='+m.amountUnits+', вход='+m.entryPrice);
}
/** Событие WS: ставка рассчитана — сервер закрыл её сам. */
function onBetClosed(m){
setLiveBet('liveBetId', String(m.id));
const won = m.statusName === 'payout_done';
setLiveBet('liveBetState', won ? 'ВЫИГРАЛ (payout_done)' : 'ПРОИГРАЛ (house_won)');
setLiveBet('liveBetExit', String(m.exitPrice));
const text = 'bet_closed: #'+m.id+', выход='+m.exitPrice+', '+(won?'ВЫИГРАЛ':'ПРОИГРАЛ');
if(won) log.ok(text); else log.err(text);
}
function copyLog(){ const t=document.getElementById('log').innerText; navigator.clipboard?.writeText(t).then(()=>log.ok('лог скопирован')).catch(()=>{}); }
@@ -338,7 +357,11 @@ function connectWs(){
ws.onmessage = (ev) => {
let m;
try{ m = JSON.parse(ev.data); }catch(e){ return; }
if(!m || m.type !== 'price') return;
if(!m || !m.type) return;
// Клиентские события жизненного цикла ставки — сервер закрывает ставку сам.
if(m.type === 'bet_open'){ onBetOpen(m); return; }
if(m.type === 'bet_closed'){ onBetClosed(m); return; }
if(m.type !== 'price') return;
wsMsgCount++;
document.getElementById('wsMsgCount').textContent = String(wsMsgCount);
const fullPrice = String(m.price);