feat: connect client to realtime WebSocket

This commit is contained in:
2026-10-25 04:42:25 +01:00
parent c32582a1cd
commit 5cb893af62
2 changed files with 37 additions and 1 deletions
+33 -1
View File
@@ -13,6 +13,8 @@
let authError = ''; let authError = '';
let authBusy = false; let authBusy = false;
let connectionNotice = ''; let connectionNotice = '';
let realtimeState = 'DISCONNECTED';
let realtimeSocket: WebSocket | undefined;
let oauthLink = ''; let oauthLink = '';
let oauthIdentity: 'streamer' | 'bot' = 'streamer'; let oauthIdentity: 'streamer' | 'bot' = 'streamer';
let oauthBusy = false; let oauthBusy = false;
@@ -60,6 +62,7 @@
authenticated = true; authenticated = true;
mustReplaceCredentials = result.must_replace_credentials; mustReplaceCredentials = result.must_replace_credentials;
connectionState = 'CONNECTED'; connectionState = 'CONNECTED';
connectRealtime();
if (!mustReplaceCredentials) password = ''; if (!mustReplaceCredentials) password = '';
if (mustReplaceCredentials) newUsername = username; if (mustReplaceCredentials) newUsername = username;
} catch (error) { } catch (error) {
@@ -95,6 +98,9 @@
} }
async function logout() { async function logout() {
realtimeSocket?.close();
realtimeSocket = undefined;
realtimeState = 'DISCONNECTED';
if (sessionToken) { if (sessionToken) {
await fetch(`${serverAddress}/api/v1/auth/logout`, { method: 'POST', headers: { authorization: `Bearer ${sessionToken}` } }).catch(() => undefined); await fetch(`${serverAddress}/api/v1/auth/logout`, { method: 'POST', headers: { authorization: `Bearer ${sessionToken}` } }).catch(() => undefined);
} }
@@ -104,6 +110,32 @@
oauthLink = ''; oauthLink = '';
} }
function connectRealtime() {
realtimeSocket?.close();
const socketUrl = serverAddress.replace(/^http/, 'ws') + '/api/v1/ws';
realtimeState = 'CONNECTING';
const socket = new WebSocket(socketUrl);
realtimeSocket = socket;
socket.onopen = () => {
socket.send(JSON.stringify({ frame_type: 'authenticate', token: sessionToken }));
};
socket.onmessage = (message) => {
try {
const event: { event_type?: string } = JSON.parse(message.data);
if (event.event_type === 'connection.ready') realtimeState = 'CONNECTED';
} catch {
realtimeState = 'DISCONNECTED';
}
};
socket.onclose = () => {
realtimeState = 'DISCONNECTED';
if (realtimeSocket === socket) realtimeSocket = undefined;
};
socket.onerror = () => {
realtimeState = 'DISCONNECTED';
};
}
async function createOAuthLink(identity: 'streamer' | 'bot') { async function createOAuthLink(identity: 'streamer' | 'bot') {
oauthBusy = true; oauthBusy = true;
oauthError = ''; oauthError = '';
@@ -188,7 +220,7 @@
<div class="empty-row">Sign in as administrator to connect accounts.</div> <div class="empty-row">Sign in as administrator to connect accounts.</div>
{/if} {/if}
</div> </div>
<div class="side-section side-footer"><div class="micro">SERVER VERSION</div><div class="mono">{serverVersion}</div></div> <div class="side-section side-footer"><div class="micro">REALTIME</div><div class="mono">{realtimeState}</div><div class="micro version-label">SERVER VERSION</div><div class="mono">{serverVersion}</div></div>
</aside> </aside>
<section class="content-area"> <section class="content-area">
+4
View File
@@ -326,6 +326,10 @@ label {
margin-top: auto; margin-top: auto;
border-bottom: 0; border-bottom: 0;
} }
.version-label {
display: block;
margin-top: 13px;
}
.content-area { .content-area {
min-width: 0; min-width: 0;
padding: 8px; padding: 8px;