feat: Global-Admin Org-Switcher fuer info@aegis-sight.de

Ermoeglicht dem Global Admin (is_global_admin Flag) zwischen
Organisationen zu wechseln. Neue Endpoints: GET /api/auth/organizations,
POST /api/auth/switch-org. Org-Dropdown im Header-Menue, nur fuer
Global Admin sichtbar. Komplett herausnehmbar (Flag + Code-Bloecke).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
Claude Dev
2026-04-08 22:25:41 +02:00
Ursprung d3e8c0adc7
Commit c22ae854fe
7 geänderte Dateien mit 178 neuen und 0 gelöschten Zeilen

Datei anzeigen

@@ -5445,3 +5445,41 @@ body.tutorial-active .tutorial-cursor {
font-size: 11px;
color: var(--text-tertiary);
}
/* --- Global Admin: Org-Switcher (herausnehmbar) --- */
.org-switcher-section {
padding: 0;
text-align: left;
}
.org-switcher-label {
font-size: 11px;
font-weight: 600;
letter-spacing: 0.5px;
color: var(--text-tertiary);
text-transform: uppercase;
margin-bottom: 6px;
display: block;
}
.org-switcher-select {
width: 100%;
padding: 6px 8px;
font-size: 13px;
border-radius: 6px;
border: 1px solid var(--border);
background: var(--bg-secondary);
color: var(--text-primary);
cursor: pointer;
outline: none;
transition: border-color 0.15s;
}
.org-switcher-select:hover {
border-color: var(--accent);
}
.org-switcher-select:focus {
border-color: var(--accent);
box-shadow: 0 0 0 2px rgba(var(--accent-rgb, 59, 130, 246), 0.15);
}

Datei anzeigen

@@ -52,6 +52,12 @@
<span class="header-dropdown-label">Organisation</span>
<span class="header-dropdown-value" id="header-org-name">-</span>
</div>
<!-- Global Admin: Org-Switcher (herausnehmbar) -->
<div id="org-switcher-section" class="org-switcher-section" style="display: none;">
<div class="credits-divider"></div>
<label class="org-switcher-label" for="org-switcher-select">Wechseln zu:</label>
<select id="org-switcher-select" class="org-switcher-select"></select>
</div>
<div class="header-dropdown-row">
<span class="header-dropdown-label">Lizenz</span>
<span class="header-dropdown-value" id="header-license-info">-</span>

Datei anzeigen

@@ -243,4 +243,13 @@ const API = {
headers: { 'Authorization': `Bearer ${token}` },
});
},
// --- Global Admin: Org-Wechsel (herausnehmbar) ---
listOrganizations() {
return this._request('GET', '/auth/organizations');
},
switchOrg(organizationId) {
return this._request('POST', '/auth/switch-org', { organization_id: organizationId });
},
};

Datei anzeigen

@@ -515,6 +515,11 @@ const App = {
warningEl.textContent = 'Lizenz abgelaufen – nur Lesezugriff';
warningEl.classList.add('visible');
}
// --- Global Admin: Org-Switcher (herausnehmbar) ---
if (user.is_global_admin) {
this._initOrgSwitcher(user.tenant_id);
}
} catch {
window.location.href = '/';
return;
@@ -2939,6 +2944,42 @@ async handleRefresh() {
}
},
// --- Global Admin: Org-Switcher (herausnehmbar) ---
async _initOrgSwitcher(currentTenantId) {
const section = document.getElementById('org-switcher-section');
const select = document.getElementById('org-switcher-select');
if (!section || !select) return;
try {
const orgs = await API.listOrganizations();
if (!orgs || orgs.length < 2) return;
section.style.display = 'block';
select.innerHTML = '';
orgs.forEach(org => {
const opt = document.createElement('option');
opt.value = org.id;
opt.textContent = org.name + (org.is_active ? '' : ' (inaktiv)');
if (org.id === currentTenantId) opt.selected = true;
select.appendChild(opt);
});
select.addEventListener('change', async () => {
const orgId = parseInt(select.value, 10);
if (orgId === currentTenantId) return;
try {
const result = await API.switchOrg(orgId);
localStorage.setItem('osint_token', result.access_token);
window.location.reload();
} catch (err) {
console.error('Org-Wechsel fehlgeschlagen:', err);
}
});
} catch {
// Kein Global Admin oder Fehler - Switcher bleibt versteckt
}
},
logout() {
localStorage.removeItem('osint_token');
localStorage.removeItem('osint_username');