fix owner change typo in main, create new client
This commit is contained in:
+266
-242
@@ -2,289 +2,313 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>WS Client</title>
|
<title>go-socket debug</title>
|
||||||
<style>
|
<style>
|
||||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
body { font-family: monospace; background: #1a1a1a; color: #e0e0e0; margin: 20px; }
|
||||||
body { font-family: monospace; background: #1a1a1a; color: #e0e0e0; padding: 20px; }
|
h1 { color: #aaa; font-size: 1.2em; margin-bottom: 12px; }
|
||||||
h2 { margin-bottom: 16px; color: #9ecfff; }
|
|
||||||
|
|
||||||
#auth-panel, #chat-panel { max-width: 600px; }
|
.config { margin-bottom: 10px; }
|
||||||
|
.config label { color: #888; }
|
||||||
|
.config input { background: #2a2a2a; color: #e0e0e0; border: 1px solid #444; padding: 4px 8px; width: 300px; }
|
||||||
|
|
||||||
label { display: block; margin-bottom: 4px; font-size: 13px; color: #aaa; }
|
/* LOG */
|
||||||
input[type="text"] {
|
#log-header { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
|
||||||
width: 100%; padding: 8px; background: #2a2a2a; border: 1px solid #444;
|
#log-header span { color: #888; font-size: 1em; }
|
||||||
color: #e0e0e0; font-family: monospace; font-size: 13px; border-radius: 4px;
|
#clear-log { background: #2a2a2a; color: #888; border: 1px solid #444; padding: 3px 10px; cursor: pointer; font-size: 0.85em; font-family: monospace; }
|
||||||
}
|
#log { background: #111; border: 1px solid #333; padding: 10px; height: 340px; overflow-y: auto; font-size: 0.85em; white-space: pre-wrap; word-break: break-all; margin-bottom: 14px; }
|
||||||
button {
|
.log-ws { color: #8af; }
|
||||||
margin-top: 10px; padding: 8px 20px; background: #4a90d9; color: #fff;
|
.log-http { color: #af8; }
|
||||||
border: none; border-radius: 4px; cursor: pointer; font-family: monospace; font-size: 13px;
|
.log-err { color: #f88; }
|
||||||
}
|
.log-info { color: #666; }
|
||||||
button:hover { background: #357abd; }
|
|
||||||
button:disabled { background: #444; cursor: default; }
|
|
||||||
|
|
||||||
#status {
|
/* BUTTON ROW */
|
||||||
margin-top: 12px; font-size: 13px; padding: 6px 10px;
|
#btn-row { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
|
||||||
border-radius: 4px; background: #2a2a2a; border-left: 3px solid #555;
|
#btn-row button {
|
||||||
|
background: #2a2a2a; color: #ccc; border: 1px solid #444;
|
||||||
|
padding: 5px 10px; cursor: pointer; font-family: monospace; font-size: 0.8em;
|
||||||
}
|
}
|
||||||
#status.ok { border-color: #4caf50; color: #81c784; }
|
#btn-row button:hover { background: #333; border-color: #666; }
|
||||||
#status.err { border-color: #f44336; color: #e57373; }
|
#btn-row button.active { background: #2a3a2a; border-color: #4a4; color: #af8; }
|
||||||
|
#btn-row button.active.ws { background: #2a2a3a; border-color: #44a; color: #88f; }
|
||||||
|
#btn-row button.warn { color: #f88; }
|
||||||
|
|
||||||
#chat-panel { display: none; margin-top: 24px; }
|
/* FORM PANEL */
|
||||||
|
#form-panel { display: none; background: #1e1e1e; border: 1px solid #333; padding: 12px; margin-bottom: 6px; }
|
||||||
#log {
|
#form-panel.open { display: block; }
|
||||||
height: 300px; overflow-y: auto; background: #111; border: 1px solid #333;
|
#form-title { color: #888; font-size: 0.85em; margin-bottom: 10px; }
|
||||||
border-radius: 4px; padding: 10px; font-size: 13px; line-height: 1.6;
|
.field { margin-bottom: 7px; }
|
||||||
}
|
.field label { display: inline-block; width: 160px; color: #777; font-size: 0.88em; }
|
||||||
.msg-in { color: #81c784; }
|
.field input { background: #2a2a2a; color: #e0e0e0; border: 1px solid #444; padding: 4px 8px; width: 260px; font-family: monospace; font-size: 0.88em; }
|
||||||
.msg-out { color: #64b5f6; }
|
.hint { color: #555; font-size: 0.78em; margin-left: 5px; }
|
||||||
.msg-sys { color: #888; font-style: italic; }
|
.form-actions { margin-top: 10px; display: flex; gap: 8px; align-items: center; }
|
||||||
|
button.send { background: #2a4a2a; color: #8f8; border: 1px solid #4a4; padding: 5px 16px; cursor: pointer; font-family: monospace; }
|
||||||
#send-row { display: flex; gap: 8px; margin-top: 10px; }
|
button.send:hover { background: #3a5a3a; }
|
||||||
#send-row input { flex: 1; margin: 0; }
|
button.ws-action { background: #2a2a4a; color: #88f; border: 1px solid #44a; padding: 5px 14px; cursor: pointer; font-family: monospace; }
|
||||||
#send-row button { margin: 0; }
|
button.ws-action:hover { background: #3a3a5a; }
|
||||||
|
button.ws-danger { background: #4a2a2a; color: #f88; border: 1px solid #a44; padding: 5px 14px; cursor: pointer; font-family: monospace; }
|
||||||
input[type="password"] {
|
.ws-status { font-size: 0.82em; }
|
||||||
width: 100%; padding: 8px; background: #2a2a2a; border: 1px solid #444;
|
.ws-status.connected { color: #8f8; }
|
||||||
color: #e0e0e0; font-family: monospace; font-size: 13px; border-radius: 4px;
|
.ws-status.disconnected { color: #f88; }
|
||||||
}
|
|
||||||
.tabs { display: flex; gap: 8px; margin-bottom: 16px; }
|
|
||||||
.tab-btn {
|
|
||||||
padding: 6px 16px; background: #2a2a2a; border: 1px solid #444;
|
|
||||||
color: #aaa; border-radius: 4px; cursor: pointer; font-family: monospace; font-size: 13px;
|
|
||||||
}
|
|
||||||
.tab-btn.active { background: #4a90d9; color: #fff; border-color: #4a90d9; }
|
|
||||||
.tab { display: none; }
|
|
||||||
.tab.active { display: block; }
|
|
||||||
#form-status {
|
|
||||||
margin-top: 10px; font-size: 13px; padding: 6px 10px;
|
|
||||||
border-radius: 4px; background: #2a2a2a; border-left: 3px solid #555; display: none;
|
|
||||||
}
|
|
||||||
#form-status.ok { border-color: #4caf50; color: #81c784; display: block; }
|
|
||||||
#form-status.err { border-color: #f44336; color: #e57373; display: block; }
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<h1>go-socket debug</h1>
|
||||||
|
|
||||||
<div id="auth-panel">
|
<div class="config">
|
||||||
<h2>WebSocket Client</h2>
|
<label>Base URL: </label>
|
||||||
|
<input id="baseUrl" value="http://localhost:8080">
|
||||||
<div class="tabs">
|
|
||||||
<button class="tab-btn active" onclick="switchTab('login')">Login</button>
|
|
||||||
<button class="tab-btn" onclick="switchTab('register')">Register</button>
|
|
||||||
<button class="tab-btn" onclick="switchTab('connect')">Connect</button>
|
|
||||||
<button class="tab-btn" onclick="switchTab('group')">Group</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="tab-login" class="tab active">
|
<div id="log-header">
|
||||||
<label>Username</label>
|
<span>Response Log</span>
|
||||||
<input id="login-username" type="text" placeholder="alice">
|
<button id="clear-log" onclick="clearLog()">Clear</button>
|
||||||
<label style="margin-top:10px;">Password</label>
|
|
||||||
<input id="login-password" type="text" placeholder="password">
|
|
||||||
<button onclick="login()">Login</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="tab-register" class="tab">
|
|
||||||
<label>Username</label>
|
|
||||||
<input id="reg-username" type="text" placeholder="alice">
|
|
||||||
<label style="margin-top:10px;">Password</label>
|
|
||||||
<input id="reg-password" type="text" placeholder="password">
|
|
||||||
<label style="margin-top:10px;">Color (r,g,b)</label>
|
|
||||||
<input id="reg-color" type="text" placeholder="255,100,50">
|
|
||||||
<button onclick="register()">Register</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="tab-connect" class="tab">
|
|
||||||
<label>Username (for display)</label>
|
|
||||||
<input id="username-input" type="text" placeholder="Your username">
|
|
||||||
<label style="margin-top:10px;">JWT Token</label>
|
|
||||||
<input id="token-input" type="text" placeholder="Paste token from login">
|
|
||||||
<button id="connect-btn" onclick="connect()">Connect</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="tab-group" class="tab">
|
|
||||||
<label>Token</label>
|
|
||||||
<input id="group-token" type="text" placeholder="Paste token from login">
|
|
||||||
<label style="margin-top:10px;">Name (optional)</label>
|
|
||||||
<input id="group-name" type="text" placeholder="Best group ever">
|
|
||||||
<label style="margin-top:10px;">Color (r,g,b or red/green/blue)</label>
|
|
||||||
<input id="group-color" type="text" placeholder="255,100,50">
|
|
||||||
<label style="margin-top:10px;">Enable client colors</label>
|
|
||||||
<input id="group-client-colors" type="text" placeholder="1 or 0">
|
|
||||||
<button onclick="createGroup()">Create Group</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="form-status"></div>
|
|
||||||
<div id="status">Not connected</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="chat-panel">
|
|
||||||
<h2>Connected as <span id="auth-name"></span></h2>
|
|
||||||
<div id="log"></div>
|
<div id="log"></div>
|
||||||
<div id="send-row">
|
|
||||||
<input id="msg-input" type="text" placeholder="Message..." onkeydown="if(event.key==='Enter') send()">
|
<div id="btn-row">
|
||||||
<button id="send-btn" onclick="send()">Send</button>
|
<button onclick="showForm('new-client')">POST /new/client</button>
|
||||||
|
<button onclick="showForm('new-token')">POST /new/token</button>
|
||||||
|
<button onclick="showForm('new-group')">POST /new/group</button>
|
||||||
|
<button onclick="showForm('add-clients')">POST /mod/group/addclients</button>
|
||||||
|
<button onclick="showForm('remove-clients')">POST /mod/group/removeclients</button>
|
||||||
|
<button onclick="showForm('group-color')">POST /mod/group/color</button>
|
||||||
|
<button onclick="showForm('group-owner')">POST /mod/group/owner</button>
|
||||||
|
<button onclick="showForm('new-message')">POST /new/message</button>
|
||||||
|
<button onclick="showForm('get-groups')">POST /get/groups</button>
|
||||||
|
<button onclick="showForm('get-members')">POST /get/group/members</button>
|
||||||
|
<button onclick="showForm('websocket')">WS /ws</button>
|
||||||
</div>
|
</div>
|
||||||
<button onclick="disconnect()" style="background:#c0392b; margin-top:10px;">Disconnect</button>
|
|
||||||
|
<div id="form-panel">
|
||||||
|
<div id="form-title"></div>
|
||||||
|
<div id="form-fields"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
let ws = null;
|
let ws = null;
|
||||||
|
let activeForm = null;
|
||||||
|
|
||||||
function switchTab(name) {
|
const forms = {
|
||||||
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
'new-client': {
|
||||||
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
title: 'POST /new/client — register new user',
|
||||||
document.getElementById('tab-' + name).classList.add('active');
|
fields: [
|
||||||
document.querySelectorAll('.tab-btn').forEach(b => {
|
{ id: 'nc-username', label: 'username', ph: 'min 4 chars' },
|
||||||
if (b.textContent.toLowerCase() === name) b.classList.add('active');
|
{ id: 'nc-password', label: 'password', ph: 'min 8 chars' },
|
||||||
});
|
{ id: 'nc-color', label: 'color', ph: '255,100,50', hint: 'R,G,B' },
|
||||||
setFormStatus('', '');
|
],
|
||||||
|
submit: () => httpPost('/new/client', { username:'nc-username', password:'nc-password', color:'nc-color' })
|
||||||
|
},
|
||||||
|
'new-token': {
|
||||||
|
title: 'POST /new/token — login / get token',
|
||||||
|
fields: [
|
||||||
|
{ id: 'nt-username', label: 'username', ph: 'min 4 chars' },
|
||||||
|
{ id: 'nt-password', label: 'password', ph: 'min 8 chars' },
|
||||||
|
],
|
||||||
|
submit: () => httpPost('/new/token', { username:'nt-username', password:'nt-password' })
|
||||||
|
},
|
||||||
|
'new-group': {
|
||||||
|
title: 'POST /new/group — create group',
|
||||||
|
fields: [
|
||||||
|
{ id: 'ng-token', label: 'token', ph: '' },
|
||||||
|
{ id: 'ng-name', label: 'name', ph: 'optional, default: Best group ever' },
|
||||||
|
{ id: 'ng-color', label: 'color', ph: 'R,G,B or name, optional', hint: 'R,G,B' },
|
||||||
|
{ id: 'ng-enableClientColors', label: 'enableClientColors', ph: '1 to enable (optional)' },
|
||||||
|
],
|
||||||
|
submit: () => httpPost('/new/group', { token:'ng-token', name:'ng-name', color:'ng-color', enableClientColors:'ng-enableClientColors' })
|
||||||
|
},
|
||||||
|
'add-clients': {
|
||||||
|
title: 'POST /mod/group/addclients — add clients (owner only)',
|
||||||
|
fields: [
|
||||||
|
{ id: 'ac-token', label: 'token', ph: '' },
|
||||||
|
{ id: 'ac-groupid', label: 'groupid', ph: 'uint32' },
|
||||||
|
{ id: 'ac-clients', label: 'clients', ph: 'id1,id2,id3', hint: 'csv of uint32' },
|
||||||
|
],
|
||||||
|
submit: () => httpPost('/mod/group/addclients', { token:'ac-token', groupid:'ac-groupid', clients:'ac-clients' })
|
||||||
|
},
|
||||||
|
'remove-clients': {
|
||||||
|
title: 'POST /mod/group/removeclients — remove clients (owner only)',
|
||||||
|
fields: [
|
||||||
|
{ id: 'rc-token', label: 'token', ph: '' },
|
||||||
|
{ id: 'rc-groupid', label: 'groupid', ph: 'uint32' },
|
||||||
|
{ id: 'rc-clients', label: 'clients', ph: 'id1,id2,id3', hint: 'csv of uint32' },
|
||||||
|
],
|
||||||
|
submit: () => httpPost('/mod/group/removeclients', { token:'rc-token', groupid:'rc-groupid', clients:'rc-clients' })
|
||||||
|
},
|
||||||
|
'group-color': {
|
||||||
|
title: 'POST /mod/group/color — change color (owner only)',
|
||||||
|
fields: [
|
||||||
|
{ id: 'gc-token', label: 'token', ph: '' },
|
||||||
|
{ id: 'gc-groupid', label: 'groupid', ph: 'uint32' },
|
||||||
|
{ id: 'gc-color', label: 'color', ph: '255,100,50', hint: 'R,G,B' },
|
||||||
|
],
|
||||||
|
submit: () => httpPost('/mod/group/color', { token:'gc-token', groupid:'gc-groupid', color:'gc-color' })
|
||||||
|
},
|
||||||
|
'group-owner': {
|
||||||
|
title: 'POST /mod/group/owner — change owner (owner only)',
|
||||||
|
fields: [
|
||||||
|
{ id: 'go-token', label: 'token', ph: '' },
|
||||||
|
{ id: 'go-groupid', label: 'groupid', ph: 'uint32' },
|
||||||
|
{ id: 'go-newOwner', label: 'newOwner', ph: 'username of new owner' },
|
||||||
|
],
|
||||||
|
submit: () => httpPost('/mod/group/color', { token:'go-token', groupid:'go-groupid', newOwner:'go-newOwner' })
|
||||||
|
},
|
||||||
|
'new-message': {
|
||||||
|
title: 'POST /new/message — send message to group',
|
||||||
|
fields: [
|
||||||
|
{ id: 'nm-token', label: 'token', ph: '' },
|
||||||
|
{ id: 'nm-subject', label: 'subject', ph: 'group ID (uint32)', hint: 'group id' },
|
||||||
|
{ id: 'nm-content', label: 'content', ph: 'message text' },
|
||||||
|
],
|
||||||
|
submit: () => httpPost('/new/message', { token:'nm-token', subject:'nm-subject', content:'nm-content' })
|
||||||
|
},
|
||||||
|
'get-groups': {
|
||||||
|
title: "POST /get/groups — get client's groups (no members)",
|
||||||
|
fields: [
|
||||||
|
{ id: 'gg-token', label: 'token', ph: '' },
|
||||||
|
],
|
||||||
|
submit: () => httpPost('/get/groups', { token:'gg-token' })
|
||||||
|
},
|
||||||
|
'get-members': {
|
||||||
|
title: 'POST /get/group/members — get group member IDs',
|
||||||
|
fields: [
|
||||||
|
{ id: 'gm-token', label: 'token', ph: '' },
|
||||||
|
{ id: 'gm-group', label: 'group', ph: 'group ID (uint32)' },
|
||||||
|
],
|
||||||
|
submit: () => httpPost('/get/group/members', { token:'gm-token', group:'gm-group' })
|
||||||
|
},
|
||||||
|
'websocket': {
|
||||||
|
title: 'WS /ws — WebSocket connection',
|
||||||
|
renderCustom: () => {
|
||||||
|
return `
|
||||||
|
<div class="form-actions" style="margin-bottom:10px">
|
||||||
|
<button class="ws-action" onclick="wsConnect()">Connect</button>
|
||||||
|
<button class="ws-danger" onclick="wsDisconnect()">Disconnect</button>
|
||||||
|
<span id="ws-status" class="ws-status disconnected">disconnected</span>
|
||||||
|
</div>
|
||||||
|
<div class="field"><label>token (auth)</label><input id="ws-token" placeholder="send as first message to authenticate"></div>
|
||||||
|
<div class="form-actions" style="margin-bottom:10px">
|
||||||
|
<button class="ws-action" onclick="wsAuth()">Send Auth Token</button>
|
||||||
|
</div>
|
||||||
|
<div class="field"><label>raw message</label><input id="ws-msg" placeholder='{"key":"value"}'></div>
|
||||||
|
<div class="form-actions">
|
||||||
|
<button class="ws-action" onclick="wsSend()">Send Raw</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function showForm(name) {
|
||||||
|
const panel = document.getElementById('form-panel');
|
||||||
|
const titleEl = document.getElementById('form-title');
|
||||||
|
const fieldsEl = document.getElementById('form-fields');
|
||||||
|
const buttons = document.querySelectorAll('#btn-row button');
|
||||||
|
|
||||||
|
// deactivate all buttons
|
||||||
|
buttons.forEach(b => b.classList.remove('active'));
|
||||||
|
|
||||||
|
if (activeForm === name) {
|
||||||
|
panel.classList.remove('open');
|
||||||
|
activeForm = null;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFormStatus(text, cls) {
|
activeForm = name;
|
||||||
const el = document.getElementById('form-status');
|
const def = forms[name];
|
||||||
el.textContent = text;
|
|
||||||
el.className = cls;
|
// activate clicked button
|
||||||
|
const idx = Object.keys(forms).indexOf(name);
|
||||||
|
if (idx >= 0) {
|
||||||
|
buttons[idx].classList.add('active');
|
||||||
|
if (name === 'websocket') buttons[idx].classList.add('ws');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function register() {
|
titleEl.textContent = def.title;
|
||||||
const username = document.getElementById('reg-username').value.trim();
|
|
||||||
const password = document.getElementById('reg-password').value;
|
|
||||||
const color = document.getElementById('reg-color').value.trim();
|
|
||||||
if (!username || !password || !color) { setFormStatus('Fill all fields', 'err'); return; }
|
|
||||||
|
|
||||||
const body = new URLSearchParams({ username, password, color });
|
if (def.renderCustom) {
|
||||||
try {
|
fieldsEl.innerHTML = def.renderCustom();
|
||||||
const res = await fetch('http://localhost:8080/new/client', { method: 'POST', body });
|
|
||||||
const text = await res.text();
|
|
||||||
if (res.ok) {
|
|
||||||
setFormStatus('Registered! Now login.', 'ok');
|
|
||||||
} else {
|
} else {
|
||||||
setFormStatus('Error: ' + text, 'err');
|
let html = '';
|
||||||
}
|
for (const f of def.fields) {
|
||||||
} catch (e) {
|
html += `<div class="field">
|
||||||
setFormStatus('Request failed: ' + e.message, 'err');
|
<label>${f.label}</label>
|
||||||
|
<input id="${f.id}" placeholder="${f.ph || ''}">
|
||||||
|
${f.hint ? `<span class="hint">${f.hint}</span>` : ''}
|
||||||
|
</div>`;
|
||||||
}
|
}
|
||||||
|
html += `<div class="form-actions"><button class="send" onclick="forms['${name}'].submit()">Send</button></div>`;
|
||||||
|
fieldsEl.innerHTML = html;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function login() {
|
panel.classList.add('open');
|
||||||
const username = document.getElementById('login-username').value.trim();
|
|
||||||
const password = document.getElementById('login-password').value;
|
|
||||||
if (!username || !password) { setFormStatus('Fill all fields', 'err'); return; }
|
|
||||||
|
|
||||||
const body = new URLSearchParams({ username, password });
|
|
||||||
try {
|
|
||||||
const res = await fetch('http://localhost:8080/new/token', { method: 'POST', body });
|
|
||||||
const text = await res.text();
|
|
||||||
if (res.ok) {
|
|
||||||
document.getElementById('username-input').value = username;
|
|
||||||
document.getElementById('token-input').value = text;
|
|
||||||
document.getElementById('group-token').value = text;
|
|
||||||
setFormStatus('Logged in! Token copied to Connect tab.', 'ok');
|
|
||||||
switchTab('connect');
|
|
||||||
} else {
|
|
||||||
setFormStatus('Error: ' + text, 'err');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
setFormStatus('Request failed: ' + e.message, 'err');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createGroup() {
|
function log(prefix, text, cls) {
|
||||||
const token = document.getElementById('group-token').value.trim();
|
const div = document.getElementById('log');
|
||||||
if (!token) { setFormStatus('Paste a token first', 'err'); return; }
|
const ts = new Date().toTimeString().slice(0, 8);
|
||||||
|
|
||||||
const body = new URLSearchParams({ token });
|
|
||||||
const name = document.getElementById('group-name').value.trim();
|
|
||||||
if (name) body.set('name', name);
|
|
||||||
const color = document.getElementById('group-color').value.trim();
|
|
||||||
if (color) body.set('color', color);
|
|
||||||
const enableClientColors = document.getElementById('group-client-colors').value.trim();
|
|
||||||
if (enableClientColors) body.set('enableClientColors', enableClientColors);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await fetch('http://localhost:8080/new/group', { method: 'POST', body });
|
|
||||||
if (res.ok) {
|
|
||||||
const buf = await res.arrayBuffer();
|
|
||||||
const id = new DataView(buf).getUint32(0, false);
|
|
||||||
setFormStatus('Group created! ID: ' + id, 'ok');
|
|
||||||
} else {
|
|
||||||
setFormStatus('Error: ' + await res.text(), 'err');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
setFormStatus('Request failed: ' + e.message, 'err');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setStatus(text, cls) {
|
|
||||||
const el = document.getElementById('status');
|
|
||||||
el.textContent = text;
|
|
||||||
el.className = cls || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function log(text, cls) {
|
|
||||||
const el = document.getElementById('log');
|
|
||||||
const line = document.createElement('div');
|
const line = document.createElement('div');
|
||||||
line.className = cls || '';
|
line.className = cls;
|
||||||
line.textContent = text;
|
let formatted = text;
|
||||||
el.appendChild(line);
|
try { formatted = JSON.stringify(JSON.parse(text), null, 2); } catch(e) {}
|
||||||
el.scrollTop = el.scrollHeight;
|
line.textContent = `[${ts}] ${prefix}: ${formatted}`;
|
||||||
|
div.appendChild(line);
|
||||||
|
div.scrollTop = div.scrollHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
function connect() {
|
function clearLog() { document.getElementById('log').innerHTML = ''; }
|
||||||
const token = document.getElementById('token-input').value.trim();
|
|
||||||
if (!token) { setStatus('Paste a token first', 'err'); return; }
|
|
||||||
const username = document.getElementById('username-input').value.trim() || '?';
|
|
||||||
|
|
||||||
document.getElementById('connect-btn').disabled = true;
|
function baseUrl() { return document.getElementById('baseUrl').value.replace(/\/$/, ''); }
|
||||||
setStatus('Connecting...');
|
|
||||||
|
|
||||||
ws = new WebSocket('ws://localhost:8080/ws');
|
async function httpPost(path, fieldMap) {
|
||||||
|
const params = new URLSearchParams();
|
||||||
ws.onopen = () => {
|
for (const [key, id] of Object.entries(fieldMap)) {
|
||||||
ws.send(JSON.stringify({ token }));
|
const el = document.getElementById(id);
|
||||||
document.getElementById('auth-name').textContent = username;
|
if (el && el.value !== '') params.append(key, el.value);
|
||||||
document.getElementById('auth-panel').style.display = 'none';
|
}
|
||||||
document.getElementById('chat-panel').style.display = 'block';
|
const url = baseUrl() + path;
|
||||||
log('Authenticated as ' + username, 'msg-sys');
|
log(`HTTP ${path}`, `→ ${params.toString()}`, 'log-info');
|
||||||
};
|
try {
|
||||||
|
const resp = await fetch(url, { method: 'POST', body: params });
|
||||||
ws.onmessage = (e) => {
|
const text = await resp.text();
|
||||||
let data;
|
log(`HTTP ${resp.status}`, text, resp.ok ? 'log-http' : 'log-err');
|
||||||
try { data = JSON.parse(e.data); } catch { log('← ' + e.data, 'msg-in'); return; }
|
} catch(e) {
|
||||||
|
log('HTTP ERR', e.message, 'log-err');
|
||||||
if (data.error) {
|
|
||||||
log('Error: ' + data.error, 'msg-sys');
|
|
||||||
} else {
|
|
||||||
log('← ' + JSON.stringify(data), 'msg-in');
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
ws.onerror = () => {
|
|
||||||
setStatus('Connection error', 'err');
|
|
||||||
document.getElementById('connect-btn').disabled = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onclose = () => {
|
|
||||||
log('Disconnected', 'msg-sys');
|
|
||||||
document.getElementById('chat-panel').style.display = 'none';
|
|
||||||
document.getElementById('auth-panel').style.display = 'block';
|
|
||||||
document.getElementById('connect-btn').disabled = false;
|
|
||||||
setStatus('Disconnected');
|
|
||||||
ws = null;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function send() {
|
function wsConnect() {
|
||||||
const input = document.getElementById('msg-input');
|
if (ws) { log('WS', 'already connected', 'log-info'); return; }
|
||||||
const text = input.value.trim();
|
const url = baseUrl().replace(/^http/, 'ws') + '/ws';
|
||||||
if (!text || !ws) return;
|
log('WS', `connecting to ${url}`, 'log-info');
|
||||||
ws.send(JSON.stringify({ message: text }));
|
ws = new WebSocket(url);
|
||||||
log('→ ' + text, 'msg-out');
|
ws.onopen = () => { setWsStatus(true); log('WS', 'connected', 'log-ws'); };
|
||||||
input.value = '';
|
ws.onmessage = (e) => { log('WS', e.data, 'log-ws'); };
|
||||||
|
ws.onerror = () => { log('WS ERR', 'error', 'log-err'); };
|
||||||
|
ws.onclose = () => { setWsStatus(false); log('WS', 'disconnected', 'log-info'); ws = null; };
|
||||||
}
|
}
|
||||||
|
|
||||||
function disconnect() {
|
function wsDisconnect() { if (ws) { ws.close(); ws = null; } }
|
||||||
if (ws) ws.close();
|
|
||||||
|
function wsAuth() {
|
||||||
|
if (!ws) { log('WS', 'not connected', 'log-err'); return; }
|
||||||
|
const token = document.getElementById('ws-token').value;
|
||||||
|
const msg = JSON.stringify({ token });
|
||||||
|
ws.send(msg);
|
||||||
|
log('WS →', msg, 'log-info');
|
||||||
|
}
|
||||||
|
|
||||||
|
function wsSend() {
|
||||||
|
if (!ws) { log('WS', 'not connected', 'log-err'); return; }
|
||||||
|
const msg = document.getElementById('ws-msg').value;
|
||||||
|
ws.send(msg);
|
||||||
|
log('WS →', msg, 'log-info');
|
||||||
|
}
|
||||||
|
|
||||||
|
function setWsStatus(connected) {
|
||||||
|
const el = document.getElementById('ws-status');
|
||||||
|
if (!el) return;
|
||||||
|
el.textContent = connected ? 'connected' : 'disconnected';
|
||||||
|
el.className = 'ws-status ' + (connected ? 'connected' : 'disconnected');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func main() {
|
|||||||
http.HandleFunc("/mod/group/addclients", withCORS(HttpHandleGroupAddClient))
|
http.HandleFunc("/mod/group/addclients", withCORS(HttpHandleGroupAddClient))
|
||||||
http.HandleFunc("/mod/group/removeclients", withCORS(HttpHandleGroupRemoveClient))
|
http.HandleFunc("/mod/group/removeclients", withCORS(HttpHandleGroupRemoveClient))
|
||||||
http.HandleFunc("/mod/group/color", withCORS(HttpHandleGroupChangeColor))
|
http.HandleFunc("/mod/group/color", withCORS(HttpHandleGroupChangeColor))
|
||||||
http.HandleFunc("/mod/group/color", withCORS(HttpHandleGroupChangeOwner))
|
http.HandleFunc("/mod/group/owner", withCORS(HttpHandleGroupChangeOwner))
|
||||||
http.HandleFunc("/new/message", withCORS(HttpHandleNewMessage))
|
http.HandleFunc("/new/message", withCORS(HttpHandleNewMessage))
|
||||||
http.HandleFunc("/get/groups", withCORS(HttpHandleGroupsGetWithoutMembers))
|
http.HandleFunc("/get/groups", withCORS(HttpHandleGroupsGetWithoutMembers))
|
||||||
http.HandleFunc("/get/group/members", withCORS(HttpHandleGroupMembersGet))
|
http.HandleFunc("/get/group/members", withCORS(HttpHandleGroupMembersGet))
|
||||||
|
|||||||
Reference in New Issue
Block a user