Files
go-socket/machine-client/index.html
T
admin c0d4483154 fix hub bugs, add channel role permission endpoints
Covers: snake_case param renames, mutex RLock/Unlock mismatches, user.Hubs keyed by hub.Id, hub color using new_name, DELETE params sent as query, delete(target.Hubs, hub.Id), root/member role Id swap, and the three new PATCH
  /hub/channel/roles/* handlers.
2026-05-03 15:51:57 +02:00

862 lines
57 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>go-socket debug</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>go-socket debug</h1>
<div class="config">
<label>Base URL: </label>
<input id="baseUrl" value="http://localhost:8080">
<span id="current-user" style="margin-left:14px;color:#666;font-size:0.85em"></span>
<span id="current-hub" style="margin-left:14px;color:#666;font-size:0.85em"></span>
</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 data-form="new-user" onclick="showForm('new-user')">POST /user</button>
<button data-form="new-token" onclick="showForm('new-token')">POST /token</button>
<button data-form="new-connection" onclick="showForm('new-connection')">POST /connection</button>
<button data-form="file-upload" onclick="showForm('file-upload')">POST /file</button>
<button data-form="mod-user-profile" onclick="showForm('mod-user-profile')">PATCH /user/profile</button>
<button data-form="mod-user-avatar" onclick="showForm('mod-user-avatar')">PATCH /user/avatar</button>
<button data-form="mod-user-profilebg" onclick="showForm('mod-user-profilebg')">PATCH /user/profilebg</button>
<button data-form="mod-connection-elevate" onclick="showForm('mod-connection-elevate')">POST /connection/elevate</button>
<button data-form="mod-connection-deelevate" onclick="showForm('mod-connection-deelevate')">POST /connection/deelevate</button>
<button data-form="get-user" onclick="showForm('get-user')">GET /user</button>
<button data-form="get-connections" onclick="showForm('get-connections')">GET /connections</button>
<button data-form="get-connections-unread" onclick="showForm('get-connections-unread')">GET /connections/unreadmessages</button>
<button data-form="get-connection-messages" onclick="showForm('get-connection-messages')">GET /connection/messages</button>
<button data-form="file-download" onclick="showForm('file-download')">GET /file</button>
<button data-form="get-user-avatar" onclick="showForm('get-user-avatar')">GET /user/avatar</button>
<button data-form="get-user-profilebg" onclick="showForm('get-user-profilebg')">GET /user/profilebg</button>
<button data-form="del-user" class="warn" onclick="showForm('del-user')">DELETE /user</button>
<button data-form="del-connection" class="warn" onclick="showForm('del-connection')">DELETE /connection</button>
<button data-form="msg-user" onclick="showForm('msg-user')">POST /connection/message</button>
<button data-form="hub-create" onclick="showForm('hub-create')">POST /hub</button>
<button data-form="hub-message" onclick="showForm('hub-message')">POST /hub/channel/message</button>
<button data-form="hub-join" onclick="showForm('hub-join')">PUT /hub/join</button>
<button data-form="get-hubs" onclick="showForm('get-hubs')">GET /hubs</button>
<button data-form="get-hub-data" onclick="showForm('get-hub-data')">GET /hub</button>
<button data-form="get-hub-channel-data" onclick="showForm('get-hub-channel-data')">GET /hub/channel</button>
<button data-form="get-hub-channels" onclick="showForm('get-hub-channels')">GET /hubs/channels</button>
<button data-form="get-hub-users" onclick="showForm('get-hub-users')">GET /hubs/users</button>
<button data-form="get-hub-roles" onclick="showForm('get-hub-roles')">GET /hubs/roles</button>
<button data-form="get-users" onclick="showForm('get-users')">GET /users</button>
<button data-form="hub-set-name" onclick="showForm('hub-set-name')">PATCH /hub/name</button>
<button data-form="hub-set-color" onclick="showForm('hub-set-color')">PATCH /hub/color</button>
<button data-form="hub-toggle-color" onclick="showForm('hub-toggle-color')">PATCH /hub/usercolorallowed</button>
<button data-form="hub-user-rename" onclick="showForm('hub-user-rename')">PATCH /hub/user/name</button>
<button data-form="hub-self-rename" onclick="showForm('hub-self-rename')">PATCH /hub/self/name</button>
<button data-form="hub-user-mute" onclick="showForm('hub-user-mute')">PATCH /hub/user/mute</button>
<button data-form="hub-role-set-name" onclick="showForm('hub-role-set-name')">PATCH /hub/role/name</button>
<button data-form="hub-role-set-color" onclick="showForm('hub-role-set-color')">PATCH /hub/role/color</button>
<button data-form="hub-role-set-perms" onclick="showForm('hub-role-set-perms')">PATCH /hub/role/permissions</button>
<button data-form="hub-channel-set-name" onclick="showForm('hub-channel-set-name')">PATCH /hub/channel/name</button>
<button data-form="hub-channel-set-desc" onclick="showForm('hub-channel-set-desc')">PATCH /hub/channel/description</button>
<button data-form="hub-channel-role-view" onclick="showForm('hub-channel-role-view')">PATCH /hub/channel/roles/view</button>
<button data-form="hub-channel-role-send" onclick="showForm('hub-channel-role-send')">PATCH /hub/channel/roles/send</button>
<button data-form="hub-channel-role-history" onclick="showForm('hub-channel-role-history')">PATCH /hub/channel/roles/history</button>
<button data-form="hub-user-add-role" onclick="showForm('hub-user-add-role')">PUT /hub/user/role</button>
<button data-form="hub-role-create" onclick="showForm('hub-role-create')">PUT /hub/role</button>
<button data-form="hub-channel-create" onclick="showForm('hub-channel-create')">PUT /hub/channel</button>
<button data-form="hub-remove" class="warn" onclick="showForm('hub-remove')">DELETE /hub</button>
<button data-form="hub-user-remove" class="warn" onclick="showForm('hub-user-remove')">DELETE /hub/user</button>
<button data-form="hub-user-remove-role" class="warn" onclick="showForm('hub-user-remove-role')">DELETE /hub/user/role</button>
<button data-form="hub-role-remove" class="warn" onclick="showForm('hub-role-remove')">DELETE /hub/role</button>
<button data-form="hub-self-remove-role" class="warn" onclick="showForm('hub-self-remove-role')">DELETE /hub/self/role</button>
<button data-form="hub-channel-remove" class="warn" onclick="showForm('hub-channel-remove')">DELETE /hub/channel</button>
<button data-form="websocket" onclick="showForm('websocket')">WS /ws</button>
</div>
<div id="form-panel">
<div id="form-title"></div>
<!-- POST /user -->
<div class="form-content" id="fc-new-user">
<div class="field"><label>username</label><input id="nu-username" placeholder="min 4 chars"></div>
<div class="field"><label>password</label><input id="nu-password" placeholder="min 8 chars"></div>
<div class="form-actions"><button class="send" onclick="submit('new-user')">Send</button></div>
</div>
<!-- POST /token -->
<div class="form-content" id="fc-new-token">
<div class="field"><label>username</label><input id="nt-username" placeholder="min 4 chars"></div>
<div class="field"><label>password</label><input id="nt-password" placeholder="min 8 chars"></div>
<div class="form-actions"><button class="send" onclick="submit('new-token')">Send</button></div>
</div>
<!-- POST /connection -->
<div class="form-content" id="fc-new-connection">
<div class="field"><label>token</label><input id="nconn-token" placeholder=""></div>
<div class="field"><label>recipient</label><input id="nconn-recipient" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submit('new-connection')">Send</button></div>
</div>
<!-- POST /file -->
<div class="form-content" id="fc-file-upload">
<div class="field"><label>token</label><input id="fu-token" placeholder=""></div>
<div class="field"><label>connectionid</label><input id="fu-connectionid" placeholder="UUID"></div>
<div class="field"><label>file</label><input id="fu-file" type="file"></div>
<div class="form-actions"><button class="send" onclick="submitFileUpload()">Send</button></div>
</div>
<!-- PATCH /user/profile -->
<div class="form-content" id="fc-mod-user-profile">
<div class="field"><label>token</label><input id="mup-token" placeholder=""></div>
<div class="field"><label>pronouns</label><input id="mup-pronouns" placeholder="optional"></div>
<div class="field"><label>description</label><input id="mup-description" placeholder="optional"></div>
<div class="field"><label>color</label><input id="mup-color" placeholder="255,100,50 optional"><span class="hint">R,G,B</span></div>
<div class="form-actions"><button class="send" onclick="submit('mod-user-profile')">Send</button></div>
</div>
<!-- PATCH /user/avatar -->
<div class="form-content" id="fc-mod-user-avatar">
<div class="field"><label>token</label><input id="mua-token" placeholder=""></div>
<div class="field"><label>file</label><input id="mua-file" type="file"></div>
<div class="form-actions"><button class="send" onclick="submitAvatarUpload()">Send</button></div>
</div>
<!-- PATCH /user/profilebg -->
<div class="form-content" id="fc-mod-user-profilebg">
<div class="field"><label>token</label><input id="mpb-token" placeholder=""></div>
<div class="field"><label>file</label><input id="mpb-file" type="file"></div>
<div class="form-actions"><button class="send" onclick="submitProfileBgUpload()">Send</button></div>
</div>
<!-- POST /connection/elevate -->
<div class="form-content" id="fc-mod-connection-elevate">
<div class="field"><label>token</label><input id="mce-token" placeholder=""></div>
<div class="field"><label>connectionid</label><input id="mce-connectionid" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submit('mod-connection-elevate')">Send</button></div>
</div>
<!-- POST /connection/deelevate -->
<div class="form-content" id="fc-mod-connection-deelevate">
<div class="field"><label>token</label><input id="mcde-token" placeholder=""></div>
<div class="field"><label>connectionid</label><input id="mcde-connectionid" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submit('mod-connection-deelevate')">Send</button></div>
</div>
<!-- GET /user -->
<div class="form-content" id="fc-get-user">
<div class="field"><label>token</label><input id="gu-token" placeholder=""></div>
<div class="field"><label>targetid</label><input id="gu-targetid" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submit('get-user')">Send</button></div>
</div>
<!-- GET /connections -->
<div class="form-content" id="fc-get-connections">
<div class="field"><label>token</label><input id="gconn-token" placeholder=""></div>
<div class="form-actions"><button class="send" onclick="submit('get-connections')">Send</button></div>
</div>
<!-- GET /connections/unreadmessages -->
<div class="form-content" id="fc-get-connections-unread">
<div class="field"><label>token</label><input id="gcur-token" placeholder=""></div>
<div class="field"><label>connections</label><input id="gcur-connections" placeholder="UUID,UUID,..."></div>
<div class="form-actions"><button class="send" onclick="submit('get-connections-unread')">Send</button></div>
</div>
<!-- GET /connection/messages -->
<div class="form-content" id="fc-get-connection-messages">
<div class="field"><label>token</label><input id="gcm-token" placeholder=""></div>
<div class="field"><label>connectionid</label><input id="gcm-connectionid" placeholder="UUID"></div>
<div class="field"><label>messages</label><input id="gcm-messages" placeholder="count (optional)"></div>
<div class="field"><label>before</label><input id="gcm-before" placeholder="RFC3339 (optional)"><span class="hint">e.g. 2025-01-01T00:00:00Z</span></div>
<div class="form-actions"><button class="send" onclick="submit('get-connection-messages')">Send</button></div>
</div>
<!-- GET /file -->
<div class="form-content" id="fc-file-download">
<div class="field"><label>token</label><input id="fd-token" placeholder=""></div>
<div class="field"><label>connectionid</label><input id="fd-connectionid" placeholder="UUID"></div>
<div class="field"><label>key</label><input id="fd-key" placeholder="returned by upload"></div>
<div class="form-actions"><button class="send" onclick="submitFileDownload()">Send</button></div>
</div>
<!-- GET /user/avatar -->
<div class="form-content" id="fc-get-user-avatar">
<div class="field"><label>token</label><input id="gua-token" placeholder=""></div>
<div class="field"><label>userid</label><input id="gua-userid" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submitGetUserAvatar()">Send</button></div>
</div>
<!-- GET /user/profilebg -->
<div class="form-content" id="fc-get-user-profilebg">
<div class="field"><label>token</label><input id="gpb-token" placeholder=""></div>
<div class="field"><label>userid</label><input id="gpb-userid" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submitGetUserProfileBg()">Send</button></div>
</div>
<!-- DELETE /user -->
<div class="form-content" id="fc-del-user">
<div class="field"><label>token</label><input id="du-token" placeholder=""></div>
<div class="form-actions"><button class="send" onclick="submit('del-user')">Send</button></div>
</div>
<!-- DELETE /connection -->
<div class="form-content" id="fc-del-connection">
<div class="field"><label>token</label><input id="dc-token" placeholder=""></div>
<div class="field"><label>connectionid</label><input id="dc-connectionid" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submit('del-connection')">Send</button></div>
</div>
<!-- POST /connection/message -->
<div class="form-content" id="fc-msg-user">
<div class="field"><label>token</label><input id="mu-token" placeholder=""></div>
<div class="field"><label>connectionid</label><input id="mu-connectionid" placeholder="UUID"></div>
<div class="field"><label>msgContent</label><input id="mu-msgContent" placeholder="message text (optional if file set)"></div>
<div class="field"><label>attachedFile</label><input id="mu-attachedFile" placeholder="key from POST /file (optional)"></div>
<div class="form-actions"><button class="send" onclick="submit('msg-user')">Send</button></div>
</div>
<!-- POST /hub -->
<div class="form-content" id="fc-hub-create">
<div class="field"><label>token</label><input id="hc-token" placeholder=""></div>
<div class="field"><label>hubname</label><input id="hc-hubname" placeholder="name of new hub"></div>
<div class="form-actions"><button class="send" onclick="submit('hub-create')">Send</button></div>
</div>
<!-- POST /hub/message -->
<div class="form-content" id="fc-hub-message">
<div class="field"><label>token</label><input id="hm-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hm-hubid" placeholder="UUID"><span class="hint">sent as header</span></div>
<div class="field"><label>channelid</label><input id="hm-channelid" placeholder="UUID"><span class="hint">sent as header</span></div>
<div class="field"><label>msgContent</label><input id="hm-msgContent" placeholder="message text (optional if file set)"></div>
<div class="field"><label>attachedFile</label><input id="hm-attachedFile" placeholder="key from POST /file (optional)"></div>
<div class="form-actions"><button class="send" onclick="submit('hub-message')">Send</button></div>
</div>
<!-- PUT /hub/join -->
<div class="form-content" id="fc-hub-join">
<div class="field"><label>token</label><input id="hj-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hj-hubid" placeholder="UUID"><span class="hint">sent as header</span></div>
<div class="form-actions"><button class="send" onclick="submit('hub-join')">Send</button></div>
</div>
<!-- GET /hubs -->
<div class="form-content" id="fc-get-hubs">
<div class="field"><label>token</label><input id="gh-token" placeholder=""></div>
<div class="form-actions"><button class="send" onclick="submit('get-hubs')">Send</button></div>
</div>
<!-- GET /users -->
<div class="form-content" id="fc-get-users">
<div class="field"><label>token</label><input id="gus-token" placeholder=""></div>
<div class="field"><label>targetids</label><input id="gus-targetids" placeholder="UUID,UUID,..."></div>
<div class="form-actions"><button class="send" onclick="submit('get-users')">Send</button></div>
</div>
<!-- GET /hubs/channels -->
<div class="form-content" id="fc-get-hub-channels">
<div class="field"><label>token</label><input id="ghc-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="ghc-hubid" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submit('get-hub-channels')">Send</button></div>
</div>
<!-- GET /hubs/users -->
<div class="form-content" id="fc-get-hub-users">
<div class="field"><label>token</label><input id="ghu-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="ghu-hubid" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submit('get-hub-users')">Send</button></div>
</div>
<!-- PATCH /hub/name -->
<div class="form-content" id="fc-hub-set-name">
<div class="field"><label>token</label><input id="hsn-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hsn-hubid" placeholder="UUID"></div>
<div class="field"><label>newname</label><input id="hsn-newname" placeholder=""></div>
<div class="form-actions"><button class="send" onclick="submit('hub-set-name')">Send</button></div>
</div>
<!-- PATCH /hub/color -->
<div class="form-content" id="fc-hub-set-color">
<div class="field"><label>token</label><input id="hsc-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hsc-hubid" placeholder="UUID"></div>
<div class="field"><label>new_color (R,G,B,A)</label><input id="hsc-newcolor" placeholder="255,100,50,255"></div>
<div class="form-actions"><button class="send" onclick="submit('hub-set-color')">Send</button></div>
</div>
<!-- DELETE /hub -->
<div class="form-content" id="fc-hub-remove">
<div class="field"><label>token</label><input id="hr-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hr-hubid" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submit('hub-remove')">Send</button></div>
</div>
<!-- PATCH /hub/usercolorallowed -->
<div class="form-content" id="fc-hub-toggle-color">
<div class="field"><label>token</label><input id="htc-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="htc-hubid" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submit('hub-toggle-color')">Send</button></div>
</div>
<!-- DELETE /hub/user -->
<div class="form-content" id="fc-hub-user-remove">
<div class="field"><label>token</label><input id="hur-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hur-hubid" placeholder="UUID"></div>
<div class="field"><label>targetid</label><input id="hur-targetid" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submit('hub-user-remove')">Send</button></div>
</div>
<!-- PATCH /hub/user/name -->
<div class="form-content" id="fc-hub-user-rename">
<div class="field"><label>token</label><input id="hun-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hun-hubid" placeholder="UUID"></div>
<div class="field"><label>targetid</label><input id="hun-targetid" placeholder="UUID"></div>
<div class="field"><label>newname</label><input id="hun-newname" placeholder=""></div>
<div class="form-actions"><button class="send" onclick="submit('hub-user-rename')">Send</button></div>
</div>
<!-- PATCH /hub/self/name -->
<div class="form-content" id="fc-hub-self-rename">
<div class="field"><label>token</label><input id="hsr-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hsr-hubid" placeholder="UUID"></div>
<div class="field"><label>newname</label><input id="hsr-newname" placeholder=""></div>
<div class="form-actions"><button class="send" onclick="submit('hub-self-rename')">Send</button></div>
</div>
<!-- PATCH /hub/user/mute -->
<div class="form-content" id="fc-hub-user-mute">
<div class="field"><label>token</label><input id="hum-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hum-hubid" placeholder="UUID"></div>
<div class="field"><label>targetid</label><input id="hum-targetid" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submit('hub-user-mute')">Send</button></div>
</div>
<!-- PUT /hub/user/role -->
<div class="form-content" id="fc-hub-user-add-role">
<div class="field"><label>token</label><input id="huar-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="huar-hubid" placeholder="UUID"></div>
<div class="field"><label>targetid</label><input id="huar-targetid" placeholder="UUID"></div>
<div class="field"><label>roleid</label><input id="huar-roleid" placeholder="uint8"></div>
<div class="form-actions"><button class="send" onclick="submit('hub-user-add-role')">Send</button></div>
</div>
<!-- DELETE /hub/user/role -->
<div class="form-content" id="fc-hub-user-remove-role">
<div class="field"><label>token</label><input id="hurr-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hurr-hubid" placeholder="UUID"></div>
<div class="field"><label>targetid</label><input id="hurr-targetid" placeholder="UUID"></div>
<div class="field"><label>roleid</label><input id="hurr-roleid" placeholder="uint8"></div>
<div class="form-actions"><button class="send" onclick="submit('hub-user-remove-role')">Send</button></div>
</div>
<!-- PUT /hub/role -->
<div class="form-content" id="fc-hub-role-create">
<div class="field"><label>token</label><input id="hrc-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hrc-hubid" placeholder="UUID"></div>
<div class="field"><label>name</label><input id="hrc-name" placeholder=""></div>
<div class="form-actions"><button class="send" onclick="submit('hub-role-create')">Send</button></div>
</div>
<!-- DELETE /hub/role -->
<div class="form-content" id="fc-hub-role-remove">
<div class="field"><label>token</label><input id="hrr-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hrr-hubid" placeholder="UUID"></div>
<div class="field"><label>roleid</label><input id="hrr-roleid" placeholder="uint8"></div>
<div class="form-actions"><button class="send" onclick="submit('hub-role-remove')">Send</button></div>
</div>
<!-- PATCH /hub/role/name -->
<div class="form-content" id="fc-hub-role-set-name">
<div class="field"><label>token</label><input id="hrsn-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hrsn-hubid" placeholder="UUID"></div>
<div class="field"><label>roleid</label><input id="hrsn-roleid" placeholder="uint8"></div>
<div class="field"><label>newname</label><input id="hrsn-newname" placeholder=""></div>
<div class="form-actions"><button class="send" onclick="submit('hub-role-set-name')">Send</button></div>
</div>
<!-- PATCH /hub/role/color -->
<div class="form-content" id="fc-hub-role-set-color">
<div class="field"><label>token</label><input id="hrsc-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hrsc-hubid" placeholder="UUID"></div>
<div class="field"><label>roleid</label><input id="hrsc-roleid" placeholder="uint8"></div>
<div class="field"><label>newcolor (R,G,B,A)</label><input id="hrsc-newcolor" placeholder="255,100,50,255"></div>
<div class="form-actions"><button class="send" onclick="submit('hub-role-set-color')">Send</button></div>
</div>
<!-- PATCH /hub/role/permissions -->
<div class="form-content" id="fc-hub-role-set-perms">
<div class="field"><label>token</label><input id="hrsp-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hrsp-hubid" placeholder="UUID"></div>
<div class="field"><label>roleid</label><input id="hrsp-roleid" placeholder="uint8"></div>
<div class="field"><label>permissions</label><input id="hrsp-permissions" placeholder="set_hub_name=true&amp;remove_hub=false"><span class="hint">key=bool pairs</span></div>
<div class="form-actions"><button class="send" onclick="submitHubRoleSetPerms()">Send</button></div>
</div>
<!-- DELETE /hub/self/role -->
<div class="form-content" id="fc-hub-self-remove-role">
<div class="field"><label>token</label><input id="hsrr-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hsrr-hubid" placeholder="UUID"></div>
<div class="field"><label>roleid</label><input id="hsrr-roleid" placeholder="uint8"></div>
<div class="form-actions"><button class="send" onclick="submit('hub-self-remove-role')">Send</button></div>
</div>
<!-- PUT /hub/channel -->
<div class="form-content" id="fc-hub-channel-create">
<div class="field"><label>token</label><input id="hcc-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hcc-hubid" placeholder="UUID"></div>
<div class="field"><label>name</label><input id="hcc-name" placeholder=""></div>
<div class="form-actions"><button class="send" onclick="submit('hub-channel-create')">Send</button></div>
</div>
<!-- DELETE /hub/channel -->
<div class="form-content" id="fc-hub-channel-remove">
<div class="field"><label>token</label><input id="hcr-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hcr-hubid" placeholder="UUID"></div>
<div class="field"><label>channel_id</label><input id="hcr-channel-id" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submit('hub-channel-remove')">Send</button></div>
</div>
<!-- PATCH /hub/channel/name -->
<div class="form-content" id="fc-hub-channel-set-name">
<div class="field"><label>token</label><input id="hcsn-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hcsn-hubid" placeholder="UUID"></div>
<div class="field"><label>channel_id</label><input id="hcsn-channel-id" placeholder="UUID"></div>
<div class="field"><label>new_name</label><input id="hcsn-newname" placeholder=""></div>
<div class="form-actions"><button class="send" onclick="submit('hub-channel-set-name')">Send</button></div>
</div>
<!-- PATCH /hub/channel/description -->
<div class="form-content" id="fc-hub-channel-set-desc">
<div class="field"><label>token</label><input id="hcsd-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hcsd-hubid" placeholder="UUID"></div>
<div class="field"><label>channel_id</label><input id="hcsd-channel-id" placeholder="UUID"></div>
<div class="field"><label>description</label><input id="hcsd-description" placeholder=""></div>
<div class="form-actions"><button class="send" onclick="submit('hub-channel-set-desc')">Send</button></div>
</div>
<!-- PATCH /hub/channel/roles/view -->
<div class="form-content" id="fc-hub-channel-role-view">
<div class="field"><label>token</label><input id="hcrv-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hcrv-hubid" placeholder="UUID"></div>
<div class="field"><label>channel_id</label><input id="hcrv-channelid" placeholder="UUID"></div>
<div class="field"><label>role_id</label><input id="hcrv-roleid" placeholder="uint8"></div>
<div class="field"><label>allow</label><input id="hcrv-allow" placeholder="true / false"><span class="hint">sent as query</span></div>
<div class="form-actions"><button class="send" onclick="submit('hub-channel-role-view')">Send</button></div>
</div>
<!-- PATCH /hub/channel/roles/send -->
<div class="form-content" id="fc-hub-channel-role-send">
<div class="field"><label>token</label><input id="hcrs-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hcrs-hubid" placeholder="UUID"></div>
<div class="field"><label>channel_id</label><input id="hcrs-channelid" placeholder="UUID"></div>
<div class="field"><label>role_id</label><input id="hcrs-roleid" placeholder="uint8"></div>
<div class="field"><label>allow</label><input id="hcrs-allow" placeholder="true / false"><span class="hint">sent as query</span></div>
<div class="form-actions"><button class="send" onclick="submit('hub-channel-role-send')">Send</button></div>
</div>
<!-- PATCH /hub/channel/roles/history -->
<div class="form-content" id="fc-hub-channel-role-history">
<div class="field"><label>token</label><input id="hcrh-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hcrh-hubid" placeholder="UUID"></div>
<div class="field"><label>channel_id</label><input id="hcrh-channelid" placeholder="UUID"></div>
<div class="field"><label>role_id</label><input id="hcrh-roleid" placeholder="uint8"></div>
<div class="field"><label>allow</label><input id="hcrh-allow" placeholder="true / false"><span class="hint">sent as query</span></div>
<div class="form-actions"><button class="send" onclick="submit('hub-channel-role-history')">Send</button></div>
</div>
<!-- GET /hub -->
<div class="form-content" id="fc-get-hub-data">
<div class="field"><label>token</label><input id="ghd-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="ghd-hubid" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submit('get-hub-data')">Send</button></div>
</div>
<!-- GET /hub/channel -->
<div class="form-content" id="fc-get-hub-channel-data">
<div class="field"><label>token</label><input id="ghcd-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="ghcd-hubid" placeholder="UUID"></div>
<div class="field"><label>channel_id</label><input id="ghcd-channelid" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submit('get-hub-channel-data')">Send</button></div>
</div>
<!-- GET /hubs/roles -->
<div class="form-content" id="fc-get-hub-roles">
<div class="field"><label>token</label><input id="ghr-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="ghr-hubid" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submit('get-hub-roles')">Send</button></div>
</div>
<!-- WS /ws -->
<div class="form-content" id="fc-websocket">
<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="sent as first message"></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>
</div>
</div>
<script>
let ws = null;
let activeForm = null;
let currentToken = '';
let currentUserId = '';
let currentHubId = '';
// method, path, which field ids go where
// dest: 'header' | 'body' | 'query'
const formDefs = {
'new-user': { method:'POST', path:'/user', title:'POST /user — register new user', fields:[{id:'nu-username',dest:'body',name:'username'},{id:'nu-password',dest:'body',name:'password'}] },
'new-token': { method:'POST', path:'/token', title:'POST /token — login / get token', fields:[{id:'nt-username',dest:'body',name:'username'},{id:'nt-password',dest:'body',name:'password'}] },
'new-connection': { method:'POST', path:'/connection', title:'POST /connection — send connection request', fields:[{id:'nconn-token',dest:'header',name:'token'},{id:'nconn-recipient',dest:'body',name:'recipient'}] },
'mod-user-profile': { method:'PATCH', path:'/user/profile', title:'PATCH /user/profile — update profile', fields:[{id:'mup-token',dest:'header',name:'token'},{id:'mup-pronouns',dest:'body',name:'pronouns'},{id:'mup-description',dest:'body',name:'description'},{id:'mup-color',dest:'body',name:'color'}] },
'mod-connection-elevate': { method:'POST', path:'/connection/elevate', title:'POST /connection/elevate — elevate', fields:[{id:'mce-token',dest:'header',name:'token'},{id:'mce-connectionid',dest:'body',name:'connection_id'}] },
'mod-connection-deelevate':{ method:'POST', path:'/connection/deelevate', title:'POST /connection/deelevate — de-elevate', fields:[{id:'mcde-token',dest:'header',name:'token'},{id:'mcde-connectionid',dest:'body',name:'connection_id'}] },
'get-user': { method:'GET', path:'/user', title:'GET /user — get user info', fields:[{id:'gu-token',dest:'header',name:'token'},{id:'gu-targetid',dest:'query',name:'target_id'}] },
'get-connections': { method:'GET', path:'/connections', title:'GET /connections — get connections', fields:[{id:'gconn-token',dest:'header',name:'token'}] },
'get-connections-unread': { method:'GET', path:'/connections/unreadmessages', title:'GET /connections/unreadmessages — unread counts for given connections (returns []uint32 in same order)', fields:[{id:'gcur-token',dest:'header',name:'token'},{id:'gcur-connections',dest:'query',name:'connections'}] },
'get-connection-messages': { method:'GET', path:'/connection/messages', title:'GET /connection/messages — message history', fields:[{id:'gcm-token',dest:'header',name:'token'},{id:'gcm-connectionid',dest:'query',name:'connection_id'},{id:'gcm-messages',dest:'query',name:'messages'},{id:'gcm-before',dest:'query',name:'before'}] },
'del-user': { method:'DELETE', path:'/user', title:'DELETE /user — delete own account', fields:[{id:'du-token',dest:'header',name:'token'}] },
'del-connection': { method:'DELETE', path:'/connection', title:'DELETE /connection — delete a connection', fields:[{id:'dc-token',dest:'header',name:'token'},{id:'dc-connectionid',dest:'query',name:'connection_id'}] },
'msg-user': { method:'POST', path:'/connection/message', title:'POST /connection/message — send direct message', fields:[{id:'mu-token',dest:'header',name:'token'},{id:'mu-connectionid',dest:'body',name:'connection_id'},{id:'mu-msgContent',dest:'body',name:'msg_content'},{id:'mu-attachedFile',dest:'body',name:'attached_file'}] },
'hub-create': { method:'POST', path:'/hub', title:'POST /hub — create a new hub', fields:[{id:'hc-token',dest:'header',name:'token'},{id:'hc-hubname',dest:'body',name:'hub_name'}] },
'hub-message': { method:'POST', path:'/hub/channel/message', title:'POST /hub/channel/message — send hub channel message', fields:[{id:'hm-token',dest:'header',name:'token'},{id:'hm-hubid',dest:'header',name:'hub_id'},{id:'hm-channelid',dest:'header',name:'channel_id'},{id:'hm-msgContent',dest:'body',name:'msg_content'},{id:'hm-attachedFile',dest:'body',name:'attached_file'}] },
'hub-join': { method:'PUT', path:'/hub/join', title:'PUT /hub/join — join hub (hubid as header)', fields:[{id:'hj-token',dest:'header',name:'token'},{id:'hj-hubid',dest:'header',name:'hub_id'}] },
'get-hubs': { method:'GET', path:'/hubs', title:'GET /hubs — get own hubs', fields:[{id:'gh-token',dest:'header',name:'token'}] },
'get-hub-data': { method:'GET', path:'/hub', title:'GET /hub — get hub data', fields:[{id:'ghd-token',dest:'header',name:'token'},{id:'ghd-hubid',dest:'header',name:'hub_id'}] },
'get-hub-channel-data': { method:'GET', path:'/hub/channel', title:'GET /hub/channel — get channel data', fields:[{id:'ghcd-token',dest:'header',name:'token'},{id:'ghcd-hubid',dest:'header',name:'hub_id'},{id:'ghcd-channelid',dest:'query',name:'channel_id'}] },
'get-hub-channels': { method:'GET', path:'/hubs/channels', title:'GET /hubs/channels — get hub channels', fields:[{id:'ghc-token',dest:'header',name:'token'},{id:'ghc-hubid',dest:'header',name:'hub_id'}] },
'get-hub-users': { method:'GET', path:'/hubs/users', title:'GET /hubs/users — get hub users (excludes self)', fields:[{id:'ghu-token',dest:'header',name:'token'},{id:'ghu-hubid',dest:'header',name:'hub_id'}] },
'get-hub-roles': { method:'GET', path:'/hubs/roles', title:'GET /hubs/roles — get hub roles', fields:[{id:'ghr-token',dest:'header',name:'token'},{id:'ghr-hubid',dest:'header',name:'hub_id'}] },
'get-users': { method:'GET', path:'/users', title:'GET /users — get multiple users by IDs', fields:[{id:'gus-token',dest:'header',name:'token'},{id:'gus-targetids',dest:'query',name:'target_ids'}] },
'hub-set-name': { method:'PATCH', path:'/hub/name', title:'PATCH /hub/name — set hub name', fields:[{id:'hsn-token',dest:'header',name:'token'},{id:'hsn-hubid',dest:'header',name:'hub_id'},{id:'hsn-newname',dest:'body',name:'new_name'}] },
'hub-set-color': { method:'PATCH', path:'/hub/color', title:'PATCH /hub/color — set hub color (R,G,B,A)', fields:[{id:'hsc-token',dest:'header',name:'token'},{id:'hsc-hubid',dest:'header',name:'hub_id'},{id:'hsc-newcolor',dest:'body',name:'new_color'}] },
'hub-remove': { method:'DELETE', path:'/hub', title:'DELETE /hub — remove hub', fields:[{id:'hr-token',dest:'header',name:'token'},{id:'hr-hubid',dest:'header',name:'hub_id'}] },
'hub-toggle-color': { method:'PATCH', path:'/hub/usercolorallowed', title:'PATCH /hub/usercolorallowed — toggle user color allowed', fields:[{id:'htc-token',dest:'header',name:'token'},{id:'htc-hubid',dest:'header',name:'hub_id'}] },
'hub-user-remove': { method:'DELETE', path:'/hub/user', title:'DELETE /hub/user — remove user from hub', fields:[{id:'hur-token',dest:'header',name:'token'},{id:'hur-hubid',dest:'header',name:'hub_id'},{id:'hur-targetid',dest:'query',name:'target_id'}] },
'hub-user-rename': { method:'PATCH', path:'/hub/user/name', title:'PATCH /hub/user/name — rename hub user', fields:[{id:'hun-token',dest:'header',name:'token'},{id:'hun-hubid',dest:'header',name:'hub_id'},{id:'hun-targetid',dest:'body',name:'target_id'},{id:'hun-newname',dest:'body',name:'new_name'}] },
'hub-self-rename': { method:'PATCH', path:'/hub/self/name', title:'PATCH /hub/self/name — rename self in hub', fields:[{id:'hsr-token',dest:'header',name:'token'},{id:'hsr-hubid',dest:'header',name:'hub_id'},{id:'hsr-newname',dest:'body',name:'new_name'}] },
'hub-user-mute': { method:'PATCH', path:'/hub/user/mute', title:'PATCH /hub/user/mute — toggle mute user', fields:[{id:'hum-token',dest:'header',name:'token'},{id:'hum-hubid',dest:'header',name:'hub_id'},{id:'hum-targetid',dest:'body',name:'target_id'}] },
'hub-user-add-role': { method:'PUT', path:'/hub/user/role', title:'PUT /hub/user/role — add role to user', fields:[{id:'huar-token',dest:'header',name:'token'},{id:'huar-hubid',dest:'header',name:'hub_id'},{id:'huar-targetid',dest:'body',name:'target_id'},{id:'huar-roleid',dest:'body',name:'role_id'}] },
'hub-user-remove-role': { method:'DELETE', path:'/hub/user/role', title:'DELETE /hub/user/role — remove role from user', fields:[{id:'hurr-token',dest:'header',name:'token'},{id:'hurr-hubid',dest:'header',name:'hub_id'},{id:'hurr-targetid',dest:'query',name:'target_id'},{id:'hurr-roleid',dest:'query',name:'role_id'}] },
'hub-role-create': { method:'PUT', path:'/hub/role', title:'PUT /hub/role — create role', fields:[{id:'hrc-token',dest:'header',name:'token'},{id:'hrc-hubid',dest:'header',name:'hub_id'},{id:'hrc-name',dest:'body',name:'name'}] },
'hub-role-remove': { method:'DELETE', path:'/hub/role', title:'DELETE /hub/role — remove role', fields:[{id:'hrr-token',dest:'header',name:'token'},{id:'hrr-hubid',dest:'header',name:'hub_id'},{id:'hrr-roleid',dest:'query',name:'role_id'}] },
'hub-role-set-name': { method:'PATCH', path:'/hub/role/name', title:'PATCH /hub/role/name — set role name', fields:[{id:'hrsn-token',dest:'header',name:'token'},{id:'hrsn-hubid',dest:'header',name:'hub_id'},{id:'hrsn-roleid',dest:'body',name:'role_id'},{id:'hrsn-newname',dest:'body',name:'new_name'}] },
'hub-role-set-color': { method:'PATCH', path:'/hub/role/color', title:'PATCH /hub/role/color — set role color (R,G,B,A)', fields:[{id:'hrsc-token',dest:'header',name:'token'},{id:'hrsc-hubid',dest:'header',name:'hub_id'},{id:'hrsc-roleid',dest:'body',name:'role_id'},{id:'hrsc-newcolor',dest:'body',name:'new_color'}] },
'hub-role-set-perms': { title:'PATCH /hub/role/permissions — set role permissions (roleid in body, perm=bool in query)' },
'hub-self-remove-role': { method:'DELETE', path:'/hub/self/role', title:'DELETE /hub/self/role — remove own role', fields:[{id:'hsrr-token',dest:'header',name:'token'},{id:'hsrr-hubid',dest:'header',name:'hub_id'},{id:'hsrr-roleid',dest:'body',name:'role_id'}] },
'hub-channel-create': { method:'PUT', path:'/hub/channel', title:'PUT /hub/channel — create channel', fields:[{id:'hcc-token',dest:'header',name:'token'},{id:'hcc-hubid',dest:'header',name:'hub_id'},{id:'hcc-name',dest:'body',name:'name'}] },
'hub-channel-remove': { method:'DELETE', path:'/hub/channel', title:'DELETE /hub/channel — remove channel', fields:[{id:'hcr-token',dest:'header',name:'token'},{id:'hcr-hubid',dest:'header',name:'hub_id'},{id:'hcr-channel-id',dest:'query',name:'channel_id'}] },
'hub-channel-set-name': { method:'PATCH', path:'/hub/channel/name', title:'PATCH /hub/channel/name — set channel name', fields:[{id:'hcsn-token',dest:'header',name:'token'},{id:'hcsn-hubid',dest:'header',name:'hub_id'},{id:'hcsn-channel-id',dest:'body',name:'channel_id'},{id:'hcsn-newname',dest:'body',name:'new_name'}] },
'hub-channel-role-view': { method:'PATCH', path:'/hub/channel/roles/view', title:'PATCH /hub/channel/roles/view — set role can view channel', fields:[{id:'hcrv-token',dest:'header',name:'token'},{id:'hcrv-hubid',dest:'header',name:'hub_id'},{id:'hcrv-channelid',dest:'body',name:'channel_id'},{id:'hcrv-roleid',dest:'body',name:'role_id'},{id:'hcrv-allow',dest:'query',name:'allow'}] },
'hub-channel-role-send': { method:'PATCH', path:'/hub/channel/roles/send', title:'PATCH /hub/channel/roles/send — set role can send in channel', fields:[{id:'hcrs-token',dest:'header',name:'token'},{id:'hcrs-hubid',dest:'header',name:'hub_id'},{id:'hcrs-channelid',dest:'body',name:'channel_id'},{id:'hcrs-roleid',dest:'body',name:'role_id'},{id:'hcrs-allow',dest:'query',name:'allow'}] },
'hub-channel-role-history':{ method:'PATCH', path:'/hub/channel/roles/history', title:'PATCH /hub/channel/roles/history — set role can read history', fields:[{id:'hcrh-token',dest:'header',name:'token'},{id:'hcrh-hubid',dest:'header',name:'hub_id'},{id:'hcrh-channelid',dest:'body',name:'channel_id'},{id:'hcrh-roleid',dest:'body',name:'role_id'},{id:'hcrh-allow',dest:'query',name:'allow'}] },
'hub-channel-set-desc': { method:'PATCH', path:'/hub/channel/description',title:'PATCH /hub/channel/description — set channel description', fields:[{id:'hcsd-token',dest:'header',name:'token'},{id:'hcsd-hubid',dest:'header',name:'hub_id'},{id:'hcsd-channel-id',dest:'body',name:'channel_id'},{id:'hcsd-description',dest:'body',name:'description'}] },
'mod-user-avatar': { title:'PATCH /user/avatar — set avatar image' },
'mod-user-profilebg': { title:'PATCH /user/profilebg — set profile background' },
'file-upload': { title:'POST /file — upload file (multipart)' },
'file-download': { title:'GET /file — get presigned download URL' },
'get-user-avatar': { title:'GET /user/avatar — get avatar URL' },
'get-user-profilebg': { title:'GET /user/profilebg — get profile background URL' },
'websocket': { title:'WS /ws — WebSocket connection' },
};
function autofillTokens() {
if (!currentToken) return;
document.querySelectorAll('input[id$="-token"]').forEach(el => { el.value = currentToken; });
}
function autofillHubIds() {
if (!currentHubId) return;
document.querySelectorAll('input[id$="-hubid"]').forEach(el => { el.value = currentHubId; });
const hubEl = document.getElementById('current-hub');
if (hubEl) hubEl.textContent = 'hubId: ' + currentHubId;
}
function showForm(name) {
const panel = document.getElementById('form-panel');
const titleEl = document.getElementById('form-title');
const buttons = document.querySelectorAll('#btn-row button');
buttons.forEach(b => b.classList.remove('active'));
if (activeForm === name) {
panel.classList.remove('open');
document.querySelectorAll('.form-content').forEach(el => el.classList.remove('active'));
activeForm = null;
return;
}
if (activeForm) {
const prev = document.getElementById('fc-' + activeForm);
if (prev) prev.classList.remove('active');
}
activeForm = name;
const def = formDefs[name];
const btn = document.querySelector('#btn-row button[data-form="' + name + '"]');
if (btn) {
btn.classList.add('active');
if (name === 'websocket') btn.classList.add('ws');
}
titleEl.textContent = def.title;
const fc = document.getElementById('fc-' + name);
if (fc) fc.classList.add('active');
panel.classList.add('open');
autofillTokens();
autofillHubIds();
}
function submit(name) {
const def = formDefs[name];
if (!def || !def.fields) return;
const headers = {};
const bodyFields = {};
const queryFields = {};
for (const f of def.fields) {
const el = document.getElementById(f.id);
const v = el ? el.value : '';
if (!v) continue;
if (f.dest === 'header') headers[f.name] = v;
else if (f.dest === 'query') queryFields[f.name] = v;
else bodyFields[f.name] = v;
}
const body = Object.keys(bodyFields).length ? new URLSearchParams(bodyFields) : null;
const query = Object.keys(queryFields).length ? queryFields : null;
apiCall(def.method, def.path, { headers, body, query });
}
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').textContent = ''; }
function baseUrl() { return document.getElementById('baseUrl').value.replace(/\/$/, ''); }
async function apiCall(method, path, { headers = {}, body = null, query = null } = {}) {
let url = baseUrl() + path;
if (query) url += '?' + new URLSearchParams(query).toString();
const opts = { method, headers };
if (body) opts.body = body;
const detail = body ? body.toString() : (query ? new URLSearchParams(query).toString() : '');
log(method + ' ' + path, detail || '(no params)', 'log-info');
try {
const resp = await fetch(url, opts);
const text = await resp.text();
log('HTTP ' + resp.status, text, resp.ok ? 'log-http' : 'log-err');
if (method === 'POST' && path === '/token' && resp.ok) {
try {
const parsed = JSON.parse(text);
currentToken = parsed.token || '';
currentUserId = parsed.userId || '';
} catch(e) {}
const userEl = document.getElementById('current-user');
if (userEl) userEl.textContent = currentUserId ? 'userId: ' + currentUserId : '';
autofillTokens();
wsConnectAndAuth();
}
if (method === 'POST' && path === '/hub' && resp.ok) {
currentHubId = text.trim();
autofillHubIds();
}
if (method === 'PUT' && path === '/hub/join' && resp.ok) {
const el = document.getElementById('hj-hubid');
if (el && el.value) {
currentHubId = el.value;
autofillHubIds();
}
}
return { ok: resp.ok, text, status: resp.status };
} catch(e) {
log('HTTP ERR', e.message, 'log-err');
return { ok: false };
}
}
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');
}
async function submitFileUpload() {
const token = document.getElementById('fu-token').value;
const connectionid = document.getElementById('fu-connectionid').value;
const fileInput = document.getElementById('fu-file');
if (!fileInput.files.length) { log('HTTP ERR', 'no file selected', 'log-err'); return; }
const form = new FormData();
form.append('connection_id', connectionid);
form.append('file', fileInput.files[0]);
log('POST /file', 'connection_id=' + connectionid + ' file=' + fileInput.files[0].name, 'log-info');
try {
const resp = await fetch(baseUrl() + '/file', { method: 'POST', headers: { token }, body: form });
const text = await resp.text();
log('HTTP ' + resp.status, text, resp.ok ? 'log-http' : 'log-err');
if (resp.ok) {
const keyEl = document.getElementById('fd-key');
if (keyEl) keyEl.value = text;
const dmKeyEl = document.getElementById('mu-attachedFile');
if (dmKeyEl) dmKeyEl.value = text;
}
} catch(e) { log('HTTP ERR', e.message, 'log-err'); }
}
async function submitFileDownload() {
const token = document.getElementById('fd-token').value;
const connectionid = document.getElementById('fd-connectionid').value;
const key = document.getElementById('fd-key').value;
const query = new URLSearchParams({ connection_id: connectionid, key });
log('GET /file', query.toString(), 'log-info');
try {
const resp = await fetch(baseUrl() + '/file?' + query.toString(), { method: 'GET', headers: { token } });
const text = await resp.text();
log('HTTP ' + resp.status, text, resp.ok ? 'log-http' : 'log-err');
} catch(e) { log('HTTP ERR', e.message, 'log-err'); }
}
async function submitAvatarUpload() {
const token = document.getElementById('mua-token').value;
const fileInput = document.getElementById('mua-file');
if (!fileInput.files.length) { log('HTTP ERR', 'no file selected', 'log-err'); return; }
const form = new FormData();
form.append('file', fileInput.files[0]);
log('PATCH /user/avatar', 'file=' + fileInput.files[0].name, 'log-info');
try {
const resp = await fetch(baseUrl() + '/user/avatar', { method: 'PATCH', headers: { token }, body: form });
const text = await resp.text();
log('HTTP ' + resp.status, text, resp.ok ? 'log-http' : 'log-err');
} catch(e) { log('HTTP ERR', e.message, 'log-err'); }
}
async function submitProfileBgUpload() {
const token = document.getElementById('mpb-token').value;
const fileInput = document.getElementById('mpb-file');
if (!fileInput.files.length) { log('HTTP ERR', 'no file selected', 'log-err'); return; }
const form = new FormData();
form.append('file', fileInput.files[0]);
log('PATCH /user/profilebg', 'file=' + fileInput.files[0].name, 'log-info');
try {
const resp = await fetch(baseUrl() + '/user/profilebg', { method: 'PATCH', headers: { token }, body: form });
const text = await resp.text();
log('HTTP ' + resp.status, text, resp.ok ? 'log-http' : 'log-err');
} catch(e) { log('HTTP ERR', e.message, 'log-err'); }
}
async function submitGetUserAvatar() {
const token = document.getElementById('gua-token').value;
const userid = document.getElementById('gua-userid').value;
const query = new URLSearchParams({ user_id: userid });
log('GET /user/avatar', query.toString(), 'log-info');
try {
const resp = await fetch(baseUrl() + '/user/avatar?' + query.toString(), { method: 'GET', headers: { token } });
const text = await resp.text();
log('HTTP ' + resp.status, text, resp.ok ? 'log-http' : 'log-err');
} catch(e) { log('HTTP ERR', e.message, 'log-err'); }
}
async function submitHubRoleSetPerms() {
const token = document.getElementById('hrsp-token').value;
const hubid = document.getElementById('hrsp-hubid').value;
const roleid = document.getElementById('hrsp-roleid').value;
const permsRaw = document.getElementById('hrsp-permissions').value;
const body = new URLSearchParams({ role_id: roleid });
let queryStr = permsRaw;
log('PATCH /hub/role/permissions', 'role_id=' + roleid + ' ' + permsRaw, 'log-info');
try {
const url = baseUrl() + '/hub/role/permissions' + (queryStr ? '?' + queryStr : '');
const resp = await fetch(url, { method: 'PATCH', headers: { token, hub_id: hubid }, body });
const text = await resp.text();
log('HTTP ' + resp.status, text, resp.ok ? 'log-http' : 'log-err');
} catch(e) { log('HTTP ERR', e.message, 'log-err'); }
}
async function submitGetUserProfileBg() {
const token = document.getElementById('gpb-token').value;
const userid = document.getElementById('gpb-userid').value;
const query = new URLSearchParams({ user_id: userid });
log('GET /user/profilebg', query.toString(), 'log-info');
try {
const resp = await fetch(baseUrl() + '/user/profilebg?' + query.toString(), { method: 'GET', headers: { token } });
const text = await resp.text();
log('HTTP ' + resp.status, text, resp.ok ? 'log-http' : 'log-err');
} catch(e) { log('HTTP ERR', e.message, 'log-err'); }
}
</script>
</body>
</html>