UNPKG

tyo-mq

Version:

Distributed Message Pub/Sub Service with socket.io

250 lines (224 loc) 10.7 kB
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>TYO MQ Manager</title> <link rel="icon" href="favicon.ico" sizes="any"> <link rel="icon" type="image/png" href="favicon.png"> <link rel="apple-touch-icon" href="apple-touch-icon.png"> <link rel="stylesheet" href="manager.css"> </head> <body> <main class="shell"> <header class="topbar"> <div> <h1>TYO MQ Manager</h1> <p id="status">Not connected</p> </div> <button id="connect-btn" type="button">Connect</button> </header> <section class="panel connection"> <div class="field grow"> <label for="server-url">Server URL</label> <input id="server-url" type="url" placeholder="https://your-mq-server.example"> </div> <div class="field token"> <label for="admin-token">Admin token</label> <input id="admin-token" type="password" autocomplete="off"> </div> <button id="clear-token-btn" class="secondary" type="button">Clear token</button> <button id="show-settings-btn" type="button">Refresh</button> <button id="reload-settings-btn" class="secondary" type="button" title="Force the server to re-read its settings file from disk">Reload from disk</button> </section> <nav class="tabs" aria-label="Manager sections"> <button id="main-tab-btn" class="tab-button active" type="button" data-tab="main">Main</button> <button id="persistence-tab-btn" class="tab-button" type="button" data-tab="persistence">Persistence</button> <button id="observability-tab-btn" class="tab-button" type="button" data-tab="observability">Observability</button> </nav> <div id="main-tab" class="tab-panel active"> <section class="grid"> <div class="panel"> <div class="section-heading"> <h2>Realms</h2> <button id="refresh-realms-btn" type="button">Refresh</button> </div> <form id="add-realm-form" class="inline-form"> <input id="new-realm" type="text" placeholder="realm name" autocomplete="off"> <label class="check"> <input id="new-realm-required" type="checkbox" checked> Require auth </label> <button type="submit">Add</button> </form> <form id="rename-realm-form" class="inline-form"> <input id="rename-from" type="text" placeholder="current realm" autocomplete="off"> <input id="rename-to" type="text" placeholder="new realm" autocomplete="off"> <button type="submit">Rename</button> </form> <div id="realms-list" class="list empty">No realms loaded</div> </div> <div class="panel"> <div class="section-heading"> <h2>Authorization Requests</h2> <div class="button-group"> <button id="refresh-request-btn" class="secondary" type="button">Refresh</button> <button id="next-request-btn" type="button">Next</button> </div> </div> <div class="field"> <label for="request-realm-filter">Realm filter</label> <select id="request-realm-filter"> <option value="">Any realm</option> </select> <input id="request-realm-custom" class="hidden-field" type="text" placeholder="Custom realm" autocomplete="off"> </div> <div id="request-card" class="request-card empty">No pending request loaded</div> <div class="decision-row"> <select id="approve-role"> <option value="both">both</option> <option value="producer">producer</option> <option value="consumer">consumer</option> </select> <button id="approve-request-btn" type="button">Approve</button> </div> <div class="decision-row"> <input id="reject-reason" type="text" placeholder="Reject reason" autocomplete="off"> <button id="reject-request-btn" type="button">Reject</button> </div> </div> <div class="panel"> <div class="section-heading"> <h2>Authorized Clients</h2> <button id="refresh-tokens-btn" type="button">Refresh</button> </div> <div id="tokens-list" class="list empty">No tokens loaded</div> </div> <div class="panel"> <div class="section-heading"> <h2>External Auth</h2> </div> <p class="panel-note"> Tokens no local credential recognizes (realm JWTs, configured tokens) are validated by POSTing them to this URL with the <code>X-MQ-Auth-Secret</code> header. Local credentials are always checked first, so enabling this cannot break existing clients. </p> <form id="external-auth-form" class="stacked-form"> <div class="field"> <label for="external-auth-url">Validation URL (auth_url)</label> <input id="external-auth-url" type="url" placeholder="https://tyoman.tyo.com.au/api/v1/mq-auth" autocomplete="off"> </div> <div class="field"> <label for="external-auth-secret">Callback secret (auth_secret)</label> <input id="external-auth-secret" type="password" autocomplete="off" placeholder="not set"> </div> <div class="form-actions"> <button type="submit">Save</button> <button id="disable-external-auth-btn" class="secondary" type="button">Disable</button> </div> </form> </div> <div class="panel"> <div class="section-heading"> <h2>Management API Tokens</h2> </div> <p class="panel-note"> Bearer tokens for the HTTP management API (<code>POST /api/realms</code>). Each token can only create or rotate realms under its prefix. Tokens are generated in the browser and shown once — copy immediately; the server keeps only a hash. </p> <form id="add-mgmt-token-form" class="inline-form"> <input id="mgmt-token-prefix" type="text" placeholder="realm prefix, e.g. realm:prefix:" autocomplete="off"> <button type="submit">Generate</button> </form> <div id="mgmt-token-reveal" class="hidden-field"></div> <div id="mgmt-tokens-list" class="list empty">No management tokens loaded</div> </div> </section> <section class="panel"> <div class="section-heading"> <h2>Settings</h2> <button id="copy-settings-btn" type="button">Copy</button> </div> <pre id="settings-output">{}</pre> </section> </div> <div id="persistence-tab" class="tab-panel"> <section class="panel"> <div class="section-heading"> <h2>Persistence</h2> <button id="refresh-persistence-btn" type="button">Refresh</button> </div> <form id="persistence-form" class="persistence-form"> <div class="field"> <label for="storage-backend">Storage backend</label> <select id="storage-backend"> <option value="memory">memory</option> <option value="sqlite">sqlite</option> <option value="redis">redis</option> <option value="custom">custom store</option> </select> </div> <div class="field"> <label for="storage-default-ttl">Default TTL seconds</label> <input id="storage-default-ttl" type="number" min="0" step="1" placeholder="86400"> </div> <div id="sqlite-options" class="backend-options"> <div class="field"> <label for="sqlite-filename">SQLite filename</label> <input id="sqlite-filename" type="text" placeholder="/data/tyo-mq.sqlite" autocomplete="off"> </div> </div> <div id="redis-options" class="backend-options"> <div class="field"> <label for="redis-url">Redis URL</label> <input id="redis-url" type="text" placeholder="redis://redis:6379/0" autocomplete="off"> </div> <div class="field"> <label for="redis-prefix">Redis key prefix</label> <input id="redis-prefix" type="text" placeholder="tyo-mq" autocomplete="off"> </div> </div> <div id="custom-options" class="backend-options"> <div class="field"> <label for="custom-module">Custom store module path</label> <input id="custom-module" type="text" placeholder="./my-store.js" autocomplete="off"> </div> <div class="field full"> <label for="custom-options-json">Custom storage_options JSON</label> <textarea id="custom-options-json" rows="8" spellcheck="false" placeholder="{ }"></textarea> </div> </div> <div class="form-actions"> <button id="save-persistence-btn" type="submit">Apply persistence</button> </div> </form> </section> </div> <div id="observability-tab" class="tab-panel"> <section class="panel"> <div class="section-heading"> <h2>Live stats</h2> <button id="refresh-stats-btn" type="button">Refresh</button> </div> <pre id="stats-output">{}</pre> </section> <section class="panel"> <div class="section-heading"> <h2>Dead-letter queue</h2> <button id="refresh-dlq-btn" type="button">Refresh</button> </div> <div class="inline-form"> <input id="dlq-realm" type="text" placeholder="realm filter (blank for all)" autocomplete="off"> </div> <div id="dlq-list" class="list empty">No DLQ entries loaded</div> </section> </div> </main> <script src="tyo-mq-client.js"></script> <script src="manager-ui.js"></script> </body> </html>