show OAuth scope status
This commit is contained in:
@@ -60,6 +60,10 @@
|
||||
let oauthIdentity: 'streamer' | 'bot' = 'streamer';
|
||||
let oauthBusy = false;
|
||||
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() {
|
||||
connectionState = 'CONNECTING';
|
||||
@@ -106,6 +110,7 @@
|
||||
await loadActivity();
|
||||
await loadNotifications();
|
||||
await loadDiagnostics();
|
||||
await loadOAuthStatus();
|
||||
await loadCollaboration();
|
||||
await loadStreamInfo();
|
||||
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') {
|
||||
oauthBusy = true;
|
||||
oauthError = '';
|
||||
@@ -500,6 +520,7 @@
|
||||
headers: { authorization: `Bearer ${sessionToken}` },
|
||||
});
|
||||
if (!response.ok) throw new Error('The server could not revoke this Twitch authorization.');
|
||||
await loadOAuthStatus();
|
||||
await loadStreamInfo();
|
||||
} catch (error) {
|
||||
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={() => createOAuthLink('bot')} disabled={oauthBusy}>CONNECT 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>
|
||||
{#if oauthLink}
|
||||
<div class="oauth-link-card">
|
||||
|
||||
@@ -463,6 +463,21 @@ label {
|
||||
gap: 6px;
|
||||
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 {
|
||||
width: 100%;
|
||||
padding: 6px;
|
||||
|
||||
Reference in New Issue
Block a user