Files
go-socket/machine-client/index.html
T

413 lines
17 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>go-socket debug</title>
<style>
body { font-family: monospace; background: #1a1a1a; color: #e0e0e0; margin: 20px; }
h1 { color: #aaa; font-size: 1.2em; margin-bottom: 12px; }
.config { margin-bottom: 10px; }
.config label { color: #888; }
.config input { background: #2a2a2a; color: #e0e0e0; border: 1px solid #444; padding: 4px 8px; width: 300px; }
/* LOG */
#log-header { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
#log-header span { color: #888; font-size: 1em; }
#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; }
.log-ws { color: #8af; }
.log-http { color: #af8; }
.log-err { color: #f88; }
.log-info { color: #666; }
/* BUTTON ROW */
#btn-row { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
#btn-row button {
background: #2a2a2a; color: #ccc; border: 1px solid #444;
padding: 5px 10px; cursor: pointer; font-family: monospace; font-size: 0.8em;
}
#btn-row button:hover { background: #333; border-color: #666; }
#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; }
/* FORM PANEL */
#form-panel { display: none; background: #1e1e1e; border: 1px solid #333; padding: 12px; margin-bottom: 6px; }
#form-panel.open { display: block; }
#form-title { color: #888; font-size: 0.85em; margin-bottom: 10px; }
.field { margin-bottom: 7px; }
.field label { display: inline-block; width: 160px; color: #777; font-size: 0.88em; }
.field input { background: #2a2a2a; color: #e0e0e0; border: 1px solid #444; padding: 4px 8px; width: 260px; font-family: monospace; font-size: 0.88em; }
.hint { color: #555; font-size: 0.78em; margin-left: 5px; }
.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; }
button.send:hover { background: #3a5a3a; }
button.ws-action { background: #2a2a4a; color: #88f; border: 1px solid #44a; padding: 5px 14px; cursor: pointer; font-family: monospace; }
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; }
.ws-status { font-size: 0.82em; }
.ws-status.connected { color: #8f8; }
.ws-status.disconnected { color: #f88; }
</style>
</head>
<body>
<h1>go-socket debug</h1>
<div class="config">
<label>Base URL: </label>
<input id="baseUrl" value="http://localhost:8080">
</div>
<div id="log-header">
<span>Response Log</span>
<button id="clear-log" onclick="clearLog()">Clear</button>
</div>
<div id="log"></div>
<div id="btn-row">
<button onclick="showForm('new-user')">POST /new/user</button>
<button onclick="showForm('new-connection')">POST /new/connection</button>
<button onclick="showForm('new-token')">POST /new/token</button>
<button onclick="showForm('new-group')">POST /new/group</button>
<button onclick="showForm('mod-user-appearance')">POST /mod/user/appearence</button>
<button onclick="showForm('mod-user-about')">POST /mod/user/about</button>
<button onclick="showForm('accept-connection')">POST /mod/connection/accept</button>
<button onclick="showForm('delete-connection')" class="warn">POST /mod/connection/delete</button>
<button onclick="showForm('add-users')">POST /mod/group/addusers</button>
<button onclick="showForm('remove-users')">POST /mod/group/removeusers</button>
<button onclick="showForm('group-color')">POST /mod/group/color</button>
<button onclick="showForm('group-owner')">POST /mod/group/owner</button>
<button onclick="showForm('get-groups')">POST /get/groups</button>
<button onclick="showForm('get-connections')">POST /get/connections</button>
<button onclick="showForm('get-members')">POST /get/group/members</button>
<button onclick="showForm('del-group')" class="warn">POST /del/group</button>
<button onclick="showForm('msg-user')">POST /msg/user</button>
<button onclick="showForm('msg-group')">POST /msg/group</button>
<button onclick="showForm('websocket')">WS /ws</button>
</div>
<div id="form-panel">
<div id="form-title"></div>
<div id="form-fields"></div>
</div>
<script>
let ws = null;
let activeForm = null;
let currentToken = '';
function autofillTokens() {
if (!currentToken) return;
document.querySelectorAll('input[id$="-token"]').forEach(el => { el.value = currentToken; });
}
var formDefs = {
'new-user': {
title: 'POST /new/user — register new user',
fields: [
{ id: 'nu-username', label: 'username', ph: 'min 4 chars' },
{ id: 'nu-password', label: 'password', ph: 'min 8 chars' },
{ id: 'nu-color', label: 'color', ph: '255,100,50', hint: 'R,G,B' },
],
submit: () => httpPost('/new/user', { username:'nu-username', password:'nu-password', color:'nu-color' })
},
'new-connection': {
title: 'POST /new/connection — send connection request',
fields: [
{ id: 'nconn-token', label: 'token', ph: '' },
{ id: 'nconn-recipient', label: 'recipient', ph: 'uint32' },
],
submit: () => httpPost('/new/connection', { token:'nconn-token', recipient:'nconn-recipient' })
},
'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 optional', hint: 'R,G,B' },
{ id: 'ng-enableUserColors', label: 'enableUserColors', ph: '1 to enable (optional)' },
],
submit: () => httpPost('/new/group', { token:'ng-token', name:'ng-name', color:'ng-color', enableUserColors:'ng-enableUserColors' })
},
'mod-user-appearance': {
title: 'POST /mod/user/appearence — change user color',
fields: [
{ id: 'mua-token', label: 'token', ph: '' },
{ id: 'mua-color', label: 'color', ph: '255,100,50', hint: 'R,G,B' },
],
submit: () => httpPost('/mod/user/appearence', { token:'mua-token', color:'mua-color' })
},
'mod-user-about': {
title: 'POST /mod/user/about — change user pronouns',
fields: [
{ id: 'mub-token', label: 'token', ph: '' },
{ id: 'mub-pronouns', label: 'pronouns', ph: '225 chars' },
],
submit: () => httpPost('/mod/user/about', { token:'mub-token', pronouns:'mub-pronouns' })
},
'accept-connection': {
title: 'POST /mod/connection/accept — accept connection request',
fields: [
{ id: 'ca-token', label: 'token', ph: '' },
{ id: 'ca-connectionid', label: 'connectionid', ph: 'UUID' },
],
submit: () => httpPost('/mod/connection/accept', { token:'ca-token', connectionid:'ca-connectionid' })
},
'delete-connection': {
title: 'POST /mod/connection/delete — delete connection',
fields: [
{ id: 'cd-token', label: 'token', ph: '' },
{ id: 'cd-connectionid', label: 'connectionid', ph: 'UUID' },
],
submit: () => httpPost('/mod/connection/delete', { token:'cd-token', connectionid:'cd-connectionid' })
},
'add-users': {
title: 'POST /mod/group/addusers — add users (owner only)',
fields: [
{ id: 'au-token', label: 'token', ph: '' },
{ id: 'au-groupid', label: 'groupid', ph: 'uint32' },
{ id: 'au-users', label: 'users', ph: 'id1,id2,id3', hint: 'csv of uint32' },
],
submit: () => httpPost('/mod/group/addusers', { token:'au-token', groupid:'au-groupid', users:'au-users' })
},
'remove-users': {
title: 'POST /mod/group/removeusers — remove users (owner only)',
fields: [
{ id: 'ru-token', label: 'token', ph: '' },
{ id: 'ru-groupid', label: 'groupid', ph: 'uint32' },
{ id: 'ru-users', label: 'users', ph: 'id1,id2,id3', hint: 'csv of uint32' },
],
submit: () => httpPost('/mod/group/removeusers', { token:'ru-token', groupid:'ru-groupid', users:'ru-users' })
},
'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/owner', { token:'go-token', groupid:'go-groupid', newOwner:'go-newOwner' })
},
'get-groups': {
title: "POST /get/groups — get user's groups (no members)",
fields: [
{ id: 'gg-token', label: 'token', ph: '' },
],
submit: () => httpPost('/get/groups', { token:'gg-token' })
},
'get-connections': {
title: "POST /get/connections — get user's connections",
fields: [
{ id: 'gconn-token', label: 'token', ph: '' },
],
submit: () => httpPost('/get/connections', { token:'gconn-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' })
},
'del-group': {
title: 'POST /del/group — delete group (owner only)',
fields: [
{ id: 'dg-token', label: 'token', ph: '' },
{ id: 'dg-groupid', label: 'groupid', ph: 'uint32' },
],
submit: () => httpPost('/del/group', { token:'dg-token', groupid:'dg-groupid' })
},
'msg-user': {
title: 'POST /msg/user — send direct message to user',
fields: [
{ id: 'mu-token', label: 'token', ph: '' },
{ id: 'mu-connectionid', label: 'connectionid', ph: 'UUID' },
{ id: 'mu-msgContent', label: 'msgContent', ph: 'message text' },
],
submit: () => httpPost('/msg/user', { token:'mu-token', connectionid:'mu-connectionid', msgContent:'mu-msgContent' })
},
'msg-group': {
title: 'POST /msg/group — send message to group',
fields: [
{ id: 'mg-token', label: 'token', ph: '' },
{ id: 'mg-groupid', label: 'groupid', ph: 'uint32' },
{ id: 'mg-content', label: 'content', ph: 'message text' },
],
submit: () => httpPost('/msg/group', { token:'mg-token', groupid:'mg-groupid', content:'mg-content' })
},
'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;
}
activeForm = name;
const def = formDefs[name];
// activate clicked button
const idx = Object.keys(formDefs).indexOf(name);
if (idx >= 0) {
buttons[idx].classList.add('active');
if (name === 'websocket') buttons[idx].classList.add('ws');
}
titleEl.textContent = def.title;
if (def.renderCustom) {
fieldsEl.innerHTML = def.renderCustom();
} else {
let html = '';
for (const f of def.fields) {
html += `<div class="field">
<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="formDefs['${name}'].submit()">Send</button></div>`;
fieldsEl.innerHTML = html;
autofillTokens();
}
panel.classList.add('open');
}
function log(prefix, text, cls) {
const div = document.getElementById('log');
const ts = new Date().toTimeString().slice(0, 8);
const line = document.createElement('div');
line.className = cls;
let formatted = text;
try { formatted = JSON.stringify(JSON.parse(text), null, 2); } catch(e) {}
line.textContent = `[${ts}] ${prefix}: ${formatted}`;
div.appendChild(line);
div.scrollTop = div.scrollHeight;
}
function clearLog() { document.getElementById('log').innerHTML = ''; }
function baseUrl() { return document.getElementById('baseUrl').value.replace(/\/$/, ''); }
async function httpPost(path, fieldMap) {
const params = new URLSearchParams();
for (const [key, id] of Object.entries(fieldMap)) {
const el = document.getElementById(id);
if (el && el.value !== '') params.append(key, el.value);
}
const url = baseUrl() + path;
log(`HTTP ${path}`, `${params.toString()}`, 'log-info');
try {
const resp = await fetch(url, { method: 'POST', body: params });
const text = await resp.text();
log(`HTTP ${resp.status}`, text, resp.ok ? 'log-http' : 'log-err');
if (path === '/new/token' && resp.ok) {
try { currentToken = JSON.parse(text).token; } catch(e) {}
autofillTokens();
wsConnectAndAuth();
}
} catch(e) {
log('HTTP ERR', e.message, 'log-err');
}
}
function wsConnectAndAuth() {
if (ws) { wsAuth(); return; }
const url = baseUrl().replace(/^http/, 'ws') + '/ws';
log('WS', `auto-connecting to ${url}`, 'log-info');
ws = new WebSocket(url);
ws.onopen = () => { setWsStatus(true); log('WS', 'connected', 'log-ws'); wsAuth(); };
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 wsConnect() {
if (ws) { log('WS', 'already connected', 'log-info'); return; }
const url = baseUrl().replace(/^http/, 'ws') + '/ws';
log('WS', `connecting to ${url}`, 'log-info');
ws = new WebSocket(url);
ws.onopen = () => { setWsStatus(true); log('WS', 'connected', 'log-ws'); };
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 wsDisconnect() { if (ws) { ws.close(); ws = null; } }
function wsAuth() {
if (!ws) { log('WS', 'not connected', 'log-err'); return; }
const el = document.getElementById('ws-token');
const token = el ? el.value : currentToken;
if (!token) { log('WS', 'no token to auth with', 'log-err'); return; }
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>
</body>
</html>