433 lines
18 KiB
HTML
433 lines
18 KiB
HTML
<!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('del-connection')" class="warn">POST /del/connection</button>
|
||
<button onclick="showForm('get-connection-messages')">POST /get/connection/messages</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: '2–25 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' })
|
||
},
|
||
'del-connection': {
|
||
title: 'POST /del/connection — delete a connection',
|
||
fields: [
|
||
{ id: 'dc-token', label: 'token', ph: '' },
|
||
{ id: 'dc-connectionid', label: 'connectionid', ph: 'UUID' },
|
||
],
|
||
submit: () => httpPost('/del/connection', { token:'dc-token', connectionid:'dc-connectionid' })
|
||
},
|
||
'get-connection-messages': {
|
||
title: 'POST /get/connection/messages — fetch message history',
|
||
fields: [
|
||
{ id: 'gcm-token', label: 'token', ph: '' },
|
||
{ id: 'gcm-connectionid', label: 'connectionid', ph: 'UUID' },
|
||
{ id: 'gcm-messages', label: 'messages', ph: 'count (optional)' },
|
||
{ id: 'gcm-before', label: 'before', ph: 'RFC3339 (optional)', hint: 'e.g. 2025-01-01T00:00:00Z' },
|
||
],
|
||
submit: () => httpPost('/get/connection/messages', { token:'gcm-token', connectionid:'gcm-connectionid', messages:'gcm-messages', before:'gcm-before' })
|
||
},
|
||
'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>
|