show OAuth scope status
This commit is contained in:
@@ -60,6 +60,10 @@
|
|||||||
let oauthIdentity: 'streamer' | 'bot' = 'streamer';
|
let oauthIdentity: 'streamer' | 'bot' = 'streamer';
|
||||||
let oauthBusy = false;
|
let oauthBusy = false;
|
||||||
let oauthError = '';
|
let oauthError = '';
|
||||||
|
let oauthScopes: Record<'streamer' | 'bot', { connected: boolean; missing_scopes: string[]; reauthorization_required: boolean }> = {
|
||||||
|
streamer: { connected: false, missing_scopes: [], reauthorization_required: false },
|
||||||
|
bot: { connected: false, missing_scopes: [], reauthorization_required: false },
|
||||||
|
};
|
||||||
|
|
||||||
async function checkServer() {
|
async function checkServer() {
|
||||||
connectionState = 'CONNECTING';
|
connectionState = 'CONNECTING';
|
||||||
@@ -106,6 +110,7 @@
|
|||||||
await loadActivity();
|
await loadActivity();
|
||||||
await loadNotifications();
|
await loadNotifications();
|
||||||
await loadDiagnostics();
|
await loadDiagnostics();
|
||||||
|
await loadOAuthStatus();
|
||||||
await loadCollaboration();
|
await loadCollaboration();
|
||||||
await loadStreamInfo();
|
await loadStreamInfo();
|
||||||
connectRealtime();
|
connectRealtime();
|
||||||
@@ -491,6 +496,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadOAuthStatus() {
|
||||||
|
for (const identity of ['streamer', 'bot'] as const) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${serverAddress}/api/v1/twitch/oauth/status?identity=${identity}`, {
|
||||||
|
headers: { authorization: `Bearer ${sessionToken}` },
|
||||||
|
});
|
||||||
|
if (!response.ok) continue;
|
||||||
|
const result = await response.json();
|
||||||
|
oauthScopes = { ...oauthScopes, [identity]: result };
|
||||||
|
} catch {
|
||||||
|
// The diagnostics panel remains usable if scope status is temporarily unavailable.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function revokeOAuth(identity: 'streamer' | 'bot') {
|
async function revokeOAuth(identity: 'streamer' | 'bot') {
|
||||||
oauthBusy = true;
|
oauthBusy = true;
|
||||||
oauthError = '';
|
oauthError = '';
|
||||||
@@ -500,6 +520,7 @@
|
|||||||
headers: { authorization: `Bearer ${sessionToken}` },
|
headers: { authorization: `Bearer ${sessionToken}` },
|
||||||
});
|
});
|
||||||
if (!response.ok) throw new Error('The server could not revoke this Twitch authorization.');
|
if (!response.ok) throw new Error('The server could not revoke this Twitch authorization.');
|
||||||
|
await loadOAuthStatus();
|
||||||
await loadStreamInfo();
|
await loadStreamInfo();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
oauthError = error instanceof Error ? error.message : 'The Twitch authorization could not be revoked.';
|
oauthError = error instanceof Error ? error.message : 'The Twitch authorization could not be revoked.';
|
||||||
@@ -561,6 +582,13 @@
|
|||||||
<button class="secondary-action" onclick={() => revokeOAuth('streamer')} disabled={oauthBusy}>DISCONNECT STREAMER</button>
|
<button class="secondary-action" onclick={() => revokeOAuth('streamer')} disabled={oauthBusy}>DISCONNECT STREAMER</button>
|
||||||
<button class="secondary-action" onclick={() => createOAuthLink('bot')} disabled={oauthBusy}>CONNECT BOT</button>
|
<button class="secondary-action" onclick={() => createOAuthLink('bot')} disabled={oauthBusy}>CONNECT BOT</button>
|
||||||
<button class="secondary-action" onclick={() => revokeOAuth('bot')} disabled={oauthBusy}>DISCONNECT BOT</button>
|
<button class="secondary-action" onclick={() => revokeOAuth('bot')} disabled={oauthBusy}>DISCONNECT BOT</button>
|
||||||
|
<button class="secondary-action" onclick={loadOAuthStatus} disabled={oauthBusy}>REFRESH SCOPE STATUS</button>
|
||||||
|
</div>
|
||||||
|
<div class="oauth-status-grid">
|
||||||
|
{#each ['streamer', 'bot'] as identity}
|
||||||
|
<div class="oauth-status-row"><span class="micro">{identity.toUpperCase()}</span><span class:good={oauthScopes[identity as 'streamer' | 'bot'].connected} class="mono">{oauthScopes[identity as 'streamer' | 'bot'].connected ? 'READY' : oauthScopes[identity as 'streamer' | 'bot'].reauthorization_required ? 'REAUTHORIZE' : 'NOT CONNECTED'}</span></div>
|
||||||
|
{#if oauthScopes[identity as 'streamer' | 'bot'].missing_scopes.length}<div class="oauth-hint">Missing: {oauthScopes[identity as 'streamer' | 'bot'].missing_scopes.join(', ')}</div>{/if}
|
||||||
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{#if oauthLink}
|
{#if oauthLink}
|
||||||
<div class="oauth-link-card">
|
<div class="oauth-link-card">
|
||||||
|
|||||||
@@ -463,6 +463,21 @@ label {
|
|||||||
gap: 6px;
|
gap: 6px;
|
||||||
margin-top: 9px;
|
margin-top: 9px;
|
||||||
}
|
}
|
||||||
|
.oauth-status-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 6px;
|
||||||
|
margin-top: 10px;
|
||||||
|
padding-top: 9px;
|
||||||
|
border-top: 1px solid var(--ui-divider);
|
||||||
|
}
|
||||||
|
.oauth-status-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.oauth-status-row .good {
|
||||||
|
color: var(--ed-green);
|
||||||
|
}
|
||||||
.secondary-action {
|
.secondary-action {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
|
|||||||
Reference in New Issue
Block a user