v0.5 Push

This commit is contained in:
root committed 2025-11-12 10:48:25 +00:00
1 parent fc778da204
commit 81b7613c73
30 files changed
+1853 -1306

No files matched your search

+44 -50
View File
@@ -1,6 +1,6 @@
![StackPulse Logo](assets/images/stackpulse.png)
# 📦 StackPulse ![Release](https://img.shields.io/badge/release-v0.2-blue.svg)
# 📦 StackPulse ![Release](https://img.shields.io/badge/release-v0.5-blue.svg)
**StackPulse** ist eine kleine Web-App, die über die Portainer-API deine Docker-Stacks verwaltet und aktualisiert.
Aktuell funktioniert StackPulse nur mit der Business-Edition von Portainer. Die Communitiy-Edition wird in einem späteren Release implementiert!
@@ -67,7 +67,7 @@ Ziel:
</details>
<details open>
<details>
<summary>✅ v0.4 – Release</summary>
### Backend
@@ -86,27 +86,35 @@ Ziel:
</details>
<details open>
<summary>🟡 v0.5 – In Entwicklung</summary>
<summary>✅ v0.5 – Release</summary>
### 🛠️ Backend
- [x] Neue API-Endpunkte für Logging, Benutzer- und Gruppenverwaltung sowie Superuser-Registrierung
- [x] Authentifizierung und Log-In
- [x] Erweiterte Protokollierung und Datenbank-Migration
- [x] Präzisere Steuerung der globalen Benutzerrechte
### 💻 Frontend
- [x] Neue Log-In-Seite
- [x] Benutzer- und Gruppenverwaltung mit granularen Rechteeinstellungen
- [x] Erweiterte Log-Ausgabe mit Filtern und Suchoptionen
- [x] Konsolidierte und vereinheitlichte Ansichten im Dashboard
### ✨ Features
- [x] Vollständiges Authentifizierungs- und Berechtigungssystem
- [x] Erweiterte Logs in allen Bereichen
- [x] Speicherung von Server- und Superuser-Einstellungen in der Datenbank
</details>
<details open>
<summary>🟡 v0.6 – In Entwicklung</summary>
### Backend
- [x] Neue API-Endpunkte für Logging, Benutzer- und Gruppenverwaltung und Superuser-Registrierung
- [x] Authentifizierung und Log-In
- [x] erweitertes Logging
- [x] Datenbank erweitert und Migration erweitert
- [ ] Feinsteuerung der globalen Userrechte am Benutzer
### Frontend
- [x] Log-In-Seite
- [ ] Benutzer- und Gruppenverwaltung
- [x] Erweiterte Log-Ausgabe
- [x] Konsolidierung der Ansichten und Filter im Frontend
- [ ] Benutzerverwaltung mit granularer Steuerung von Einzelrechten
### Features
- [ ] Authentifizierungssystem und Benutzerverwaltung mit Gruppenrechten
- [x] Erweiterung der Logs auf alle Bereiche
- [x] Speichern der Server- und Superusereinstellungen in die Datenbank
</details>
@@ -117,10 +125,8 @@ Ziel:
- Monitoring (Status, CPU/RAM)
- Verbesserte UI/UX
- Erweiterte Filterungen udn Sortierungen
- Multi-Server/Endpoint Verwaltung
- Multi-Server Verwaltung
- Integration Community Edition
- Integration Community Edition
</details>
---
@@ -158,41 +164,29 @@ stackpulse/
### Mit Compose starten
```bash
version: "2.4"
version: '3.8'
services:
stackpulse:
container_name: stackpulse
image: ghcr.io/mboehmlaender/stackpulse
ports:
- '4001:4001'
volumes:
- stackpulse_data:/app/backend/data
environment:
PORTAINER_URL: "Your_Portainer_Server_Address"
PORTAINER_API_KEY: "Your_Portainer_API_Key"
PORTAINER_ENDPOINT_ID: "Your_Portainer_Endpoint_ID"
SELF_STACK_ID: "Stackpulse ID"
restart: unless-stopped
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "4001:4001"
volumes:
- stackpulse_data:/app/backend/data
restart: unless-stopped
environment:
- PORTAINER_URL=Your_Portainer_Server_Adress (optional)
- PORTAINER_API_KEY=Your_Portainer_API_Key (optional)
- SUPERUSER_USERNAME=Your_Superuser_Username (optional)
- SUPERUSER_EMAIL=Your_Superuser_Email (optional)
- SUPERUSER_PASSWORD=Your_Superuser_Password (optional)
- SELF_STACK_ID=Your_StackPulse_Stack_ID (optional)
volumes:
stackpulse_data:
```
Die PORTAINER_ENDPOINT_ID erhältst du, wenn du die die URL im Browser ansiehst, wenn du das Dashboard in Portainer öffnest:
![PORTAINER_ENDPOINT_ID](assets/images/ENDPOINT_ID.png)
Die 3 wäre in diesem Fall Endpoint-ID.
Die STACK_SELF_ID findest du, wenn du das Frontend von StackPulse öffnest:
![SELF_STACK_ID](assets/images/SELF_STACK_ID.png)
Diese ID kann erst nach dem Deploy von Stackpulse ausgelesen werden. Vergiss daher nicht, nach dem Hinterlegen der ID in den Variablen das Stack noch einmal zu redeployen!
---
## 📋 Voraussetzungen
- Node.js >= 20
Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

+3 -4
View File
@@ -1,7 +1,6 @@
PORTAINER_URL=Your_Portainer_Server_Adress
PORTAINER_API_KEY=Your_Portainer_API_Key
PORTAINER_ENDPOINT_ID=Your_Portainer_Endpoint_ID
PORTAINER_URL=Your_Portainer_Server_Adress (optional)
PORTAINER_API_KEY=Your_Portainer_API_Key (optional)
SUPERUSER_USERNAME=Your_Superuser_Username (optional)
SUPERUSER_EMAIL=Your_Superuser_Email (optional)
SUPERUSER_PASSWORD=Your_Superuser_Password (optional)
SELF_STACK_ID=Your_StackPulse_Stack_ID
SELF_STACK_ID=Your_StackPulse_Stack_ID (optional)
-27
View File
@@ -159,18 +159,6 @@ CREATE TABLE servers (
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE endpoints (
id INTEGER PRIMARY KEY AUTOINCREMENT,
server_id INTEGER NOT NULL,
name TEXT NOT NULL,
external_id TEXT NOT NULL,
is_default INTEGER NOT NULL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (server_id) REFERENCES servers(id) ON DELETE CASCADE,
UNIQUE (server_id, external_id)
);
CREATE TABLE user_server_permission_overrides (
user_id INTEGER NOT NULL,
server_id INTEGER NOT NULL,
@@ -186,21 +174,6 @@ CREATE TABLE user_server_permission_overrides (
CREATE INDEX idx_user_server_permission_overrides_user_server
ON user_server_permission_overrides (user_id, server_id);
CREATE TABLE user_endpoint_permission_overrides (
user_id INTEGER NOT NULL,
endpoint_id INTEGER NOT NULL,
permission_id INTEGER NOT NULL,
change_type TEXT NOT NULL CHECK (change_type IN ('ADD', 'REMOVE')),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE (user_id, endpoint_id, permission_id),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (endpoint_id) REFERENCES endpoints(id) ON DELETE CASCADE,
FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE
);
CREATE INDEX idx_user_endpoint_permission_overrides_user_endpoint
ON user_endpoint_permission_overrides (user_id, endpoint_id);
CREATE TABLE server_api_keys (
id INTEGER PRIMARY KEY AUTOINCREMENT,
server_id INTEGER NOT NULL UNIQUE,
+33 -5
View File
@@ -177,12 +177,12 @@ const PERMISSION_BLUEPRINT = [
groups: [
{
key: 'maintenance-server-group',
title: 'Server & Endpoints',
title: 'Server',
sortOrder: 0,
items: [
{
key: 'maintenance-server-manage',
label: 'Server/Endpoint-Sektion',
label: 'Server-Sektion',
sortOrder: 0,
defaultLevel: 'none',
levels: ['full', 'read', 'none'],
@@ -192,7 +192,7 @@ const PERMISSION_BLUEPRINT = [
},
{
key: 'maintenance-server-delete',
label: 'Server/Endpoint löschen',
label: 'Server löschen',
sortOrder: 1,
defaultLevel: 'none',
levels: ['full', 'none'],
@@ -203,10 +203,27 @@ const PERMISSION_BLUEPRINT = [
}
]
},
{
key: 'maintenance-superuser-group',
title: 'Superuser',
sortOrder: 1,
items: [
{
key: 'maintenance-superuser-delete',
label: 'Superuser löschen',
sortOrder: 0,
defaultLevel: 'none',
levels: ['full', 'none'],
dependencies: [
{ dependsOnKey: 'maintenance-access', requiredLevel: '!=none' }
]
}
]
},
{
key: 'maintenance-portainer-group',
title: 'Portainer',
sortOrder: 1,
sortOrder: 2,
items: [
{
key: 'maintenance-portainer',
@@ -245,7 +262,7 @@ const PERMISSION_BLUEPRINT = [
{
key: 'maintenance-duplicates-group',
title: 'Doppelte Stacks',
sortOrder: 2,
sortOrder: 3,
items: [
{
key: 'maintenance-duplicates',
@@ -350,6 +367,16 @@ const tableExists = (tableName) => {
return Boolean(result);
};
const dropDeprecatedArtifacts = () => {
const deprecatedTables = ['user_endpoint_permission_overrides', 'endpoints'];
deprecatedTables.forEach((table) => {
if (tableExists(table)) {
db.exec(`DROP TABLE IF EXISTS ${table}`);
console.log(`ℹ️ Veraltete Tabelle ${table} entfernt`);
}
});
};
const getExistingColumns = (tableName) => {
const pragma = db.prepare(`PRAGMA table_info(${tableName})`).all();
return new Set(pragma.map((column) => column.name));
@@ -701,6 +728,7 @@ export const ensureDatabaseSchema = () => {
const tableDefinitions = parseTableDefinitions(statements);
const indexDefinitions = parseIndexStatements(statements);
dropDeprecatedArtifacts();
ensureTablesAndColumns(tableDefinitions);
ensureIndexes(indexDefinitions);
ensurePermissionSeeds();
+349 -183
View File
@@ -34,15 +34,12 @@ import { getSetting, setSetting, deleteSetting } from './db/settings.js';
import { activateMaintenanceMode, deactivateMaintenanceMode, getMaintenanceState, isMaintenanceModeActive } from './maintenance/state.js';
import {
ensureDefaultsFromEnv,
getActiveEndpointExternalId,
getActiveApiKey,
getActiveServerUrl,
hasServer,
hasEndpoint,
hasApiKey,
getSetupStatus,
completeSetup,
removeEndpoint,
removeServer,
setServerApiKey
} from './setup/index.js';
@@ -101,16 +98,6 @@ app.get('*', (req, res, next) => {
const PORT = 4001;
const requireActiveEndpointId = () => {
const value = getActiveEndpointExternalId();
if (!value) {
const error = new Error('ENDPOINT_NOT_CONFIGURED');
error.code = 'ENDPOINT_NOT_CONFIGURED';
throw error;
}
return value;
};
const agent = new https.Agent({ rejectUnauthorized: false });
const resolvePortainerBaseUrl = () => {
@@ -142,6 +129,111 @@ axiosInstance.interceptors.request.use((config) => {
return config;
});
const PORTAINER_ENVIRONMENT_CACHE_TTL_MS = 5 * 60 * 1000;
let cachedPortainerEnvironment = { id: null, fetchedAt: 0 };
let resolvingEnvironmentPromise = null;
const resolvePortainerEnvironmentId = async () => {
const now = Date.now();
if (cachedPortainerEnvironment.id && now - cachedPortainerEnvironment.fetchedAt < PORTAINER_ENVIRONMENT_CACHE_TTL_MS) {
return cachedPortainerEnvironment.id;
}
if (resolvingEnvironmentPromise) {
return resolvingEnvironmentPromise;
}
resolvingEnvironmentPromise = (async () => {
try {
const endpointsRes = await axiosInstance.get('/api/endpoints');
const endpoints = Array.isArray(endpointsRes.data) ? endpointsRes.data : [];
if (!endpoints.length) {
const error = new Error('PORTAINER_ENVIRONMENT_NOT_FOUND');
error.code = 'PORTAINER_ENVIRONMENT_NOT_FOUND';
throw error;
}
const preferred =
endpoints.find((endpoint) => endpoint.Type === 1 && (!endpoint.URL || endpoint.URL === '')) ||
endpoints[0];
const resolvedId = preferred?.Id ?? preferred?.ID ?? null;
if (!resolvedId) {
const error = new Error('PORTAINER_ENVIRONMENT_NOT_FOUND');
error.code = 'PORTAINER_ENVIRONMENT_NOT_FOUND';
throw error;
}
cachedPortainerEnvironment = {
id: String(resolvedId),
fetchedAt: Date.now()
};
return cachedPortainerEnvironment.id;
} catch (error) {
const normalized = error.response?.data?.message || error.message || 'PORTAINER_ENVIRONMENT_NOT_FOUND';
console.error('⚠️ [Portainer] Environment-Erkennung fehlgeschlagen:', normalized);
const wrapped = new Error('PORTAINER_ENVIRONMENT_NOT_FOUND');
wrapped.code = 'PORTAINER_ENVIRONMENT_NOT_FOUND';
throw wrapped;
} finally {
resolvingEnvironmentPromise = null;
}
})();
return resolvingEnvironmentPromise;
};
const normalizeExternalPortainerUrl = (value = '') => {
if (typeof value !== 'string') return '';
const trimmed = value.trim();
if (!trimmed) return '';
try {
const hasProtocol = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(trimmed);
const candidate = hasProtocol ? trimmed : `https://${trimmed}`;
const normalized = new URL(candidate);
normalized.hash = '';
return normalized.toString().replace(/\/$/, '');
} catch {
return '';
}
};
const createPortainerSetupClient = ({ baseURL, apiKey }) => {
if (!baseURL) {
const error = new Error('SERVER_URL_REQUIRED');
error.code = 'SERVER_URL_REQUIRED';
throw error;
}
if (!apiKey) {
const error = new Error('API_KEY_REQUIRED');
error.code = 'API_KEY_REQUIRED';
throw error;
}
return axios.create({
baseURL,
httpsAgent: agent,
headers: {
'X-API-Key': apiKey
}
});
};
const extractPortainerCredentials = (payload = {}) => {
const rawServer = payload.server ?? {};
const serverUrl =
typeof rawServer.url === 'string' ? rawServer.url :
typeof payload.serverUrl === 'string' ? payload.serverUrl :
typeof payload.url === 'string' ? payload.url : '';
const apiKey =
typeof payload.apiKey === 'string' ? payload.apiKey :
typeof payload.key === 'string' ? payload.key :
typeof payload.api === 'string' ? payload.api :
typeof payload?.api?.value === 'string' ? payload.api.value :
'';
return {
serverUrl: serverUrl ? serverUrl.trim() : '',
apiKey: apiKey ? apiKey.trim() : ''
};
};
const REDEPLOY_PHASES = {
QUEUED: 'queued',
STARTED: 'started',
@@ -157,10 +249,20 @@ const DEFAULT_SESSION_TTL_MS = 1000 * 60 * 60 * 12;
const envSessionTtl = Number(process.env.AUTH_SESSION_TTL_MS);
const AUTH_SESSION_TTL_MS = Number.isFinite(envSessionTtl) && envSessionTtl > 0 ? envSessionTtl : DEFAULT_SESSION_TTL_MS;
const parseBooleanEnv = (value, fallback) => {
if (value === undefined || value === null) return fallback;
const normalized = String(value).trim().toLowerCase();
if (['1', 'true', 'yes', 'on'].includes(normalized)) return true;
if (['0', 'false', 'no', 'off'].includes(normalized)) return false;
return fallback;
};
const AUTH_COOKIE_SECURE = parseBooleanEnv(process.env.AUTH_COOKIE_SECURE, false);
const AUTH_COOKIE_OPTIONS = {
httpOnly: true,
sameSite: 'lax',
secure: process.env.NODE_ENV === 'production',
secure: AUTH_COOKIE_SECURE,
path: '/'
};
@@ -177,7 +279,9 @@ const PUBLIC_API_ROUTES = [
{ method: 'POST', matcher: /^\/api\/auth\/recover\/verify$/ },
{ method: 'POST', matcher: /^\/api\/auth\/recover\/reset$/ },
{ method: 'GET', matcher: /^\/api\/setup\/status$/ },
{ method: 'POST', matcher: /^\/api\/setup\/complete$/ }
{ method: 'POST', matcher: /^\/api\/setup\/complete$/ },
{ method: 'POST', matcher: /^\/api\/setup\/test-portainer$/ },
{ method: 'POST', matcher: /^\/api\/setup\/portainer-stacks$/ }
];
const sanitizeUser = (user) => ({
@@ -422,7 +526,6 @@ const logStackEvent = ({
status,
message,
redeployType = null,
endpointId = null,
metadata = {}
}) => {
const metadataPayload = {
@@ -433,11 +536,8 @@ const logStackEvent = ({
metadataPayload.redeployType = redeployType;
}
if (endpointId !== undefined && endpointId !== null) {
metadataPayload.endpointId = String(endpointId);
}
const hasMetadata = Object.keys(metadataPayload).length > 0;
const serverContextId = getActiveServerUrl() || null;
logEvent({
category: 'stack',
@@ -447,8 +547,8 @@ const logStackEvent = ({
entityType: 'stack',
entityId: stackId !== undefined && stackId !== null ? String(stackId) : null,
entityName: stackName ?? null,
contextType: endpointId !== undefined && endpointId !== null ? 'endpoint' : null,
contextId: endpointId !== undefined && endpointId !== null ? String(endpointId) : null,
contextType: serverContextId ? 'server' : null,
contextId: serverContextId,
message: message ?? null,
metadata: hasMetadata ? metadataPayload : null
});
@@ -463,7 +563,76 @@ const parseJsonColumn = (value) => {
}
};
const SELF_STACK_ID = process.env.SELF_STACK_ID ? String(process.env.SELF_STACK_ID) : null;
const SELF_STACK_SETTING_KEY = 'self_stack_id';
let cachedSelfStackId = null;
let selfStackLoaded = false;
const loadSelfStackId = () => {
try {
const stored = getSetting(SELF_STACK_SETTING_KEY);
if (stored && stored.value) {
cachedSelfStackId = String(stored.value);
} else if (process.env.SELF_STACK_ID) {
cachedSelfStackId = String(process.env.SELF_STACK_ID);
} else {
cachedSelfStackId = null;
}
} catch (error) {
console.warn('⚠️ [Setup] Konnte Self-Stack-ID nicht laden:', error.message);
cachedSelfStackId = process.env.SELF_STACK_ID ? String(process.env.SELF_STACK_ID) : null;
} finally {
selfStackLoaded = true;
}
};
const getSelfStackId = () => {
if (!selfStackLoaded) {
loadSelfStackId();
}
return cachedSelfStackId;
};
const persistSelfStackId = (value) => {
const normalized = typeof value === 'string'
? value.trim()
: value !== undefined && value !== null
? String(value).trim()
: '';
if (!normalized) {
deleteSetting(SELF_STACK_SETTING_KEY);
cachedSelfStackId = process.env.SELF_STACK_ID ? String(process.env.SELF_STACK_ID) : null;
} else {
setSetting(SELF_STACK_SETTING_KEY, normalized);
cachedSelfStackId = normalized;
}
selfStackLoaded = true;
return cachedSelfStackId;
};
const getEnvSelfStackId = () => {
const raw = typeof process.env.SELF_STACK_ID === 'string' ? process.env.SELF_STACK_ID : '';
return raw.trim();
};
const applySelfStackStatus = (status) => {
const envSelfStackId = getEnvSelfStackId();
const currentSelfStackId = getSelfStackId();
const envDefaults = { ...(status.envDefaults || {}) };
envDefaults.selfStackId = currentSelfStackId || envSelfStackId || envDefaults.selfStackId || '';
return {
...status,
envDefaults,
selfStack: {
current: currentSelfStackId,
envProvided: Boolean(envSelfStackId)
}
};
};
const buildSetupStatusResponse = () => applySelfStackStatus(getSetupStatus());
const PORTAINER_SCRIPT_SETTING_KEY = 'portainer_update_script';
const PORTAINER_SSH_CONFIG_KEY = 'portainer_ssh_config';
@@ -810,7 +979,7 @@ const testSshConnection = async (configOverride = null) => {
const detectPortainerContainer = async () => {
try {
const endpointId = requireActiveEndpointId();
const endpointId = await resolvePortainerEnvironmentId();
const containersRes = await axiosInstance.get(`/api/endpoints/${endpointId}/docker/containers/json`, {
params: { all: true }
});
@@ -1128,7 +1297,7 @@ let currentPortainerUpdatePromise = null;
const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) => {
let maintenanceActivated = false;
const endpointId = requireActiveEndpointId();
const serverContextId = getActiveServerUrl() || null;
try {
addUpdateLog(`Portainer-Update gestartet (Quelle: ${scriptSource})`, 'info');
@@ -1158,8 +1327,8 @@ const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) =
entityType: 'service',
entityId: 'portainer',
entityName: 'Portainer',
contextType: 'endpoint',
contextId: String(endpointId),
contextType: serverContextId ? 'server' : null,
contextId: serverContextId,
message: `Portainer Update gestartet (Ziel: ${targetVersion ?? 'unbekannt'})`,
metadata: {
targetVersion: targetVersion ?? null,
@@ -1176,8 +1345,8 @@ const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) =
entityType: 'system',
entityId: 'wartung',
entityName: 'StackPulse Wartung',
contextType: 'endpoint',
contextId: String(endpointId),
contextType: serverContextId ? 'server' : null,
contextId: serverContextId,
message: 'Wartungsmodus aktiviert (Portainer Update)',
metadata: {
reason: 'portainer-update',
@@ -1215,8 +1384,8 @@ const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) =
entityType: 'service',
entityId: 'portainer',
entityName: 'Portainer',
contextType: 'endpoint',
contextId: String(endpointId),
contextType: serverContextId ? 'server' : null,
contextId: serverContextId,
message: `Portainer Update abgeschlossen (Version: ${finalVersion ?? 'unbekannt'})`,
metadata: {
resultVersion: finalVersion ?? null
@@ -1247,8 +1416,8 @@ const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) =
entityType: 'system',
entityId: 'wartung',
entityName: 'StackPulse Wartung',
contextType: 'endpoint',
contextId: String(endpointId),
contextType: serverContextId ? 'server' : null,
contextId: serverContextId,
message: 'Wartungsmodus deaktiviert',
metadata: {
reason: 'portainer-update'
@@ -1266,8 +1435,8 @@ const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) =
entityType: 'service',
entityId: 'portainer',
entityName: 'Portainer',
contextType: 'endpoint',
contextId: String(endpointId),
contextType: serverContextId ? 'server' : null,
contextId: serverContextId,
message,
source: 'system'
});
@@ -1287,19 +1456,19 @@ const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) =
deactivateMaintenanceMode({ message: 'Portainer Update fehlgeschlagen' });
logEvent({
category: 'wartung',
eventType: 'Wartungsmodus',
action: 'deaktivieren',
status: 'fehler',
entityType: 'system',
entityId: 'wartung',
entityName: 'StackPulse Wartung',
contextType: 'endpoint',
contextId: String(endpointId),
message: 'Wartungsmodus deaktiviert (Fehler)',
metadata: {
reason: 'portainer-update'
},
source: 'system'
eventType: 'Wartungsmodus',
action: 'deaktivieren',
status: 'fehler',
entityType: 'system',
entityId: 'wartung',
entityName: 'StackPulse Wartung',
contextType: serverContextId ? 'server' : null,
contextId: serverContextId,
message: 'Wartungsmodus deaktiviert (Fehler)',
metadata: {
reason: 'portainer-update'
},
source: 'system'
});
}
} finally {
@@ -1308,17 +1477,17 @@ const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) =
};
const fetchPortainerStacks = async () => {
const endpointId = requireActiveEndpointId();
const stacksRes = await axiosInstance.get('/api/stacks');
return stacksRes.data.filter((stack) => String(stack.EndpointId) === String(endpointId));
return Array.isArray(stacksRes.data) ? stacksRes.data : [];
};
const buildStackCollections = (stacks = []) => {
const currentSelfStackId = getSelfStackId();
const collections = new Map();
stacks.forEach((stack) => {
const name = stack.Name || 'Unbenannt';
const isSelf = SELF_STACK_ID && String(stack.Id) === SELF_STACK_ID;
const isSelf = currentSelfStackId && String(stack.Id) === currentSelfStackId;
const entry = collections.get(name);
if (!entry) {
@@ -1390,10 +1559,11 @@ const isStackOutdated = async (stackId) => {
};
const filterOutdatedStacks = async (stacks = []) => {
const currentSelfStackId = getSelfStackId();
const results = await Promise.all(
stacks.map(async (stack) => ({
stack,
outdated: SELF_STACK_ID && String(stack.Id) === SELF_STACK_ID
outdated: currentSelfStackId && String(stack.Id) === currentSelfStackId
? false
: await isStackOutdated(stack.Id)
}))
@@ -1427,16 +1597,11 @@ const shouldFallbackToStackFile = (message) => {
const redeployStackById = async (stackId, redeployType) => {
let stack;
const messages = getRedeployMessages(redeployType);
const endpointId = requireActiveEndpointId();
try {
const stackRes = await axiosInstance.get(`/api/stacks/${stackId}`);
stack = stackRes.data;
if (String(stack.EndpointId) !== String(endpointId)) {
throw new Error(`Stack gehört nicht zum Endpoint ${endpointId}`);
}
const targetId = stack.Id || stackId;
const targetName = stack.Name || `Stack ${stackId}`;
@@ -1451,7 +1616,6 @@ const redeployStackById = async (stackId, redeployType) => {
stackName: targetName,
status: 'started',
message: messages.started,
endpointId: stack.EndpointId,
redeployType
});
@@ -1531,7 +1695,6 @@ const redeployStackById = async (stackId, redeployType) => {
stackName: targetName,
status: 'success',
message: messages.success,
endpointId: stack.EndpointId,
redeployType
});
@@ -1553,7 +1716,6 @@ const redeployStackById = async (stackId, redeployType) => {
stackName: fallbackName,
status: 'error',
message: errorMessage,
endpointId: stack?.EndpointId || endpointId,
redeployType
});
@@ -1575,7 +1737,7 @@ app.use((req, res, next) => {
return res.status(403).json({ error: 'SUPERUSER_REQUIRED' });
}
if (!hasServer() || !hasEndpoint() || !hasApiKey()) {
if (!hasServer() || !hasApiKey()) {
return res.status(403).json({ error: 'SETUP_REQUIRED' });
}
@@ -1623,11 +1785,11 @@ app.use((req, res, next) => {
next();
});
// --- API Endpoints ---
// --- API Routes ---
app.get('/api/setup/status', (req, res) => {
try {
const status = getSetupStatus();
const status = buildSetupStatusResponse();
res.json(status);
} catch (error) {
console.error('⚠️ [Setup] Statusabfrage fehlgeschlagen:', error);
@@ -1635,12 +1797,60 @@ app.get('/api/setup/status', (req, res) => {
}
});
app.post('/api/setup/test-portainer', async (req, res) => {
const { serverUrl, apiKey } = extractPortainerCredentials(req.body);
const normalizedUrl = normalizeExternalPortainerUrl(serverUrl);
if (!normalizedUrl) {
return res.status(400).json({ error: 'SERVER_URL_INVALID' });
}
if (!apiKey) {
return res.status(400).json({ error: 'API_KEY_REQUIRED' });
}
try {
const client = createPortainerSetupClient({ baseURL: normalizedUrl, apiKey });
const statusRes = await client.get('/api/status');
res.json({
success: true,
status: statusRes.data ?? null
});
} catch (error) {
const message = error.response?.data?.message || error.message || 'PORTAINER_TEST_FAILED';
const status = error.response?.status ?? 400;
res.status(status === 401 ? 401 : 400).json({ error: 'PORTAINER_TEST_FAILED', message });
}
});
app.post('/api/setup/portainer-stacks', async (req, res) => {
const { serverUrl, apiKey } = extractPortainerCredentials(req.body);
const normalizedUrl = normalizeExternalPortainerUrl(serverUrl);
if (!normalizedUrl) {
return res.status(400).json({ error: 'SERVER_URL_INVALID' });
}
if (!apiKey) {
return res.status(400).json({ error: 'API_KEY_REQUIRED' });
}
try {
const client = createPortainerSetupClient({ baseURL: normalizedUrl, apiKey });
const stacksRes = await client.get('/api/stacks');
const stacks = Array.isArray(stacksRes.data) ? stacksRes.data : [];
res.json({ success: true, stacks });
} catch (error) {
const message = error.response?.data?.message || error.message || 'PORTAINER_STACK_FETCH_FAILED';
const status = error.response?.status ?? 400;
res.status(status === 401 ? 401 : 400).json({ error: 'PORTAINER_STACK_FETCH_FAILED', message });
}
});
app.post('/api/setup/complete', (req, res) => {
const { superuser: superuserInput, server: serverInput, endpoint: endpointInput, apiKey: apiKeyInput } = req.body ?? {};
const { superuser: superuserInput, server: serverInput, apiKey: apiKeyInput, selfStackId: selfStackInput } = req.body ?? {};
const hasSelfStackInput = req.body ? Object.prototype.hasOwnProperty.call(req.body, 'selfStackId') : false;
const initialStatus = getSetupStatus();
const needsSuperuser = initialStatus.requirements.superuser;
const needsServer = initialStatus.requirements.server;
const needsEndpoint = initialStatus.requirements.endpoint;
const needsApiKey = initialStatus.requirements.apiKey;
const created = {};
@@ -1658,22 +1868,6 @@ app.post('/api/setup/complete', (req, res) => {
};
};
const normalizeEndpointInput = (input) => {
if (!input) return null;
const name = typeof input.name === 'string' ? input.name.trim() : '';
const externalRaw = input.externalId !== undefined && input.externalId !== null
? String(input.externalId).trim()
: '';
const serverIdRaw = input.serverId !== undefined && input.serverId !== null ? Number(input.serverId) : null;
const serverId = Number.isFinite(serverIdRaw) ? serverIdRaw : null;
return {
name: name || null,
externalId: externalRaw || '',
serverId
};
};
const normalizeApiKeyInput = (input) => {
if (!input) return null;
if (typeof input === 'string') {
@@ -1691,53 +1885,20 @@ app.post('/api/setup/complete', (req, res) => {
};
try {
let serverPayload = normalizeServerInput(serverInput);
let endpointPayload = normalizeEndpointInput(endpointInput);
const serverPayload = normalizeServerInput(serverInput);
const apiKeyPayload = normalizeApiKeyInput(apiKeyInput);
if (needsServer && (!serverPayload || !serverPayload.url)) {
return res.status(400).json({ error: 'SERVER_DETAILS_REQUIRED' });
}
if (endpointPayload && !endpointPayload.externalId) {
endpointPayload.externalId = '';
}
if (needsEndpoint && (!endpointPayload || !endpointPayload.externalId)) {
const fallbackExternal = initialStatus.envDefaults.endpointExternalId?.trim();
if (fallbackExternal) {
endpointPayload = endpointPayload || {};
endpointPayload.externalId = fallbackExternal;
endpointPayload.name = endpointPayload.name || initialStatus.envDefaults.endpointName || `Endpoint ${fallbackExternal}`;
} else {
return res.status(400).json({ error: 'ENDPOINT_DETAILS_REQUIRED' });
}
}
const shouldHandleInfrastructure =
needsServer ||
needsEndpoint ||
(serverPayload && serverPayload.url) ||
(endpointPayload && endpointPayload.externalId);
if (shouldHandleInfrastructure) {
const endpointInputPayload = endpointPayload;
if (!endpointInputPayload || !endpointInputPayload.externalId) {
return res.status(400).json({ error: 'ENDPOINT_DETAILS_REQUIRED' });
}
if (needsServer || (serverPayload && serverPayload.url)) {
const setupResult = completeSetup({
server: serverPayload && serverPayload.url ? serverPayload : null,
endpoint: endpointInputPayload
server: serverPayload && serverPayload.url ? serverPayload : null
});
created.server = setupResult.server;
created.endpoint = setupResult.endpoint;
created.defaultEndpoint = setupResult.defaultEndpoint;
}
let targetServerId = apiKeyPayload?.serverId ?? created.server?.id ?? endpointPayload?.serverId ?? null;
if (needsSuperuser) {
const username = typeof superuserInput?.username === 'string' ? superuserInput.username.trim() : '';
const email = typeof superuserInput?.email === 'string' ? superuserInput.email.trim() : '';
@@ -1753,6 +1914,7 @@ app.post('/api/setup/complete', (req, res) => {
const statusSnapshot = getSetupStatus();
let targetServerId = apiKeyPayload?.serverId ?? created.server?.id ?? null;
if (!targetServerId) {
targetServerId = statusSnapshot.servers.items?.[0]?.id ?? null;
}
@@ -1767,8 +1929,18 @@ app.post('/api/setup/complete', (req, res) => {
return res.status(400).json({ error: 'API_KEY_REQUIRED' });
}
const finalStatus = getSetupStatus();
created.defaultEndpoint = finalStatus.endpoints.default;
if (hasSelfStackInput) {
const normalizedSelfStackId = typeof selfStackInput === 'string'
? selfStackInput.trim()
: selfStackInput !== undefined && selfStackInput !== null
? String(selfStackInput).trim()
: '';
const nextSelfStackId = persistSelfStackId(normalizedSelfStackId);
created.selfStackId = nextSelfStackId || null;
}
const finalStatus = buildSetupStatusResponse();
res.status(201).json({
success: finalStatus.setupComplete,
status: finalStatus,
@@ -1780,8 +1952,6 @@ app.post('/api/setup/complete', (req, res) => {
case 'SERVER_URL_REQUIRED':
case 'SERVER_NAME_REQUIRED':
case 'SERVER_DETAILS_REQUIRED':
case 'ENDPOINT_DETAILS_REQUIRED':
case 'ENDPOINT_EXTERNAL_ID_REQUIRED':
case 'USERNAME_REQUIRED':
case 'EMAIL_INVALID':
case 'PASSWORD_TOO_SHORT':
@@ -1802,31 +1972,6 @@ app.post('/api/setup/complete', (req, res) => {
}
});
app.delete('/api/setup/endpoints/:id', requirePermission('maintenance-server-delete', 'full'), (req, res) => {
const { id } = req.params;
const numericId = Number(id);
if (!Number.isFinite(numericId)) {
return res.status(400).json({ error: 'ENDPOINT_ID_INVALID' });
}
try {
const result = removeEndpoint(numericId);
const status = getSetupStatus();
res.json({ success: true, removed: result, status });
} catch (error) {
const code = error.code || error.message;
switch (code) {
case 'ENDPOINT_ID_INVALID':
return res.status(400).json({ error: 'ENDPOINT_ID_INVALID' });
case 'ENDPOINT_NOT_FOUND':
return res.status(404).json({ error: 'ENDPOINT_NOT_FOUND' });
default:
console.error('⚠️ [Setup] Endpoint konnte nicht gelöscht werden:', error);
return res.status(500).json({ error: 'ENDPOINT_DELETE_FAILED' });
}
}
});
app.delete('/api/setup/servers/:id', requirePermission('maintenance-server-delete', 'full'), (req, res) => {
const { id } = req.params;
const numericId = Number(id);
@@ -1836,7 +1981,7 @@ app.delete('/api/setup/servers/:id', requirePermission('maintenance-server-delet
try {
const result = removeServer(numericId);
const status = getSetupStatus();
const status = buildSetupStatusResponse();
res.json({ success: true, removed: result, status });
} catch (error) {
const code = error.code || error.message;
@@ -1863,7 +2008,7 @@ app.put('/api/setup/servers/:id/api-key', requirePermission('maintenance-server-
try {
const result = setServerApiKey({ serverId: numericId, apiKey: rawValue });
const status = getSetupStatus();
const status = buildSetupStatusResponse();
res.json({ success: true, updated: result, status });
} catch (error) {
const code = error.code || error.message;
@@ -1884,12 +2029,39 @@ app.put('/api/setup/servers/:id/api-key', requirePermission('maintenance-server-
}
});
app.put('/api/setup/self-stack', requirePermission('maintenance-server-manage', 'full'), (req, res) => {
const hasPayload = req.body ? Object.prototype.hasOwnProperty.call(req.body, 'selfStackId') : false;
if (!hasPayload) {
return res.status(400).json({ error: 'SELF_STACK_ID_MISSING' });
}
const inputValue = req.body.selfStackId;
const normalizedSelfStackId = typeof inputValue === 'string'
? inputValue.trim()
: inputValue !== undefined && inputValue !== null
? String(inputValue).trim()
: '';
try {
const nextValue = persistSelfStackId(normalizedSelfStackId) || null;
const status = buildSetupStatusResponse();
res.json({
success: true,
value: nextValue,
status
});
} catch (error) {
console.error('⚠️ [Setup] Self-Stack-ID konnte nicht aktualisiert werden:', error);
res.status(500).json({ error: 'SELF_STACK_ID_UPDATE_FAILED' });
}
});
app.post('/api/auth/login', (req, res) => {
if (!hasSuperuser()) {
return res.status(403).json({ error: 'SUPERUSER_REQUIRED' });
}
if (!hasServer() || !hasEndpoint() || !hasApiKey()) {
if (!hasServer() || !hasApiKey()) {
return res.status(403).json({ error: 'SETUP_REQUIRED' });
}
@@ -2006,7 +2178,7 @@ app.get('/api/auth/session', (req, res) => {
return res.status(403).json({ error: 'SUPERUSER_REQUIRED' });
}
if (!hasServer() || !hasEndpoint() || !hasApiKey()) {
if (!hasServer() || !hasApiKey()) {
return res.status(403).json({ error: 'SETUP_REQUIRED' });
}
@@ -2571,7 +2743,7 @@ app.post('/api/auth/superuser/register', (req, res) => {
}
});
app.delete('/api/auth/superuser', (req, res) => {
app.delete('/api/auth/superuser', requirePermission('maintenance-superuser-delete', 'full'), (req, res) => {
if (!hasSuperuser()) {
return res.status(404).json({ error: 'SUPERUSER_NOT_FOUND' });
}
@@ -2600,6 +2772,7 @@ app.get('/api/stacks', maintenanceGuard, async (req, res) => {
console.warn(`⚠️ [API] GET /api/stacks: Doppelte Stack-Namen erkannt: ${duplicateNames.join(', ')}`);
}
const currentSelfStackId = getSelfStackId();
const stacksWithStatus = await Promise.all(
canonicalStacks.map(async (stack) => {
try {
@@ -2615,7 +2788,7 @@ app.get('/api/stacks', maintenanceGuard, async (req, res) => {
redeploying: redeployPhase === REDEPLOY_PHASES.STARTED || redeployPhase === REDEPLOY_PHASES.QUEUED,
redeployPhase,
redeployQueued: redeployPhase === REDEPLOY_PHASES.QUEUED,
redeployDisabled: SELF_STACK_ID ? String(stack.Id) === SELF_STACK_ID : false,
redeployDisabled: currentSelfStackId ? String(stack.Id) === currentSelfStackId : false,
duplicateName: duplicateNameSet.has(stack.Name)
};
} catch (err) {
@@ -2628,7 +2801,7 @@ app.get('/api/stacks', maintenanceGuard, async (req, res) => {
redeploying: redeployPhase === REDEPLOY_PHASES.STARTED || redeployPhase === REDEPLOY_PHASES.QUEUED,
redeployPhase,
redeployQueued: redeployPhase === REDEPLOY_PHASES.QUEUED,
redeployDisabled: SELF_STACK_ID ? String(stack.Id) === SELF_STACK_ID : false,
redeployDisabled: currentSelfStackId ? String(stack.Id) === currentSelfStackId : false,
duplicateName: duplicateNameSet.has(stack.Name)
};
}
@@ -2959,7 +3132,6 @@ app.post(
stackName: target.canonical.Name,
status: 'started',
message: `Bereinigung doppelter Stacks gestartet (${duplicatesToDelete.length} Einträge)`,
endpointId: target.canonical.EndpointId,
redeployType: REDEPLOY_TYPES.MAINTENANCE,
metadata: {
duplicateIds: duplicateIds
@@ -2978,7 +3150,6 @@ app.post(
results.push({
id: stack.Id,
name: stack.Name,
endpointId: stack.EndpointId,
status: 'deleted'
});
} catch (err) {
@@ -2988,7 +3159,6 @@ app.post(
results.push({
id: stack.Id,
name: stack.Name,
endpointId: stack.EndpointId,
status: 'error',
message
});
@@ -3002,7 +3172,6 @@ app.post(
stackName: target.canonical.Name,
status: 'error',
message: `Bereinigung fehlgeschlagen für IDs: ${failedIds}`,
endpointId: target.canonical.EndpointId,
redeployType: REDEPLOY_TYPES.MAINTENANCE,
metadata: {
duplicateIds: duplicateIds
@@ -3013,8 +3182,7 @@ app.post(
success: false,
canonical: {
id: target.canonical.Id,
name: target.canonical.Name,
endpointId: target.canonical.EndpointId
name: target.canonical.Name
},
results
});
@@ -3025,7 +3193,6 @@ app.post(
stackName: target.canonical.Name,
status: 'success',
message: `Bereinigung abgeschlossen. Entfernte IDs: ${results.map((entry) => entry.id).join(', ')}`,
endpointId: target.canonical.EndpointId,
redeployType: REDEPLOY_TYPES.MAINTENANCE,
metadata: {
removedDuplicates: results.filter((entry) => entry.status === 'deleted').map((entry) => entry.id)
@@ -3036,8 +3203,7 @@ app.post(
success: true,
canonical: {
id: target.canonical.Id,
name: target.canonical.Name,
endpointId: target.canonical.EndpointId
name: target.canonical.Name
},
removed: results.length,
results
@@ -3107,7 +3273,9 @@ app.get('/api/logs', requirePermission('logs-access', 'read'), (req, res) => {
const metadata = parseJsonColumn(row.metadata);
const legacyStack = row.entityType === 'stack' ? (row.entityId ?? null) : null;
const legacyStackName = row.entityType === 'stack' ? (row.entityName ?? row.entityId ?? null) : null;
const legacyEndpoint = row.contextType === 'endpoint' ? (row.contextId ?? null) : null;
const serverContext = row.contextType === 'server'
? (row.contextId ?? null)
: null;
return {
...row,
@@ -3118,7 +3286,7 @@ app.get('/api/logs', requirePermission('logs-access', 'read'), (req, res) => {
stackId: legacyStack,
stackName: legacyStackName,
redeployType: row.eventType ?? null,
endpoint: legacyEndpoint
server: serverContext
};
});
const total = db.prepare(countQuery).get(params)?.total ?? logs.length;
@@ -3204,12 +3372,11 @@ app.put(
async (req, res) => {
console.log(`🚀 PUT /api/stacks/redeploy-all: Redeploy ALL gestartet`);
let endpointId;
const serverContextId = getActiveServerUrl() || null;
try {
endpointId = requireActiveEndpointId();
const stacksRes = await axiosInstance.get('/api/stacks');
const filteredStacks = stacksRes.data.filter(stack => String(stack.EndpointId) === String(endpointId));
const filteredStacks = Array.isArray(stacksRes.data) ? stacksRes.data : [];
console.log("📦 Redeploy ALL für folgende Stacks:");
filteredStacks.forEach(s => console.log(` - ${s.Name}`));
@@ -3232,8 +3399,8 @@ app.put(
entityType: 'bulk-operation',
entityId: 'redeploy-alle',
entityName: 'Redeploy ALLE',
contextType: 'endpoint',
contextId: String(endpointId),
contextType: serverContextId ? 'server' : null,
contextId: serverContextId,
message: `Redeploy ALLE gestartet für: ${stackSummary}`,
metadata: {
stacks: stackSummaryList
@@ -3271,8 +3438,8 @@ app.put(
entityType: 'bulk-operation',
entityId: 'redeploy-alle',
entityName: 'Redeploy ALLE',
contextType: 'endpoint',
contextId: String(endpointId),
contextType: serverContextId ? 'server' : null,
contextId: serverContextId,
message: 'Redeploy ALLE abgeschlossen',
metadata: {
processedStacks: stackSummaryList
@@ -3291,8 +3458,8 @@ app.put(
entityType: 'bulk-operation',
entityId: 'redeploy-alle',
entityName: 'Redeploy ALLE',
contextType: endpointId !== undefined && endpointId !== null ? 'endpoint' : null,
contextId: endpointId !== undefined && endpointId !== null ? String(endpointId) : null,
contextType: serverContextId ? 'server' : null,
contextId: serverContextId,
message,
source: 'system'
});
@@ -3315,10 +3482,9 @@ app.put(
return res.status(400).json({ error: 'stackIds muss eine nicht leere Array sein' });
}
let endpointId;
const serverContextId = getActiveServerUrl() || null;
try {
endpointId = requireActiveEndpointId();
const normalizedIds = stackIds.map((id) => String(id));
const { filteredStacks } = await loadStackCollections();
const stacksById = new Map(filteredStacks.map((stack) => [String(stack.Id), stack]));
@@ -3352,8 +3518,8 @@ app.put(
entityType: 'bulk-operation',
entityId: 'redeploy-auswahl',
entityName: `Redeploy Auswahl (${stackIds.length})`,
contextType: 'endpoint',
contextId: String(endpointId),
contextType: serverContextId ? 'server' : null,
contextId: serverContextId,
message: `Redeploy Auswahl gestartet für: ${summaryText}`,
metadata: {
requestedStackIds: normalizedIds
@@ -3370,8 +3536,8 @@ app.put(
entityType: 'bulk-operation',
entityId: 'redeploy-auswahl',
entityName: `Redeploy Auswahl (${stackIds.length})`,
contextType: 'endpoint',
contextId: String(endpointId),
contextType: serverContextId ? 'server' : null,
contextId: serverContextId,
message: 'Redeploy Auswahl übersprungen: keine veralteten Stacks',
metadata: {
requestedStackIds: normalizedIds,
@@ -3407,8 +3573,8 @@ app.put(
entityType: 'bulk-operation',
entityId: 'redeploy-auswahl',
entityName: `Redeploy Auswahl (${stackIds.length})`,
contextType: 'endpoint',
contextId: String(endpointId),
contextType: serverContextId ? 'server' : null,
contextId: serverContextId,
message: 'Redeploy Auswahl abgeschlossen',
metadata: {
processedStackIds: eligibleStacks.map((stack) => String(stack.Id))
@@ -3428,8 +3594,8 @@ app.put(
entityType: 'bulk-operation',
entityId: 'redeploy-auswahl',
entityName: `Redeploy Auswahl (${Array.isArray(stackIds) ? stackIds.length : 0})`,
contextType: endpointId !== undefined && endpointId !== null ? 'endpoint' : null,
contextId: endpointId !== undefined && endpointId !== null ? String(endpointId) : null,
contextType: serverContextId ? 'server' : null,
contextId: serverContextId,
message,
metadata: {
requestedStackIds: normalized
+6 -6
View File
@@ -98,14 +98,14 @@ export function buildEventLogFilter(queryParams = {}) {
filters.push(`(entity_type = 'stack' AND entity_id IN (${placeholders.join(', ')}))`);
}
const legacyEndpoints = valueToArray(queryParams.endpoints ?? queryParams.endpoint);
if (legacyEndpoints.length) {
const placeholders = legacyEndpoints.map((_, idx) => {
const key = `legacyEndpoint${idx}`;
params[key] = legacyEndpoints[idx];
const serverContexts = valueToArray(queryParams.servers ?? queryParams.server);
if (serverContexts.length) {
const placeholders = serverContexts.map((_, idx) => {
const key = `serverContext${idx}`;
params[key] = serverContexts[idx];
return `@${key}`;
});
filters.push(`(context_type = 'endpoint' AND context_id IN (${placeholders.join(', ')}))`);
filters.push(`(context_type = 'server' AND context_id IN (${placeholders.join(', ')}))`);
}
const messageQuery = singleValue(queryParams.message ?? queryParams.text);
+20
View File
@@ -257,6 +257,26 @@ export function saveGroupPermissionValues(groupId, values = {}) {
normalizedEntries.push({ item, level });
});
const getSubmittedLevel = (permissionKey) => {
const entry = normalizedEntries.find((entry) => entry.item.key === permissionKey);
if (entry) {
return entry.level;
}
const fallback = itemMap.get(permissionKey);
return fallback ? fallback.defaultLevel || 'none' : 'none';
};
const serverManageLevel = getSubmittedLevel('maintenance-server-manage');
const serverDeleteLevel = getSubmittedLevel('maintenance-server-delete');
if (getLevelPriority(serverDeleteLevel) >= getLevelPriority('full') &&
getLevelPriority(serverManageLevel) < getLevelPriority('full')) {
const error = new Error('PERMISSION_DEPENDENCY_LEVEL');
error.code = 'PERMISSION_DEPENDENCY_LEVEL';
error.permissionKey = 'maintenance-server-delete';
throw error;
}
const runSave = db.transaction(() => {
deleteGroupPermissionValuesStmt.run(numericId);
normalizedEntries.forEach(({ item, level }) => {
-1
View File
@@ -1 +0,0 @@
*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#eee}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Roboto,sans-serif;font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#bdbdbd}input::placeholder,textarea::placeholder{opacity:1;color:#bdbdbd}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(33 150 243 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(33 150 243 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width: 540px){.container{max-width:540px}}@media (min-width: 720px){.container{max-width:720px}}@media (min-width: 960px){.container{max-width:960px}}@media (min-width: 1140px){.container{max-width:1140px}}@media (min-width: 1320px){.container{max-width:1320px}}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.visible{visibility:visible}.\!invisible{visibility:hidden!important}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.\!absolute{position:absolute!important}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bLine truncated
-94
View File
@@ -1,94 +0,0 @@
function Vj(e,t){for(var r=0;r<t.length;r++){const n=t[r];if(typeof n!="string"&&!Array.isArray(n)){for(const o in n)if(o!=="default"&&!(o in e)){const a=Object.getOwnPropertyDescriptor(n,o);a&&Object.defineProperty(e,o,a.get?a:{enumerable:!0,get:()=>n[o]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))n(o);new MutationObserver(o=>{for(const a of o)if(a.type==="childList")for(const i of a.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&n(i)}).observe(document,{childList:!0,subtree:!0});function r(o){const a={};return o.integrity&&(a.integrity=o.integrity),o.referrerPolicy&&(a.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?a.credentials="include":o.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function n(o){if(o.ep)return;o.ep=!0;const a=r(o);fetch(o.href,a)}})();function Yw(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function ou(e){if(e.__esModule)return e;var t=e.default;if(typeof t=="function"){var r=function n(){return this instanceof n?Reflect.construct(t,arguments,this.constructor):t.apply(this,arguments)};r.prototype=t.prototype}else r={};return Object.defineProperty(r,"__esModule",{value:!0}),Object.keys(e).forEach(function(n){var o=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(r,n,o.get?o:{enumerable:!0,get:function(){return e[n]}})}),r}var Qw={exports:{}},Hd={},Xw={exports:{}},vt={};/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var au=Symbol.for("react.element"),Uj=Symbol.for("react.portal"),Wj=Symbol.for("react.fragment"),$j=Symbol.for("react.strict_mode"),Hj=Symbol.for("react.profiler"),qj=Symbol.for("react.provider"),Gj=Symbol.for("react.context"),Kj=Symbol.for("react.forward_ref"),Yj=Symbol.for("react.suspense"),Qj=Symbol.for("react.memo"),Xj=Symbol.for("react.lazy"),oy=Symbol.iterator;function Zj(e){return e===null||typeof e!="object"?null:(e=oy&&e[oy]||e["@@iterator"],typeof e=="function"?e:null)}var Zw={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Jw=Object.assign,e_={};function yl(e,t,r){this.props=e,this.context=t,this.refs=e_,this.updater=r||Zw}yl.prototype.isReactComponent={};yl.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};yl.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function t_(){}t_.prototype=yl.prototype;function sg(e,t,r){this.props=e,this.context=t,this.refs=e_,this.updater=r||Zw}var ug=sg.prototype=new t_;ug.constructor=sg;Jw(ug,yl.prototype);ug.isPureReactComponent=!0;var ay=Array.isArray,r_=Object.prototype.hasOwnProperty,cg={current:null},n_={key:!0,ref:!0,__self:!0,__source:!0};function o_(e,t,r){var n,o={},a=null,i=null;if(t!=null)for(n in t.ref!==void 0&&(i=t.ref),t.key!==void 0&&(a=""+t.key),t)r_.call(t,n)&&!n_.hasOwnProperty(n)&&(o[n]=t[n]);var s=arguments.length-2;if(s===1)o.children=r;else if(1<s){for(var f=Array(s),g=0;g<s;g++)f[g]=arguments[g+2];o.children=f}if(e&&e.defaultProps)for(n in s=e.defaultProps,s)o[n]===void 0&&(o[n]=s[n]);return{$$typeof:au,type:e,key:a,ref:i,props:o,_owner:cg.current}}function Jj(e,t){return{$$typeof:au,type:e.type,key:t,ref:e.ref,props:e.props,_owner:e._owner}}function dg(e){return typeof e=="object"&&e!==null&&e.$$typeof===au}function e2(e){var t={"=":"=0",":":"=2"};return"$"+e.replace(/[=:]/g,function(r){return t[r]})}var iy=/\/+/g;function kp(e,t){return typeof e=="object"&&e!==null&&e.key!=null?e2(""+e.key):t.toString(36)}function vc(e,t,r,n,o){var a=typeof e;(a==="undefined"||a==="boolean")&&(e=null);var i=!1;if(e===null)i=!0;else switch(a){case"string":case"number":i=!0;break;case"object":switch(e.$$typeof){case au:case Uj:i=!0}}if(i)return i=e,o=o(i),e=n===""?"."+kp(i,0):n,ay(o)?(r="",e!=null&&(r=e.replace(iy,"$&/")+"/"),vc(o,t,r,"",function(g){return g})):o!=null&&(dg(o)&&(o=Jj(o,r+(!o.key||i&&i.key===o.key?"":(""+o.key).replace(iy,"$&/")+"/")+e)),t.push(o)),1;if(i=0,n=n===""?".":n+":",ay(e))for(var s=0;s<e.length;s++){a=e[s];var f=n+kp(a,s);i+=vc(a,t,r,f,o)}else if(f=Zj(e),typeof f=="function")for(e=f.call(e),s=0;!(a=e.next()).done;)a=a.value,f=n+kp(a,s++),i+=vc(a,t,r,f,o);else if(a==="object")throw t=String(e),Error("Objects are not valid as a React child (found: "+(t==="[object Object]"?"object with keys {"+Object.keys(e).join(", ")+"}":t)+"). If you meant to render a collection of children, use an array instead.");return i}function Wu(e,t,r){if(e==null)return e;var n=[],o=0;return vc(e,n,"","",function(a){return t.call(r,a,o++)}),n}function t2(e){if(e._status===-1){var t=e._result;t=t(),t.then(function(r){(e._status===0||e._status===-1)&&(e._status=1,e._result=r)},function(r){(e._status===0||e._status===-1)&&(e._status=2,e._result=r)}),e._status===-1&&(e._status=0,e._result=t)}if(e._status===1)return e._result.default;throw e._result}var Vr={current:null},yc={transition:null},r2={ReactCurrentDispatcher:Vr,ReactCurrentBatchConfig:yc,ReactCurrentOwner:cg};vt.Children={map:Wu,forEach:function(e,t,r){Wu(e,function(){t.apply(this,arguments)},r)},count:function(e){var t=0;return Wu(e,function(){t++}),t},toArray:function(e){return Wu(e,function(t){return t})||[]},only:function(e){if(!dg(e))throw Error("React.Children.only expected to receive a single React element child.");return e}};vt.Component=yl;vt.Fragment=Wj;vt.Profiler=Hj;vt.PureComponent=sg;vt.StrictMode=$j;vt.Suspense=Yj;vt.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=r2;vt.cloneElement=function(e,t,r){if(e==null)throw Error("React.cloneElement(...): The argument must be a React element, but you passed "+e+".");var n=Jw({},e.props),o=e.key,a=e.ref,i=e._owner;if(t!=null){if(t.ref!==void 0&&(a=t.ref,i=cg.current),t.key!==void 0&&(o=""+t.key),e.type&&e.type.defaultProps)var s=e.type.defaultProps;for(f in t)r_.call(t,f)&&!n_.hasOwnProperty(f)&&(n[f]=t[f]===void 0&&s!==void 0?s[f]:t[f])}var f=arguments.length-2;if(f===1)n.children=r;else if(1<f){s=Array(f);for(var g=0;g<f;g++)s[g]=arguments[g+2];n.children=s}return{$$typeof:au,type:e.type,key:o,ref:a,props:n,_owner:i}};vt.createContext=function(e){return e={$$typeof:Gj,_currentValue:e,_currentValue2:e,_threadCount:0,Provider:null,Consumer:null,_defaultValue:null,_globalName:null},e.Provider={$$typeof:qj,_conLine truncated
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var n2=k,o2=Symbol.for("react.element"),a2=Symbol.for("react.fragment"),i2=Object.prototype.hasOwnProperty,l2=n2.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,s2={key:!0,ref:!0,__self:!0,__source:!0};function a_(e,t,r){var n,o={},a=null,i=null;r!==void 0&&(a=""+r),t.key!==void 0&&(a=""+t.key),t.ref!==void 0&&(i=t.ref);for(n in t)i2.call(t,n)&&!s2.hasOwnProperty(n)&&(o[n]=t[n]);if(e&&e.defaultProps)for(n in t=e.defaultProps,t)o[n]===void 0&&(o[n]=t[n]);return{$$typeof:o2,type:e,key:a,ref:i,props:o,_owner:l2.current}}Hd.Fragment=a2;Hd.jsx=a_;Hd.jsxs=a_;Qw.exports=Hd;var x=Qw.exports;function i_(e,t){return function(){return e.apply(t,arguments)}}const{toString:u2}=Object.prototype,{getPrototypeOf:pg}=Object,qd=(e=>t=>{const r=u2.call(t);return e[r]||(e[r]=r.slice(8,-1).toLowerCase())})(Object.create(null)),to=e=>(e=e.toLowerCase(),t=>qd(t)===e),Gd=e=>t=>typeof t===e,{isArray:xl}=Array,Ns=Gd("undefined");function c2(e){return e!==null&&!Ns(e)&&e.constructor!==null&&!Ns(e.constructor)&&sn(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}const l_=to("ArrayBuffer");function d2(e){let t;return typeof ArrayBuffer<"u"&&ArrayBuffer.isView?t=ArrayBuffer.isView(e):t=e&&e.buffer&&l_(e.buffer),t}const f2=Gd("string"),sn=Gd("function"),s_=Gd("number"),Kd=e=>e!==null&&typeof e=="object",p2=e=>e===!0||e===!1,xc=e=>{if(qd(e)!=="object")return!1;const t=pg(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(Symbol.toStringTag in e)&&!(Symbol.iterator in e)},h2=to("Date"),g2=to("File"),m2=to("Blob"),b2=to("FileList"),v2=e=>Kd(e)&&sn(e.pipe),y2=e=>{let t;return e&&(typeof FormData=="function"&&e instanceof FormData||sn(e.append)&&((t=qd(e))==="formdata"||t==="object"&&sn(e.toString)&&e.toString()==="[object FormData]"))},x2=to("URLSearchParams"),[w2,_2,S2,C2]=["ReadableStream","Request","Response","Headers"].map(to),T2=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"");function iu(e,t,{allOwnKeys:r=!1}={}){if(e===null||typeof e>"u")return;let n,o;if(typeof e!="object"&&(e=[e]),xl(e))for(n=0,o=e.length;n<o;n++)t.call(null,e[n],n,e);else{const a=r?Object.getOwnPropertyNames(e):Object.keys(e),i=a.length;let s;for(n=0;n<i;n++)s=a[n],t.call(null,e[s],s,e)}}function u_(e,t){t=t.toLowerCase();const r=Object.keys(e);let n=r.length,o;for(;n-- >0;)if(o=r[n],t===o.toLowerCase())return o;return null}const Ua=(()=>typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:global)(),c_=e=>!Ns(e)&&e!==Ua;function kh(){const{caseless:e}=c_(this)&&this||{},t={},r=(n,o)=>{const a=e&&u_(t,o)||o;xc(t[a])&&xc(n)?t[a]=kh(t[a],n):xc(n)?t[a]=kh({},n):xl(n)?t[a]=n.slice():t[a]=n};for(let n=0,o=arguments.length;n<o;n++)arguments[n]&&iu(arguments[n],r);return t}const O2=(e,t,r,{allOwnKeys:n}={})=>(iu(t,(o,a)=>{r&&sn(o)?e[a]=i_(o,r):e[a]=o},{allOwnKeys:n}),e),P2=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),k2=(e,t,r,n)=>{e.prototype=Object.create(t.prototype,n),e.prototype.constructor=e,Object.defineProperty(e,"super",{value:t.prototype}),r&&Object.assign(e.prototype,r)},E2=(e,t,r,n)=>{let o,a,i;const s={};if(t=t||{},e==null)return t;do{for(o=Object.getOwnPropertyNames(e),a=o.length;a-- >0;)i=o[a],(!n||n(i,e,t))&&!s[i]&&(t[i]=e[i],s[i]=!0);e=r!==!1&&pg(e)}while(e&&(!r||r(e,t))&&e!==Object.prototype);return t},N2=(e,t,r)=>{e=String(e),(r===void 0||r>e.length)&&(r=e.length),r-=t.length;const n=e.indexOf(t,r);return n!==-1&&n===r},j2=e=>{if(!e)return null;if(xl(e))return e;let t=e.length;if(!s_(t))return null;const r=new Array(t);for(;t-- >0;)r[t]=e[t];return r},R2=(e=>t=>e&&t instanceof e)(typeof Uint8Array<"u"&&pg(Uint8Array)),A2=(e,t)=>{const n=(e&&e[Symbol.iterator]).call(e);let o;for(;(o=n.next())&&!o.done;){const a=o.value;t.call(e,a[0],a[1])}},M2=(e,t)=>{let r;const n=[];for(;(r=e.exec(t))!==null;)n.push(r);return n},I2=to("HTMLFormElement"),D2=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(r,n,o){return n.toUpperCase()+o}),ly=(({hasOwnProperty:e})=>(t,r)=>e.call(t,r))(Object.prototype),L2=to("RegExp"),d_=(e,t)=>{const r=Object.getOwnPropertyDescriptors(e),n={};iu(r,(o,a)=>{let i;(i=t(o,a,e))!==!1&&(n[a]=i||o)}),Object.defineProperties(e,n)},F2=e=>{d_(e,(t,r)=>{if(sn(e)&&["arguments","caller","callee"].indexOf(r)!==-1)return!1;const n=e[r];if(sn(n)){if(t.enumerable=!1,"writable"in t){t.writable=!1;return}t.set||(t.set=()=>{throw Error("Can not rewrite read-only method '"+r+"'")})}})},z2=(e,t)=>{const r={},n=o=>{o.forEach(a=>{r[a]=!0})};return xl(e)?n(e):n(String(e).split(t)),r},B2=()=>{},V2=(e,t)=>e!=null&&Number.isFinite(e=+e)?e:t,Ep="abcdefghijklmnopqrstuvwxyz",sy="0123456789",f_={DIGIT:sy,ALPHA:Ep,ALPHA_DIGIT:Ep+Ep.toUpperCase()+sy},U2=(e=16,t=f_.ALPHA_DIGIT)=>{let r="";const{length:n}=t;for(;e--;)r+=t[Math.random()*n|0];return r};function W2(e){return!!(e&&sn(e.append)&&e[Symbol.toStringTag]==="FormData"&&e[Symbol.iterator])}const $2=e=>{const t=new Array(10),r=(n,o)=>{if(Kd(n)){if(t.indexOf(n)>=0)return;if(!("toJSON"in n)){t[o]=n;const a=xLine truncated
`).forEach(function(i){o=i.indexOf(":"),r=i.substring(0,o).trim().toLowerCase(),n=i.substring(o+1).trim(),!(!r||t[r]&&dR[r])&&(r==="set-cookie"?t[r]?t[r].push(n):t[r]=[n]:t[r]=t[r]?t[r]+", "+n:n)}),t},fy=Symbol("internals");function Ql(e){return e&&String(e).trim().toLowerCase()}function wc(e){return e===!1||e==null?e:_e.isArray(e)?e.map(wc):String(e)}function pR(e){const t=Object.create(null),r=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let n;for(;n=r.exec(e);)t[n[1]]=n[2];return t}const hR=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function Np(e,t,r,n,o){if(_e.isFunction(n))return n.call(this,t,r);if(o&&(t=r),!!_e.isString(t)){if(_e.isString(n))return t.indexOf(n)!==-1;if(_e.isRegExp(n))return n.test(t)}}function gR(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(t,r,n)=>r.toUpperCase()+n)}function mR(e,t){const r=_e.toCamelCase(" "+t);["get","set","has"].forEach(n=>{Object.defineProperty(e,n+r,{value:function(o,a,i){return this[n].call(this,t,o,a,i)},configurable:!0})})}class Qd{constructor(t){t&&this.set(t)}set(t,r,n){const o=this;function a(s,f,g){const b=Ql(f);if(!b)throw new Error("header name must be a non-empty string");const S=_e.findKey(o,b);(!S||o[S]===void 0||g===!0||g===void 0&&o[S]!==!1)&&(o[S||f]=wc(s))}const i=(s,f)=>_e.forEach(s,(g,b)=>a(g,b,f));if(_e.isPlainObject(t)||t instanceof this.constructor)i(t,r);else if(_e.isString(t)&&(t=t.trim())&&!hR(t))i(fR(t),r);else if(_e.isHeaders(t))for(const[s,f]of t.entries())a(f,s,n);else t!=null&&a(r,t,n);return this}get(t,r){if(t=Ql(t),t){const n=_e.findKey(this,t);if(n){const o=this[n];if(!r)return o;if(r===!0)return pR(o);if(_e.isFunction(r))return r.call(this,o,n);if(_e.isRegExp(r))return r.exec(o);throw new TypeError("parser must be boolean|regexp|function")}}}has(t,r){if(t=Ql(t),t){const n=_e.findKey(this,t);return!!(n&&this[n]!==void 0&&(!r||Np(this,this[n],n,r)))}return!1}delete(t,r){const n=this;let o=!1;function a(i){if(i=Ql(i),i){const s=_e.findKey(n,i);s&&(!r||Np(n,n[s],s,r))&&(delete n[s],o=!0)}}return _e.isArray(t)?t.forEach(a):a(t),o}clear(t){const r=Object.keys(this);let n=r.length,o=!1;for(;n--;){const a=r[n];(!t||Np(this,this[a],a,t,!0))&&(delete this[a],o=!0)}return o}normalize(t){const r=this,n={};return _e.forEach(this,(o,a)=>{const i=_e.findKey(n,a);if(i){r[i]=wc(o),delete r[a];return}const s=t?gR(a):String(a).trim();s!==a&&delete r[a],r[s]=wc(o),n[s]=!0}),this}concat(...t){return this.constructor.concat(this,...t)}toJSON(t){const r=Object.create(null);return _e.forEach(this,(n,o)=>{n!=null&&n!==!1&&(r[o]=t&&_e.isArray(n)?n.join(", "):n)}),r}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([t,r])=>t+": "+r).join(`
`)}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(t){return t instanceof this?t:new this(t)}static concat(t,...r){const n=new this(t);return r.forEach(o=>n.set(o)),n}static accessor(t){const n=(this[fy]=this[fy]={accessors:{}}).accessors,o=this.prototype;function a(i){const s=Ql(i);n[s]||(mR(o,i),n[s]=!0)}return _e.isArray(t)?t.forEach(a):a(t),this}}Qd.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);_e.reduceDescriptors(Qd.prototype,({value:e},t)=>{let r=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(n){this[r]=n}}});_e.freezeMethods(Qd);const qn=Qd;function jp(e,t){const r=this||bg,n=t||r,o=qn.from(n.headers);let a=n.data;return _e.forEach(e,function(s){a=s.call(r,a,o.normalize(),t?t.status:void 0)}),o.normalize(),a}function w_(e){return!!(e&&e.__CANCEL__)}function wl(e,t,r){dt.call(this,e??"canceled",dt.ERR_CANCELED,t,r),this.name="CanceledError"}_e.inherits(wl,dt,{__CANCEL__:!0});function __(e,t,r){const n=r.config.validateStatus;!r.status||!n||n(r.status)?e(r):t(new dt("Request failed with status code "+r.status,[dt.ERR_BAD_REQUEST,dt.ERR_BAD_RESPONSE][Math.floor(r.status/100)-4],r.config,r.request,r))}function bR(e){const t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||""}function vR(e,t){e=e||10;const r=new Array(e),n=new Array(e);let o=0,a=0,i;return t=t!==void 0?t:1e3,function(f){const g=Date.now(),b=n[a];i||(i=g),r[o]=f,n[o]=g;let S=a,O=0;for(;S!==o;)O+=r[S++],S=S%e;if(o=(o+1)%e,o===a&&(a=(a+1)%e),g-i<t)return;const T=b&&g-b;return T?Math.round(O*1e3/T):void 0}}function yR(e,t){let r=0,n=1e3/t,o,a;const i=(g,b=Date.now())=>{r=b,o=null,a&&(clearTimeout(a),a=null),e.apply(null,g)};return[(...g)=>{const b=Date.now(),S=b-r;S>=n?i(g,b):(o=g,a||(a=setTimeout(()=>{a=null,i(o)},n-S)))},()=>o&&i(o)]}const Yc=(e,t,r=3)=>{let n=0;const o=vR(50,250);return yR(a=>{const i=a.loaded,s=a.lengthComputable?a.total:void 0,f=i-n,g=o(f),b=i<=s;n=i;const S={loaded:i,total:s,progress:s?i/s:void 0,bytes:f,rate:g||void 0,estimated:g&&s&&b?(s-i)/g:void 0,event:a,lengthComputable:s!=null,[t?"download":"upload"]:!0};e(S)},r)},py=(e,t)=>{const r=e!=null;return[n=>t[0]({lengthComputable:r,total:e,loaded:n}),t[1]]},hy=e=>(...t)=>_e.asap(()=>e(...t)),xR=un.hasStandardBrowserEnv?function(){const t=un.navigator&&/(msie|trident)/i.test(un.navigator.userAgent),r=document.createElement("a");let n;function o(a){let i=a;return t&&(r.setAttribute("href",i),i=r.href),r.setAttribute("href",i),{href:r.href,protocol:r.protocol?r.protocol.replace(/:$/,""):"",host:r.host,search:r.search?r.search.replace(/^\?/,""):"",hash:r.hash?r.hash.replace(/^#/,""):"",hostname:r.hostname,port:r.port,pathname:r.pathname.charAt(0)==="/"?r.pathname:"/"+r.pathname}}return n=o(window.location.href),function(i){const s=_e.isString(i)?o(i):i;return s.protocol===n.protocol&&s.host===n.host}}():function(){return function(){return!0}}(),wR=un.hasStandardBrowserEnv?{write(e,t,r,n,o,a){const i=[e+"="+encodeURIComponent(t)];_e.isNumber(r)&&i.push("expires="+new Date(r).toGMTString()),_e.isString(n)&&i.push("path="+n),_e.isString(o)&&i.push("domain="+o),a===!0&&i.push("secure"),document.cookie=i.join("; ")},read(e){const t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove(e){this.write(e,"",Date.now()-864e5)}}:{write(){},read(){return null},remove(){}};function _R(e){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e)}function SR(e,t){return t?e.replace(/\/?\/$/,"")+"/"+t.replace(/^\/+/,""):e}function S_(e,t){return e&&!_R(t)?SR(e,t):t}const gy=e=>e instanceof qn?{...e}:e;function Xa(e,t){t=t||{};const r={};function n(g,b,S){return _e.isPlainObject(g)&&_e.isPlainObject(b)?_e.merge.call({caseless:S},g,b):_e.isPlainObject(b)?_e.merge({},b):_e.isArray(b)?b.slice():b}function o(g,b,S){if(_e.isUndefined(b)){if(!_e.isUndefined(g))return n(void 0,g,S)}else return n(g,b,S)}function a(g,b){if(!_e.isUndefined(b))return n(void 0,b)}function i(g,b){if(_e.isUndefined(b)){if(!_e.isUndefined(g))return n(void 0,g)}else return n(void 0,b)}function s(g,b,S){if(S in t)return n(g,b);if(S in e)return n(void 0,g)}const f={url:a,method:a,data:a,baseURL:i,transformRequest:i,transformResponse:i,paramsSerializer:i,timeout:i,timeoutMessage:i,withCredentials:i,withXSRFToken:i,adapter:i,responseType:i,xsrfCookieName:i,xsrfHeaderName:i,onUploadProgress:i,onDownloadProgress:i,decompress:i,maxContentLength:i,maxBodyLength:i,beforeRedirect:i,transport:i,httpAgent:i,httpsAgent:i,cancelToken:i,socketPath:i,responseEncoding:i,validateStatus:s,headers:(g,b)=>o(gy(g),gy(b),!0)};return _e.forEach(Object.keys(Object.assign({},e,t)),function(b){const S=f[b]||o,O=S(e[b],t[b],b);_e.isUndefined(O)&&S!==s||(r[b]=O)}),r}const C_=e=>{const t=Xa({},e);let{data:r,withXSRFToken:n,xsrfHeaderName:o,xsrfCookieName:a,headers:i,auth:s}=t;t.headers=i=qn.from(i),t.url=v_(S_(t.baseURL,t.url),e.params,e.paramsSerializer),s&&i.set("Authorization","Basic "+btoa((s.username||"")+":"+(s.password?unescape(enco Line truncated
`+a.map(vy).join(`
`):" "+vy(a[0]):"as no adapter specified";throw new dt("There is no suitable adapter to dispatch the request "+i,"ERR_NOT_SUPPORT")}return n},adapters:Ah};function Rp(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new wl(null,e)}function yy(e){return Rp(e),e.headers=qn.from(e.headers),e.data=jp.call(e,e.transformRequest),["post","put","patch"].indexOf(e.method)!==-1&&e.headers.setContentType("application/x-www-form-urlencoded",!1),P_.getAdapter(e.adapter||bg.adapter)(e).then(function(n){return Rp(e),n.data=jp.call(e,e.transformResponse,n),n.headers=qn.from(n.headers),n},function(n){return w_(n)||(Rp(e),n&&n.response&&(n.response.data=jp.call(e,e.transformResponse,n.response),n.response.headers=qn.from(n.response.headers))),Promise.reject(n)})}const k_="1.7.5",vg={};["object","boolean","number","function","string","symbol"].forEach((e,t)=>{vg[e]=function(n){return typeof n===e||"a"+(t<1?"n ":" ")+e}});const xy={};vg.transitional=function(t,r,n){function o(a,i){return"[Axios v"+k_+"] Transitional option '"+a+"'"+i+(n?". "+n:"")}return(a,i,s)=>{if(t===!1)throw new dt(o(i," has been removed"+(r?" in "+r:"")),dt.ERR_DEPRECATED);return r&&!xy[i]&&(xy[i]=!0,console.warn(o(i," has been deprecated since v"+r+" and will be removed in the near future"))),t?t(a,i,s):!0}};function IR(e,t,r){if(typeof e!="object")throw new dt("options must be an object",dt.ERR_BAD_OPTION_VALUE);const n=Object.keys(e);let o=n.length;for(;o-- >0;){const a=n[o],i=t[a];if(i){const s=e[a],f=s===void 0||i(s,a,e);if(f!==!0)throw new dt("option "+a+" must be "+f,dt.ERR_BAD_OPTION_VALUE);continue}if(r!==!0)throw new dt("Unknown option "+a,dt.ERR_BAD_OPTION)}}const Mh={assertOptions:IR,validators:vg},Jo=Mh.validators;class Xc{constructor(t){this.defaults=t,this.interceptors={request:new dy,response:new dy}}async request(t,r){try{return await this._request(t,r)}catch(n){if(n instanceof Error){let o;Error.captureStackTrace?Error.captureStackTrace(o={}):o=new Error;const a=o.stack?o.stack.replace(/^.+\n/,""):"";try{n.stack?a&&!String(n.stack).endsWith(a.replace(/^.+\n.+\n/,""))&&(n.stack+=`
`+a):n.stack=a}catch{}}throw n}}_request(t,r){typeof t=="string"?(r=r||{},r.url=t):r=t||{},r=Xa(this.defaults,r);const{transitional:n,paramsSerializer:o,headers:a}=r;n!==void 0&&Mh.assertOptions(n,{silentJSONParsing:Jo.transitional(Jo.boolean),forcedJSONParsing:Jo.transitional(Jo.boolean),clarifyTimeoutError:Jo.transitional(Jo.boolean)},!1),o!=null&&(_e.isFunction(o)?r.paramsSerializer={serialize:o}:Mh.assertOptions(o,{encode:Jo.function,serialize:Jo.function},!0)),r.method=(r.method||this.defaults.method||"get").toLowerCase();let i=a&&_e.merge(a.common,a[r.method]);a&&_e.forEach(["delete","get","head","post","put","patch","common"],C=>{delete a[C]}),r.headers=qn.concat(i,a);const s=[];let f=!0;this.interceptors.request.forEach(function(w){typeof w.runWhen=="function"&&w.runWhen(r)===!1||(f=f&&w.synchronous,s.unshift(w.fulfilled,w.rejected))});const g=[];this.interceptors.response.forEach(function(w){g.push(w.fulfilled,w.rejected)});let b,S=0,O;if(!f){const C=[yy.bind(this),void 0];for(C.unshift.apply(C,s),C.push.apply(C,g),O=C.length,b=Promise.resolve(r);S<O;)b=b.then(C[S++],C[S++]);return b}O=s.length;let T=r;for(S=0;S<O;){const C=s[S++],w=s[S++];try{T=C(T)}catch(m){w.call(this,m);break}}try{b=yy.call(this,T)}catch(C){return Promise.reject(C)}for(S=0,O=g.length;S<O;)b=b.then(g[S++],g[S++]);return b}getUri(t){t=Xa(this.defaults,t);const r=S_(t.baseURL,t.url);return v_(r,t.params,t.paramsSerializer)}}_e.forEach(["delete","get","head","options"],function(t){Xc.prototype[t]=function(r,n){return this.request(Xa(n||{},{method:t,url:r,data:(n||{}).data}))}});_e.forEach(["post","put","patch"],function(t){function r(n){return function(a,i,s){return this.request(Xa(s||{},{method:t,headers:n?{"Content-Type":"multipart/form-data"}:{},url:a,data:i}))}}Xc.prototype[t]=r(),Xc.prototype[t+"Form"]=r(!0)});const _c=Xc;class yg{constructor(t){if(typeof t!="function")throw new TypeError("executor must be a function.");let r;this.promise=new Promise(function(a){r=a});const n=this;this.promise.then(o=>{if(!n._listeners)return;let a=n._listeners.length;for(;a-- >0;)n._listeners[a](o);n._listeners=null}),this.promise.then=o=>{let a;const i=new Promise(s=>{n.subscribe(s),a=s}).then(o);return i.cancel=function(){n.unsubscribe(a)},i},t(function(a,i,s){n.reason||(n.reason=new wl(a,i,s),r(n.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(t){if(this.reason){t(this.reason);return}this._listeners?this._listeners.push(t):this._listeners=[t]}unsubscribe(t){if(!this._listeners)return;const r=this._listeners.indexOf(t);r!==-1&&this._listeners.splice(r,1)}static source(){let t;return{token:new yg(function(o){t=o}),cancel:t}}}const DR=yg;function LR(e){return function(r){return e.apply(null,r)}}function FR(e){return _e.isObject(e)&&e.isAxiosError===!0}const Ih={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(Ih).forEach(([e,t])=>{Ih[t]=e});const zR=Ih;function E_(e){const t=new _c(e),r=i_(_c.prototype.request,t);return _e.extend(r,_c.prototype,t,{allOwnKeys:!0}),_e.extend(r,t,null,{allOwnKeys:!0}),r.create=function(o){return E_(Xa(e,o))},r}const lr=E_(bg);lr.Axios=_c;lr.CanceledError=wl;lr.CancelToken=DR;lr.isCancel=w_;lr.VERSION=k_;lr.toFormData=Yd;lr.AxiosError=dt;lr.Cancel=lr.CanceledError;lr.all=function(t){return Promise.all(t)};lr.spread=LR;lr.isAxiosError=FR;lr.mergeConfig=Xa;lr.AxiosHeaders=qn;lr.formToJSON=e=>x_(_e.isHTMLForm(e)?new FormData(e):e);lr.getAdapter=P_.getAdapter;lr.HttpStatusCode=zR;lr.default=lr;const nt=lr;var Dh={},N_={exports:{}},hn={},j_={exports:{}},R_={};/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/(function(e){function t(W,z){var $=W.length;W.push(z);e:for(;0<$;){var q=$-1>>>1,Z=W[q];if(0<o(Z,z))W[q]=z,W[$]=Z,$=q;else break e}}function r(W){return W.length===0?null:W[0]}function n(W){if(W.length===0)return null;var z=W[0],$=W.pop();if($!==z){W[0]=$;e:for(var q=0,Z=W.length,ae=Z>>>1;q<ae;){var ge=2*(q+1)-1,ue=W[ge],te=ge+1,Se=W[te];if(0>o(ue,$))te<Z&&0>o(Se,ue)?(W[q]=Se,W[te]=$,q=te):(W[q]=ue,W[ge]=$,q=ge);else if(te<Z&&0>o(Se,$))W[q]=Se,W[te]=$,q=te;else break e}}return z}function o(W,z){var $=W.sortIndex-z.sortIndex;return $!==0?$:W.id-z.id}if(typeof performance=="object"&&typeof performance.now=="function"){var a=performance;e.unstable_now=function(){return a.now()}}else{var i=Date,s=i.now();e.unstable_now=function(){return i.now()-s}}var f=[],g=[],b=1,S=null,O=3,T=!1,C=!1,w=!1,m=typeof setTimeout=="function"?setTimeout:null,d=typeof clearTimeout=="function"?clearTimeout:null,l=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function c(W){for(var z=r(g);z!==null;){if(z.callback===null)n(g);else if(z.startTime<=W)n(g),z.sortIndex=z.expirationTime,t(f,z);else break;z=r(g)}}function u(W){if(w=!1,c(W),!C)if(r(f)!==null)C=!0,H(h);else{var z=r(g);z!==null&&Q(u,z.startTime-W)}}function h(W,z){C=!1,w&&(w=!1,d(E),E=-1),T=!0;var $=O;try{for(c(z),S=r(f);S!==null&&(!(S.expirationTime>z)||W&&!R());){var q=S.callback;if(typeof q=="function"){S.callback=null,O=S.priorityLevel;var Z=q(S.expirationTime<=z);z=e.unstable_now(),typeof Z=="function"?S.callback=Z:S===r(f)&&n(f),c(z)}else n(f);S=r(f)}if(S!==null)var ae=!0;else{var ge=r(g);ge!==null&&Q(u,ge.startTime-z),ae=!1}return ae}finally{S=null,O=$,T=!1}}var v=!1,y=null,E=-1,P=5,N=-1;function R(){return!(e.unstable_now()-N<P)}function M(){if(y!==null){var W=e.unstable_now();N=W;var z=!0;try{z=y(!0,W)}finally{z?L():(v=!1,y=null)}}else v=!1}var L;if(typeof l=="function")L=function(){l(M)};else if(typeof MessageChannel<"u"){var D=new MessageChannel,B=D.port2;D.port1.onmessage=M,L=function(){B.postMessage(null)}}else L=function(){m(M,0)};function H(W){y=W,v||(v=!0,L())}function Q(W,z){E=m(function(){W(e.unstable_now())},z)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(W){W.callback=null},e.unstable_continueExecution=function(){C||T||(C=!0,H(h))},e.unstable_forceFrameRate=function(W){0>W||125<W?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):P=0<W?Math.floor(1e3/W):5},e.unstable_getCurrentPriorityLevel=function(){return O},e.unstable_getFirstCallbackNode=function(){return r(f)},e.unstable_next=function(W){switch(O){case 1:case 2:case 3:var z=3;break;default:z=O}var $=O;O=z;try{return W()}finally{O=$}},e.unstable_pauseExecution=function(){},e.unstable_requestPaint=function(){},e.unstable_runWithPriority=function(W,z){switch(W){case 1:case 2:case 3:case 4:case 5:break;default:W=3}var $=O;O=W;try{return z()}finally{O=$}},e.unstable_scheduleCallback=function(W,z,$){var q=e.unstable_now();switch(typeof $=="object"&&$!==null?($=$.delay,$=typeof $=="number"&&0<$?q+$:q):$=q,W){case 1:var Z=-1;break;case 2:Z=250;break;case 5:Z=1073741823;break;case 4:Z=1e4;break;default:Z=5e3}return Z=$+Z,W={id:b++,callback:z,priorityLevel:W,startTime:$,expirationTime:Z,sortIndex:-1},$>q?(W.sortIndex=$,t(g,W),r(f)===null&&W===r(g)&&(w?(d(E),E=-1):w=!0,Q(u,$-q))):(W.sortIndex=Z,t(f,W),C||T||(C=!0,H(h))),W},e.unstable_shouldYield=R,e.unstable_wrapCallback=function(W){var z=O;return function(){var $=O;O=z;try{return W.apply(this,arguments)}finally{O=$}}}})(R_);j_.exports=R_;var BR=j_.exports;/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var A_=k,fn=BR;function Re(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,r=1;r<arguments.length;r++)t+="&args[]="+encodeURIComponent(arguments[r]);return"Minified React error #"+e+"; visit "+t+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}var M_=new Set,js={};function li(e,t){nl(e,t),nl(e+"Capture",t)}function nl(e,t){for(js[e]=t,e=0;e<t.length;e++)M_.add(t[e])}var Io=!(typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Lh=Object.prototype.hasOwnProperty,VR=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,wy={},_y={};function UR(e){return Lh.call(_y,e)?!0:Lh.call(wy,e)?!1:VR.test(e)?_y[e]=!0:(wy[e]=!0,!1)}function WR(e,t,r,n){if(r!==null&&r.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return n?!1:r!==null?!r.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function $R(e,t,r,n){if(t===null||typeof t>"u"||WR(e,t,r,n))return!0;if(n)return!1;if(r!==null)switch(r.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function Ur(e,t,r,n,o,a,i){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=n,this.attributeNamespace=o,this.mustUseProperty=r,this.propertyName=e,this.type=t,this.sanitizeURL=a,this.removeEmptyString=i}var _r={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){_r[e]=new Ur(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];_r[t]=new Ur(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){_r[e]=new Ur(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){_r[e]=new Ur(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){_r[e]=new Ur(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){_r[e]=new Ur(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){_r[e]=new Ur(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){_r[e]=new Ur(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){_r[e]=new Ur(e,5,!1,e.toLowerCase(),null,!1,!1)});var xg=/[\-:]([a-z])/g;function wg(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(xg,wg);_r[t]=new Ur(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(xg,wg);_r[t]=new Ur(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(xg,wg);_r[t]=new Ur(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){_r[e]=new Ur(e,1,!1,e.toLowerCase(),null,!1,!1)});_r.xlinkHref=new Ur("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){_r[e]=new Ur(e,1,!1,e.toLowerCase(),null,!0,!0)});function _g(e,t,r,n){var o=_r.hasOwnProperty(t)?_r[t]:null;(o!==null?o.type!==0:n||!(2<t.length)||t[0]!=="o"&&t[0]!== Line truncated
`+Ap+e}var Mp=!1;function Ip(e,t){if(!e||Mp)return"";Mp=!0;var r=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{if(t)if(t=function(){throw Error()},Object.defineProperty(t.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(t,[])}catch(g){var n=g}Reflect.construct(e,[],t)}else{try{t.call()}catch(g){n=g}e.call(t.prototype)}else{try{throw Error()}catch(g){n=g}e()}}catch(g){if(g&&n&&typeof g.stack=="string"){for(var o=g.stack.split(`
`),a=n.stack.split(`
`),i=o.length-1,s=a.length-1;1<=i&&0<=s&&o[i]!==a[s];)s--;for(;1<=i&&0<=s;i--,s--)if(o[i]!==a[s]){if(i!==1||s!==1)do if(i--,s--,0>s||o[i]!==a[s]){var f=`
`+o[i].replace(" at new "," at ");return e.displayName&&f.includes("<anonymous>")&&(f=f.replace("<anonymous>",e.displayName)),f}while(1<=i&&0<=s);break}}}finally{Mp=!1,Error.prepareStackTrace=r}return(e=e?e.displayName||e.name:"")?us(e):""}function HR(e){switch(e.tag){case 5:return us(e.type);case 16:return us("Lazy");case 13:return us("Suspense");case 19:return us("SuspenseList");case 0:case 2:case 15:return e=Ip(e.type,!1),e;case 11:return e=Ip(e.type.render,!1),e;case 1:return e=Ip(e.type,!0),e;default:return""}}function Vh(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Ai:return"Fragment";case Ri:return"Portal";case Fh:return"Profiler";case Sg:return"StrictMode";case zh:return"Suspense";case Bh:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case D_:return(e.displayName||"Context")+".Consumer";case I_:return(e._context.displayName||"Context")+".Provider";case Cg:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case Tg:return t=e.displayName||null,t!==null?t:Vh(e.type)||"Memo";case aa:t=e._payload,e=e._init;try{return Vh(e(t))}catch{}}return null}function qR(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return Vh(t);case 8:return t===Sg?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function Ca(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function F_(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function GR(e){var t=F_(e)?"checked":"value",r=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),n=""+e[t];if(!e.hasOwnProperty(t)&&typeof r<"u"&&typeof r.get=="function"&&typeof r.set=="function"){var o=r.get,a=r.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return o.call(this)},set:function(i){n=""+i,a.call(this,i)}}),Object.defineProperty(e,t,{enumerable:r.enumerable}),{getValue:function(){return n},setValue:function(i){n=""+i},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Hu(e){e._valueTracker||(e._valueTracker=GR(e))}function z_(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var r=t.getValue(),n="";return e&&(n=F_(e)?e.checked?"true":"false":e.value),e=n,e!==r?(t.setValue(e),!0):!1}function Zc(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function Uh(e,t){var r=t.checked;return er({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:r??e._wrapperState.initialChecked})}function Cy(e,t){var r=t.defaultValue==null?"":t.defaultValue,n=t.checked!=null?t.checked:t.defaultChecked;r=Ca(t.value!=null?t.value:r),e._wrapperState={initialChecked:n,initialValue:r,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function B_(e,t){t=t.checked,t!=null&&_g(e,"checked",t,!1)}function Wh(e,t){B_(e,t);var r=Ca(t.value),n=t.type;if(r!=null)n==="number"?(r===0&&e.value===""||e.value!=r)&&(e.value=""+r):e.value!==""+r&&(e.value=""+r);else if(n==="submit"||n==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?$h(e,t.type,r):t.hasOwnProperty("defaultValue")&&$h(e,t.type,Ca(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function Ty(e,t,r){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var n=t.type;if(!(n!=="submit"&&n!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,r||t===e.value||(e.value=t),e.defaultValue=t}r=e.name,r!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,r!==""&&(e.name=r)}function $h(e,t,r){(t!=="number"||Zc(e.ownerDocument)!==e)&&(r==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+r&&(e.defaultValue=""+r))}var cs=Array.isArray;function qi(e,t,r,n){if(e=e.options,t){t={};for(var o=0;o<r.length;o++)t["$"+r[o]]=!0;for(r=0;r<e.length;r++)o=t.hasOwnProperty("$"+e[r].value),e[r].selected!==o&&(e[r].selected=o),o&&n&&(e[r].defaultSelected=!0)}else{for(r=""+Ca(r),t=null,o=0;o<e.length;o++){if(e[o].value===r){e[o].selected=!0,n&&(e[o].defaultSelected=!0);return}t!==null||e[o].disabled||(t=e[o])}t!==null&&(t.selected=!0)}}function Hh(e,t){if(t.dan Line truncated
`).replace(nM,"")}function Ju(e,t,r){if(t=Ky(t),Ky(e)!==t&&r)throw Error(Re(425))}function id(){}var o0=null,a0=null;function i0(e,t){return e==="textarea"||e==="noscript"||typeof t.children=="string"||typeof t.children=="number"||typeof t.dangerouslySetInnerHTML=="object"&&t.dangerouslySetInnerHTML!==null&&t.dangerouslySetInnerHTML.__html!=null}var l0=typeof setTimeout=="function"?setTimeout:void 0,oM=typeof clearTimeout=="function"?clearTimeout:void 0,Yy=typeof Promise=="function"?Promise:void 0,aM=typeof queueMicrotask=="function"?queueMicrotask:typeof Yy<"u"?function(e){return Yy.resolve(null).then(e).catch(iM)}:l0;function iM(e){setTimeout(function(){throw e})}function Kp(e,t){var r=t,n=0;do{var o=r.nextSibling;if(e.removeChild(r),o&&o.nodeType===8)if(r=o.data,r==="/$"){if(n===0){e.removeChild(o),Ds(t);return}n--}else r!=="$"&&r!=="$?"&&r!=="$!"||n++;r=o}while(r);Ds(t)}function ma(e){for(;e!=null;e=e.nextSibling){var t=e.nodeType;if(t===1||t===3)break;if(t===8){if(t=e.data,t==="$"||t==="$!"||t==="$?")break;if(t==="/$")return null}}return e}function Qy(e){e=e.previousSibling;for(var t=0;e;){if(e.nodeType===8){var r=e.data;if(r==="$"||r==="$!"||r==="$?"){if(t===0)return e;t--}else r==="/$"&&t++}e=e.previousSibling}return null}var Sl=Math.random().toString(36).slice(2),co="__reactFiber$"+Sl,Vs="__reactProps$"+Sl,Do="__reactContainer$"+Sl,s0="__reactEvents$"+Sl,lM="__reactListeners$"+Sl,sM="__reactHandles$"+Sl;function Wa(e){var t=e[co];if(t)return t;for(var r=e.parentNode;r;){if(t=r[Do]||r[co]){if(r=t.alternate,t.child!==null||r!==null&&r.child!==null)for(e=Qy(e);e!==null;){if(r=e[co])return r;e=Qy(e)}return t}e=r,r=e.parentNode}return null}function uu(e){return e=e[co]||e[Do],!e||e.tag!==5&&e.tag!==6&&e.tag!==13&&e.tag!==3?null:e}function Li(e){if(e.tag===5||e.tag===6)return e.stateNode;throw Error(Re(33))}function rf(e){return e[Vs]||null}var u0=[],Fi=-1;function ka(e){return{current:e}}function Vt(e){0>Fi||(e.current=u0[Fi],u0[Fi]=null,Fi--)}function Ft(e,t){Fi++,u0[Fi]=e.current,e.current=t}var Ta={},Ar=ka(Ta),Zr=ka(!1),Za=Ta;function ol(e,t){var r=e.type.contextTypes;if(!r)return Ta;var n=e.stateNode;if(n&&n.__reactInternalMemoizedUnmaskedChildContext===t)return n.__reactInternalMemoizedMaskedChildContext;var o={},a;for(a in r)o[a]=t[a];return n&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=o),o}function Jr(e){return e=e.childContextTypes,e!=null}function ld(){Vt(Zr),Vt(Ar)}function Xy(e,t,r){if(Ar.current!==Ta)throw Error(Re(168));Ft(Ar,t),Ft(Zr,r)}function k5(e,t,r){var n=e.stateNode;if(t=t.childContextTypes,typeof n.getChildContext!="function")return r;n=n.getChildContext();for(var o in n)if(!(o in t))throw Error(Re(108,qR(e)||"Unknown",o));return er({},r,n)}function sd(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||Ta,Za=Ar.current,Ft(Ar,e),Ft(Zr,Zr.current),!0}function Zy(e,t,r){var n=e.stateNode;if(!n)throw Error(Re(169));r?(e=k5(e,t,Za),n.__reactInternalMemoizedMergedChildContext=e,Vt(Zr),Vt(Ar),Ft(Ar,e)):Vt(Zr),Ft(Zr,r)}var Eo=null,nf=!1,Yp=!1;function E5(e){Eo===null?Eo=[e]:Eo.push(e)}function uM(e){nf=!0,E5(e)}function Ea(){if(!Yp&&Eo!==null){Yp=!0;var e=0,t=kt;try{var r=Eo;for(kt=1;e<r.length;e++){var n=r[e];do n=n(!0);while(n!==null)}Eo=null,nf=!1}catch(o){throw Eo!==null&&(Eo=Eo.slice(e+1)),e5(Pg,Ea),o}finally{kt=t,Yp=!1}}return null}var zi=[],Bi=0,ud=null,cd=0,Tn=[],On=0,Ja=null,No=1,jo="";function za(e,t){zi[Bi++]=cd,zi[Bi++]=ud,ud=e,cd=t}function N5(e,t,r){Tn[On++]=No,Tn[On++]=jo,Tn[On++]=Ja,Ja=e;var n=No;e=jo;var o=32-Gn(n)-1;n&=~(1<<o),r+=1;var a=32-Gn(t)+o;if(30<a){var i=o-o%5;a=(n&(1<<i)-1).toString(32),n>>=i,o-=i,No=1<<32-Gn(t)+o|r<<o|n,jo=a+e}else No=1<<a|r<<o|n,jo=e}function Dg(e){e.return!==null&&(za(e,1),N5(e,1,0))}function Lg(e){for(;e===ud;)ud=zi[--Bi],zi[Bi]=null,cd=zi[--Bi],zi[Bi]=null;for(;e===Ja;)Ja=Tn[--On],Tn[On]=null,jo=Tn[--On],Tn[On]=null,No=Tn[--On],Tn[On]=null}var cn=null,an=null,qt=!1,Wn=null;function j5(e,t){var r=kn(5,null,null,0);r.elementType="DELETED",r.stateNode=t,r.return=e,t=e.deletions,t===null?(e.deletions=[r],e.flags|=16):t.push(r)}function Jy(e,t){switch(e.tag){case 5:var r=e.type;return t=t.nodeType!==1||r.toLowerCase()!==t.nodeName.toLowerCase()?null:t,t!==null?(e.stateNode=t,cn=e,an=ma(t.firstChild),!0):!1;case 6:return t=e.pendingProps===""||t.nodeType!==3?null:t,t!==null?(e.stateNode=t,cn=e,an=null,!0):!1;case 13:return t=t.nodeType!==8?null:t,t!==null?(r=Ja!==null?{id:No,overflow:jo}:null,e.memoizedState={dehydrated:t,treeContext:r,retryLane:1073741824},r=kn(18,null,null,0),r.stateNode=t,r.return=e,e.child=r,cn=e,an=null,!0):!1;default:return!1}}function c0(e){return(e.mode&1)!==0&&(e.flags&128)===0}function d0(e){if(qt){var t=an;if(t){var r=t;if(!Jy(e,t)){if(c0(e))throw Error(Re(418));t=ma(r.nextSibling);var n=cn;t&&Jy(e,t)?j5(n,r):(e.flags=e.flags&-4097|2,qt=!1,cn=e)}}else{if(c0(e))throw Error(Re(418));e.flags=e.flags&-4097|2,qt=!1,cn=e}}}function ex(e){ Line truncated
Error generating stack: `+a.message+`
`+a.stack}return{value:e,source:t,stack:o,digest:null}}function eh(e,t,r){return{value:e,source:null,stack:r??null,digest:t??null}}function g0(e,t){try{console.error(t.value)}catch(r){setTimeout(function(){throw r})}}var vM=typeof WeakMap=="function"?WeakMap:Map;function a1(e,t,r){r=Ro(-1,r),r.tag=3,r.payload={element:null};var n=t.value;return r.callback=function(){vd||(vd=!0,T0=n),g0(e,t)},r}function i1(e,t,r){r=Ro(-1,r),r.tag=3;var n=e.type.getDerivedStateFromError;if(typeof n=="function"){var o=t.value;r.payload=function(){return n(o)},r.callback=function(){g0(e,t)}}var a=e.stateNode;return a!==null&&typeof a.componentDidCatch=="function"&&(r.callback=function(){g0(e,t),typeof n!="function"&&(va===null?va=new Set([this]):va.add(this));var i=t.stack;this.componentDidCatch(t.value,{componentStack:i!==null?i:""})}),r}function sx(e,t,r){var n=e.pingCache;if(n===null){n=e.pingCache=new vM;var o=new Set;n.set(t,o)}else o=n.get(t),o===void 0&&(o=new Set,n.set(t,o));o.has(r)||(o.add(r),e=RM.bind(null,e,t,r),t.then(e,e))}function ux(e){do{var t;if((t=e.tag===13)&&(t=e.memoizedState,t=t!==null?t.dehydrated!==null:!0),t)return e;e=e.return}while(e!==null);return null}function cx(e,t,r,n,o){return e.mode&1?(e.flags|=65536,e.lanes=o,e):(e===t?e.flags|=65536:(e.flags|=128,r.flags|=131072,r.flags&=-52805,r.tag===1&&(r.alternate===null?r.tag=17:(t=Ro(-1,1),t.tag=2,ba(r,t,1))),r.lanes|=1),e)}var yM=Vo.ReactCurrentOwner,Qr=!1;function zr(e,t,r,n){t.child=e===null?F5(t,null,r,n):il(t,e.child,r,n)}function dx(e,t,r,n,o){r=r.render;var a=t.ref;return Qi(t,o),n=Kg(e,t,r,n,a,o),r=Yg(),e!==null&&!Qr?(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~o,Fo(e,t,o)):(qt&&r&&Dg(t),t.flags|=1,zr(e,t,n,o),t.child)}function fx(e,t,r,n,o){if(e===null){var a=r.type;return typeof a=="function"&&!am(a)&&a.defaultProps===void 0&&r.compare===null&&r.defaultProps===void 0?(t.tag=15,t.type=a,l1(e,t,a,n,o)):(e=Rc(r.type,null,n,t,t.mode,o),e.ref=t.ref,e.return=t,t.child=e)}if(a=e.child,!(e.lanes&o)){var i=a.memoizedProps;if(r=r.compare,r=r!==null?r:Fs,r(i,n)&&e.ref===t.ref)return Fo(e,t,o)}return t.flags|=1,e=xa(a,n),e.ref=t.ref,e.return=t,t.child=e}function l1(e,t,r,n,o){if(e!==null){var a=e.memoizedProps;if(Fs(a,n)&&e.ref===t.ref)if(Qr=!1,t.pendingProps=n=a,(e.lanes&o)!==0)e.flags&131072&&(Qr=!0);else return t.lanes=e.lanes,Fo(e,t,o)}return m0(e,t,r,n,o)}function s1(e,t,r){var n=t.pendingProps,o=n.children,a=e!==null?e.memoizedState:null;if(n.mode==="hidden")if(!(t.mode&1))t.memoizedState={baseLanes:0,cachePool:null,transitions:null},Ft(Wi,on),on|=r;else{if(!(r&1073741824))return e=a!==null?a.baseLanes|r:r,t.lanes=t.childLanes=1073741824,t.memoizedState={baseLanes:e,cachePool:null,transitions:null},t.updateQueue=null,Ft(Wi,on),on|=e,null;t.memoizedState={baseLanes:0,cachePool:null,transitions:null},n=a!==null?a.baseLanes:r,Ft(Wi,on),on|=n}else a!==null?(n=a.baseLanes|r,t.memoizedState=null):n=r,Ft(Wi,on),on|=n;return zr(e,t,o,r),t.child}function u1(e,t){var r=t.ref;(e===null&&r!==null||e!==null&&e.ref!==r)&&(t.flags|=512,t.flags|=2097152)}function m0(e,t,r,n,o){var a=Jr(r)?Za:Ar.current;return a=ol(t,a),Qi(t,o),r=Kg(e,t,r,n,a,o),n=Yg(),e!==null&&!Qr?(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~o,Fo(e,t,o)):(qt&&n&&Dg(t),t.flags|=1,zr(e,t,r,o),t.child)}function px(e,t,r,n,o){if(Jr(r)){var a=!0;sd(t)}else a=!1;if(Qi(t,o),t.stateNode===null)Ec(e,t),D5(t,r,n),h0(t,r,n,o),n=!0;else if(e===null){var i=t.stateNode,s=t.memoizedProps;i.props=s;var f=i.context,g=r.contextType;typeof g=="object"&&g!==null?g=Rn(g):(g=Jr(r)?Za:Ar.current,g=ol(t,g));var b=r.getDerivedStateFromProps,S=typeof b=="function"||typeof i.getSnapshotBeforeUpdate=="function";S||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(s!==n||f!==g)&&ox(t,i,n,g),ia=!1;var O=t.memoizedState;i.state=O,pd(t,n,i,o),f=t.memoizedState,s!==n||O!==f||Zr.current||ia?(typeof b=="function"&&(p0(t,r,b,n),f=t.memoizedState),(s=ia||nx(t,r,s,n,O,f,g))?(S||typeof i.UNSAFE_componentWillMount!="function"&&typeof i.componentWillMount!="function"||(typeof i.componentWillMount=="function"&&i.componentWillMount(),typeof i.UNSAFE_componentWillMount=="function"&&i.UNSAFE_componentWillMount()),typeof i.componentDidMount=="function"&&(t.flags|=4194308)):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),t.memoizedProps=n,t.memoizedState=f),i.props=n,i.state=f,i.context=g,n=s):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),n=!1)}else{i=t.stateNode,M5(e,t),s=t.memoizedProps,g=t.type===t.elementType?s:Bn(t.type,s),i.props=g,S=t.pendingProps,O=i.context,f=r.contextType,typeof f=="object"&&f!==null?f=Rn(f):(f=Jr(r)?Za:Ar.current,f=ol(t,f));var T=r.getDerivedStateFromProps;(b=typeof T=="function"||typeof i.getSnapshotBeforeUpdate=="function")||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(s!==S||O!==f)&&ox(t,i,n,f),ia=!1,O=t.memoizedState,i.state=O,pd(t,n,i,o);var C=t.memoizedS Line truncated
* @remix-run/router v1.10.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/function Ks(){return Ks=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},Ks.apply(this,arguments)}var fa;(function(e){e.Pop="POP",e.Push="PUSH",e.Replace="REPLACE"})(fa||(fa={}));const Px="popstate";function WM(e){e===void 0&&(e={});function t(n,o){let{pathname:a,search:i,hash:s}=n.location;return E0("",{pathname:a,search:i,hash:s},o.state&&o.state.usr||null,o.state&&o.state.key||"default")}function r(n,o){return typeof o=="string"?o:_d(o)}return HM(t,r,null,e)}function Jt(e,t){if(e===!1||e===null||typeof e>"u")throw new Error(t)}function cm(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function $M(){return Math.random().toString(36).substr(2,8)}function kx(e,t){return{usr:e.state,key:e.key,idx:t}}function E0(e,t,r,n){return r===void 0&&(r=null),Ks({pathname:typeof e=="string"?e:e.pathname,search:"",hash:""},typeof t=="string"?Cl(t):t,{state:r,key:t&&t.key||n||$M()})}function _d(e){let{pathname:t="/",search:r="",hash:n=""}=e;return r&&r!=="?"&&(t+=r.charAt(0)==="?"?r:"?"+r),n&&n!=="#"&&(t+=n.charAt(0)==="#"?n:"#"+n),t}function Cl(e){let t={};if(e){let r=e.indexOf("#");r>=0&&(t.hash=e.substr(r),e=e.substr(0,r));let n=e.indexOf("?");n>=0&&(t.search=e.substr(n),e=e.substr(0,n)),e&&(t.pathname=e)}return t}function HM(e,t,r,n){n===void 0&&(n={});let{window:o=document.defaultView,v5Compat:a=!1}=n,i=o.history,s=fa.Pop,f=null,g=b();g==null&&(g=0,i.replaceState(Ks({},i.state,{idx:g}),""));function b(){return(i.state||{idx:null}).idx}function S(){s=fa.Pop;let m=b(),d=m==null?null:m-g;g=m,f&&f({action:s,location:w.location,delta:d})}function O(m,d){s=fa.Push;let l=E0(w.location,m,d);r&&r(l,m),g=b()+1;let c=kx(l,g),u=w.createHref(l);try{i.pushState(c,"",u)}catch(h){if(h instanceof DOMException&&h.name==="DataCloneError")throw h;o.location.assign(u)}a&&f&&f({action:s,location:w.location,delta:1})}function T(m,d){s=fa.Replace;let l=E0(w.location,m,d);r&&r(l,m),g=b();let c=kx(l,g),u=w.createHref(l);i.replaceState(c,"",u),a&&f&&f({action:s,location:w.location,delta:0})}function C(m){let d=o.location.origin!=="null"?o.location.origin:o.location.href,l=typeof m=="string"?m:_d(m);return Jt(d,"No window.location.(origin|href) available to create URL for href: "+l),new URL(l,d)}let w={get action(){return s},get location(){return e(o,i)},listen(m){if(f)throw new Error("A history only accepts one active listener");return o.addEventListener(Px,S),f=m,()=>{o.removeEventListener(Px,S),f=null}},createHref(m){return t(o,m)},createURL:C,encodeLocation(m){let d=C(m);return{pathname:d.pathname,search:d.search,hash:d.hash}},push:O,replace:T,go(m){return i.go(m)}};return w}var Ex;(function(e){e.data="data",e.deferred="deferred",e.redirect="redirect",e.error="error"})(Ex||(Ex={}));function qM(e,t,r){r===void 0&&(r="/");let n=typeof t=="string"?Cl(t):t,o=Ys(n.pathname||"/",r);if(o==null)return null;let a=A1(e);GM(a);let i=null;for(let s=0;i==null&&s<a.length;++s)i=rI(a[s],oI(o));return i}function A1(e,t,r,n){t===void 0&&(t=[]),r===void 0&&(r=[]),n===void 0&&(n="");let o=(a,i,s)=>{let f={relativePath:s===void 0?a.path||"":s,caseSensitive:a.caseSensitive===!0,childrenIndex:i,route:a};f.relativePath.startsWith("/")&&(Jt(f.relativePath.startsWith(n),'Absolute route path "'+f.relativePath+'" nested under path '+('"'+n+'" is not valid. An absolute child route path ')+"must start with the combined path of all its parent routes."),f.relativePath=f.relativePath.slice(n.length));let g=wa([n,f.relativePath]),b=r.concat(f);a.children&&a.children.length>0&&(Jt(a.index!==!0,"Index routes must not have child routes. Please remove "+('all child routes from route path "'+g+'".')),A1(a.children,t,b,g)),!(a.path==null&&!a.index)&&t.push({path:g,score:eI(g,a.index),routesMeta:b})};return e.forEach((a,i)=>{var s;if(a.path===""||!((s=a.path)!=null&&s.includes("?")))o(a,i);else for(let f of M1(a.path))o(a,i,f)}),t}function M1(e){let t=e.split("/");if(t.length===0)return[];let[r,...n]=t,o=r.endsWith("?"),a=r.replace(/\?$/,"");if(n.length===0)return o?[a,""]:[a];let i=M1(n.join("/")),s=[];return s.push(...i.map(f=>f===""?a:[a,f].join("/"))),o&&s.push(...i),s.map(f=>e.startsWith("/")&&f===""?"/":f)}function GM(e){e.sort((t,r)=>t.score!==r.score?r.score-t.score:tI(t.routesMeta.map(n=>n.childrenIndex),r.routesMeta.map(n=>n.childrenIndex)))}const KM=/^:\w+$/,YM=3,QM=2,XM=1,ZM=10,JM=-2,Nx=e=>e==="*";function eI(e,t){let r=e.split("/"),n=r.length;return r.some(Nx)&&(n+=JM),t&&(n+=QM),r.filter(o=>!Nx(o)).reduce((o,a)=>o+(KM.test(a)?YM:a===""?XM:ZM),n)}function tI(e,t){return e.length===t.length&&e.slice(0,-1).every((n,o)=>n===t[o])?e[e.length-1]-t[t.length-1]:0}function rI(e,t){let{routesMeta:r}=e,n={},o="/",a=[];for(let i=0;i<r.length;++i){let s=r[i],f=i===r.length-1,g=o==="/"?t:t.slice(o.length)||"/",b=N0({path:s.relativePath,caseSensitive:s.caseSensitive,end:f},g);if(!b)return null;Object.assign(n,b.paLine truncated
* React Router v6.17.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/function Sd(){return Sd=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},Sd.apply(this,arguments)}const hf=k.createContext(null),D1=k.createContext(null),ui=k.createContext(null),gf=k.createContext(null),Uo=k.createContext({outlet:null,matches:[],isDataRoute:!1}),L1=k.createContext(null);function pI(e,t){let{relative:r}=t===void 0?{}:t;Tl()||Jt(!1);let{basename:n,navigator:o}=k.useContext(ui),{hash:a,pathname:i,search:s}=mf(e,{relative:r}),f=i;return n!=="/"&&(f=i==="/"?n:wa([n,i])),o.createHref({pathname:f,search:s,hash:a})}function Tl(){return k.useContext(gf)!=null}function Na(){return Tl()||Jt(!1),k.useContext(gf).location}function F1(e){k.useContext(ui).static||k.useLayoutEffect(e)}function yo(){let{isDataRoute:e}=k.useContext(Uo);return e?OI():hI()}function hI(){Tl()||Jt(!1);let e=k.useContext(hf),{basename:t,navigator:r}=k.useContext(ui),{matches:n}=k.useContext(Uo),{pathname:o}=Na(),a=JSON.stringify(dm(n).map(f=>f.pathnameBase)),i=k.useRef(!1);return F1(()=>{i.current=!0}),k.useCallback(function(f,g){if(g===void 0&&(g={}),!i.current)return;if(typeof f=="number"){r.go(f);return}let b=fm(f,JSON.parse(a),o,g.relative==="path");e==null&&t!=="/"&&(b.pathname=b.pathname==="/"?t:wa([t,b.pathname])),(g.replace?r.replace:r.push)(b,g.state,g)},[t,r,a,o,e])}function z1(){let{matches:e}=k.useContext(Uo),t=e[e.length-1];return t?t.params:{}}function mf(e,t){let{relative:r}=t===void 0?{}:t,{matches:n}=k.useContext(Uo),{pathname:o}=Na(),a=JSON.stringify(dm(n).map(i=>i.pathnameBase));return k.useMemo(()=>fm(e,JSON.parse(a),o,r==="path"),[e,a,o,r])}function gI(e,t){return mI(e,t)}function mI(e,t,r){Tl()||Jt(!1);let{navigator:n}=k.useContext(ui),{matches:o}=k.useContext(Uo),a=o[o.length-1],i=a?a.params:{};a&&a.pathname;let s=a?a.pathnameBase:"/";a&&a.route;let f=Na(),g;if(t){var b;let w=typeof t=="string"?Cl(t):t;s==="/"||(b=w.pathname)!=null&&b.startsWith(s)||Jt(!1),g=w}else g=f;let S=g.pathname||"/",O=s==="/"?S:S.slice(s.length)||"/",T=qM(e,{pathname:O}),C=wI(T&&T.map(w=>Object.assign({},w,{params:Object.assign({},i,w.params),pathname:wa([s,n.encodeLocation?n.encodeLocation(w.pathname).pathname:w.pathname]),pathnameBase:w.pathnameBase==="/"?s:wa([s,n.encodeLocation?n.encodeLocation(w.pathnameBase).pathname:w.pathnameBase])})),o,r);return t&&C?k.createElement(gf.Provider,{value:{location:Sd({pathname:"/",search:"",hash:"",state:null,key:"default"},g),navigationType:fa.Pop}},C):C}function bI(){let e=TI(),t=dI(e)?e.status+" "+e.statusText:e instanceof Error?e.message:JSON.stringify(e),r=e instanceof Error?e.stack:null,o={padding:"0.5rem",backgroundColor:"rgba(200,200,200, 0.5)"},a=null;return k.createElement(k.Fragment,null,k.createElement("h2",null,"Unexpected Application Error!"),k.createElement("h3",{style:{fontStyle:"italic"}},t),r?k.createElement("pre",{style:o},r):null,a)}const vI=k.createElement(bI,null);class yI extends k.Component{constructor(t){super(t),this.state={location:t.location,revalidation:t.revalidation,error:t.error}}static getDerivedStateFromError(t){return{error:t}}static getDerivedStateFromProps(t,r){return r.location!==t.location||r.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error||r.error,location:r.location,revalidation:t.revalidation||r.revalidation}}componentDidCatch(t,r){console.error("React Router caught the following error during render",t,r)}render(){return this.state.error?k.createElement(Uo.Provider,{value:this.props.routeContext},k.createElement(L1.Provider,{value:this.state.error,children:this.props.component})):this.props.children}}function xI(e){let{routeContext:t,match:r,children:n}=e,o=k.useContext(hf);return o&&o.static&&o.staticContext&&(r.route.errorElement||r.route.ErrorBoundary)&&(o.staticContext._deepestRenderedBoundaryId=r.route.id),k.createElement(Uo.Provider,{value:t},n)}function wI(e,t,r){var n;if(t===void 0&&(t=[]),r===void 0&&(r=null),e==null){var o;if((o=r)!=null&&o.errors)e=r.matches;else return null}let a=e,i=(n=r)==null?void 0:n.errors;if(i!=null){let s=a.findIndex(f=>f.route.id&&(i==null?void 0:i[f.route.id]));s>=0||Jt(!1),a=a.slice(0,Math.min(a.length,s+1))}return a.reduceRight((s,f,g)=>{let b=f.route.id?i==null?void 0:i[f.route.id]:null,S=null;r&&(S=f.route.errorElement||vI);let O=t.concat(a.slice(0,g+1)),T=()=>{let C;return b?C=S:f.route.Component?C=k.createElement(f.route.Component,null):f.route.element?C=f.route.element:C=s,k.createElement(xI,{match:f,routeContext:{outlet:s,matches:O,isDataRoute:r!=null},children:C})};return r&&(f.route.ErrorBoundary||f.route.errorElement||g===0)?k.createElement(yI,{location:r.location,revalidation:r.revalidation,component:S,error:b,children:T(),routeContext:{outlet:null,matches:O,isDataRoute:!0}}):T()},null)}var B1=function(e){return e.UseBlocker="useBlocker",e.UseRevalidator="useRevalidator",e.UseNavigateStable=Line truncated
* React Router DOM v6.17.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/function Td(){return Td=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},Td.apply(this,arguments)}function U1(e,t){if(e==null)return{};var r={},n=Object.keys(e),o,a;for(a=0;a<n.length;a++)o=n[a],!(t.indexOf(o)>=0)&&(r[o]=e[o]);return r}function EI(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function NI(e,t){return e.button===0&&(!t||t==="_self")&&!EI(e)}const jI=["onClick","relative","reloadDocument","replace","state","target","to","preventScrollReset","unstable_viewTransition"],RI=["aria-current","caseSensitive","className","end","style","to","unstable_viewTransition","children"],AI=k.createContext({isTransitioning:!1}),MI="startTransition",jx=fg[MI];function II(e){let{basename:t,children:r,future:n,window:o}=e,a=k.useRef();a.current==null&&(a.current=WM({window:o,v5Compat:!0}));let i=a.current,[s,f]=k.useState({action:i.action,location:i.location}),{v7_startTransition:g}=n||{},b=k.useCallback(S=>{g&&jx?jx(()=>f(S)):f(S)},[f,g]);return k.useLayoutEffect(()=>i.listen(b),[i,b]),k.createElement(kI,{basename:t,children:r,location:s.location,navigationType:s.action,navigator:i})}const DI=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",LI=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,W1=k.forwardRef(function(t,r){let{onClick:n,relative:o,reloadDocument:a,replace:i,state:s,target:f,to:g,preventScrollReset:b,unstable_viewTransition:S}=t,O=U1(t,jI),{basename:T}=k.useContext(ui),C,w=!1;if(typeof g=="string"&&LI.test(g)&&(C=g,DI))try{let c=new URL(window.location.href),u=g.startsWith("//")?new URL(c.protocol+g):new URL(g),h=Ys(u.pathname,T);u.origin===c.origin&&h!=null?g=h+u.search+u.hash:w=!0}catch{}let m=pI(g,{relative:o}),d=BI(g,{replace:i,state:s,target:f,preventScrollReset:b,relative:o,unstable_viewTransition:S});function l(c){n&&n(c),c.defaultPrevented||d(c)}return k.createElement("a",Td({},O,{href:C||m,onClick:w||a?n:l,ref:r,target:f}))}),FI=k.forwardRef(function(t,r){let{"aria-current":n="page",caseSensitive:o=!1,className:a="",end:i=!1,style:s,to:f,unstable_viewTransition:g,children:b}=t,S=U1(t,RI),O=mf(f,{relative:S.relative}),T=Na(),C=k.useContext(D1),{navigator:w}=k.useContext(ui),m=C!=null&&VI(O)&&g===!0,d=w.encodeLocation?w.encodeLocation(O).pathname:O.pathname,l=T.pathname,c=C&&C.navigation&&C.navigation.location?C.navigation.location.pathname:null;o||(l=l.toLowerCase(),c=c?c.toLowerCase():null,d=d.toLowerCase());let u=l===d||!i&&l.startsWith(d)&&l.charAt(d.length)==="/",h=c!=null&&(c===d||!i&&c.startsWith(d)&&c.charAt(d.length)==="/"),v={isActive:u,isPending:h,isTransitioning:m},y=u?n:void 0,E;typeof a=="function"?E=a(v):E=[a,u?"active":null,h?"pending":null,m?"transitioning":null].filter(Boolean).join(" ");let P=typeof s=="function"?s(v):s;return k.createElement(W1,Td({},S,{"aria-current":y,className:E,ref:r,style:P,to:f,unstable_viewTransition:g}),typeof b=="function"?b(v):b)});var R0;(function(e){e.UseScrollRestoration="useScrollRestoration",e.UseSubmit="useSubmit",e.UseSubmitFetcher="useSubmitFetcher",e.UseFetcher="useFetcher",e.useViewTransitionState="useViewTransitionState"})(R0||(R0={}));var Rx;(function(e){e.UseFetchers="useFetchers",e.UseScrollRestoration="useScrollRestoration"})(Rx||(Rx={}));function zI(e){let t=k.useContext(hf);return t||Jt(!1),t}function BI(e,t){let{target:r,replace:n,state:o,preventScrollReset:a,relative:i,unstable_viewTransition:s}=t===void 0?{}:t,f=yo(),g=Na(),b=mf(e,{relative:i});return k.useCallback(S=>{if(NI(S,r)){S.preventDefault();let O=n!==void 0?n:_d(g)===_d(b);f(e,{replace:O,state:o,preventScrollReset:a,relative:i,unstable_viewTransition:s})}},[g,f,b,n,o,r,e,a,i,s])}function VI(e,t){t===void 0&&(t={});let r=k.useContext(AI);r==null&&Jt(!1);let{basename:n}=zI(R0.useViewTransitionState),o=mf(e,{relative:t.relative});if(!r.isTransitioning)return!1;let a=Ys(r.currentLocation.pathname,n)||r.currentLocation.pathname,i=Ys(r.nextLocation.pathname,n)||r.nextLocation.pathname;return N0(o.pathname,i)!=null||N0(o.pathname,a)!=null}function UI({title:e,titleId:t,...r},n){return k.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":"true",ref:n,"aria-labelledby":t},r),e?k.createElement("title",{id:t},e):null,k.createElement("path",{fillRule:"evenodd",d:"M7.5 3.75A1.5 1.5 0 006 5.25v13.5a1.5 1.5 0 001.5 1.5h6a1.5 1.5 0 001.5-1.5V15a.75.75 0 011.5 0v3.75a3 3 0 01-3 3h-6a3 3 0 01-3-3V5.25a3 3 0 013-3h6a3 3 0 013 3V9A.75.75 0 0115 9V5.25a1.5 1.5 0 00-1.5-1.5h-6zm5.03 4.72a.75.75 0 010 1.06l-1.72 1.72h10.94a.75.75 0 010 1.5H10.81l1.72 1.72a.75.75 0 11-1.06 1.06l-3-3a.75.75 0 010-1.06l3-3a.75.75 0 011.06 0z",clipRule:"evenodd"}))}const WI=k.forwardRef(UI),$I=WI;function HI({title:e,titleId:t,...r},n){return k.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":"true",rLine truncated
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/(function(e){(function(){var t={}.hasOwnProperty;function r(){for(var n=[],o=0;o<arguments.length;o++){var a=arguments[o];if(a){var i=typeof a;if(i==="string"||i==="number")n.push(a);else if(Array.isArray(a)){if(a.length){var s=r.apply(null,a);s&&n.push(s)}}else if(i==="object"){if(a.toString!==Object.prototype.toString&&!a.toString.toString().includes("[native code]")){n.push(a.toString());continue}for(var f in a)t.call(a,f)&&a[f]&&n.push(f)}}}return n.join(" ")}e.exports?(r.default=r,e.exports=r):window.classNames=r})()})(q1);var at=q1.exports,G1={exports:{}},zn={};function hm(){for(var e,t,r=0,n="";r<arguments.length;)(e=arguments[r++])&&(t=K1(e))&&(n&&(n+=" "),n+=t);return n}function K1(e){if(typeof e=="string")return e;for(var t,r="",n=0;n<e.length;n++)e[n]&&(t=K1(e[n]))&&(r&&(r+=" "),r+=t);return r}function A0(){return A0=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},A0.apply(this,arguments)}function x4(e){var t=function(r){var n=r.theme,o=r.prefix,a={nextPart:new Map,validators:[]};return function(i,s){return s?i.map(function(f){return[f[0],f[1].map(function(g){return typeof g=="string"?s+g:typeof g=="object"?Object.fromEntries(Object.entries(g).map(function(b){return[s+b[0],b[1]]})):g})]}):i}(Object.entries(r.classGroups),o).forEach(function(i){M0(i[1],a,i[0],n)}),a}(e);return{getClassGroupId:function(r){var n=r.split("-");return n[0]===""&&n.length!==1&&n.shift(),Y1(n,t)||function(o){if(Ax.test(o)){var a=Ax.exec(o)[1],i=a==null?void 0:a.substring(0,a.indexOf(":"));if(i)return"arbitrary.."+i}}(r)},getConflictingClassGroupIds:function(r){return e.conflictingClassGroups[r]||[]}}}function Y1(e,t){var r;if(e.length===0)return t.classGroupId;var n=t.nextPart.get(e[0]),o=n?Y1(e.slice(1),n):void 0;if(o)return o;if(t.validators.length!==0){var a=e.join("-");return(r=t.validators.find(function(i){return(0,i.validator)(a)}))==null?void 0:r.classGroupId}}Object.defineProperty(zn,"__esModule",{value:!0});var Ax=/^\[(.+)\]$/;function M0(e,t,r,n){e.forEach(function(o){if(typeof o!="string"){if(typeof o=="function")return o.isThemeGetter?void M0(o(n),t,r,n):void t.validators.push({validator:o,classGroupId:r});Object.entries(o).forEach(function(a){M0(a[1],Mx(t,a[0]),r,n)})}else(o===""?t:Mx(t,o)).classGroupId=r})}function Mx(e,t){var r=e;return t.split("-").forEach(function(n){r.nextPart.has(n)||r.nextPart.set(n,{nextPart:new Map,validators:[]}),r=r.nextPart.get(n)}),r}function w4(e){if(e<1)return{get:function(){},set:function(){}};var t=0,r=new Map,n=new Map;function o(a,i){r.set(a,i),++t>e&&(t=0,n=r,r=new Map)}return{get:function(a){var i=r.get(a);return i!==void 0?i:(i=n.get(a))!==void 0?(o(a,i),i):void 0},set:function(a,i){r.has(a)?r.set(a,i):o(a,i)}}}function _4(e){var t=e.separator||":";return function(r){for(var n=0,o=[],a=0,i=0;i<r.length;i++){var s=r[i];n===0&&s===t[0]&&(t.length!==1&&r.slice(i,i+t.length)!==t||(o.push(r.slice(a,i)),a=i+t.length)),s==="["?n++:s==="]"&&n--}var f=o.length===0?r:r.substring(a),g=f.startsWith("!");return{modifiers:o,hasImportantModifier:g,baseClassName:g?f.substring(1):f}}}function S4(e){return A0({cache:w4(e.cacheSize),splitModifiers:_4(e)},x4(e))}var C4=/\s+/;function T4(e,t){var r=t.splitModifiers,n=t.getClassGroupId,o=t.getConflictingClassGroupIds,a=new Set;return e.trim().split(C4).map(function(i){var s=r(i),f=s.modifiers,g=s.hasImportantModifier,b=n(s.baseClassName);if(!b)return{isTailwindClass:!1,originalClassName:i};var S=function(O){if(O.length<=1)return O;var T=[],C=[];return O.forEach(function(w){w[0]==="["?(T.push.apply(T,C.sort().concat([w])),C=[]):C.push(w)}),T.push.apply(T,C.sort()),T}(f).join(":");return{isTailwindClass:!0,modifierId:g?S+"!":S,classGroupId:b,originalClassName:i}}).reverse().filter(function(i){if(!i.isTailwindClass)return!0;var s=i.modifierId,f=i.classGroupId,g=s+f;return!a.has(g)&&(a.add(g),o(f).forEach(function(b){return a.add(s+b)}),!0)}).reverse().map(function(i){return i.originalClassName}).join(" ")}function I0(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];var n,o,a,i=s;function s(g){var b=t[0],S=t.slice(1).reduce(function(O,T){return T(O)},b());return n=S4(S),o=n.cache.get,a=n.cache.set,i=f,f(g)}function f(g){var b=o(g);if(b)return b;var S=T4(g,n);return a(g,S),S}return function(){return i(hm.apply(null,arguments))}}function Bt(e){var t=function(r){return r[e]||[]};return t.isThemeGetter=!0,t}var ja=/^\[(.+)\]$/,O4=/^\d+\/\d+$/,P4=new Set(["px","full","screen"]),k4=/^(\d+)?(xs|sm|md|lg|xl)$/,E4=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh)/,N4=/^-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/;function Sn(e){return!Number.isNaN(Number(e))||P4.has(e)||O4.test(e)||la(e)}function la(e){var t,r=(t=ja.exec(e))==null?void 0:t[1];return!!r&&(r.startsWith("length:")||E4.test(r))}function Q1(e){var t,r=(t=ja.exec(e))==null?void 0:t[1];return!!r&&r.startsWLine truncated
* tabbable 6.2.0
* @license MIT, https://github.com/focus-trap/tabbable/blob/master/LICENSE
*/var xF=["input:not([inert])","select:not([inert])","textarea:not([inert])","a[href]:not([inert])","button:not([inert])","[tabindex]:not(slot):not([inert])","audio[controls]:not([inert])","video[controls]:not([inert])",'[contenteditable]:not([contenteditable="false"]):not([inert])',"details>summary:first-of-type:not([inert])","details:not([inert])"],Q0=xF.join(","),nP=typeof Element>"u",tu=nP?function(){}:Element.prototype.matches||Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector,Id=!nP&&Element.prototype.getRootNode?function(e){var t;return e==null||(t=e.getRootNode)===null||t===void 0?void 0:t.call(e)}:function(e){return e==null?void 0:e.ownerDocument},Dd=function e(t,r){var n;r===void 0&&(r=!0);var o=t==null||(n=t.getAttribute)===null||n===void 0?void 0:n.call(t,"inert"),a=o===""||o==="true",i=a||r&&t&&e(t.parentNode);return i},wF=function(t){var r,n=t==null||(r=t.getAttribute)===null||r===void 0?void 0:r.call(t,"contenteditable");return n===""||n==="true"},_F=function(t,r,n){if(Dd(t))return[];var o=Array.prototype.slice.apply(t.querySelectorAll(Q0));return r&&tu.call(t,Q0)&&o.unshift(t),o=o.filter(n),o},SF=function e(t,r,n){for(var o=[],a=Array.from(t);a.length;){var i=a.shift();if(!Dd(i,!1))if(i.tagName==="SLOT"){var s=i.assignedElements(),f=s.length?s:i.children,g=e(f,!0,n);n.flatten?o.push.apply(o,g):o.push({scopeParent:i,candidates:g})}else{var b=tu.call(i,Q0);b&&n.filter(i)&&(r||!t.includes(i))&&o.push(i);var S=i.shadowRoot||typeof n.getShadowRoot=="function"&&n.getShadowRoot(i),O=!Dd(S,!1)&&(!n.shadowRootFilter||n.shadowRootFilter(i));if(S&&O){var T=e(S===!0?i.children:S.children,!0,n);n.flatten?o.push.apply(o,T):o.push({scopeParent:i,candidates:T})}else a.unshift.apply(a,i.children)}}return o},oP=function(t){return!isNaN(parseInt(t.getAttribute("tabindex"),10))},aP=function(t){if(!t)throw new Error("No node provided");return t.tabIndex<0&&(/^(AUDIO|VIDEO|DETAILS)$/.test(t.tagName)||wF(t))&&!oP(t)?0:t.tabIndex},CF=function(t,r){var n=aP(t);return n<0&&r&&!oP(t)?0:n},TF=function(t,r){return t.tabIndex===r.tabIndex?t.documentOrder-r.documentOrder:t.tabIndex-r.tabIndex},iP=function(t){return t.tagName==="INPUT"},OF=function(t){return iP(t)&&t.type==="hidden"},PF=function(t){var r=t.tagName==="DETAILS"&&Array.prototype.slice.apply(t.children).some(function(n){return n.tagName==="SUMMARY"});return r},kF=function(t,r){for(var n=0;n<t.length;n++)if(t[n].checked&&t[n].form===r)return t[n]},EF=function(t){if(!t.name)return!0;var r=t.form||Id(t),n=function(s){return r.querySelectorAll('input[type="radio"][name="'+s+'"]')},o;if(typeof window<"u"&&typeof window.CSS<"u"&&typeof window.CSS.escape=="function")o=n(window.CSS.escape(t.name));else try{o=n(t.name)}catch(i){return console.error("Looks like you have a radio button with a name attribute containing invalid CSS selector characters and need the CSS.escape polyfill: %s",i.message),!1}var a=kF(o,t.form);return!a||a===t},NF=function(t){return iP(t)&&t.type==="radio"},jF=function(t){return NF(t)&&!EF(t)},RF=function(t){var r,n=t&&Id(t),o=(r=n)===null||r===void 0?void 0:r.host,a=!1;if(n&&n!==t){var i,s,f;for(a=!!((i=o)!==null&&i!==void 0&&(s=i.ownerDocument)!==null&&s!==void 0&&s.contains(o)||t!=null&&(f=t.ownerDocument)!==null&&f!==void 0&&f.contains(t));!a&&o;){var g,b,S;n=Id(o),o=(g=n)===null||g===void 0?void 0:g.host,a=!!((b=o)!==null&&b!==void 0&&(S=b.ownerDocument)!==null&&S!==void 0&&S.contains(o))}}return a},sw=function(t){var r=t.getBoundingClientRect(),n=r.width,o=r.height;return n===0&&o===0},AF=function(t,r){var n=r.displayCheck,o=r.getShadowRoot;if(getComputedStyle(t).visibility==="hidden")return!0;var a=tu.call(t,"details>summary:first-of-type"),i=a?t.parentElement:t;if(tu.call(i,"details:not([open]) *"))return!0;if(!n||n==="full"||n==="legacy-full"){if(typeof o=="function"){for(var s=t;t;){var f=t.parentElement,g=Id(t);if(f&&!f.shadowRoot&&o(f)===!0)return sw(t);t.assignedSlot?t=t.assignedSlot:!f&&g!==t.ownerDocument?t=g.host:t=f}t=s}if(RF(t))return!t.getClientRects().length;if(n!=="legacy-full")return!0}else if(n==="non-zero-area")return sw(t);return!1},MF=function(t){if(/^(INPUT|BUTTON|SELECT|TEXTAREA)$/.test(t.tagName))for(var r=t.parentElement;r;){if(r.tagName==="FIELDSET"&&r.disabled){for(var n=0;n<r.children.length;n++){var o=r.children.item(n);if(o.tagName==="LEGEND")return tu.call(r,"fieldset[disabled] *")?!0:!o.contains(t)}return!0}r=r.parentElement}return!1},IF=function(t,r){return!(r.disabled||Dd(r)||OF(r)||AF(r,t)||PF(r)||MF(r))},uw=function(t,r){return!(jF(r)||aP(r)<0||!IF(t,r))},DF=function(t){var r=parseInt(t.getAttribute("tabindex"),10);return!!(isNaN(r)||r>=0)},LF=function e(t){var r=[],n=[];return t.forEach(function(o,a){var i=!!o.scopeParent,s=i?o.scopeParent:o,f=CF(s,i),g=i?e(o.candidates):s;f===0?i?r.push.apply(r,g):r.push(s):n.push({documentOrder:a,tabIndex:f,item:o,isScope:i,content:g})}),n.sort(TF).reduce(function(o,a){return a.isScope?o.push.apply(o,a.content):o.push(a.content),o},[]).concatLine truncated
More details: https://www.material-tailwind.com/docs/react/collapse
`);var P=r.default.useRef(null),N=(0,g.useTheme)().navbar,R=N.styles,M=R.base.mobileNav;u=u??{},h=h??"";var L=(0,s.twMerge)((0,i.default)((0,f.default)(M)),h),D={unmount:{height:0,opacity:0,transition:{duration:.3,times:"[0.4, 0, 0.2, 1]"}},mount:{opacity:1,height:"".concat((E=P.current)===null||E===void 0?void 0:E.scrollHeight,"px"),transition:{duration:.3,times:"[0.4, 0, 0.2, 1]"}}},B=(0,a.default)(D,u),H=n.AnimatePresence,Q=(0,o.useMergeRefs)([l,P]);return r.default.createElement(n.LazyMotion,{features:n.domAnimation},r.default.createElement(H,null,r.default.createElement(n.m.div,S({},y,{ref:Q,className:L,initial:"unmount",exit:"unmount",animate:c?"mount":"unmount",variants:B}),v)))});w.displayName="MaterialTailwind.MobileNav",w.propTypes={open:b.propTypesOpen,animate:b.propTypesAnimate,className:b.propTypesClassName,children:b.propTypesChildren};var m=w})(YP);(function(e){Object.defineProperty(e,"__esModule",{value:!0});function t(l,c){for(var u in c)Object.defineProperty(l,u,{enumerable:!0,get:c[u]})}t(e,{Navbar:function(){return m},MobileNav:function(){return g.MobileNav},default:function(){return d}});var r=T(k),n=T(Ze),o=T(at),a=Xe,i=T(Gt),s=T(Ye),f=Ke,g=YP,b=bf;function S(l,c,u){return c in l?Object.defineProperty(l,c,{value:u,enumerable:!0,configurable:!0,writable:!0}):l[c]=u,l}function O(){return O=Object.assign||function(l){for(var c=1;c<arguments.length;c++){var u=arguments[c];for(var h in u)Object.prototype.hasOwnProperty.call(u,h)&&(l[h]=u[h])}return l},O.apply(this,arguments)}function T(l){return l&&l.__esModule?l:{default:l}}function C(l,c){if(l==null)return{};var u=w(l,c),h,v;if(Object.getOwnPropertySymbols){var y=Object.getOwnPropertySymbols(l);for(v=0;v<y.length;v++)h=y[v],!(c.indexOf(h)>=0)&&Object.prototype.propertyIsEnumerable.call(l,h)&&(u[h]=l[h])}return u}function w(l,c){if(l==null)return{};var u={},h=Object.keys(l),v,y;for(y=0;y<h.length;y++)v=h[y],!(c.indexOf(v)>=0)&&(u[v]=l[v]);return u}var m=r.default.forwardRef(function(l,c){var u=l.variant,h=l.color,v=l.shadow,y=l.blurred,E=l.fullWidth,P=l.className,N=l.children,R=C(l,["variant","color","shadow","blurred","fullWidth","className","children"]),M=(0,f.useTheme)().navbar,L=M.defaultProps,D=M.valid,B=M.styles,H=B.base,Q=B.variants;u=u??L.variant,h=h??L.color,v=v??L.shadow,y=y??L.blurred,E=E??L.fullWidth,P=P??L.className;var W,z=(0,o.default)((0,s.default)(H.navbar.initial),(W={},S(W,(0,s.default)(H.navbar.shadow),v),S(W,(0,s.default)(H.navbar.blurred),y&&h==="white"),S(W,(0,s.default)(H.navbar.fullWidth),E),W)),$=(0,o.default)((0,s.default)(Q[(0,i.default)(D.variants,u,"filled")][(0,i.default)(D.colors,h,"white")])),q=(0,a.twMerge)((0,o.default)(z,$),P);return r.default.createElement("nav",O({},R,{ref:c,className:q}),N)});m.propTypes={variant:n.default.oneOf(b.propTypesVariant),color:n.default.oneOf(b.propTypesColor),shadow:b.propTypesShadow,blurred:b.propTypesBlurred,fullWidth:b.propTypesFullWidth,className:b.propTypesClassName,children:b.propTypesChildren},m.displayName="MaterialTailwind.Navbar";var d=Object.assign(m,{MobileNav:g.MobileNav})})(KP);var QP={},Ff={},Al={};(function(e){Object.defineProperty(e,"__esModule",{value:!0});function t(m,d){for(var l in d)Object.defineProperty(m,l,{enumerable:!0,get:d[l]})}t(e,{propTypesOpen:function(){return a},propTypesHandler:function(){return i},propTypesPlacement:function(){return s},propTypesOffset:function(){return f},propTypesDismiss:function(){return g},propTypesAnimate:function(){return b},propTypesContent:function(){return S},propTypesInteractive:function(){return O},propTypesClassName:function(){return T},propTypesChildren:function(){return C},propTypesContextValue:function(){return w}});var r=o(Ze),n=Ut;function o(m){return m&&m.__esModule?m:{default:m}}var a=r.default.bool,i=r.default.func,s=n.propTypesPlacements,f=n.propTypesOffsetType,g=n.propTypesDismissType,b=n.propTypesAnimation,S=r.default.node,O=r.default.bool,T=r.default.string,C=r.default.node.isRequired,w=r.default.shape({open:r.default.bool.isRequired,strategy:r.default.oneOf(["fixed","absolute"]).isRequired,x:r.default.number,y:r.default.number,context:r.default.instanceOf(Object).isRequired,reference:r.default.func.isRequired,floating:r.default.func.isRequired,getReferenceProps:r.default.func.isRequired,getFloatingProps:r.default.func.isRequired,appliedAnimation:b.isRequired,labelId:r.default.string.isRequired,descriptionId:r.default.string.isRequired}).isRequired})(Al);(function(e){Object.defineProperty(e,"__esModule",{value:!0});function t(f,g){for(var b in g)Object.defineProperty(f,b,{enumerable:!0,get:g[b]})}t(e,{PopoverContext:function(){return a},usePopover:function(){return i},PopoverContextProvider:function(){return s}});var r=o(k),n=Al;function o(f){return f&&f.__esModule?f:{default:f}}var a=r.default.createContext(null);a.displayName="MaterialTailwind.PopoverContext";function i(){var f=r.default.useContext(a);if(!f)throw new Error("usePopover() must be used within a Popover. It happens when you us Line truncated
${ie?"border-purple-500 ring-1 ring-purple-500/40":"border-transparent"}
${!ft||he?"opacity-75 bg-stormGrey-200/20":""}`,children:[x.jsxs("div",{className:"flex items-center space-x-4",id:"left",children:[P&&x.jsx("input",{type:"checkbox",checked:ie,onChange:()=>je(ee.Id,!ht),className:`h-5 w-5 text-purple-500 focus:ring-purple-400 border-gray-600 bg-gray-900 rounded ${ht?"":"opacity-40 cursor-not-allowed"}`,disabled:!ht}),x.jsx("div",{className:`w-12 h-12 flex items-center justify-center rounded-full
${ee.updateStatus==="✅"?"bg-mossGreen-600":ee.updateStatus==="⚠️"?"bg-warmAmberGlow-400":"bg-sunsetCoral-500"}`}),x.jsxs("div",{children:[x.jsx("p",{className:"text-lg antialiased font-sans font-semibold leading-normal text-inherit",children:ee.Name}),x.jsxs("p",{className:"text-sm antialiased font-sans font-light leading-normal text-inherit",children:["ID: ",ee.Id]}),ee.duplicateName&&x.jsx("p",{className:"text-xs text-amber-700",children:"⚠️ Doppelter Name erkannt"})]})]}),x.jsx("div",{className:"ml-auto flex flex-col items-end gap-1 text-sm",id:"right",children:xe?x.jsxs(x.Fragment,{children:[x.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-warmAmberGlow-600",children:"Redeploy"}),x.jsx("span",{className:"text-warmAmberGlow-500",children:"läuft…"})]}):Ce?x.jsxs(x.Fragment,{children:[x.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-warmAmberGlow-600",children:"Redeploy"}),x.jsx("span",{className:"text-warmAmberGlow-500",children:"in Warteliste…"})]}):tt?x.jsxs(x.Fragment,{children:[x.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-stormGrey-500",children:"System"}),x.jsx("span",{className:"text-gray-300",children:"Redeploy deaktiviert"})]}):De?x.jsxs(x.Fragment,{children:[x.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-stormGrey-500",children:"Status"}),x.jsx("span",{className:"text-mossGreen-600",children:"Aktuell"})]}):E?x.jsx(G.Button,{onClick:()=>st(ee.Id),disabled:he,className:"disabled:opacity-50 disabled:cursor-not-allowed",children:"Redeploy"}):null})]})},ee.Id)})}),x.jsx(Wf,{disabled:r||!!o})]})}const ZB={idle:"Bereit",running:"Läuft",success:"Erfolgreich",error:"Fehlgeschlagen"},ss={idle:"bg-stormGrey-700 text-gray-200",running:"bg-arcticBlue-600 text-white",success:"bg-mossGreen-600 text-white",error:"bg-sunsetCoral-600 text-white"},JB={initializing:"Vorbereitung","activating-maintenance":"Wartungsmodus aktivieren","executing-script":"Skript wird ausgeführt",waiting:"Warte auf Portainer",completed:"Abgeschlossen",failed:"Fehlgeschlagen"},eV={info:"text-arcticBlue-700",success:"text-mossGreen-700",warning:"text-warmAmberGlow-700",error:"text-sunsetCoral-700",stdout:"text-stormGrey-700",stderr:"text-warmAmberGlow-700",debug:"text-lavenderSmoke-700"},tV=e=>{if(!e)return"-";const t=new Date(e);return Number.isNaN(t.getTime())?e:t.toLocaleString("de-DE",{hour:"2-digit",minute:"2-digit",second:"2-digit"})},Fa=e=>{if(!e&&e!==0)return"-";const r=(n=>{if(n instanceof Date)return n;if(typeof n=="number"){const o=n>1e12?n:n*1e3;return new Date(o)}if(typeof n=="string"){const o=Number(n);if(!Number.isNaN(o)){const a=o>1e12?o:o*1e3;return new Date(a)}return new Date(n)}return null})(e);return!r||Number.isNaN(r.getTime())?String(e):r.toLocaleString("de-DE",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"})},$w=e=>e===1?"Git":e===2?"Compose":e??"-",Oh=()=>({host:"",port:"22",username:"",password:"",extraSshArgs:""});function rV(){var _i,Si,Nu;const{hasPermission:e,user:t}=mn(),r=k.useCallback(()=>{},[]),{maintenance:n,update:o,script:a,ssh:i,loading:s,error:f,setMaintenanceMode:g,triggerUpdate:b,saveScript:S,resetScript:O,saveSshConfig:T,deleteSshConfig:C,testSshConnection:w,fetchSuperuserStatus:m,fetchSetupStatus:d,deleteSetupEndpoint:l,deleteSetupServer:c,updateSetupApiKey:u,removeSuperuserAccount:h}=mi(),v=!!(t!=null&&t.isSuperuser),y=!!(v||e("maintenance-access","read")),E=!!(v||e("maintenance-access","full")),P=!!(v||e("maintenance-server-manage","read")),N=!!(v||e("maintenance-server-manage","full")),R=!!(v||e("maintenance-server-delete","full")),M=!!(v||e("maintenance-portainer","read")),L=!!(v||e("maintenance-update","full")),D=!!(v||e("maintenance-ssh-update","read")),B=!!(v||e("maintenance-ssh-update","full")),H=!!(v||e("maintenance-duplicates","read")),Q=!!(v||e("maintenance-duplicates","full")),W=!!(n!=null&&n.active),z=n==null?void 0:n.message,$=(_i=n==null?void 0:n.extra)==null?void 0:_i.type,q=!!(o!=null&&o.running),[Z,ae]=k.useState(""),[ge,ue]=k.useState(!1),[te,Se]=k.useState(!1),[ve,pe]=k.useState(""),[re,me]=k.useState(!1),[Te,Ne]=k.useState(()=>Oh()),[Pe,Oe]=k.useState(!1),[je,ze]=k.useState(!1),[qe,it]=k.useState(!1),[et,st]=k.useState(!1),[Ge,we]=k.useState(!1),[Ee,ke]=k.useState(null);k.useEffect(()=>{if(!a)return;const be=a.custom??a.default??a.effective??"";ae(be)},[a]),k.useEffect(()=>{if(!i){Ne(Oh()),Oe(!1),ze(!1),ke(null);return}Ne({host:i.host??"",port:String(i.port??"22"),username:i.username??"",password:"",extraSshArgs:Array.isArray(i.extraSshArgs)?i.extraSshArgs.join(`
`):""}),Oe(!!i.passwordStored),ze(!1),ke(null)},[i]);const Ie=k.useMemo(()=>a?a.source==="custom"&&typeof a.custom=="string"?a.custom:a.default??a.effective??"":"",[a]),ce=a?Z!==Ie:!1,Ae=(a==null?void 0:a.source)==="custom"?"Benutzerdefiniert":"Standard",[fe,ee]=k.useState(!0),[le,Ce]=k.useState(!1),[xe,he]=k.useState(null),[ie,De]=k.useState(""),[tt,ft]=k.useState(!1),[ht,mt]=k.useState(null),[ct,Kt]=k.useState(!0),[Wt,Mt]=k.useState(""),[Mr,$t]=k.useState({}),[Cr,Yt]=k.useState(null),[tn,Tr]=k.useState(null),[Wr,Mn]=k.useState(null),[$r,bn]=k.useState([]),[pr,ro]=k.useState(!0),[J,Me]=k.useState(""),[Be,Ot]=k.useState(!1),[It,bt]=k.useState(null),[Pt,sr]=k.useState(null),xt=k.useRef(null),[_t,Hr]=k.useState(null),[or,vn]=k.useState(!0),[wo,$o]=k.useState(""),[_o,Ho]=k.useState(!1),[Ra,So]=k.useState(null),In=k.useRef(null),rn=k.useCallback(async({silent:be=!1}={})=>{var Fe,Ve;ee(!0),De("");try{const We=await m(),rt=!!(We!=null&&We.exists);Ce(rt),he(rt?We.user??null:null)}catch(We){const rt=((Ve=(Fe=We.response)==null?void 0:Fe.data)==null?void 0:Ve.error)||We.message||"Superuser-Status konnte nicht geladen werden";Ce(!1),he(null),De(rt),be||r({variant:"error",title:"Statusabfrage fehlgeschlagen",description:rt})}finally{ee(!1)}},[m,r]),Ir=k.useCallback(async({silent:be=!1}={})=>{var Fe,Ve;be||Kt(!0),Mt("");try{const We=await d();mt(We),$t({})}catch(We){const rt=((Ve=(Fe=We.response)==null?void 0:Fe.data)==null?void 0:Ve.error)||We.message||"Setup-Daten konnten nicht geladen werden";Mt(rt),be||r({variant:"error",title:"Setup-Daten",description:rt})}finally{Kt(!1)}},[d,r]),Aa=k.useCallback(async({silent:be=!1}={})=>{if(In.current)return In.current;const Fe=(async()=>{var Ve,We;be?Ho(!0):vn(!0),$o("");try{const Rt=(await nt.get("/api/maintenance/portainer-status")).data??{};Hr(Rt),So(new Date)}catch(rt){const Rt=((We=(Ve=rt.response)==null?void 0:Ve.data)==null?void 0:We.error)||rt.message||"Fehler beim Prüfen des Portainer-Status";$o(Rt),r({variant:"error",title:"Statusaktualisierung fehlgeschlagen",description:Rt})}finally{be?Ho(!1):vn(!1),In.current=null}})();return In.current=Fe,Fe},[r]),Co=k.useCallback(async({silent:be=!1}={})=>{if(W||q){bn([]),Me("Wartungsmodus aktiv – Duplikat-Verwaltung ist vorübergehend deaktiviert."),ro(!1),Ot(!1);return}if(xt.current)return xt.current;const Fe=(async()=>{var Ve,We,rt;be?Ot(!0):ro(!0),Me("");try{const Nt=(await nt.get("/api/maintenance/duplicates")).data,Lt=Array.isArray(Nt)?Nt:(Nt==null?void 0:Nt.items)??[];bn(Lt),sr(new Date)}catch(Rt){if(((Ve=Rt.response)==null?void 0:Ve.status)===423){const Nt="Wartungsmodus aktiv – Duplikat-Verwaltung ist vorübergehend deaktiviert.";bn([]),Me(Nt),r({variant:"warning",title:"Duplikat-Verwaltung gesperrt",description:Nt})}else{const Nt=((rt=(We=Rt.response)==null?void 0:We.data)==null?void 0:rt.error)||Rt.message||"Fehler beim Laden der Wartungsdaten";Me(Nt),r({variant:"error",title:"Duplikate konnten nicht geladen werden",description:Nt})}}finally{be?Ot(!1):ro(!1),xt.current=null}})();return xt.current=Fe,Fe},[W,q,r]);k.useEffect(()=>{rn({silent:!0})},[rn]),k.useEffect(()=>{Ir()},[Ir]),k.useEffect(()=>{Aa()},[Aa]),k.useEffect(()=>{Co()},[Co]);const Dn=k.useMemo(()=>{var be;return((be=ht==null?void 0:ht.servers)==null?void 0:be.items)??[]},[ht]),Dr=k.useMemo(()=>{var be;return((be=ht==null?void 0:ht.endpoints)==null?void 0:be.items)??[]},[ht]),Dt=k.useMemo(()=>{var Fe;const be=((Fe=ht==null?void 0:ht.apiKeys)==null?void 0:Fe.items)??[];return new Map(be.map(Ve=>[Ve.serverId,Ve]))},[ht]),tr=k.useMemo(()=>!!(ht!=null&&ht.setupComplete),[ht]),Et=k.useMemo(()=>{const be=Array.isArray($r)?$r.length:0,Fe=Array.isArray($r)?$r.reduce((Ve,We)=>{var rt;return Ve+(((rt=We==null?void 0:We.duplicates)==null?void 0:rt.length)||0)},0):0;return{groups:be,duplicateCount:Fe}},[$r]),Or=k.useCallback((be,Fe)=>{B&&(Ne(Ve=>({...Ve,[be]:Fe})),be==="password"&&Oe(!1))},[B]),yn=k.useMemo(()=>{const be={host:Te.host.trim(),port:Number.parseInt(Te.port,10)||22,username:Te.username.trim(),extraSshArgs:(Te.extraSshArgs||"").split(/\r?\n/).map(Ve=>Ve.trim()).filter(Boolean)},Fe=Te.password??"";return Fe?be.password=Fe:Pe||(be.password=""),be},[Te,Pe]),no=k.useCallback(async()=>{var be,Fe;if(B&&a)try{ue(!0),await S(Z),r({variant:"success",title:"Skript gespeichert",description:"Das benutzerdefinierte Portainer-Update-Skript wurde aktualisiert."})}catch(Ve){const We=((Fe=(be=Ve.response)==null?void 0:be.data)==null?void 0:Fe.error)||Ve.message||"Skript konnte nicht gespeichert werden";r({variant:"error",title:"Speichern fehlgeschlagen",description:We})}finally{ue(!1)}},[B,S,Z,a,r]),Ma=k.useCallback(async()=>{var be,Fe;if(B)try{ue(!0),await O(),r({variant:"info",title:"Standardskript wiederhergestellt",description:"Es wird wieder das Standard-Update-Skript verwendet."})}catch(Ve){const We=((Fe=(be=Ve.response)==null?void 0:be.data)==null?void 0:Fe.error)||Ve.message||"Standardskript konnte nicht wiederhergestellt werden";r({variant:"error",title:"Zurü Line truncated
Dies setzt die Superuser-Einrichtung zurück.`)))try{ft(!0);const We=await h(),rt=Number((We==null?void 0:We.usersRemoved)??0);r({variant:"success",title:"Superuser gelöscht",description:rt>1?`${rt} Konten aus der Superuser-Gruppe wurden entfernt.`:"Superuser-Konto wurde entfernt."}),await rn({silent:!0})}catch(We){if(((be=We.response)==null?void 0:be.status)===404)await rn({silent:!0}),r({variant:"info",title:"Kein Superuser vorhanden",description:"Es ist kein Superuser mehr vorhanden."});else{const Rt=((Ve=(Fe=We.response)==null?void 0:Fe.data)==null?void 0:Ve.error)||We.message||"Superuser konnte nicht gelöscht werden";r({variant:"error",title:"Löschen fehlgeschlagen",description:Rt})}}finally{ft(!1)}},[tt,fe,le,h,rn,r]),Hf=k.useCallback(async be=>{var rt,Rt;if(!R||Wr===be)return;const Fe=Dr.find(Nt=>Nt.id===be);if(!Fe)return;const Ve=Fe.name||`Endpoint ${Fe.external_id}`,We=`Endpoint "${Ve}" wirklich löschen?${Fe.is_default?`
Dieser Endpoint ist aktuell als Standard markiert.`:""}`;if(!(typeof window<"u"&&!window.confirm(We))){Mn(be);try{await l(be),r({variant:"success",title:"Endpoint gelöscht",description:`Endpoint "${Ve}" wurde entfernt.`}),await Ir({silent:!0})}catch(Nt){const Lt=((Rt=(rt=Nt.response)==null?void 0:rt.data)==null?void 0:Rt.error)||Nt.message||"Endpoint konnte nicht gelöscht werden.";r({variant:"error",title:"Endpoint löschen fehlgeschlagen",description:Lt})}finally{Mn(null)}}},[R,l,Wr,Ir,Dr,r]),qf=k.useCallback(async be=>{var Rt,Nt;if(!R||tn===be)return;const Fe=Dn.find(Lt=>Lt.id===be);if(!Fe)return;const Ve=Dr.filter(Lt=>Lt.server_id===be),We=Fe.name||Fe.url||`Server ${be}`,rt=[`Server "${We}" wirklich löschen?`,Ve.length?`Dabei werden ${Ve.length} zugeordnete Endpoint(s) entfernt.`:null,"Das System benötigt anschließend erneut einen gültigen Server/Endpoint im Setup."].filter(Boolean).join(`
`);if(!(typeof window<"u"&&!window.confirm(rt))){Tr(be);try{await c(be),r({variant:"success",title:"Server gelöscht",description:`Server "${We}" wurde entfernt.`}),await Ir({silent:!0})}catch(Lt){const yr=((Nt=(Rt=Lt.response)==null?void 0:Rt.data)==null?void 0:Nt.error)||Lt.message||"Server konnte nicht gelöscht werden.";r({variant:"error",title:"Server löschen fehlgeschlagen",description:yr})}finally{Tr(null)}}},[R,c,Ir,tn,Dr,Dn,r]),Gf=k.useCallback((be,Fe)=>{N&&$t(Ve=>({...Ve,[be]:Fe}))},[N]),Kf=k.useCallback(async be=>{var Ve,We;if(!N)return;const Fe=(Mr[be]??"").trim();if(!Fe){r({variant:"warning",title:"API-Key fehlt",description:"Bitte gib einen API-Key ein."});return}if(Cr!==be){Yt(be);try{await u(be,Fe),r({variant:"success",title:"API-Key aktualisiert",description:"Der API-Key wurde gespeichert."}),$t(rt=>({...rt,[be]:""})),await Ir({silent:!0})}catch(rt){const Rt=((We=(Ve=rt.response)==null?void 0:Ve.data)==null?void 0:We.error)||rt.message||"API-Key konnte nicht aktualisiert werden.";r({variant:"error",title:"Aktualisierung fehlgeschlagen",description:Rt})}finally{Yt(null)}}},[Mr,Cr,N,Ir,r,u]),Ou=k.useCallback(async be=>{var Fe,Ve;if(E&&!(s||re)&&be!==W){if(q){r({variant:"warning",title:"Aktion nicht möglich",description:"Während eines laufenden Updates kann der Wartungsmodus nicht geändert werden."});return}me(!0);try{const We=be&&!!$,rt={active:be};We&&z?rt.message=z:rt.message=null,await g(rt),r({variant:be?"info":"success",title:be?"Wartungsmodus aktiviert":"Wartungsmodus deaktiviert",description:be?"Benutzer sehen jetzt die Wartungsseite.":"StackPulse steht wieder zur Verfügung."})}catch(We){const rt=((Ve=(Fe=We.response)==null?void 0:Fe.data)==null?void 0:Ve.error)||We.message||"Wartungsmodus konnte nicht geändert werden";r({variant:"error",title:"Änderung fehlgeschlagen",description:rt})}finally{me(!1)}}},[E,s,re,W,q,g,z,$,r]),To=k.useCallback(async()=>{var Fe,Ve;if(!L)return;if(W||q){r({variant:"warning",title:"Update nicht möglich",description:"Während eines aktiven Wartungsmodus kann kein weiteres Update gestartet werden."});return}const be=(_t==null?void 0:_t.latestVersion)??(_t==null?void 0:_t.currentVersion)??"unbekannt";if(!(typeof window<"u"&&!window.confirm(`Portainer-Update starten?
Zielversion: ${be}.
Während des Updates befindet sich StackPulse im Wartungsmodus.
Fortfahren?`)))try{pe(""),Se(!0),await b(),r({variant:"info",title:"Update gestartet",description:"Das Portainer-Update wurde gestartet."})}catch(We){const rt=((Ve=(Fe=We.response)==null?void 0:Fe.data)==null?void 0:Ve.error)||We.message||"Portainer-Update konnte nicht gestartet werden";pe(rt),r({variant:"error",title:"Update fehlgeschlagen",description:rt})}finally{Se(!1)}},[L,W,q,_t,b,r]),vi=k.useCallback(async be=>{var rt,Rt,Nt,Lt;if(!Q||!be)return;if(W||q){r({variant:"warning",title:"Aktion nicht möglich",description:"Während des Wartungsmodus sind Bereinigungen deaktiviert."});return}const Fe=(rt=be.canonical)==null?void 0:rt.Id;if(!Fe)return;const Ve=(be.duplicates||[]).map(yr=>yr.Id).filter(Boolean);if(!Ve.length)return;const We=((Rt=be.canonical)==null?void 0:Rt.Name)||be.name||`Stack ${Fe}`;if(!(typeof window<"u"&&!window.confirm(`Bereinigung für "${We}" starten?
Es werden ${Ve.length} Duplikate entfernt: ${Ve.join(", ")}`))){bt(String(Fe));try{const qo=(await nt.post("/api/maintenance/duplicates/cleanup",{canonicalId:Fe,duplicateIds:Ve})).data??{};if(qo.success===!1)throw new Error(qo.error||"Bereinigung fehlgeschlagen");const Xf=Array.isArray(qo.results)?qo.results.filter(Ci=>Ci.status==="deleted").map(Ci=>Ci.id):Ve;r({variant:"success",title:"Bereinigung abgeschlossen",description:`${We} – entfernte IDs: ${Xf.join(", ")}`}),await Co({silent:!0})}catch(yr){const qo=((Lt=(Nt=yr.response)==null?void 0:Nt.data)==null?void 0:Lt.error)||yr.message||"Bereinigung fehlgeschlagen";r({variant:"error",title:"Bereinigung fehlgeschlagen",description:qo})}finally{bt(null)}}},[Q,Co,W,r,q]);n!=null&&n.activatedAt&&Fa(n.activatedAt),n!=null&&n.updatedAt&&Fa(n.updatedAt);const oo=W||q,yi=(o==null?void 0:o.status)??(q?"running":"idle"),Pu=ZB[yi]??yi,ku=ss[yi]??ss.idle,xi=o!=null&&o.stage?JB[o.stage]??o.stage:"–",zl=(o==null?void 0:o.logs)??[],Yf=(o==null?void 0:o.targetVersion)??"-",Eu=(o==null?void 0:o.resultVersion)??"-",wi=o!=null&&o.startedAt?Fa(o.startedAt):"-",Qf=o!=null&&o.finishedAt?Fa(o.finishedAt):"-",Bl=(o==null?void 0:o.message)??(q?"Update läuft…":""),Vl=q||ge||s;return y?x.jsxs("div",{className:"mt-12 mb-8 flex flex-col gap-12",children:[(W||q)&&x.jsx("div",{className:"rounded-lg border border-cyan-500/60 bg-cyan-900/30 px-4 py-3 text-sm text-bluegray-100",children:x.jsxs("div",{className:"flex flex-col gap-1",children:[x.jsxs("span",{children:["Wartungsmodus aktiv",z?` – ${z}`:q?" – Portainer-Update läuft":"","."]}),q&&x.jsxs("span",{className:"text-xs text-indigo-900",children:["Phase: ",xi]})]})}),y&&x.jsxs(G.Card,{children:[x.jsx(G.CardHeader,{variant:"gradient",color:"gray",className:"mb-5 p-4",children:x.jsx(G.Typography,{variant:"h6",color:"white",className:"flex items-center justify-between",children:x.jsx("span",{children:"Wartungsmodus"})})}),x.jsxs(G.CardBody,{className:"flex flex-col gap-4 p-4",children:[x.jsxs("div",{className:"flex items-start justify-between gap-4",children:[x.jsx("div",{className:"space-y-1",children:x.jsx("p",{className:"text-sm antialiased font-sans text-sm font-light leading-normal text-inherit",children:"Schaltet StackPulse für Benutzer in den Wartungsmodus."})}),x.jsx(G.Switch,{checked:W,disabled:!E||re||s||q,onChange:be=>Ou(be.target.checked),ripple:!1,color:"amber"})]}),re&&x.jsx("p",{className:"text-xs text-stormGrey-500",children:"Wartungsmodus wird aktualisiert…"})]})]}),P&&x.jsxs(G.Card,{children:[x.jsx(G.CardHeader,{variant:"gradient",color:"gray",className:"mb-5 p-4",children:x.jsx(G.Typography,{variant:"h6",color:"white",className:"flex items-center justify-between",children:x.jsx("span",{children:"Server & Endpoints"})})}),x.jsxs(G.CardBody,{className:"flex flex-col gap-4 p-4",children:[ct&&x.jsx("p",{className:"text-sm text-stormGrey-500",children:"Daten werden geladen…"}),!ct&&Wt&&x.jsx(Alert,{color:"red",className:"border border-red-200 bg-red-50 text-red-700",children:Wt}),!ct&&!Wt&&x.jsxs("div",{className:"flex flex-col gap-6",children:[x.jsxs("div",{className:"flex flex-col gap-3",children:[x.jsxs("div",{className:"flex items-center justify-between",children:[x.jsx(G.Typography,{variant:"small",color:"blue-gray",className:"font-semibold uppercase",children:"Server"}),x.jsxs(G.Typography,{variant:"small",color:"blue-gray",children:[Dn.length," vorhanden"]})]}),Dn.length===0?x.jsx("p",{className:"text-sm text-stormGrey-500",children:"Es sind derzeit keine Server hinterlegt."}):x.jsx("div",{className:"flex flex-col gap-3",children:Dn.map(be=>{const Fe=Dt.get(be.id)||null,Ve=!!(Fe!=null&&Fe.hasKey),We=Fe!=null&&Fe.updatedAt?new Date(Fe.updatedAt).toLocaleString("de-DE",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"}):null;return x.jsxs("div",{className:"flex flex-col gap-3 rounded-md border border-blue-gray-100 p-3 md:flex-row md:items-start md:justify-between",children:[x.jsxs("div",{className:"flex flex-col gap-1",children:[x.jsx("p",{className:"text-sm font-medium text-stormGrey-900",children:be.name||"Ohne Namen"}),x.jsx("p",{className:"text-xs text-stormGrey-500 break-all",children:be.url}),x.jsxs("p",{className:"text-xs text-stormGrey-500",children:["API-Key: ",Ve?"vorhanden":"nicht hinterlegt",We?` (aktualisiert am ${We})`:""]})]}),x.jsxs("div",{className:"flex w-full flex-col gap-2 md:max-w-xs",children:[x.jsx(G.Input,{type:"password",label:"Neuer API-Key",value:Mr[be.id]??"",onChange:rt=>Gf(be.id,rt.target.value),disabled:!N||ct||Cr===be.id}),x.jsxs("div",{className:"flex items-center gap-2 justify-end",children:[x.jsx(G.Button,{variant:"text",color:"red",size:"sm",onClick:()=>qf(be.id),disabled:!R||ct||tn===be.id||Wr!==null||Cr===be.id,children:tn===be.id?"Lösche…":"Löschen"}),x.jsx(G.Button,{color:"blue",size:"sm",onClick:()=>Kf(be.id),disabled:!N||ct||Cr===be.id,children:Cr===be.id?"Speichere…":"API-Key speichern"})]})]})]},be.id)})})]}),x.jsxs("div",{cl Line truncated
`)},xV=(e=[])=>{if(!Array.isArray(e))return[];const t=[];for(let r=0;r<e.length;r+=4)t.push(e.slice(r,r+4));return t};function wV(){const e=yo(),{user:t,setSession:r,refreshSession:n}=mn(),[o,a]=k.useState(!0),[i,s]=k.useState(""),[f,g]=k.useState([]),[b,S]=k.useState(!1),[O,T]=k.useState(!1),C=!!(t!=null&&t.requiresSecurityPhraseDownload),w=k.useMemo(()=>xV(f),[f]),m=k.useCallback(u=>{if(!u)return;const h=new Blob([u],{type:"text/plain;charset=utf-8"}),v=URL.createObjectURL(h),y=document.createElement("a");y.href=v;const E=t!=null&&t.username?`${t.username}-sicherheitsschluessel`:"security-phrase";y.download=`${E}.txt`,document.body.appendChild(y),y.click(),document.body.removeChild(y),URL.revokeObjectURL(v)},[t==null?void 0:t.username]),d=k.useCallback(async()=>{var u,h,v,y,E,P,N;a(!0),s("");try{const R=await nt.get("/api/users/me/security-phrase"),M=Array.isArray((h=(u=R.data)==null?void 0:u.item)==null?void 0:h.words)?R.data.item.words:[];if(!M.length)throw new Error("EMPTY_SECURITY_PHRASE");g(M),T(!1)}catch(R){if(((v=R==null?void 0:R.response)==null?void 0:v.status)===409||((E=(y=R==null?void 0:R.response)==null?void 0:y.data)==null?void 0:E.error)==="SECURITY_PHRASE_ALREADY_DOWNLOADED"){await n(),e("/dashboard/stacks",{replace:!0});return}const M=((N=(P=R==null?void 0:R.response)==null?void 0:P.data)==null?void 0:N.error)||(R==null?void 0:R.message)||"Sicherheitsschlüssel konnte nicht geladen werden.";s(M)}finally{a(!1)}},[e,n]);k.useEffect(()=>{if(!t){e("/auth/sign-in",{replace:!0});return}if(!C){e("/dashboard/stacks",{replace:!0});return}d()},[t,C,d,e]);const l=k.useCallback(()=>{d()},[d]),c=k.useCallback(async()=>{var u,h,v,y,E;if(!(b||!Array.isArray(f)||f.length===0)){S(!0),s("");try{const P=yV(f);m(P);const N=await nt.post("/api/users/me/security-phrase/downloaded"),R=((h=(u=N.data)==null?void 0:u.item)==null?void 0:h.downloadedAt)||null,M=(v=N.data)==null?void 0:v.session;M!=null&&M.user?r(M):await n(),T(!!R),e("/dashboard/stacks",{replace:!0})}catch(P){const N=((E=(y=P==null?void 0:P.response)==null?void 0:y.data)==null?void 0:E.error)||(P==null?void 0:P.message)||"Download konnte nicht bestätigt werden.";s(N)}finally{S(!1)}}},[f,b,m,n,e,r]);return C?x.jsxs("div",{className:"flex min-h-screen flex-col items-center justify-center bg-blue-gray-50/50 px-4 py-10",children:[x.jsxs("div",{className:"mb-6 text-center",children:[x.jsx(G.Typography,{variant:"h2",className:"font-semibold text-blue-gray-800",children:"Sicherheitsschlüssel sichern"}),x.jsx(G.Typography,{color:"blue-gray",className:"mt-2 max-w-3xl text-base",children:"Bitte lade den einmaligen Sicherheitsschlüssel herunter und bewahre ihn sicher auf. Er wird benötigt, um dein Passwort zurückzusetzen. Ohne den Download kannst du die Anwendung nicht weiter nutzen."})]}),x.jsx(G.Card,{className:"w-full max-w-3xl shadow-lg",children:x.jsxs(G.CardBody,{className:"space-y-6",children:[o&&x.jsx("div",{className:"flex items-center justify-center py-10",children:x.jsx(G.Spinner,{className:"h-10 w-10 text-blue-gray-500"})}),!o&&i&&x.jsx(G.Alert,{color:"red",className:"border border-red-200 bg-red-50 text-red-700",icon:x.jsx(Dz,{className:"h-5 w-5"}),children:x.jsxs("div",{className:"flex items-start justify-between gap-4",children:[x.jsxs("div",{children:[x.jsx(G.Typography,{variant:"h6",color:"red",children:"Fehler"}),x.jsx(G.Typography,{color:"red",className:"text-sm",children:i})]}),x.jsx(G.Button,{size:"sm",color:"red",variant:"text",onClick:l,className:"normal-case",children:"Erneut versuchen"})]})}),!o&&!i&&x.jsxs(x.Fragment,{children:[x.jsxs("div",{className:"rounded-lg border border-blue-gray-100 bg-blue-gray-50/60 p-6",children:[x.jsx(G.Typography,{variant:"h5",className:"mb-4 text-blue-gray-800",children:"Deine Sicherheitsphrase"}),x.jsx("div",{className:"space-y-3",children:w.map((u,h)=>x.jsx("div",{className:"flex flex-wrap items-center justify-center gap-4 text-lg font-mono tracking-wide text-blue-gray-900 md:text-xl",children:u.map((v,y)=>x.jsx("span",{className:"uppercase",children:v},`phrase-${h}-${y}`))},`phrase-row-${h}`))})]}),x.jsxs("div",{className:"flex flex-col items-center justify-between gap-4 md:flex-row",children:[x.jsx("div",{className:"text-sm text-blue-gray-600",children:"Der Download startet im TXT-Format. Bewahre die Datei sicher auf – sie kann später nicht erneut angezeigt werden."}),x.jsxs(G.Button,{color:"blue",size:"md",className:"flex items-center gap-2 normal-case",onClick:c,disabled:b||O||f.length===0,children:[x.jsx(Az,{className:"h-5 w-5"}),"Sicherheitsschlüssel herunterladen"]})]})]})]})})]}):null}const _V="/assets/pattern-68ea7609.png";function SV(){const e=yo(),{setSession:t,refreshSession:r}=mn(),[n,o]=k.useState(""),[a,i]=k.useState(""),[s,f]=k.useState(!1),[g,b]=k.useState(null),[S,O]=k.useState(!1);k.useEffect(()=>{let C=!0;const w=new AbortController;return(async()=>{try{const d=await fetch("/api/setup/status",{credentials:"include",signal:w.signal});if(d.ok&&!(await d.json()).setupComplete& Line truncated
+93
View File
@@ -0,0 +1,93 @@
function $j(e,t){for(var r=0;r<t.length;r++){const n=t[r];if(typeof n!="string"&&!Array.isArray(n)){for(const o in n)if(o!=="default"&&!(o in e)){const a=Object.getOwnPropertyDescriptor(n,o);a&&Object.defineProperty(e,o,a.get?a:{enumerable:!0,get:()=>n[o]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))n(o);new MutationObserver(o=>{for(const a of o)if(a.type==="childList")for(const i of a.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&n(i)}).observe(document,{childList:!0,subtree:!0});function r(o){const a={};return o.integrity&&(a.integrity=o.integrity),o.referrerPolicy&&(a.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?a.credentials="include":o.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function n(o){if(o.ep)return;o.ep=!0;const a=r(o);fetch(o.href,a)}})();function t_(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function lu(e){if(e.__esModule)return e;var t=e.default;if(typeof t=="function"){var r=function n(){return this instanceof n?Reflect.construct(t,arguments,this.constructor):t.apply(this,arguments)};r.prototype=t.prototype}else r={};return Object.defineProperty(r,"__esModule",{value:!0}),Object.keys(e).forEach(function(n){var o=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(r,n,o.get?o:{enumerable:!0,get:function(){return e[n]}})}),r}var r_={exports:{}},tf={},n_={exports:{}},xt={};/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var uu=Symbol.for("react.element"),Hj=Symbol.for("react.portal"),qj=Symbol.for("react.fragment"),Gj=Symbol.for("react.strict_mode"),Kj=Symbol.for("react.profiler"),Yj=Symbol.for("react.provider"),Qj=Symbol.for("react.context"),Xj=Symbol.for("react.forward_ref"),Zj=Symbol.for("react.suspense"),Jj=Symbol.for("react.memo"),e2=Symbol.for("react.lazy"),uy=Symbol.iterator;function t2(e){return e===null||typeof e!="object"?null:(e=uy&&e[uy]||e["@@iterator"],typeof e=="function"?e:null)}var o_={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},a_=Object.assign,i_={};function _s(e,t,r){this.props=e,this.context=t,this.refs=i_,this.updater=r||o_}_s.prototype.isReactComponent={};_s.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};_s.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function s_(){}s_.prototype=_s.prototype;function bg(e,t,r){this.props=e,this.context=t,this.refs=i_,this.updater=r||o_}var vg=bg.prototype=new s_;vg.constructor=bg;a_(vg,_s.prototype);vg.isPureReactComponent=!0;var cy=Array.isArray,l_=Object.prototype.hasOwnProperty,yg={current:null},u_={key:!0,ref:!0,__self:!0,__source:!0};function c_(e,t,r){var n,o={},a=null,i=null;if(t!=null)for(n in t.ref!==void 0&&(i=t.ref),t.key!==void 0&&(a=""+t.key),t)l_.call(t,n)&&!u_.hasOwnProperty(n)&&(o[n]=t[n]);var c=arguments.length-2;if(c===1)o.children=r;else if(1<c){for(var f=Array(c),g=0;g<c;g++)f[g]=arguments[g+2];o.children=f}if(e&&e.defaultProps)for(n in c=e.defaultProps,c)o[n]===void 0&&(o[n]=c[n]);return{$$typeof:uu,type:e,key:a,ref:i,props:o,_owner:yg.current}}function r2(e,t){return{$$typeof:uu,type:e.type,key:t,ref:e.ref,props:e.props,_owner:e._owner}}function xg(e){return typeof e=="object"&&e!==null&&e.$$typeof===uu}function n2(e){var t={"=":"=0",":":"=2"};return"$"+e.replace(/[=:]/g,function(r){return t[r]})}var dy=/\/+/g;function Dp(e,t){return typeof e=="object"&&e!==null&&e.key!=null?n2(""+e.key):t.toString(36)}function kc(e,t,r,n,o){var a=typeof e;(a==="undefined"||a==="boolean")&&(e=null);var i=!1;if(e===null)i=!0;else switch(a){case"string":case"number":i=!0;break;case"object":switch(e.$$typeof){case uu:case Hj:i=!0}}if(i)return i=e,o=o(i),e=n===""?"."+Dp(i,0):n,cy(o)?(r="",e!=null&&(r=e.replace(dy,"$&/")+"/"),kc(o,t,r,"",function(g){return g})):o!=null&&(xg(o)&&(o=r2(o,r+(!o.key||i&&i.key===o.key?"":(""+o.key).replace(dy,"$&/")+"/")+e)),t.push(o)),1;if(i=0,n=n===""?".":n+":",cy(e))for(var c=0;c<e.length;c++){a=e[c];var f=n+Dp(a,c);i+=kc(a,t,r,f,o)}else if(f=t2(e),typeof f=="function")for(e=f.call(e),c=0;!(a=e.next()).done;)a=a.value,f=n+Dp(a,c++),i+=kc(a,t,r,f,o);else if(a==="object")throw t=String(e),Error("Objects are not valid as a React child (found: "+(t==="[object Object]"?"object with keys {"+Object.keys(e).join(", ")+"}":t)+"). If you meant to render a collection of children, use an array instead.");return i}function Ju(e,t,r){if(e==null)return e;var n=[],o=0;return kc(e,n,"","",function(a){return t.call(r,a,o++)}),n}function o2(e){if(e._status===-1){var t=e._result;t=t(),t.then(function(r){(e._status===0||e._status===-1)&&(e._status=1,e._result=r)},function(r){(e._status===0||e._status===-1)&&(e._status=2,e._result=r)}),e._status===-1&&(e._status=0,e._result=t)}if(e._status===1)return e._result.default;throw e._result}var Br={current:null},Nc={transition:null},a2={ReactCurrentDispatcher:Br,ReactCurrentBatchConfig:Nc,ReactCurrentOwner:yg};xt.Children={map:Ju,forEach:function(e,t,r){Ju(e,function(){t.apply(this,arguments)},r)},count:function(e){var t=0;return Ju(e,function(){t++}),t},toArray:function(e){return Ju(e,function(t){return t})||[]},only:function(e){if(!xg(e))throw Error("React.Children.only expected to receive a single React element child.");return e}};xt.Component=_s;xt.Fragment=qj;xt.Profiler=Kj;xt.PureComponent=bg;xt.StrictMode=Gj;xt.Suspense=Zj;xt.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=a2;xt.cloneElement=function(e,t,r){if(e==null)throw Error("React.cloneElement(...): The argument must be a React element, but you passed "+e+".");var n=a_({},e.props),o=e.key,a=e.ref,i=e._owner;if(t!=null){if(t.ref!==void 0&&(a=t.ref,i=yg.current),t.key!==void 0&&(o=""+t.key),e.type&&e.type.defaultProps)var c=e.type.defaultProps;for(f in t)l_.call(t,f)&&!u_.hasOwnProperty(f)&&(n[f]=t[f]===void 0&&c!==void 0?c[f]:t[f])}var f=arguments.length-2;if(f===1)n.children=r;else if(1<f){c=Array(f);for(var g=0;g<f;g++)c[g]=arguments[g+2];n.children=c}return{$$typeof:uu,type:e.type,key:o,ref:a,props:n,_owner:i}};xt.createContext=function(e){return e={$$typeof:Qj,_currentValue:e,_currentValue2:e,_threadCount:0,Provider:null,Consumer:null,_defaultValue:null,_globalName:null},e.Provider={$$typeof:Yj,_conLine truncated
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var i2=P,s2=Symbol.for("react.element"),l2=Symbol.for("react.fragment"),u2=Object.prototype.hasOwnProperty,c2=i2.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,d2={key:!0,ref:!0,__self:!0,__source:!0};function d_(e,t,r){var n,o={},a=null,i=null;r!==void 0&&(a=""+r),t.key!==void 0&&(a=""+t.key),t.ref!==void 0&&(i=t.ref);for(n in t)u2.call(t,n)&&!d2.hasOwnProperty(n)&&(o[n]=t[n]);if(e&&e.defaultProps)for(n in t=e.defaultProps,t)o[n]===void 0&&(o[n]=t[n]);return{$$typeof:s2,type:e,key:a,ref:i,props:o,_owner:c2.current}}tf.Fragment=l2;tf.jsx=d_;tf.jsxs=d_;r_.exports=tf;var y=r_.exports;function f_(e,t){return function(){return e.apply(t,arguments)}}const{toString:f2}=Object.prototype,{getPrototypeOf:_g}=Object,rf=(e=>t=>{const r=f2.call(t);return e[r]||(e[r]=r.slice(8,-1).toLowerCase())})(Object.create(null)),ro=e=>(e=e.toLowerCase(),t=>rf(t)===e),nf=e=>t=>typeof t===e,{isArray:Ss}=Array,Ml=nf("undefined");function p2(e){return e!==null&&!Ml(e)&&e.constructor!==null&&!Ml(e.constructor)&&ln(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}const p_=ro("ArrayBuffer");function h2(e){let t;return typeof ArrayBuffer<"u"&&ArrayBuffer.isView?t=ArrayBuffer.isView(e):t=e&&e.buffer&&p_(e.buffer),t}const g2=nf("string"),ln=nf("function"),h_=nf("number"),of=e=>e!==null&&typeof e=="object",m2=e=>e===!0||e===!1,Ec=e=>{if(rf(e)!=="object")return!1;const t=_g(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(Symbol.toStringTag in e)&&!(Symbol.iterator in e)},b2=ro("Date"),v2=ro("File"),y2=ro("Blob"),x2=ro("FileList"),w2=e=>of(e)&&ln(e.pipe),_2=e=>{let t;return e&&(typeof FormData=="function"&&e instanceof FormData||ln(e.append)&&((t=rf(e))==="formdata"||t==="object"&&ln(e.toString)&&e.toString()==="[object FormData]"))},S2=ro("URLSearchParams"),[C2,T2,O2,P2]=["ReadableStream","Request","Response","Headers"].map(ro),k2=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"");function cu(e,t,{allOwnKeys:r=!1}={}){if(e===null||typeof e>"u")return;let n,o;if(typeof e!="object"&&(e=[e]),Ss(e))for(n=0,o=e.length;n<o;n++)t.call(null,e[n],n,e);else{const a=r?Object.getOwnPropertyNames(e):Object.keys(e),i=a.length;let c;for(n=0;n<i;n++)c=a[n],t.call(null,e[c],c,e)}}function g_(e,t){t=t.toLowerCase();const r=Object.keys(e);let n=r.length,o;for(;n-- >0;)if(o=r[n],t===o.toLowerCase())return o;return null}const qa=(()=>typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:global)(),m_=e=>!Ml(e)&&e!==qa;function Lh(){const{caseless:e}=m_(this)&&this||{},t={},r=(n,o)=>{const a=e&&g_(t,o)||o;Ec(t[a])&&Ec(n)?t[a]=Lh(t[a],n):Ec(n)?t[a]=Lh({},n):Ss(n)?t[a]=n.slice():t[a]=n};for(let n=0,o=arguments.length;n<o;n++)arguments[n]&&cu(arguments[n],r);return t}const N2=(e,t,r,{allOwnKeys:n}={})=>(cu(t,(o,a)=>{r&&ln(o)?e[a]=f_(o,r):e[a]=o},{allOwnKeys:n}),e),E2=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),j2=(e,t,r,n)=>{e.prototype=Object.create(t.prototype,n),e.prototype.constructor=e,Object.defineProperty(e,"super",{value:t.prototype}),r&&Object.assign(e.prototype,r)},A2=(e,t,r,n)=>{let o,a,i;const c={};if(t=t||{},e==null)return t;do{for(o=Object.getOwnPropertyNames(e),a=o.length;a-- >0;)i=o[a],(!n||n(i,e,t))&&!c[i]&&(t[i]=e[i],c[i]=!0);e=r!==!1&&_g(e)}while(e&&(!r||r(e,t))&&e!==Object.prototype);return t},R2=(e,t,r)=>{e=String(e),(r===void 0||r>e.length)&&(r=e.length),r-=t.length;const n=e.indexOf(t,r);return n!==-1&&n===r},M2=e=>{if(!e)return null;if(Ss(e))return e;let t=e.length;if(!h_(t))return null;const r=new Array(t);for(;t-- >0;)r[t]=e[t];return r},I2=(e=>t=>e&&t instanceof e)(typeof Uint8Array<"u"&&_g(Uint8Array)),D2=(e,t)=>{const n=(e&&e[Symbol.iterator]).call(e);let o;for(;(o=n.next())&&!o.done;){const a=o.value;t.call(e,a[0],a[1])}},L2=(e,t)=>{let r;const n=[];for(;(r=e.exec(t))!==null;)n.push(r);return n},F2=ro("HTMLFormElement"),z2=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(r,n,o){return n.toUpperCase()+o}),fy=(({hasOwnProperty:e})=>(t,r)=>e.call(t,r))(Object.prototype),B2=ro("RegExp"),b_=(e,t)=>{const r=Object.getOwnPropertyDescriptors(e),n={};cu(r,(o,a)=>{let i;(i=t(o,a,e))!==!1&&(n[a]=i||o)}),Object.defineProperties(e,n)},V2=e=>{b_(e,(t,r)=>{if(ln(e)&&["arguments","caller","callee"].indexOf(r)!==-1)return!1;const n=e[r];if(ln(n)){if(t.enumerable=!1,"writable"in t){t.writable=!1;return}t.set||(t.set=()=>{throw Error("Can not rewrite read-only method '"+r+"'")})}})},U2=(e,t)=>{const r={},n=o=>{o.forEach(a=>{r[a]=!0})};return Ss(e)?n(e):n(String(e).split(t)),r},W2=()=>{},$2=(e,t)=>e!=null&&Number.isFinite(e=+e)?e:t,Lp="abcdefghijklmnopqrstuvwxyz",py="0123456789",v_={DIGIT:py,ALPHA:Lp,ALPHA_DIGIT:Lp+Lp.toUpperCase()+py},H2=(e=16,t=v_.ALPHA_DIGIT)=>{let r="";const{length:n}=t;for(;e--;)r+=t[Math.random()*n|0];return r};function q2(e){return!!(e&&ln(e.append)&&e[Symbol.toStringTag]==="FormData"&&e[Symbol.iterator])}const G2=e=>{const t=new Array(10),r=(n,o)=>{if(of(n)){if(t.indexOf(n)>=0)return;if(!("toJSON"in n)){t[o]=n;const a=SLine truncated
`).forEach(function(i){o=i.indexOf(":"),r=i.substring(0,o).trim().toLowerCase(),n=i.substring(o+1).trim(),!(!r||t[r]&&hA[r])&&(r==="set-cookie"?t[r]?t[r].push(n):t[r]=[n]:t[r]=t[r]?t[r]+", "+n:n)}),t},by=Symbol("internals");function Js(e){return e&&String(e).trim().toLowerCase()}function jc(e){return e===!1||e==null?e:_e.isArray(e)?e.map(jc):String(e)}function mA(e){const t=Object.create(null),r=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let n;for(;n=r.exec(e);)t[n[1]]=n[2];return t}const bA=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function Fp(e,t,r,n,o){if(_e.isFunction(n))return n.call(this,t,r);if(o&&(t=r),!!_e.isString(t)){if(_e.isString(n))return t.indexOf(n)!==-1;if(_e.isRegExp(n))return n.test(t)}}function vA(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(t,r,n)=>r.toUpperCase()+n)}function yA(e,t){const r=_e.toCamelCase(" "+t);["get","set","has"].forEach(n=>{Object.defineProperty(e,n+r,{value:function(o,a,i){return this[n].call(this,t,o,a,i)},configurable:!0})})}class sf{constructor(t){t&&this.set(t)}set(t,r,n){const o=this;function a(c,f,g){const b=Js(f);if(!b)throw new Error("header name must be a non-empty string");const S=_e.findKey(o,b);(!S||o[S]===void 0||g===!0||g===void 0&&o[S]!==!1)&&(o[S||f]=jc(c))}const i=(c,f)=>_e.forEach(c,(g,b)=>a(g,b,f));if(_e.isPlainObject(t)||t instanceof this.constructor)i(t,r);else if(_e.isString(t)&&(t=t.trim())&&!bA(t))i(gA(t),r);else if(_e.isHeaders(t))for(const[c,f]of t.entries())a(f,c,n);else t!=null&&a(r,t,n);return this}get(t,r){if(t=Js(t),t){const n=_e.findKey(this,t);if(n){const o=this[n];if(!r)return o;if(r===!0)return mA(o);if(_e.isFunction(r))return r.call(this,o,n);if(_e.isRegExp(r))return r.exec(o);throw new TypeError("parser must be boolean|regexp|function")}}}has(t,r){if(t=Js(t),t){const n=_e.findKey(this,t);return!!(n&&this[n]!==void 0&&(!r||Fp(this,this[n],n,r)))}return!1}delete(t,r){const n=this;let o=!1;function a(i){if(i=Js(i),i){const c=_e.findKey(n,i);c&&(!r||Fp(n,n[c],c,r))&&(delete n[c],o=!0)}}return _e.isArray(t)?t.forEach(a):a(t),o}clear(t){const r=Object.keys(this);let n=r.length,o=!1;for(;n--;){const a=r[n];(!t||Fp(this,this[a],a,t,!0))&&(delete this[a],o=!0)}return o}normalize(t){const r=this,n={};return _e.forEach(this,(o,a)=>{const i=_e.findKey(n,a);if(i){r[i]=jc(o),delete r[a];return}const c=t?vA(a):String(a).trim();c!==a&&delete r[a],r[c]=jc(o),n[c]=!0}),this}concat(...t){return this.constructor.concat(this,...t)}toJSON(t){const r=Object.create(null);return _e.forEach(this,(n,o)=>{n!=null&&n!==!1&&(r[o]=t&&_e.isArray(n)?n.join(", "):n)}),r}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([t,r])=>t+": "+r).join(`
`)}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(t){return t instanceof this?t:new this(t)}static concat(t,...r){const n=new this(t);return r.forEach(o=>n.set(o)),n}static accessor(t){const n=(this[by]=this[by]={accessors:{}}).accessors,o=this.prototype;function a(i){const c=Js(i);n[c]||(yA(o,i),n[c]=!0)}return _e.isArray(t)?t.forEach(a):a(t),this}}sf.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);_e.reduceDescriptors(sf.prototype,({value:e},t)=>{let r=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(n){this[r]=n}}});_e.freezeMethods(sf);const Gn=sf;function zp(e,t){const r=this||Og,n=t||r,o=Gn.from(n.headers);let a=n.data;return _e.forEach(e,function(c){a=c.call(r,a,o.normalize(),t?t.status:void 0)}),o.normalize(),a}function P_(e){return!!(e&&e.__CANCEL__)}function Cs(e,t,r){mt.call(this,e??"canceled",mt.ERR_CANCELED,t,r),this.name="CanceledError"}_e.inherits(Cs,mt,{__CANCEL__:!0});function k_(e,t,r){const n=r.config.validateStatus;!r.status||!n||n(r.status)?e(r):t(new mt("Request failed with status code "+r.status,[mt.ERR_BAD_REQUEST,mt.ERR_BAD_RESPONSE][Math.floor(r.status/100)-4],r.config,r.request,r))}function xA(e){const t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||""}function wA(e,t){e=e||10;const r=new Array(e),n=new Array(e);let o=0,a=0,i;return t=t!==void 0?t:1e3,function(f){const g=Date.now(),b=n[a];i||(i=g),r[o]=f,n[o]=g;let S=a,O=0;for(;S!==o;)O+=r[S++],S=S%e;if(o=(o+1)%e,o===a&&(a=(a+1)%e),g-i<t)return;const T=b&&g-b;return T?Math.round(O*1e3/T):void 0}}function _A(e,t){let r=0,n=1e3/t,o,a;const i=(g,b=Date.now())=>{r=b,o=null,a&&(clearTimeout(a),a=null),e.apply(null,g)};return[(...g)=>{const b=Date.now(),S=b-r;S>=n?i(g,b):(o=g,a||(a=setTimeout(()=>{a=null,i(o)},n-S)))},()=>o&&i(o)]}const ad=(e,t,r=3)=>{let n=0;const o=wA(50,250);return _A(a=>{const i=a.loaded,c=a.lengthComputable?a.total:void 0,f=i-n,g=o(f),b=i<=c;n=i;const S={loaded:i,total:c,progress:c?i/c:void 0,bytes:f,rate:g||void 0,estimated:g&&c&&b?(c-i)/g:void 0,event:a,lengthComputable:c!=null,[t?"download":"upload"]:!0};e(S)},r)},vy=(e,t)=>{const r=e!=null;return[n=>t[0]({lengthComputable:r,total:e,loaded:n}),t[1]]},yy=e=>(...t)=>_e.asap(()=>e(...t)),SA=un.hasStandardBrowserEnv?function(){const t=un.navigator&&/(msie|trident)/i.test(un.navigator.userAgent),r=document.createElement("a");let n;function o(a){let i=a;return t&&(r.setAttribute("href",i),i=r.href),r.setAttribute("href",i),{href:r.href,protocol:r.protocol?r.protocol.replace(/:$/,""):"",host:r.host,search:r.search?r.search.replace(/^\?/,""):"",hash:r.hash?r.hash.replace(/^#/,""):"",hostname:r.hostname,port:r.port,pathname:r.pathname.charAt(0)==="/"?r.pathname:"/"+r.pathname}}return n=o(window.location.href),function(i){const c=_e.isString(i)?o(i):i;return c.protocol===n.protocol&&c.host===n.host}}():function(){return function(){return!0}}(),CA=un.hasStandardBrowserEnv?{write(e,t,r,n,o,a){const i=[e+"="+encodeURIComponent(t)];_e.isNumber(r)&&i.push("expires="+new Date(r).toGMTString()),_e.isString(n)&&i.push("path="+n),_e.isString(o)&&i.push("domain="+o),a===!0&&i.push("secure"),document.cookie=i.join("; ")},read(e){const t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove(e){this.write(e,"",Date.now()-864e5)}}:{write(){},read(){return null},remove(){}};function TA(e){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e)}function OA(e,t){return t?e.replace(/\/?\/$/,"")+"/"+t.replace(/^\/+/,""):e}function N_(e,t){return e&&!TA(t)?OA(e,t):t}const xy=e=>e instanceof Gn?{...e}:e;function ti(e,t){t=t||{};const r={};function n(g,b,S){return _e.isPlainObject(g)&&_e.isPlainObject(b)?_e.merge.call({caseless:S},g,b):_e.isPlainObject(b)?_e.merge({},b):_e.isArray(b)?b.slice():b}function o(g,b,S){if(_e.isUndefined(b)){if(!_e.isUndefined(g))return n(void 0,g,S)}else return n(g,b,S)}function a(g,b){if(!_e.isUndefined(b))return n(void 0,b)}function i(g,b){if(_e.isUndefined(b)){if(!_e.isUndefined(g))return n(void 0,g)}else return n(void 0,b)}function c(g,b,S){if(S in t)return n(g,b);if(S in e)return n(void 0,g)}const f={url:a,method:a,data:a,baseURL:i,transformRequest:i,transformResponse:i,paramsSerializer:i,timeout:i,timeoutMessage:i,withCredentials:i,withXSRFToken:i,adapter:i,responseType:i,xsrfCookieName:i,xsrfHeaderName:i,onUploadProgress:i,onDownloadProgress:i,decompress:i,maxContentLength:i,maxBodyLength:i,beforeRedirect:i,transport:i,httpAgent:i,httpsAgent:i,cancelToken:i,socketPath:i,responseEncoding:i,validateStatus:c,headers:(g,b)=>o(xy(g),xy(b),!0)};return _e.forEach(Object.keys(Object.assign({},e,t)),function(b){const S=f[b]||o,O=S(e[b],t[b],b);_e.isUndefined(O)&&S!==c||(r[b]=O)}),r}const E_=e=>{const t=ti({},e);let{data:r,withXSRFToken:n,xsrfHeaderName:o,xsrfCookieName:a,headers:i,auth:c}=t;t.headers=i=Gn.from(i),t.url=C_(N_(t.baseURL,t.url),e.params,e.paramsSerializer),c&&i.set("Authorization","Basic "+btoa((c.username||"")+":"+(c.password?unescape(enco Line truncated
`+a.map(Sy).join(`
`):" "+Sy(a[0]):"as no adapter specified";throw new mt("There is no suitable adapter to dispatch the request "+i,"ERR_NOT_SUPPORT")}return n},adapters:Uh};function Bp(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new Cs(null,e)}function Cy(e){return Bp(e),e.headers=Gn.from(e.headers),e.data=zp.call(e,e.transformRequest),["post","put","patch"].indexOf(e.method)!==-1&&e.headers.setContentType("application/x-www-form-urlencoded",!1),R_.getAdapter(e.adapter||Og.adapter)(e).then(function(n){return Bp(e),n.data=zp.call(e,e.transformResponse,n),n.headers=Gn.from(n.headers),n},function(n){return P_(n)||(Bp(e),n&&n.response&&(n.response.data=zp.call(e,e.transformResponse,n.response),n.response.headers=Gn.from(n.response.headers))),Promise.reject(n)})}const M_="1.7.5",Pg={};["object","boolean","number","function","string","symbol"].forEach((e,t)=>{Pg[e]=function(n){return typeof n===e||"a"+(t<1?"n ":" ")+e}});const Ty={};Pg.transitional=function(t,r,n){function o(a,i){return"[Axios v"+M_+"] Transitional option '"+a+"'"+i+(n?". "+n:"")}return(a,i,c)=>{if(t===!1)throw new mt(o(i," has been removed"+(r?" in "+r:"")),mt.ERR_DEPRECATED);return r&&!Ty[i]&&(Ty[i]=!0,console.warn(o(i," has been deprecated since v"+r+" and will be removed in the near future"))),t?t(a,i,c):!0}};function FA(e,t,r){if(typeof e!="object")throw new mt("options must be an object",mt.ERR_BAD_OPTION_VALUE);const n=Object.keys(e);let o=n.length;for(;o-- >0;){const a=n[o],i=t[a];if(i){const c=e[a],f=c===void 0||i(c,a,e);if(f!==!0)throw new mt("option "+a+" must be "+f,mt.ERR_BAD_OPTION_VALUE);continue}if(r!==!0)throw new mt("Unknown option "+a,mt.ERR_BAD_OPTION)}}const Wh={assertOptions:FA,validators:Pg},Jo=Wh.validators;class sd{constructor(t){this.defaults=t,this.interceptors={request:new my,response:new my}}async request(t,r){try{return await this._request(t,r)}catch(n){if(n instanceof Error){let o;Error.captureStackTrace?Error.captureStackTrace(o={}):o=new Error;const a=o.stack?o.stack.replace(/^.+\n/,""):"";try{n.stack?a&&!String(n.stack).endsWith(a.replace(/^.+\n.+\n/,""))&&(n.stack+=`
`+a):n.stack=a}catch{}}throw n}}_request(t,r){typeof t=="string"?(r=r||{},r.url=t):r=t||{},r=ti(this.defaults,r);const{transitional:n,paramsSerializer:o,headers:a}=r;n!==void 0&&Wh.assertOptions(n,{silentJSONParsing:Jo.transitional(Jo.boolean),forcedJSONParsing:Jo.transitional(Jo.boolean),clarifyTimeoutError:Jo.transitional(Jo.boolean)},!1),o!=null&&(_e.isFunction(o)?r.paramsSerializer={serialize:o}:Wh.assertOptions(o,{encode:Jo.function,serialize:Jo.function},!0)),r.method=(r.method||this.defaults.method||"get").toLowerCase();let i=a&&_e.merge(a.common,a[r.method]);a&&_e.forEach(["delete","get","head","post","put","patch","common"],C=>{delete a[C]}),r.headers=Gn.concat(i,a);const c=[];let f=!0;this.interceptors.request.forEach(function(x){typeof x.runWhen=="function"&&x.runWhen(r)===!1||(f=f&&x.synchronous,c.unshift(x.fulfilled,x.rejected))});const g=[];this.interceptors.response.forEach(function(x){g.push(x.fulfilled,x.rejected)});let b,S=0,O;if(!f){const C=[Cy.bind(this),void 0];for(C.unshift.apply(C,c),C.push.apply(C,g),O=C.length,b=Promise.resolve(r);S<O;)b=b.then(C[S++],C[S++]);return b}O=c.length;let T=r;for(S=0;S<O;){const C=c[S++],x=c[S++];try{T=C(T)}catch(m){x.call(this,m);break}}try{b=Cy.call(this,T)}catch(C){return Promise.reject(C)}for(S=0,O=g.length;S<O;)b=b.then(g[S++],g[S++]);return b}getUri(t){t=ti(this.defaults,t);const r=N_(t.baseURL,t.url);return C_(r,t.params,t.paramsSerializer)}}_e.forEach(["delete","get","head","options"],function(t){sd.prototype[t]=function(r,n){return this.request(ti(n||{},{method:t,url:r,data:(n||{}).data}))}});_e.forEach(["post","put","patch"],function(t){function r(n){return function(a,i,c){return this.request(ti(c||{},{method:t,headers:n?{"Content-Type":"multipart/form-data"}:{},url:a,data:i}))}}sd.prototype[t]=r(),sd.prototype[t+"Form"]=r(!0)});const Ac=sd;class kg{constructor(t){if(typeof t!="function")throw new TypeError("executor must be a function.");let r;this.promise=new Promise(function(a){r=a});const n=this;this.promise.then(o=>{if(!n._listeners)return;let a=n._listeners.length;for(;a-- >0;)n._listeners[a](o);n._listeners=null}),this.promise.then=o=>{let a;const i=new Promise(c=>{n.subscribe(c),a=c}).then(o);return i.cancel=function(){n.unsubscribe(a)},i},t(function(a,i,c){n.reason||(n.reason=new Cs(a,i,c),r(n.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(t){if(this.reason){t(this.reason);return}this._listeners?this._listeners.push(t):this._listeners=[t]}unsubscribe(t){if(!this._listeners)return;const r=this._listeners.indexOf(t);r!==-1&&this._listeners.splice(r,1)}static source(){let t;return{token:new kg(function(o){t=o}),cancel:t}}}const zA=kg;function BA(e){return function(r){return e.apply(null,r)}}function VA(e){return _e.isObject(e)&&e.isAxiosError===!0}const $h={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries($h).forEach(([e,t])=>{$h[t]=e});const UA=$h;function I_(e){const t=new Ac(e),r=f_(Ac.prototype.request,t);return _e.extend(r,Ac.prototype,t,{allOwnKeys:!0}),_e.extend(r,t,null,{allOwnKeys:!0}),r.create=function(o){return I_(ti(e,o))},r}const ur=I_(Og);ur.Axios=Ac;ur.CanceledError=Cs;ur.CancelToken=zA;ur.isCancel=P_;ur.VERSION=M_;ur.toFormData=af;ur.AxiosError=mt;ur.Cancel=ur.CanceledError;ur.all=function(t){return Promise.all(t)};ur.spread=BA;ur.isAxiosError=VA;ur.mergeConfig=ti;ur.AxiosHeaders=Gn;ur.formToJSON=e=>O_(_e.isHTMLForm(e)?new FormData(e):e);ur.getAdapter=R_.getAdapter;ur.HttpStatusCode=UA;ur.default=ur;const lt=ur;var Hh={},D_={exports:{}},hn={},L_={exports:{}},F_={};/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/(function(e){function t(q,z){var $=q.length;q.push(z);e:for(;0<$;){var K=$-1>>>1,X=q[K];if(0<o(X,z))q[K]=z,q[$]=X,$=K;else break e}}function r(q){return q.length===0?null:q[0]}function n(q){if(q.length===0)return null;var z=q[0],$=q.pop();if($!==z){q[0]=$;e:for(var K=0,X=q.length,te=X>>>1;K<te;){var me=2*(K+1)-1,de=q[me],ee=me+1,Se=q[ee];if(0>o(de,$))ee<X&&0>o(Se,de)?(q[K]=Se,q[ee]=$,K=ee):(q[K]=de,q[me]=$,K=me);else if(ee<X&&0>o(Se,$))q[K]=Se,q[ee]=$,K=ee;else break e}}return z}function o(q,z){var $=q.sortIndex-z.sortIndex;return $!==0?$:q.id-z.id}if(typeof performance=="object"&&typeof performance.now=="function"){var a=performance;e.unstable_now=function(){return a.now()}}else{var i=Date,c=i.now();e.unstable_now=function(){return i.now()-c}}var f=[],g=[],b=1,S=null,O=3,T=!1,C=!1,x=!1,m=typeof setTimeout=="function"?setTimeout:null,d=typeof clearTimeout=="function"?clearTimeout:null,s=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function u(q){for(var z=r(g);z!==null;){if(z.callback===null)n(g);else if(z.startTime<=q)n(g),z.sortIndex=z.expirationTime,t(f,z);else break;z=r(g)}}function l(q){if(x=!1,u(q),!C)if(r(f)!==null)C=!0,U(h);else{var z=r(g);z!==null&&Y(l,z.startTime-q)}}function h(q,z){C=!1,x&&(x=!1,d(j),j=-1),T=!0;var $=O;try{for(u(z),S=r(f);S!==null&&(!(S.expirationTime>z)||q&&!A());){var K=S.callback;if(typeof K=="function"){S.callback=null,O=S.priorityLevel;var X=K(S.expirationTime<=z);z=e.unstable_now(),typeof X=="function"?S.callback=X:S===r(f)&&n(f),u(z)}else n(f);S=r(f)}if(S!==null)var te=!0;else{var me=r(g);me!==null&&Y(l,me.startTime-z),te=!1}return te}finally{S=null,O=$,T=!1}}var v=!1,w=null,j=-1,N=5,k=-1;function A(){return!(e.unstable_now()-k<N)}function M(){if(w!==null){var q=e.unstable_now();k=q;var z=!0;try{z=w(!0,q)}finally{z?D():(v=!1,w=null)}}else v=!1}var D;if(typeof s=="function")D=function(){s(M)};else if(typeof MessageChannel<"u"){var L=new MessageChannel,B=L.port2;L.port1.onmessage=M,D=function(){B.postMessage(null)}}else D=function(){m(M,0)};function U(q){w=q,v||(v=!0,D())}function Y(q,z){j=m(function(){q(e.unstable_now())},z)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(q){q.callback=null},e.unstable_continueExecution=function(){C||T||(C=!0,U(h))},e.unstable_forceFrameRate=function(q){0>q||125<q?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):N=0<q?Math.floor(1e3/q):5},e.unstable_getCurrentPriorityLevel=function(){return O},e.unstable_getFirstCallbackNode=function(){return r(f)},e.unstable_next=function(q){switch(O){case 1:case 2:case 3:var z=3;break;default:z=O}var $=O;O=z;try{return q()}finally{O=$}},e.unstable_pauseExecution=function(){},e.unstable_requestPaint=function(){},e.unstable_runWithPriority=function(q,z){switch(q){case 1:case 2:case 3:case 4:case 5:break;default:q=3}var $=O;O=q;try{return z()}finally{O=$}},e.unstable_scheduleCallback=function(q,z,$){var K=e.unstable_now();switch(typeof $=="object"&&$!==null?($=$.delay,$=typeof $=="number"&&0<$?K+$:K):$=K,q){case 1:var X=-1;break;case 2:X=250;break;case 5:X=1073741823;break;case 4:X=1e4;break;default:X=5e3}return X=$+X,q={id:b++,callback:z,priorityLevel:q,startTime:$,expirationTime:X,sortIndex:-1},$>K?(q.sortIndex=$,t(g,q),r(f)===null&&q===r(g)&&(x?(d(j),j=-1):x=!0,Y(l,$-K))):(q.sortIndex=X,t(f,q),C||T||(C=!0,U(h))),q},e.unstable_shouldYield=A,e.unstable_wrapCallback=function(q){var z=O;return function(){var $=O;O=z;try{return q.apply(this,arguments)}finally{O=$}}}})(F_);L_.exports=F_;var WA=L_.exports;/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var z_=P,fn=WA;function Re(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,r=1;r<arguments.length;r++)t+="&args[]="+encodeURIComponent(arguments[r]);return"Minified React error #"+e+"; visit "+t+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}var B_=new Set,Il={};function di(e,t){is(e,t),is(e+"Capture",t)}function is(e,t){for(Il[e]=t,e=0;e<t.length;e++)B_.add(t[e])}var Fo=!(typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),qh=Object.prototype.hasOwnProperty,$A=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,Oy={},Py={};function HA(e){return qh.call(Py,e)?!0:qh.call(Oy,e)?!1:$A.test(e)?Py[e]=!0:(Oy[e]=!0,!1)}function qA(e,t,r,n){if(r!==null&&r.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return n?!1:r!==null?!r.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function GA(e,t,r,n){if(t===null||typeof t>"u"||qA(e,t,r,n))return!0;if(n)return!1;if(r!==null)switch(r.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function Vr(e,t,r,n,o,a,i){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=n,this.attributeNamespace=o,this.mustUseProperty=r,this.propertyName=e,this.type=t,this.sanitizeURL=a,this.removeEmptyString=i}var Cr={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){Cr[e]=new Vr(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];Cr[t]=new Vr(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){Cr[e]=new Vr(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){Cr[e]=new Vr(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){Cr[e]=new Vr(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){Cr[e]=new Vr(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){Cr[e]=new Vr(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){Cr[e]=new Vr(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){Cr[e]=new Vr(e,5,!1,e.toLowerCase(),null,!1,!1)});var Ng=/[\-:]([a-z])/g;function Eg(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(Ng,Eg);Cr[t]=new Vr(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(Ng,Eg);Cr[t]=new Vr(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(Ng,Eg);Cr[t]=new Vr(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){Cr[e]=new Vr(e,1,!1,e.toLowerCase(),null,!1,!1)});Cr.xlinkHref=new Vr("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){Cr[e]=new Vr(e,1,!1,e.toLowerCase(),null,!0,!0)});function jg(e,t,r,n){var o=Cr.hasOwnProperty(t)?Cr[t]:null;(o!==null?o.type!==0:n||!(2<t.length)||t[0]!=="o"&&t[0]!== Line truncated
`+Vp+e}var Up=!1;function Wp(e,t){if(!e||Up)return"";Up=!0;var r=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{if(t)if(t=function(){throw Error()},Object.defineProperty(t.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(t,[])}catch(g){var n=g}Reflect.construct(e,[],t)}else{try{t.call()}catch(g){n=g}e.call(t.prototype)}else{try{throw Error()}catch(g){n=g}e()}}catch(g){if(g&&n&&typeof g.stack=="string"){for(var o=g.stack.split(`
`),a=n.stack.split(`
`),i=o.length-1,c=a.length-1;1<=i&&0<=c&&o[i]!==a[c];)c--;for(;1<=i&&0<=c;i--,c--)if(o[i]!==a[c]){if(i!==1||c!==1)do if(i--,c--,0>c||o[i]!==a[c]){var f=`
`+o[i].replace(" at new "," at ");return e.displayName&&f.includes("<anonymous>")&&(f=f.replace("<anonymous>",e.displayName)),f}while(1<=i&&0<=c);break}}}finally{Up=!1,Error.prepareStackTrace=r}return(e=e?e.displayName||e.name:"")?pl(e):""}function KA(e){switch(e.tag){case 5:return pl(e.type);case 16:return pl("Lazy");case 13:return pl("Suspense");case 19:return pl("SuspenseList");case 0:case 2:case 15:return e=Wp(e.type,!1),e;case 11:return e=Wp(e.type.render,!1),e;case 1:return e=Wp(e.type,!0),e;default:return""}}function Qh(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Di:return"Fragment";case Ii:return"Portal";case Gh:return"Profiler";case Ag:return"StrictMode";case Kh:return"Suspense";case Yh:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case U_:return(e.displayName||"Context")+".Consumer";case V_:return(e._context.displayName||"Context")+".Provider";case Rg:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case Mg:return t=e.displayName||null,t!==null?t:Qh(e.type)||"Memo";case aa:t=e._payload,e=e._init;try{return Qh(e(t))}catch{}}return null}function YA(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return Qh(t);case 8:return t===Ag?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function Ca(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function $_(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function QA(e){var t=$_(e)?"checked":"value",r=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),n=""+e[t];if(!e.hasOwnProperty(t)&&typeof r<"u"&&typeof r.get=="function"&&typeof r.set=="function"){var o=r.get,a=r.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return o.call(this)},set:function(i){n=""+i,a.call(this,i)}}),Object.defineProperty(e,t,{enumerable:r.enumerable}),{getValue:function(){return n},setValue:function(i){n=""+i},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function tc(e){e._valueTracker||(e._valueTracker=QA(e))}function H_(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var r=t.getValue(),n="";return e&&(n=$_(e)?e.checked?"true":"false":e.value),e=n,e!==r?(t.setValue(e),!0):!1}function ld(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function Xh(e,t){var r=t.checked;return rr({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:r??e._wrapperState.initialChecked})}function Ny(e,t){var r=t.defaultValue==null?"":t.defaultValue,n=t.checked!=null?t.checked:t.defaultChecked;r=Ca(t.value!=null?t.value:r),e._wrapperState={initialChecked:n,initialValue:r,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function q_(e,t){t=t.checked,t!=null&&jg(e,"checked",t,!1)}function Zh(e,t){q_(e,t);var r=Ca(t.value),n=t.type;if(r!=null)n==="number"?(r===0&&e.value===""||e.value!=r)&&(e.value=""+r):e.value!==""+r&&(e.value=""+r);else if(n==="submit"||n==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?Jh(e,t.type,r):t.hasOwnProperty("defaultValue")&&Jh(e,t.type,Ca(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function Ey(e,t,r){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var n=t.type;if(!(n!=="submit"&&n!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,r||t===e.value||(e.value=t),e.defaultValue=t}r=e.name,r!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,r!==""&&(e.name=r)}function Jh(e,t,r){(t!=="number"||ld(e.ownerDocument)!==e)&&(r==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+r&&(e.defaultValue=""+r))}var hl=Array.isArray;function Yi(e,t,r,n){if(e=e.options,t){t={};for(var o=0;o<r.length;o++)t["$"+r[o]]=!0;for(r=0;r<e.length;r++)o=t.hasOwnProperty("$"+e[r].value),e[r].selected!==o&&(e[r].selected=o),o&&n&&(e[r].defaultSelected=!0)}else{for(r=""+Ca(r),t=null,o=0;o<e.length;o++){if(e[o].value===r){e[o].selected=!0,n&&(e[o].defaultSelected=!0);return}t!==null||e[o].disabled||(t=e[o])}t!==null&&(t.selected=!0)}}function e0(e,t){if(t.dan Line truncated
`).replace(iM,"")}function uc(e,t,r){if(t=Jy(t),Jy(e)!==t&&r)throw Error(Re(425))}function md(){}var p0=null,h0=null;function g0(e,t){return e==="textarea"||e==="noscript"||typeof t.children=="string"||typeof t.children=="number"||typeof t.dangerouslySetInnerHTML=="object"&&t.dangerouslySetInnerHTML!==null&&t.dangerouslySetInnerHTML.__html!=null}var m0=typeof setTimeout=="function"?setTimeout:void 0,sM=typeof clearTimeout=="function"?clearTimeout:void 0,ex=typeof Promise=="function"?Promise:void 0,lM=typeof queueMicrotask=="function"?queueMicrotask:typeof ex<"u"?function(e){return ex.resolve(null).then(e).catch(uM)}:m0;function uM(e){setTimeout(function(){throw e})}function rh(e,t){var r=t,n=0;do{var o=r.nextSibling;if(e.removeChild(r),o&&o.nodeType===8)if(r=o.data,r==="/$"){if(n===0){e.removeChild(o),Bl(t);return}n--}else r!=="$"&&r!=="$?"&&r!=="$!"||n++;r=o}while(r);Bl(t)}function ma(e){for(;e!=null;e=e.nextSibling){var t=e.nodeType;if(t===1||t===3)break;if(t===8){if(t=e.data,t==="$"||t==="$!"||t==="$?")break;if(t==="/$")return null}}return e}function tx(e){e=e.previousSibling;for(var t=0;e;){if(e.nodeType===8){var r=e.data;if(r==="$"||r==="$!"||r==="$?"){if(t===0)return e;t--}else r==="/$"&&t++}e=e.previousSibling}return null}var Os=Math.random().toString(36).slice(2),go="__reactFiber$"+Os,Hl="__reactProps$"+Os,zo="__reactContainer$"+Os,b0="__reactEvents$"+Os,cM="__reactListeners$"+Os,dM="__reactHandles$"+Os;function Ga(e){var t=e[go];if(t)return t;for(var r=e.parentNode;r;){if(t=r[zo]||r[go]){if(r=t.alternate,t.child!==null||r!==null&&r.child!==null)for(e=tx(e);e!==null;){if(r=e[go])return r;e=tx(e)}return t}e=r,r=e.parentNode}return null}function pu(e){return e=e[go]||e[zo],!e||e.tag!==5&&e.tag!==6&&e.tag!==13&&e.tag!==3?null:e}function Bi(e){if(e.tag===5||e.tag===6)return e.stateNode;throw Error(Re(33))}function pf(e){return e[Hl]||null}var v0=[],Vi=-1;function ka(e){return{current:e}}function qt(e){0>Vi||(e.current=v0[Vi],v0[Vi]=null,Vi--)}function Ut(e,t){Vi++,v0[Vi]=e.current,e.current=t}var Ta={},Rr=ka(Ta),Qr=ka(!1),ri=Ta;function ss(e,t){var r=e.type.contextTypes;if(!r)return Ta;var n=e.stateNode;if(n&&n.__reactInternalMemoizedUnmaskedChildContext===t)return n.__reactInternalMemoizedMaskedChildContext;var o={},a;for(a in r)o[a]=t[a];return n&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=o),o}function Xr(e){return e=e.childContextTypes,e!=null}function bd(){qt(Qr),qt(Rr)}function rx(e,t,r){if(Rr.current!==Ta)throw Error(Re(168));Ut(Rr,t),Ut(Qr,r)}function MS(e,t,r){var n=e.stateNode;if(t=t.childContextTypes,typeof n.getChildContext!="function")return r;n=n.getChildContext();for(var o in n)if(!(o in t))throw Error(Re(108,YA(e)||"Unknown",o));return rr({},r,n)}function vd(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||Ta,ri=Rr.current,Ut(Rr,e),Ut(Qr,Qr.current),!0}function nx(e,t,r){var n=e.stateNode;if(!n)throw Error(Re(169));r?(e=MS(e,t,ri),n.__reactInternalMemoizedMergedChildContext=e,qt(Qr),qt(Rr),Ut(Rr,e)):qt(Qr),Ut(Qr,r)}var Ao=null,hf=!1,nh=!1;function IS(e){Ao===null?Ao=[e]:Ao.push(e)}function fM(e){hf=!0,IS(e)}function Na(){if(!nh&&Ao!==null){nh=!0;var e=0,t=Dt;try{var r=Ao;for(Dt=1;e<r.length;e++){var n=r[e];do n=n(!0);while(n!==null)}Ao=null,hf=!1}catch(o){throw Ao!==null&&(Ao=Ao.slice(e+1)),iS(Dg,Na),o}finally{Dt=t,nh=!1}}return null}var Ui=[],Wi=0,yd=null,xd=0,_n=[],Sn=0,ni=null,Ro=1,Mo="";function Wa(e,t){Ui[Wi++]=xd,Ui[Wi++]=yd,yd=e,xd=t}function DS(e,t,r){_n[Sn++]=Ro,_n[Sn++]=Mo,_n[Sn++]=ni,ni=e;var n=Ro;e=Mo;var o=32-Kn(n)-1;n&=~(1<<o),r+=1;var a=32-Kn(t)+o;if(30<a){var i=o-o%5;a=(n&(1<<i)-1).toString(32),n>>=i,o-=i,Ro=1<<32-Kn(t)+o|r<<o|n,Mo=a+e}else Ro=1<<a|r<<o|n,Mo=e}function Hg(e){e.return!==null&&(Wa(e,1),DS(e,1,0))}function qg(e){for(;e===yd;)yd=Ui[--Wi],Ui[Wi]=null,xd=Ui[--Wi],Ui[Wi]=null;for(;e===ni;)ni=_n[--Sn],_n[Sn]=null,Mo=_n[--Sn],_n[Sn]=null,Ro=_n[--Sn],_n[Sn]=null}var cn=null,an=null,Qt=!1,$n=null;function LS(e,t){var r=Tn(5,null,null,0);r.elementType="DELETED",r.stateNode=t,r.return=e,t=e.deletions,t===null?(e.deletions=[r],e.flags|=16):t.push(r)}function ox(e,t){switch(e.tag){case 5:var r=e.type;return t=t.nodeType!==1||r.toLowerCase()!==t.nodeName.toLowerCase()?null:t,t!==null?(e.stateNode=t,cn=e,an=ma(t.firstChild),!0):!1;case 6:return t=e.pendingProps===""||t.nodeType!==3?null:t,t!==null?(e.stateNode=t,cn=e,an=null,!0):!1;case 13:return t=t.nodeType!==8?null:t,t!==null?(r=ni!==null?{id:Ro,overflow:Mo}:null,e.memoizedState={dehydrated:t,treeContext:r,retryLane:1073741824},r=Tn(18,null,null,0),r.stateNode=t,r.return=e,e.child=r,cn=e,an=null,!0):!1;default:return!1}}function y0(e){return(e.mode&1)!==0&&(e.flags&128)===0}function x0(e){if(Qt){var t=an;if(t){var r=t;if(!ox(e,t)){if(y0(e))throw Error(Re(418));t=ma(r.nextSibling);var n=cn;t&&ox(e,t)?LS(n,r):(e.flags=e.flags&-4097|2,Qt=!1,cn=e)}}else{if(y0(e))throw Error(Re(418));e.flags=e.flags&-4097|2,Qt=!1,cn=e}}}function ax(e){ Line truncated
Error generating stack: `+a.message+`
`+a.stack}return{value:e,source:t,stack:o,digest:null}}function lh(e,t,r){return{value:e,source:null,stack:r??null,digest:t??null}}function C0(e,t){try{console.error(t.value)}catch(r){setTimeout(function(){throw r})}}var wM=typeof WeakMap=="function"?WeakMap:Map;function d5(e,t,r){r=Io(-1,r),r.tag=3,r.payload={element:null};var n=t.value;return r.callback=function(){kd||(kd=!0,M0=n),C0(e,t)},r}function f5(e,t,r){r=Io(-1,r),r.tag=3;var n=e.type.getDerivedStateFromError;if(typeof n=="function"){var o=t.value;r.payload=function(){return n(o)},r.callback=function(){C0(e,t)}}var a=e.stateNode;return a!==null&&typeof a.componentDidCatch=="function"&&(r.callback=function(){C0(e,t),typeof n!="function"&&(va===null?va=new Set([this]):va.add(this));var i=t.stack;this.componentDidCatch(t.value,{componentStack:i!==null?i:""})}),r}function px(e,t,r){var n=e.pingCache;if(n===null){n=e.pingCache=new wM;var o=new Set;n.set(t,o)}else o=n.get(t),o===void 0&&(o=new Set,n.set(t,o));o.has(r)||(o.add(r),e=IM.bind(null,e,t,r),t.then(e,e))}function hx(e){do{var t;if((t=e.tag===13)&&(t=e.memoizedState,t=t!==null?t.dehydrated!==null:!0),t)return e;e=e.return}while(e!==null);return null}function gx(e,t,r,n,o){return e.mode&1?(e.flags|=65536,e.lanes=o,e):(e===t?e.flags|=65536:(e.flags|=128,r.flags|=131072,r.flags&=-52805,r.tag===1&&(r.alternate===null?r.tag=17:(t=Io(-1,1),t.tag=2,ba(r,t,1))),r.lanes|=1),e)}var _M=$o.ReactCurrentOwner,Kr=!1;function Fr(e,t,r,n){t.child=e===null?$S(t,null,r,n):us(t,e.child,r,n)}function mx(e,t,r,n,o){r=r.render;var a=t.ref;return Ji(t,o),n=nm(e,t,r,n,a,o),r=om(),e!==null&&!Kr?(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~o,Vo(e,t,o)):(Qt&&r&&Hg(t),t.flags|=1,Fr(e,t,n,o),t.child)}function bx(e,t,r,n,o){if(e===null){var a=r.type;return typeof a=="function"&&!hm(a)&&a.defaultProps===void 0&&r.compare===null&&r.defaultProps===void 0?(t.tag=15,t.type=a,p5(e,t,a,n,o)):(e=Uc(r.type,null,n,t,t.mode,o),e.ref=t.ref,e.return=t,t.child=e)}if(a=e.child,!(e.lanes&o)){var i=a.memoizedProps;if(r=r.compare,r=r!==null?r:Ul,r(i,n)&&e.ref===t.ref)return Vo(e,t,o)}return t.flags|=1,e=xa(a,n),e.ref=t.ref,e.return=t,t.child=e}function p5(e,t,r,n,o){if(e!==null){var a=e.memoizedProps;if(Ul(a,n)&&e.ref===t.ref)if(Kr=!1,t.pendingProps=n=a,(e.lanes&o)!==0)e.flags&131072&&(Kr=!0);else return t.lanes=e.lanes,Vo(e,t,o)}return T0(e,t,r,n,o)}function h5(e,t,r){var n=t.pendingProps,o=n.children,a=e!==null?e.memoizedState:null;if(n.mode==="hidden")if(!(t.mode&1))t.memoizedState={baseLanes:0,cachePool:null,transitions:null},Ut(qi,on),on|=r;else{if(!(r&1073741824))return e=a!==null?a.baseLanes|r:r,t.lanes=t.childLanes=1073741824,t.memoizedState={baseLanes:e,cachePool:null,transitions:null},t.updateQueue=null,Ut(qi,on),on|=e,null;t.memoizedState={baseLanes:0,cachePool:null,transitions:null},n=a!==null?a.baseLanes:r,Ut(qi,on),on|=n}else a!==null?(n=a.baseLanes|r,t.memoizedState=null):n=r,Ut(qi,on),on|=n;return Fr(e,t,o,r),t.child}function g5(e,t){var r=t.ref;(e===null&&r!==null||e!==null&&e.ref!==r)&&(t.flags|=512,t.flags|=2097152)}function T0(e,t,r,n,o){var a=Xr(r)?ri:Rr.current;return a=ss(t,a),Ji(t,o),r=nm(e,t,r,n,a,o),n=om(),e!==null&&!Kr?(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~o,Vo(e,t,o)):(Qt&&n&&Hg(t),t.flags|=1,Fr(e,t,r,o),t.child)}function vx(e,t,r,n,o){if(Xr(r)){var a=!0;vd(t)}else a=!1;if(Ji(t,o),t.stateNode===null)zc(e,t),US(t,r,n),S0(t,r,n,o),n=!0;else if(e===null){var i=t.stateNode,c=t.memoizedProps;i.props=c;var f=i.context,g=r.contextType;typeof g=="object"&&g!==null?g=Nn(g):(g=Xr(r)?ri:Rr.current,g=ss(t,g));var b=r.getDerivedStateFromProps,S=typeof b=="function"||typeof i.getSnapshotBeforeUpdate=="function";S||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(c!==n||f!==g)&&ux(t,i,n,g),ia=!1;var O=t.memoizedState;i.state=O,Sd(t,n,i,o),f=t.memoizedState,c!==n||O!==f||Qr.current||ia?(typeof b=="function"&&(_0(t,r,b,n),f=t.memoizedState),(c=ia||lx(t,r,c,n,O,f,g))?(S||typeof i.UNSAFE_componentWillMount!="function"&&typeof i.componentWillMount!="function"||(typeof i.componentWillMount=="function"&&i.componentWillMount(),typeof i.UNSAFE_componentWillMount=="function"&&i.UNSAFE_componentWillMount()),typeof i.componentDidMount=="function"&&(t.flags|=4194308)):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),t.memoizedProps=n,t.memoizedState=f),i.props=n,i.state=f,i.context=g,n=c):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),n=!1)}else{i=t.stateNode,BS(e,t),c=t.memoizedProps,g=t.type===t.elementType?c:Vn(t.type,c),i.props=g,S=t.pendingProps,O=i.context,f=r.contextType,typeof f=="object"&&f!==null?f=Nn(f):(f=Xr(r)?ri:Rr.current,f=ss(t,f));var T=r.getDerivedStateFromProps;(b=typeof T=="function"||typeof i.getSnapshotBeforeUpdate=="function")||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(c!==S||O!==f)&&ux(t,i,n,f),ia=!1,O=t.memoizedState,i.state=O,Sd(t,n,i,o);var C=t.memoizedS Line truncated
* @remix-run/router v1.10.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/function Zl(){return Zl=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},Zl.apply(this,arguments)}var fa;(function(e){e.Pop="POP",e.Push="PUSH",e.Replace="REPLACE"})(fa||(fa={}));const Ax="popstate";function qM(e){e===void 0&&(e={});function t(n,o){let{pathname:a,search:i,hash:c}=n.location;return F0("",{pathname:a,search:i,hash:c},o.state&&o.state.usr||null,o.state&&o.state.key||"default")}function r(n,o){return typeof o=="string"?o:Ad(o)}return KM(t,r,null,e)}function tr(e,t){if(e===!1||e===null||typeof e>"u")throw new Error(t)}function ym(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function GM(){return Math.random().toString(36).substr(2,8)}function Rx(e,t){return{usr:e.state,key:e.key,idx:t}}function F0(e,t,r,n){return r===void 0&&(r=null),Zl({pathname:typeof e=="string"?e:e.pathname,search:"",hash:""},typeof t=="string"?Ps(t):t,{state:r,key:t&&t.key||n||GM()})}function Ad(e){let{pathname:t="/",search:r="",hash:n=""}=e;return r&&r!=="?"&&(t+=r.charAt(0)==="?"?r:"?"+r),n&&n!=="#"&&(t+=n.charAt(0)==="#"?n:"#"+n),t}function Ps(e){let t={};if(e){let r=e.indexOf("#");r>=0&&(t.hash=e.substr(r),e=e.substr(0,r));let n=e.indexOf("?");n>=0&&(t.search=e.substr(n),e=e.substr(0,n)),e&&(t.pathname=e)}return t}function KM(e,t,r,n){n===void 0&&(n={});let{window:o=document.defaultView,v5Compat:a=!1}=n,i=o.history,c=fa.Pop,f=null,g=b();g==null&&(g=0,i.replaceState(Zl({},i.state,{idx:g}),""));function b(){return(i.state||{idx:null}).idx}function S(){c=fa.Pop;let m=b(),d=m==null?null:m-g;g=m,f&&f({action:c,location:x.location,delta:d})}function O(m,d){c=fa.Push;let s=F0(x.location,m,d);r&&r(s,m),g=b()+1;let u=Rx(s,g),l=x.createHref(s);try{i.pushState(u,"",l)}catch(h){if(h instanceof DOMException&&h.name==="DataCloneError")throw h;o.location.assign(l)}a&&f&&f({action:c,location:x.location,delta:1})}function T(m,d){c=fa.Replace;let s=F0(x.location,m,d);r&&r(s,m),g=b();let u=Rx(s,g),l=x.createHref(s);i.replaceState(u,"",l),a&&f&&f({action:c,location:x.location,delta:0})}function C(m){let d=o.location.origin!=="null"?o.location.origin:o.location.href,s=typeof m=="string"?m:Ad(m);return tr(d,"No window.location.(origin|href) available to create URL for href: "+s),new URL(s,d)}let x={get action(){return c},get location(){return e(o,i)},listen(m){if(f)throw new Error("A history only accepts one active listener");return o.addEventListener(Ax,S),f=m,()=>{o.removeEventListener(Ax,S),f=null}},createHref(m){return t(o,m)},createURL:C,encodeLocation(m){let d=C(m);return{pathname:d.pathname,search:d.search,hash:d.hash}},push:O,replace:T,go(m){return i.go(m)}};return x}var Mx;(function(e){e.data="data",e.deferred="deferred",e.redirect="redirect",e.error="error"})(Mx||(Mx={}));function YM(e,t,r){r===void 0&&(r="/");let n=typeof t=="string"?Ps(t):t,o=Jl(n.pathname||"/",r);if(o==null)return null;let a=z5(e);QM(a);let i=null;for(let c=0;i==null&&c<a.length;++c)i=aI(a[c],sI(o));return i}function z5(e,t,r,n){t===void 0&&(t=[]),r===void 0&&(r=[]),n===void 0&&(n="");let o=(a,i,c)=>{let f={relativePath:c===void 0?a.path||"":c,caseSensitive:a.caseSensitive===!0,childrenIndex:i,route:a};f.relativePath.startsWith("/")&&(tr(f.relativePath.startsWith(n),'Absolute route path "'+f.relativePath+'" nested under path '+('"'+n+'" is not valid. An absolute child route path ')+"must start with the combined path of all its parent routes."),f.relativePath=f.relativePath.slice(n.length));let g=wa([n,f.relativePath]),b=r.concat(f);a.children&&a.children.length>0&&(tr(a.index!==!0,"Index routes must not have child routes. Please remove "+('all child routes from route path "'+g+'".')),z5(a.children,t,b,g)),!(a.path==null&&!a.index)&&t.push({path:g,score:nI(g,a.index),routesMeta:b})};return e.forEach((a,i)=>{var c;if(a.path===""||!((c=a.path)!=null&&c.includes("?")))o(a,i);else for(let f of B5(a.path))o(a,i,f)}),t}function B5(e){let t=e.split("/");if(t.length===0)return[];let[r,...n]=t,o=r.endsWith("?"),a=r.replace(/\?$/,"");if(n.length===0)return o?[a,""]:[a];let i=B5(n.join("/")),c=[];return c.push(...i.map(f=>f===""?a:[a,f].join("/"))),o&&c.push(...i),c.map(f=>e.startsWith("/")&&f===""?"/":f)}function QM(e){e.sort((t,r)=>t.score!==r.score?r.score-t.score:oI(t.routesMeta.map(n=>n.childrenIndex),r.routesMeta.map(n=>n.childrenIndex)))}const XM=/^:\w+$/,ZM=3,JM=2,eI=1,tI=10,rI=-2,Ix=e=>e==="*";function nI(e,t){let r=e.split("/"),n=r.length;return r.some(Ix)&&(n+=rI),t&&(n+=JM),r.filter(o=>!Ix(o)).reduce((o,a)=>o+(XM.test(a)?ZM:a===""?eI:tI),n)}function oI(e,t){return e.length===t.length&&e.slice(0,-1).every((n,o)=>n===t[o])?e[e.length-1]-t[t.length-1]:0}function aI(e,t){let{routesMeta:r}=e,n={},o="/",a=[];for(let i=0;i<r.length;++i){let c=r[i],f=i===r.length-1,g=o==="/"?t:t.slice(o.length)||"/",b=z0({path:c.relativePath,caseSensitive:c.caseSensitive,end:f},g);if(!b)return null;Object.assign(n,b.paLine truncated
* React Router v6.17.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/function Rd(){return Rd=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},Rd.apply(this,arguments)}const Cf=P.createContext(null),U5=P.createContext(null),pi=P.createContext(null),Tf=P.createContext(null),Ho=P.createContext({outlet:null,matches:[],isDataRoute:!1}),W5=P.createContext(null);function mI(e,t){let{relative:r}=t===void 0?{}:t;ks()||tr(!1);let{basename:n,navigator:o}=P.useContext(pi),{hash:a,pathname:i,search:c}=Of(e,{relative:r}),f=i;return n!=="/"&&(f=i==="/"?n:wa([n,i])),o.createHref({pathname:f,search:c,hash:a})}function ks(){return P.useContext(Tf)!=null}function Ea(){return ks()||tr(!1),P.useContext(Tf).location}function $5(e){P.useContext(pi).static||P.useLayoutEffect(e)}function So(){let{isDataRoute:e}=P.useContext(Ho);return e?NI():bI()}function bI(){ks()||tr(!1);let e=P.useContext(Cf),{basename:t,navigator:r}=P.useContext(pi),{matches:n}=P.useContext(Ho),{pathname:o}=Ea(),a=JSON.stringify(xm(n).map(f=>f.pathnameBase)),i=P.useRef(!1);return $5(()=>{i.current=!0}),P.useCallback(function(f,g){if(g===void 0&&(g={}),!i.current)return;if(typeof f=="number"){r.go(f);return}let b=wm(f,JSON.parse(a),o,g.relative==="path");e==null&&t!=="/"&&(b.pathname=b.pathname==="/"?t:wa([t,b.pathname])),(g.replace?r.replace:r.push)(b,g.state,g)},[t,r,a,o,e])}function H5(){let{matches:e}=P.useContext(Ho),t=e[e.length-1];return t?t.params:{}}function Of(e,t){let{relative:r}=t===void 0?{}:t,{matches:n}=P.useContext(Ho),{pathname:o}=Ea(),a=JSON.stringify(xm(n).map(i=>i.pathnameBase));return P.useMemo(()=>wm(e,JSON.parse(a),o,r==="path"),[e,a,o,r])}function vI(e,t){return yI(e,t)}function yI(e,t,r){ks()||tr(!1);let{navigator:n}=P.useContext(pi),{matches:o}=P.useContext(Ho),a=o[o.length-1],i=a?a.params:{};a&&a.pathname;let c=a?a.pathnameBase:"/";a&&a.route;let f=Ea(),g;if(t){var b;let x=typeof t=="string"?Ps(t):t;c==="/"||(b=x.pathname)!=null&&b.startsWith(c)||tr(!1),g=x}else g=f;let S=g.pathname||"/",O=c==="/"?S:S.slice(c.length)||"/",T=YM(e,{pathname:O}),C=CI(T&&T.map(x=>Object.assign({},x,{params:Object.assign({},i,x.params),pathname:wa([c,n.encodeLocation?n.encodeLocation(x.pathname).pathname:x.pathname]),pathnameBase:x.pathnameBase==="/"?c:wa([c,n.encodeLocation?n.encodeLocation(x.pathnameBase).pathname:x.pathnameBase])})),o,r);return t&&C?P.createElement(Tf.Provider,{value:{location:Rd({pathname:"/",search:"",hash:"",state:null,key:"default"},g),navigationType:fa.Pop}},C):C}function xI(){let e=kI(),t=hI(e)?e.status+" "+e.statusText:e instanceof Error?e.message:JSON.stringify(e),r=e instanceof Error?e.stack:null,o={padding:"0.5rem",backgroundColor:"rgba(200,200,200, 0.5)"},a=null;return P.createElement(P.Fragment,null,P.createElement("h2",null,"Unexpected Application Error!"),P.createElement("h3",{style:{fontStyle:"italic"}},t),r?P.createElement("pre",{style:o},r):null,a)}const wI=P.createElement(xI,null);class _I extends P.Component{constructor(t){super(t),this.state={location:t.location,revalidation:t.revalidation,error:t.error}}static getDerivedStateFromError(t){return{error:t}}static getDerivedStateFromProps(t,r){return r.location!==t.location||r.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error||r.error,location:r.location,revalidation:t.revalidation||r.revalidation}}componentDidCatch(t,r){console.error("React Router caught the following error during render",t,r)}render(){return this.state.error?P.createElement(Ho.Provider,{value:this.props.routeContext},P.createElement(W5.Provider,{value:this.state.error,children:this.props.component})):this.props.children}}function SI(e){let{routeContext:t,match:r,children:n}=e,o=P.useContext(Cf);return o&&o.static&&o.staticContext&&(r.route.errorElement||r.route.ErrorBoundary)&&(o.staticContext._deepestRenderedBoundaryId=r.route.id),P.createElement(Ho.Provider,{value:t},n)}function CI(e,t,r){var n;if(t===void 0&&(t=[]),r===void 0&&(r=null),e==null){var o;if((o=r)!=null&&o.errors)e=r.matches;else return null}let a=e,i=(n=r)==null?void 0:n.errors;if(i!=null){let c=a.findIndex(f=>f.route.id&&(i==null?void 0:i[f.route.id]));c>=0||tr(!1),a=a.slice(0,Math.min(a.length,c+1))}return a.reduceRight((c,f,g)=>{let b=f.route.id?i==null?void 0:i[f.route.id]:null,S=null;r&&(S=f.route.errorElement||wI);let O=t.concat(a.slice(0,g+1)),T=()=>{let C;return b?C=S:f.route.Component?C=P.createElement(f.route.Component,null):f.route.element?C=f.route.element:C=c,P.createElement(SI,{match:f,routeContext:{outlet:c,matches:O,isDataRoute:r!=null},children:C})};return r&&(f.route.ErrorBoundary||f.route.errorElement||g===0)?P.createElement(_I,{location:r.location,revalidation:r.revalidation,component:S,error:b,children:T(),routeContext:{outlet:null,matches:O,isDataRoute:!0}}):T()},null)}var q5=function(e){return e.UseBlocker="useBlocker",e.UseRevalidator="useRevalidator",e.UseNavigateStable=Line truncated
* React Router DOM v6.17.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/function Id(){return Id=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},Id.apply(this,arguments)}function K5(e,t){if(e==null)return{};var r={},n=Object.keys(e),o,a;for(a=0;a<n.length;a++)o=n[a],!(t.indexOf(o)>=0)&&(r[o]=e[o]);return r}function AI(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function RI(e,t){return e.button===0&&(!t||t==="_self")&&!AI(e)}const MI=["onClick","relative","reloadDocument","replace","state","target","to","preventScrollReset","unstable_viewTransition"],II=["aria-current","caseSensitive","className","end","style","to","unstable_viewTransition","children"],DI=P.createContext({isTransitioning:!1}),LI="startTransition",Dx=wg[LI];function FI(e){let{basename:t,children:r,future:n,window:o}=e,a=P.useRef();a.current==null&&(a.current=qM({window:o,v5Compat:!0}));let i=a.current,[c,f]=P.useState({action:i.action,location:i.location}),{v7_startTransition:g}=n||{},b=P.useCallback(S=>{g&&Dx?Dx(()=>f(S)):f(S)},[f,g]);return P.useLayoutEffect(()=>i.listen(b),[i,b]),P.createElement(jI,{basename:t,children:r,location:c.location,navigationType:c.action,navigator:i})}const zI=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",BI=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Y5=P.forwardRef(function(t,r){let{onClick:n,relative:o,reloadDocument:a,replace:i,state:c,target:f,to:g,preventScrollReset:b,unstable_viewTransition:S}=t,O=K5(t,MI),{basename:T}=P.useContext(pi),C,x=!1;if(typeof g=="string"&&BI.test(g)&&(C=g,zI))try{let u=new URL(window.location.href),l=g.startsWith("//")?new URL(u.protocol+g):new URL(g),h=Jl(l.pathname,T);l.origin===u.origin&&h!=null?g=h+l.search+l.hash:x=!0}catch{}let m=mI(g,{relative:o}),d=WI(g,{replace:i,state:c,target:f,preventScrollReset:b,relative:o,unstable_viewTransition:S});function s(u){n&&n(u),u.defaultPrevented||d(u)}return P.createElement("a",Id({},O,{href:C||m,onClick:x||a?n:s,ref:r,target:f}))}),VI=P.forwardRef(function(t,r){let{"aria-current":n="page",caseSensitive:o=!1,className:a="",end:i=!1,style:c,to:f,unstable_viewTransition:g,children:b}=t,S=K5(t,II),O=Of(f,{relative:S.relative}),T=Ea(),C=P.useContext(U5),{navigator:x}=P.useContext(pi),m=C!=null&&$I(O)&&g===!0,d=x.encodeLocation?x.encodeLocation(O).pathname:O.pathname,s=T.pathname,u=C&&C.navigation&&C.navigation.location?C.navigation.location.pathname:null;o||(s=s.toLowerCase(),u=u?u.toLowerCase():null,d=d.toLowerCase());let l=s===d||!i&&s.startsWith(d)&&s.charAt(d.length)==="/",h=u!=null&&(u===d||!i&&u.startsWith(d)&&u.charAt(d.length)==="/"),v={isActive:l,isPending:h,isTransitioning:m},w=l?n:void 0,j;typeof a=="function"?j=a(v):j=[a,l?"active":null,h?"pending":null,m?"transitioning":null].filter(Boolean).join(" ");let N=typeof c=="function"?c(v):c;return P.createElement(Y5,Id({},S,{"aria-current":w,className:j,ref:r,style:N,to:f,unstable_viewTransition:g}),typeof b=="function"?b(v):b)});var V0;(function(e){e.UseScrollRestoration="useScrollRestoration",e.UseSubmit="useSubmit",e.UseSubmitFetcher="useSubmitFetcher",e.UseFetcher="useFetcher",e.useViewTransitionState="useViewTransitionState"})(V0||(V0={}));var Lx;(function(e){e.UseFetchers="useFetchers",e.UseScrollRestoration="useScrollRestoration"})(Lx||(Lx={}));function UI(e){let t=P.useContext(Cf);return t||tr(!1),t}function WI(e,t){let{target:r,replace:n,state:o,preventScrollReset:a,relative:i,unstable_viewTransition:c}=t===void 0?{}:t,f=So(),g=Ea(),b=Of(e,{relative:i});return P.useCallback(S=>{if(RI(S,r)){S.preventDefault();let O=n!==void 0?n:Ad(g)===Ad(b);f(e,{replace:O,state:o,preventScrollReset:a,relative:i,unstable_viewTransition:c})}},[g,f,b,n,o,r,e,a,i,c])}function $I(e,t){t===void 0&&(t={});let r=P.useContext(DI);r==null&&tr(!1);let{basename:n}=UI(V0.useViewTransitionState),o=Of(e,{relative:t.relative});if(!r.isTransitioning)return!1;let a=Jl(r.currentLocation.pathname,n)||r.currentLocation.pathname,i=Jl(r.nextLocation.pathname,n)||r.nextLocation.pathname;return z0(o.pathname,i)!=null||z0(o.pathname,a)!=null}function HI({title:e,titleId:t,...r},n){return P.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":"true",ref:n,"aria-labelledby":t},r),e?P.createElement("title",{id:t},e):null,P.createElement("path",{fillRule:"evenodd",d:"M7.5 3.75A1.5 1.5 0 006 5.25v13.5a1.5 1.5 0 001.5 1.5h6a1.5 1.5 0 001.5-1.5V15a.75.75 0 011.5 0v3.75a3 3 0 01-3 3h-6a3 3 0 01-3-3V5.25a3 3 0 013-3h6a3 3 0 013 3V9A.75.75 0 0115 9V5.25a1.5 1.5 0 00-1.5-1.5h-6zm5.03 4.72a.75.75 0 010 1.06l-1.72 1.72h10.94a.75.75 0 010 1.5H10.81l1.72 1.72a.75.75 0 11-1.06 1.06l-3-3a.75.75 0 010-1.06l3-3a.75.75 0 011.06 0z",clipRule:"evenodd"}))}const qI=P.forwardRef(HI),GI=qI;function KI({title:e,titleId:t,...r},n){return P.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":"true",rLine truncated
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/(function(e){(function(){var t={}.hasOwnProperty;function r(){for(var n=[],o=0;o<arguments.length;o++){var a=arguments[o];if(a){var i=typeof a;if(i==="string"||i==="number")n.push(a);else if(Array.isArray(a)){if(a.length){var c=r.apply(null,a);c&&n.push(c)}}else if(i==="object"){if(a.toString!==Object.prototype.toString&&!a.toString.toString().includes("[native code]")){n.push(a.toString());continue}for(var f in a)t.call(a,f)&&a[f]&&n.push(f)}}}return n.join(" ")}e.exports?(r.default=r,e.exports=r):window.classNames=r})()})(Z5);var ct=Z5.exports,J5={exports:{}},Bn={};function Sm(){for(var e,t,r=0,n="";r<arguments.length;)(e=arguments[r++])&&(t=e1(e))&&(n&&(n+=" "),n+=t);return n}function e1(e){if(typeof e=="string")return e;for(var t,r="",n=0;n<e.length;n++)e[n]&&(t=e1(e[n]))&&(r&&(r+=" "),r+=t);return r}function U0(){return U0=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},U0.apply(this,arguments)}function S4(e){var t=function(r){var n=r.theme,o=r.prefix,a={nextPart:new Map,validators:[]};return function(i,c){return c?i.map(function(f){return[f[0],f[1].map(function(g){return typeof g=="string"?c+g:typeof g=="object"?Object.fromEntries(Object.entries(g).map(function(b){return[c+b[0],b[1]]})):g})]}):i}(Object.entries(r.classGroups),o).forEach(function(i){W0(i[1],a,i[0],n)}),a}(e);return{getClassGroupId:function(r){var n=r.split("-");return n[0]===""&&n.length!==1&&n.shift(),t1(n,t)||function(o){if(Fx.test(o)){var a=Fx.exec(o)[1],i=a==null?void 0:a.substring(0,a.indexOf(":"));if(i)return"arbitrary.."+i}}(r)},getConflictingClassGroupIds:function(r){return e.conflictingClassGroups[r]||[]}}}function t1(e,t){var r;if(e.length===0)return t.classGroupId;var n=t.nextPart.get(e[0]),o=n?t1(e.slice(1),n):void 0;if(o)return o;if(t.validators.length!==0){var a=e.join("-");return(r=t.validators.find(function(i){return(0,i.validator)(a)}))==null?void 0:r.classGroupId}}Object.defineProperty(Bn,"__esModule",{value:!0});var Fx=/^\[(.+)\]$/;function W0(e,t,r,n){e.forEach(function(o){if(typeof o!="string"){if(typeof o=="function")return o.isThemeGetter?void W0(o(n),t,r,n):void t.validators.push({validator:o,classGroupId:r});Object.entries(o).forEach(function(a){W0(a[1],zx(t,a[0]),r,n)})}else(o===""?t:zx(t,o)).classGroupId=r})}function zx(e,t){var r=e;return t.split("-").forEach(function(n){r.nextPart.has(n)||r.nextPart.set(n,{nextPart:new Map,validators:[]}),r=r.nextPart.get(n)}),r}function C4(e){if(e<1)return{get:function(){},set:function(){}};var t=0,r=new Map,n=new Map;function o(a,i){r.set(a,i),++t>e&&(t=0,n=r,r=new Map)}return{get:function(a){var i=r.get(a);return i!==void 0?i:(i=n.get(a))!==void 0?(o(a,i),i):void 0},set:function(a,i){r.has(a)?r.set(a,i):o(a,i)}}}function T4(e){var t=e.separator||":";return function(r){for(var n=0,o=[],a=0,i=0;i<r.length;i++){var c=r[i];n===0&&c===t[0]&&(t.length!==1&&r.slice(i,i+t.length)!==t||(o.push(r.slice(a,i)),a=i+t.length)),c==="["?n++:c==="]"&&n--}var f=o.length===0?r:r.substring(a),g=f.startsWith("!");return{modifiers:o,hasImportantModifier:g,baseClassName:g?f.substring(1):f}}}function O4(e){return U0({cache:C4(e.cacheSize),splitModifiers:T4(e)},S4(e))}var P4=/\s+/;function k4(e,t){var r=t.splitModifiers,n=t.getClassGroupId,o=t.getConflictingClassGroupIds,a=new Set;return e.trim().split(P4).map(function(i){var c=r(i),f=c.modifiers,g=c.hasImportantModifier,b=n(c.baseClassName);if(!b)return{isTailwindClass:!1,originalClassName:i};var S=function(O){if(O.length<=1)return O;var T=[],C=[];return O.forEach(function(x){x[0]==="["?(T.push.apply(T,C.sort().concat([x])),C=[]):C.push(x)}),T.push.apply(T,C.sort()),T}(f).join(":");return{isTailwindClass:!0,modifierId:g?S+"!":S,classGroupId:b,originalClassName:i}}).reverse().filter(function(i){if(!i.isTailwindClass)return!0;var c=i.modifierId,f=i.classGroupId,g=c+f;return!a.has(g)&&(a.add(g),o(f).forEach(function(b){return a.add(c+b)}),!0)}).reverse().map(function(i){return i.originalClassName}).join(" ")}function $0(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];var n,o,a,i=c;function c(g){var b=t[0],S=t.slice(1).reduce(function(O,T){return T(O)},b());return n=O4(S),o=n.cache.get,a=n.cache.set,i=f,f(g)}function f(g){var b=o(g);if(b)return b;var S=k4(g,n);return a(g,S),S}return function(){return i(Sm.apply(null,arguments))}}function Ht(e){var t=function(r){return r[e]||[]};return t.isThemeGetter=!0,t}var ja=/^\[(.+)\]$/,N4=/^\d+\/\d+$/,E4=new Set(["px","full","screen"]),j4=/^(\d+)?(xs|sm|md|lg|xl)$/,A4=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh)/,R4=/^-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/;function xn(e){return!Number.isNaN(Number(e))||E4.has(e)||N4.test(e)||sa(e)}function sa(e){var t,r=(t=ja.exec(e))==null?void 0:t[1];return!!r&&(r.startsWith("length:")||A4.test(r))}function r1(e){var t,r=(t=ja.exec(e))==null?void 0:t[1];return!!r&&r.startsWLine truncated
* tabbable 6.2.0
* @license MIT, https://github.com/focus-trap/tabbable/blob/master/LICENSE
*/var SF=["input:not([inert])","select:not([inert])","textarea:not([inert])","a[href]:not([inert])","button:not([inert])","[tabindex]:not(slot):not([inert])","audio[controls]:not([inert])","video[controls]:not([inert])",'[contenteditable]:not([contenteditable="false"]):not([inert])',"details>summary:first-of-type:not([inert])","details:not([inert])"],ag=SF.join(","),uP=typeof Element>"u",au=uP?function(){}:Element.prototype.matches||Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector,Hd=!uP&&Element.prototype.getRootNode?function(e){var t;return e==null||(t=e.getRootNode)===null||t===void 0?void 0:t.call(e)}:function(e){return e==null?void 0:e.ownerDocument},qd=function e(t,r){var n;r===void 0&&(r=!0);var o=t==null||(n=t.getAttribute)===null||n===void 0?void 0:n.call(t,"inert"),a=o===""||o==="true",i=a||r&&t&&e(t.parentNode);return i},CF=function(t){var r,n=t==null||(r=t.getAttribute)===null||r===void 0?void 0:r.call(t,"contenteditable");return n===""||n==="true"},TF=function(t,r,n){if(qd(t))return[];var o=Array.prototype.slice.apply(t.querySelectorAll(ag));return r&&au.call(t,ag)&&o.unshift(t),o=o.filter(n),o},OF=function e(t,r,n){for(var o=[],a=Array.from(t);a.length;){var i=a.shift();if(!qd(i,!1))if(i.tagName==="SLOT"){var c=i.assignedElements(),f=c.length?c:i.children,g=e(f,!0,n);n.flatten?o.push.apply(o,g):o.push({scopeParent:i,candidates:g})}else{var b=au.call(i,ag);b&&n.filter(i)&&(r||!t.includes(i))&&o.push(i);var S=i.shadowRoot||typeof n.getShadowRoot=="function"&&n.getShadowRoot(i),O=!qd(S,!1)&&(!n.shadowRootFilter||n.shadowRootFilter(i));if(S&&O){var T=e(S===!0?i.children:S.children,!0,n);n.flatten?o.push.apply(o,T):o.push({scopeParent:i,candidates:T})}else a.unshift.apply(a,i.children)}}return o},cP=function(t){return!isNaN(parseInt(t.getAttribute("tabindex"),10))},dP=function(t){if(!t)throw new Error("No node provided");return t.tabIndex<0&&(/^(AUDIO|VIDEO|DETAILS)$/.test(t.tagName)||CF(t))&&!cP(t)?0:t.tabIndex},PF=function(t,r){var n=dP(t);return n<0&&r&&!cP(t)?0:n},kF=function(t,r){return t.tabIndex===r.tabIndex?t.documentOrder-r.documentOrder:t.tabIndex-r.tabIndex},fP=function(t){return t.tagName==="INPUT"},NF=function(t){return fP(t)&&t.type==="hidden"},EF=function(t){var r=t.tagName==="DETAILS"&&Array.prototype.slice.apply(t.children).some(function(n){return n.tagName==="SUMMARY"});return r},jF=function(t,r){for(var n=0;n<t.length;n++)if(t[n].checked&&t[n].form===r)return t[n]},AF=function(t){if(!t.name)return!0;var r=t.form||Hd(t),n=function(c){return r.querySelectorAll('input[type="radio"][name="'+c+'"]')},o;if(typeof window<"u"&&typeof window.CSS<"u"&&typeof window.CSS.escape=="function")o=n(window.CSS.escape(t.name));else try{o=n(t.name)}catch(i){return console.error("Looks like you have a radio button with a name attribute containing invalid CSS selector characters and need the CSS.escape polyfill: %s",i.message),!1}var a=jF(o,t.form);return!a||a===t},RF=function(t){return fP(t)&&t.type==="radio"},MF=function(t){return RF(t)&&!AF(t)},IF=function(t){var r,n=t&&Hd(t),o=(r=n)===null||r===void 0?void 0:r.host,a=!1;if(n&&n!==t){var i,c,f;for(a=!!((i=o)!==null&&i!==void 0&&(c=i.ownerDocument)!==null&&c!==void 0&&c.contains(o)||t!=null&&(f=t.ownerDocument)!==null&&f!==void 0&&f.contains(t));!a&&o;){var g,b,S;n=Hd(o),o=(g=n)===null||g===void 0?void 0:g.host,a=!!((b=o)!==null&&b!==void 0&&(S=b.ownerDocument)!==null&&S!==void 0&&S.contains(o))}}return a},pw=function(t){var r=t.getBoundingClientRect(),n=r.width,o=r.height;return n===0&&o===0},DF=function(t,r){var n=r.displayCheck,o=r.getShadowRoot;if(getComputedStyle(t).visibility==="hidden")return!0;var a=au.call(t,"details>summary:first-of-type"),i=a?t.parentElement:t;if(au.call(i,"details:not([open]) *"))return!0;if(!n||n==="full"||n==="legacy-full"){if(typeof o=="function"){for(var c=t;t;){var f=t.parentElement,g=Hd(t);if(f&&!f.shadowRoot&&o(f)===!0)return pw(t);t.assignedSlot?t=t.assignedSlot:!f&&g!==t.ownerDocument?t=g.host:t=f}t=c}if(IF(t))return!t.getClientRects().length;if(n!=="legacy-full")return!0}else if(n==="non-zero-area")return pw(t);return!1},LF=function(t){if(/^(INPUT|BUTTON|SELECT|TEXTAREA)$/.test(t.tagName))for(var r=t.parentElement;r;){if(r.tagName==="FIELDSET"&&r.disabled){for(var n=0;n<r.children.length;n++){var o=r.children.item(n);if(o.tagName==="LEGEND")return au.call(r,"fieldset[disabled] *")?!0:!o.contains(t)}return!0}r=r.parentElement}return!1},FF=function(t,r){return!(r.disabled||qd(r)||NF(r)||DF(r,t)||EF(r)||LF(r))},hw=function(t,r){return!(MF(r)||dP(r)<0||!FF(t,r))},zF=function(t){var r=parseInt(t.getAttribute("tabindex"),10);return!!(isNaN(r)||r>=0)},BF=function e(t){var r=[],n=[];return t.forEach(function(o,a){var i=!!o.scopeParent,c=i?o.scopeParent:o,f=PF(c,i),g=i?e(o.candidates):c;f===0?i?r.push.apply(r,g):r.push(c):n.push({documentOrder:a,tabIndex:f,item:o,isScope:i,content:g})}),n.sort(kF).reduce(function(o,a){return a.isScope?o.push.apply(o,a.content):o.push(a.content),o},[]).concatLine truncated
More details: https://www.material-tailwind.com/docs/react/collapse
`);var N=r.default.useRef(null),k=(0,g.useTheme)().navbar,A=k.styles,M=A.base.mobileNav;l=l??{},h=h??"";var D=(0,c.twMerge)((0,i.default)((0,f.default)(M)),h),L={unmount:{height:0,opacity:0,transition:{duration:.3,times:"[0.4, 0, 0.2, 1]"}},mount:{opacity:1,height:"".concat((j=N.current)===null||j===void 0?void 0:j.scrollHeight,"px"),transition:{duration:.3,times:"[0.4, 0, 0.2, 1]"}}},B=(0,a.default)(L,l),U=n.AnimatePresence,Y=(0,o.useMergeRefs)([s,N]);return r.default.createElement(n.LazyMotion,{features:n.domAnimation},r.default.createElement(U,null,r.default.createElement(n.m.div,S({},w,{ref:Y,className:D,initial:"unmount",exit:"unmount",animate:u?"mount":"unmount",variants:B}),v)))});x.displayName="MaterialTailwind.MobileNav",x.propTypes={open:b.propTypesOpen,animate:b.propTypesAnimate,className:b.propTypesClassName,children:b.propTypesChildren};var m=x})(tk);(function(e){Object.defineProperty(e,"__esModule",{value:!0});function t(s,u){for(var l in u)Object.defineProperty(s,l,{enumerable:!0,get:u[l]})}t(e,{Navbar:function(){return m},MobileNav:function(){return g.MobileNav},default:function(){return d}});var r=T(P),n=T(nt),o=T(ct),a=rt,i=T(Xt),c=T(et),f=Ze,g=tk,b=Pf;function S(s,u,l){return u in s?Object.defineProperty(s,u,{value:l,enumerable:!0,configurable:!0,writable:!0}):s[u]=l,s}function O(){return O=Object.assign||function(s){for(var u=1;u<arguments.length;u++){var l=arguments[u];for(var h in l)Object.prototype.hasOwnProperty.call(l,h)&&(s[h]=l[h])}return s},O.apply(this,arguments)}function T(s){return s&&s.__esModule?s:{default:s}}function C(s,u){if(s==null)return{};var l=x(s,u),h,v;if(Object.getOwnPropertySymbols){var w=Object.getOwnPropertySymbols(s);for(v=0;v<w.length;v++)h=w[v],!(u.indexOf(h)>=0)&&Object.prototype.propertyIsEnumerable.call(s,h)&&(l[h]=s[h])}return l}function x(s,u){if(s==null)return{};var l={},h=Object.keys(s),v,w;for(w=0;w<h.length;w++)v=h[w],!(u.indexOf(v)>=0)&&(l[v]=s[v]);return l}var m=r.default.forwardRef(function(s,u){var l=s.variant,h=s.color,v=s.shadow,w=s.blurred,j=s.fullWidth,N=s.className,k=s.children,A=C(s,["variant","color","shadow","blurred","fullWidth","className","children"]),M=(0,f.useTheme)().navbar,D=M.defaultProps,L=M.valid,B=M.styles,U=B.base,Y=B.variants;l=l??D.variant,h=h??D.color,v=v??D.shadow,w=w??D.blurred,j=j??D.fullWidth,N=N??D.className;var q,z=(0,o.default)((0,c.default)(U.navbar.initial),(q={},S(q,(0,c.default)(U.navbar.shadow),v),S(q,(0,c.default)(U.navbar.blurred),w&&h==="white"),S(q,(0,c.default)(U.navbar.fullWidth),j),q)),$=(0,o.default)((0,c.default)(Y[(0,i.default)(L.variants,l,"filled")][(0,i.default)(L.colors,h,"white")])),K=(0,a.twMerge)((0,o.default)(z,$),N);return r.default.createElement("nav",O({},A,{ref:u,className:K}),k)});m.propTypes={variant:n.default.oneOf(b.propTypesVariant),color:n.default.oneOf(b.propTypesColor),shadow:b.propTypesShadow,blurred:b.propTypesBlurred,fullWidth:b.propTypesFullWidth,className:b.propTypesClassName,children:b.propTypesChildren},m.displayName="MaterialTailwind.Navbar";var d=Object.assign(m,{MobileNav:g.MobileNav})})(ek);var rk={},Kf={},Ds={};(function(e){Object.defineProperty(e,"__esModule",{value:!0});function t(m,d){for(var s in d)Object.defineProperty(m,s,{enumerable:!0,get:d[s]})}t(e,{propTypesOpen:function(){return a},propTypesHandler:function(){return i},propTypesPlacement:function(){return c},propTypesOffset:function(){return f},propTypesDismiss:function(){return g},propTypesAnimate:function(){return b},propTypesContent:function(){return S},propTypesInteractive:function(){return O},propTypesClassName:function(){return T},propTypesChildren:function(){return C},propTypesContextValue:function(){return x}});var r=o(nt),n=Gt;function o(m){return m&&m.__esModule?m:{default:m}}var a=r.default.bool,i=r.default.func,c=n.propTypesPlacements,f=n.propTypesOffsetType,g=n.propTypesDismissType,b=n.propTypesAnimation,S=r.default.node,O=r.default.bool,T=r.default.string,C=r.default.node.isRequired,x=r.default.shape({open:r.default.bool.isRequired,strategy:r.default.oneOf(["fixed","absolute"]).isRequired,x:r.default.number,y:r.default.number,context:r.default.instanceOf(Object).isRequired,reference:r.default.func.isRequired,floating:r.default.func.isRequired,getReferenceProps:r.default.func.isRequired,getFloatingProps:r.default.func.isRequired,appliedAnimation:b.isRequired,labelId:r.default.string.isRequired,descriptionId:r.default.string.isRequired}).isRequired})(Ds);(function(e){Object.defineProperty(e,"__esModule",{value:!0});function t(f,g){for(var b in g)Object.defineProperty(f,b,{enumerable:!0,get:g[b]})}t(e,{PopoverContext:function(){return a},usePopover:function(){return i},PopoverContextProvider:function(){return c}});var r=o(P),n=Ds;function o(f){return f&&f.__esModule?f:{default:f}}var a=r.default.createContext(null);a.displayName="MaterialTailwind.PopoverContext";function i(){var f=r.default.useContext(a);if(!f)throw new Error("usePopover() must be used within a Popover. It happens when you us Line truncated
${ie?"border-purple-500 ring-1 ring-purple-500/40":"border-transparent"}
${!Ce||se?"opacity-75 bg-stormGrey-200/20":""}`,children:[y.jsxs("div",{className:"flex items-center space-x-4",id:"left",children:[N&&y.jsx("input",{type:"checkbox",checked:ie,onChange:()=>je(ae.Id,!Ue),className:`h-5 w-5 text-purple-500 focus:ring-purple-400 border-gray-600 bg-gray-900 rounded ${Ue?"":"opacity-40 cursor-not-allowed"}`,disabled:!Ue}),y.jsx("div",{className:`w-12 h-12 flex items-center justify-center rounded-full
${ae.updateStatus==="✅"?"bg-mossGreen-600":ae.updateStatus==="⚠️"?"bg-warmAmberGlow-400":"bg-sunsetCoral-500"}`}),y.jsxs("div",{children:[y.jsx("p",{className:"text-lg antialiased font-sans font-semibold leading-normal text-inherit",children:ae.Name}),y.jsxs("p",{className:"text-sm antialiased font-sans font-light leading-normal text-inherit",children:["ID: ",ae.Id]}),ae.duplicateName&&y.jsx("p",{className:"text-xs text-amber-700",children:"⚠️ Doppelter Name erkannt"})]})]}),y.jsx("div",{className:"ml-auto flex flex-col items-end gap-1 text-sm",id:"right",children:ve?y.jsxs(y.Fragment,{children:[y.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-warmAmberGlow-600",children:"Redeploy"}),y.jsx("span",{className:"text-warmAmberGlow-500",children:"läuft…"})]}):oe?y.jsxs(y.Fragment,{children:[y.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-warmAmberGlow-600",children:"Redeploy"}),y.jsx("span",{className:"text-warmAmberGlow-500",children:"in Warteliste…"})]}):Oe?y.jsxs(y.Fragment,{children:[y.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-stormGrey-500",children:"System"}),y.jsx("span",{className:"text-gray-300",children:"Redeploy deaktiviert"})]}):he?y.jsxs(y.Fragment,{children:[y.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-stormGrey-500",children:"Status"}),y.jsx("span",{className:"text-mossGreen-600",children:"Aktuell"})]}):j?y.jsx(H.Button,{onClick:()=>Ge(ae.Id),disabled:L||se,className:"disabled:opacity-50 disabled:cursor-not-allowed",children:"Redeploy"}):null})]})},ae.Id)})}),y.jsx(Jf,{disabled:r||!!o})]})}const tV={idle:"Bereit",running:"Läuft",success:"Erfolgreich",error:"Fehlgeschlagen"},dl={idle:"bg-stormGrey-700 text-gray-200",running:"bg-arcticBlue-600 text-white",success:"bg-mossGreen-600 text-white",error:"bg-sunsetCoral-600 text-white"},rV={initializing:"Vorbereitung","activating-maintenance":"Wartungsmodus aktivieren","executing-script":"Skript wird ausgeführt",waiting:"Warte auf Portainer",completed:"Abgeschlossen",failed:"Fehlgeschlagen"},nV={info:"text-arcticBlue-700",success:"text-mossGreen-700",warning:"text-warmAmberGlow-700",error:"text-sunsetCoral-700",stdout:"text-stormGrey-700",stderr:"text-warmAmberGlow-700",debug:"text-lavenderSmoke-700"},oV=e=>{if(!e)return"-";const t=new Date(e);return Number.isNaN(t.getTime())?e:t.toLocaleString("de-DE",{hour:"2-digit",minute:"2-digit",second:"2-digit"})},Ua=e=>{if(!e&&e!==0)return"-";const r=(n=>{if(n instanceof Date)return n;if(typeof n=="number"){const o=n>1e12?n:n*1e3;return new Date(o)}if(typeof n=="string"){const o=Number(n);if(!Number.isNaN(o)){const a=o>1e12?o:o*1e3;return new Date(a)}return new Date(n)}return null})(e);return!r||Number.isNaN(r.getTime())?String(e):r.toLocaleString("de-DE",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"})},Yw=e=>e===1?"Git":e===2?"Compose":e??"-",Mh=()=>({host:"",port:"22",username:"",password:"",extraSshArgs:""});function aV(){var Ws,Vu,Uu,$s;const{hasPermission:e,user:t}=Ur(),r=P.useCallback(()=>{},[]),{maintenance:n,update:o,script:a,ssh:i,loading:c,error:f,setMaintenanceMode:g,triggerUpdate:b,saveScript:S,resetScript:O,saveSshConfig:T,deleteSshConfig:C,testSshConnection:x,fetchSuperuserStatus:m,fetchSetupStatus:d,deleteSetupServer:s,updateSetupApiKey:u,updateSelfStackId:l,removeSuperuserAccount:h}=Aa(),v=!!(t!=null&&t.isSuperuser),w=!!(v||e("maintenance-access","read")),j=!!(v||e("maintenance-access","full")),N=!!(v||e("maintenance-server-manage","read")),k=!!(v||e("maintenance-server-manage","full")),A=!!(v||e("maintenance-server-delete","full")),M=!!(v||e("maintenance-superuser-delete","full")),D=!!(v||e("maintenance-portainer","read")),L=!!(v||e("maintenance-update","full")),B=!!(v||e("maintenance-ssh-update","read")),U=!!(v||e("maintenance-ssh-update","full")),Y=!!(v||e("maintenance-duplicates","read")),q=!!(v||e("maintenance-duplicates","full")),z=!!(n!=null&&n.active),$=n==null?void 0:n.message,K=(Ws=n==null?void 0:n.extra)==null?void 0:Ws.type,X=!!(o!=null&&o.running),te=z||X,[me,de]=P.useState(""),[ee,Se]=P.useState(!1),[pe,fe]=P.useState(!1),[be,ye]=P.useState(""),[Pe,Me]=P.useState(!1),[ke,ce]=P.useState(()=>Mh()),[je,He]=P.useState(!1),[qe,Je]=P.useState(!1),[Qe,Ge]=P.useState(!1),[Xe,ht]=P.useState(!1),[Ve,ot]=P.useState(!1),[_t,Ne]=P.useState(null);P.useEffect(()=>{if(!a)return;const xe=a.custom??a.default??a.effective??"";de(xe)},[a]),P.useEffect(()=>{if(!i){ce(Mh()),He(!1),Je(!1),Ne(null);return}ce({host:i.host??"",port:String(i.port??"22"),username:i.username??"",password:"",extraSshArgs:Array.isArray(i.extraSshArgs)?i.extraSshArgs.join(`
`):""}),He(!!i.passwordStored),Je(!1),Ne(null)},[i]);const at=P.useMemo(()=>a?a.source==="custom"&&typeof a.custom=="string"?a.custom:a.default??a.effective??"":"",[a]),Te=a?me!==at:!1,ae=(a==null?void 0:a.source)==="custom"?"Benutzerdefiniert":"Standard",[ge,oe]=P.useState(!0),[ve,se]=P.useState(!1),[ie,he]=P.useState(null),[Oe,Ce]=P.useState(""),[Ue,Fe]=P.useState(!1),[Ie,Ee]=P.useState(null),[dt,ft]=P.useState(!0),[Ft,Kt]=P.useState(""),[Pt,Rt]=P.useState({}),[hr,ir]=P.useState(null),[Jr,jn]=P.useState(null),[Mr,en]=P.useState(""),[wr,no]=P.useState(!1),[An,J]=P.useState(""),[Ae,Be]=P.useState([]),[Mt,yt]=P.useState(!0),[Et,It]=P.useState(""),[or,kt]=P.useState(!1),[Rn,Ir]=P.useState(null),[gr,Mn]=P.useState(null),oo=P.useRef(null),[Ct,Ra]=P.useState(null),[To,ao]=P.useState(!0),[io,Oo]=P.useState(""),[Ma,xi]=P.useState(!1),[wi,Ia]=P.useState(null),mn=P.useRef(null),jt=P.useCallback(async({silent:xe=!1}={})=>{var Le,ze;oe(!0),Ce("");try{const We=await m(),it=!!(We!=null&&We.exists);se(it),he(it?We.user??null:null)}catch(We){const it=((ze=(Le=We.response)==null?void 0:Le.data)==null?void 0:ze.error)||We.message||"Superuser-Status konnte nicht geladen werden";se(!1),he(null),Ce(it),xe||r({variant:"error",title:"Statusabfrage fehlgeschlagen",description:it})}finally{oe(!1)}},[m,r]),Nt=P.useCallback(async({silent:xe=!1}={})=>{var Le,ze;xe||ft(!0),Kt("");try{const We=await d();Ee(We),Rt({})}catch(We){const it=((ze=(Le=We.response)==null?void 0:Le.data)==null?void 0:ze.error)||We.message||"Setup-Daten konnten nicht geladen werden";Kt(it),xe||r({variant:"error",title:"Setup-Daten",description:it})}finally{ft(!1)}},[d,r]),zt=P.useCallback(async({silent:xe=!1}={})=>{if(mn.current)return mn.current;const Le=(async()=>{var ze,We;xe?xi(!0):ao(!0),Oo("");try{const Wt=(await lt.get("/api/maintenance/portainer-status")).data??{};Ra(Wt),Ia(new Date)}catch(it){const Wt=((We=(ze=it.response)==null?void 0:ze.data)==null?void 0:We.error)||it.message||"Fehler beim Prüfen des Portainer-Status";Oo(Wt),r({variant:"error",title:"Statusaktualisierung fehlgeschlagen",description:Wt})}finally{xe?xi(!1):ao(!1),mn.current=null}})();return mn.current=Le,Le},[r]),Or=P.useCallback(async({silent:xe=!1}={})=>{if(z||X){Be([]),It("Wartungsmodus aktiv – Duplikat-Verwaltung ist vorübergehend deaktiviert."),yt(!1),kt(!1);return}if(oo.current)return oo.current;const Le=(async()=>{var ze,We,it;xe?kt(!0):yt(!0),It("");try{const Vt=(await lt.get("/api/maintenance/duplicates")).data,rn=Array.isArray(Vt)?Vt:(Vt==null?void 0:Vt.items)??[];Be(rn),Mn(new Date)}catch(Wt){if(((ze=Wt.response)==null?void 0:ze.status)===423){const Vt="Wartungsmodus aktiv – Duplikat-Verwaltung ist vorübergehend deaktiviert.";Be([]),It(Vt),r({variant:"warning",title:"Duplikat-Verwaltung gesperrt",description:Vt})}else{const Vt=((it=(We=Wt.response)==null?void 0:We.data)==null?void 0:it.error)||Wt.message||"Fehler beim Laden der Wartungsdaten";It(Vt),r({variant:"error",title:"Duplikate konnten nicht geladen werden",description:Vt})}}finally{xe?kt(!1):yt(!1),oo.current=null}})();return oo.current=Le,Le},[z,X,r]);P.useEffect(()=>{jt({silent:!0})},[jt]),P.useEffect(()=>{Nt()},[Nt]),P.useEffect(()=>{zt()},[zt]),P.useEffect(()=>{Or()},[Or]);const tn=P.useMemo(()=>{var xe;return((xe=Ie==null?void 0:Ie.servers)==null?void 0:xe.items)??[]},[Ie]),so=P.useMemo(()=>{var Le;const xe=((Le=Ie==null?void 0:Ie.apiKeys)==null?void 0:Le.items)??[];return new Map(xe.map(ze=>[ze.serverId,ze]))},[Ie]),Da=P.useMemo(()=>!!(Ie!=null&&Ie.setupComplete),[Ie]),In=(Ie==null?void 0:Ie.selfStack)??null,Dn=(In==null?void 0:In.current)??"",_i=((Vu=Ie==null?void 0:Ie.envDefaults)==null?void 0:Vu.selfStackId)??"";P.useEffect(()=>{en(Dn||""),J("")},[Dn]);const Bs=Mr!==Dn,Si=P.useMemo(()=>{const xe=Array.isArray(Ae)?Ae.length:0,Le=Array.isArray(Ae)?Ae.reduce((ze,We)=>{var it;return ze+(((it=We==null?void 0:We.duplicates)==null?void 0:it.length)||0)},0):0;return{groups:xe,duplicateCount:Le}},[Ae]),La=P.useCallback((xe,Le)=>{U&&(ce(ze=>({...ze,[xe]:Le})),xe==="password"&&He(!1))},[U]),Ci=P.useMemo(()=>{const xe={host:ke.host.trim(),port:Number.parseInt(ke.port,10)||22,username:ke.username.trim(),extraSshArgs:(ke.extraSshArgs||"").split(/\r?\n/).map(ze=>ze.trim()).filter(Boolean)},Le=ke.password??"";return Le?xe.password=Le:je||(xe.password=""),xe},[ke,je]),ep=P.useCallback(async()=>{var xe,Le;if(U&&a)try{Se(!0),await S(me),r({variant:"success",title:"Skript gespeichert",description:"Das benutzerdefinierte Portainer-Update-Skript wurde aktualisiert."})}catch(ze){const We=((Le=(xe=ze.response)==null?void 0:xe.data)==null?void 0:Le.error)||ze.message||"Skript konnte nicht gespeichert werden";r({variant:"error",title:"Speichern fehlgeschlagen",description:We})}finally{Se(!1)}},[U,S,me,a,r]),Eu=P.useCallback(async()=>{var xe,Le;if(U)try{Se(!0),await O(),r({variant:"info",title:"Standardskript wiederhergestellt",description:"Es wird wieder das Standard-Update-Skript verwendet."})}catch(ze){const We=((Le=(xe=ze. Line truncated
Dies setzt die Superuser-Einrichtung zurück.`)))try{Fe(!0);const We=await h(),it=Number((We==null?void 0:We.usersRemoved)??0);r({variant:"success",title:"Superuser gelöscht",description:it>1?`${it} Konten aus der Superuser-Gruppe wurden entfernt.`:"Superuser-Konto wurde entfernt."}),await jt({silent:!0})}catch(We){if(((xe=We.response)==null?void 0:xe.status)===404)await jt({silent:!0}),r({variant:"info",title:"Kein Superuser vorhanden",description:"Es ist kein Superuser mehr vorhanden."});else{const Wt=((ze=(Le=We.response)==null?void 0:Le.data)==null?void 0:ze.error)||We.message||"Superuser konnte nicht gelöscht werden";r({variant:"error",title:"Löschen fehlgeschlagen",description:Wt})}}finally{Fe(!1)}},[M,Ue,ge,ve,h,jt,r]),Ru=P.useCallback(async xe=>{var it,Wt;if(!A||Jr===xe)return;const Le=tn.find(Vt=>Vt.id===xe);if(!Le)return;const ze=Le.name||Le.url||`Server ${xe}`,We=[`Server "${ze}" wirklich löschen?`,"Das System benötigt anschließend erneut einen gültigen Server im Setup."].filter(Boolean).join(`
`);if(!(typeof window<"u"&&!window.confirm(We))){jn(xe);try{await s(xe),r({variant:"success",title:"Server gelöscht",description:`Server "${ze}" wurde entfernt.`}),await Nt({silent:!0})}catch(Vt){const rn=((Wt=(it=Vt.response)==null?void 0:it.data)==null?void 0:Wt.error)||Vt.message||"Server konnte nicht gelöscht werden.";r({variant:"error",title:"Server löschen fehlgeschlagen",description:rn})}finally{jn(null)}}},[A,s,Nt,Jr,tn,r]),Mu=P.useCallback((xe,Le)=>{k&&Rt(ze=>({...ze,[xe]:Le}))},[k]),Vs=P.useCallback(async xe=>{var ze,We;if(!k)return;const Le=(Pt[xe]??"").trim();if(!Le){r({variant:"warning",title:"API-Key fehlt",description:"Bitte gib einen API-Key ein."});return}if(hr!==xe){ir(xe);try{await u(xe,Le),r({variant:"success",title:"API-Key aktualisiert",description:"Der API-Key wurde gespeichert."}),Rt(it=>({...it,[xe]:""})),await Nt({silent:!0})}catch(it){const Wt=((We=(ze=it.response)==null?void 0:ze.data)==null?void 0:We.error)||it.message||"API-Key konnte nicht aktualisiert werden.";r({variant:"error",title:"Aktualisierung fehlgeschlagen",description:Wt})}finally{ir(null)}}},[Pt,hr,k,Nt,r,u]),Iu=P.useCallback(xe=>{k&&(en(xe),J(""))},[k]),tp=P.useCallback(async()=>{var Le,ze;if(!k||!Bs)return;no(!0),J("");const xe=typeof Mr=="string"?Mr.trim():"";try{await l(xe),r({variant:"success",title:"Self-Stack-ID gespeichert",description:xe?`Self-Stack-ID wurde auf "${xe}" gesetzt.`:"Self-Stack-ID wurde entfernt."}),await Nt({silent:!0})}catch(We){const it=((ze=(Le=We.response)==null?void 0:Le.data)==null?void 0:ze.error)||We.message||"Self-Stack-ID konnte nicht aktualisiert werden.";J(it),r({variant:"error",title:"Self-Stack-ID",description:it})}finally{no(!1)}},[k,Nt,Bs,Mr,r,l]),Du=P.useCallback(async()=>{var xe,Le;if(k&&!(!Mr&&!Dn)&&!(typeof window<"u"&&!window.confirm("Self-Stack-ID wirklich entfernen?"))){no(!0),J("");try{await l(""),en(""),r({variant:"success",title:"Self-Stack-ID entfernt",description:"Die Self-Stack-ID wurde gelöscht."}),await Nt({silent:!0})}catch(ze){const We=((Le=(xe=ze.response)==null?void 0:xe.data)==null?void 0:Le.error)||ze.message||"Self-Stack-ID konnte nicht entfernt werden.";J(We),r({variant:"error",title:"Self-Stack-ID",description:We})}finally{no(!1)}}},[k,Dn,Nt,Mr,r,l]),Oi=P.useCallback(async xe=>{var Le,ze;if(j&&!(c||Pe)&&xe!==z){if(X){r({variant:"warning",title:"Aktion nicht möglich",description:"Während eines laufenden Updates kann der Wartungsmodus nicht geändert werden."});return}Me(!0);try{const We=xe&&!!K,it={active:xe};We&&$?it.message=$:it.message=null,await g(it),r({variant:xe?"info":"success",title:xe?"Wartungsmodus aktiviert":"Wartungsmodus deaktiviert",description:xe?"Benutzer sehen jetzt die Wartungsseite.":"StackPulse steht wieder zur Verfügung."})}catch(We){const it=((ze=(Le=We.response)==null?void 0:Le.data)==null?void 0:ze.error)||We.message||"Wartungsmodus konnte nicht geändert werden";r({variant:"error",title:"Änderung fehlgeschlagen",description:it})}finally{Me(!1)}}},[j,c,Pe,z,X,g,$,K,r]),rp=P.useCallback(async()=>{var Le,ze;if(!L)return;if(z||X){r({variant:"warning",title:"Update nicht möglich",description:"Während eines aktiven Wartungsmodus kann kein weiteres Update gestartet werden."});return}const xe=(Ct==null?void 0:Ct.latestVersion)??(Ct==null?void 0:Ct.currentVersion)??"unbekannt";if(!(typeof window<"u"&&!window.confirm(`Portainer-Update starten?
Zielversion: ${xe}.
Während des Updates befindet sich StackPulse im Wartungsmodus.
Fortfahren?`)))try{ye(""),fe(!0),await b(),r({variant:"info",title:"Update gestartet",description:"Das Portainer-Update wurde gestartet."})}catch(We){const it=((ze=(Le=We.response)==null?void 0:Le.data)==null?void 0:ze.error)||We.message||"Portainer-Update konnte nicht gestartet werden";ye(it),r({variant:"error",title:"Update fehlgeschlagen",description:it})}finally{fe(!1)}},[L,z,X,Ct,b,r]),Lu=P.useCallback(async xe=>{var it,Wt,Vt,rn;if(!q||!xe)return;if(z||X){r({variant:"warning",title:"Aktion nicht möglich",description:"Während des Wartungsmodus sind Bereinigungen deaktiviert."});return}const Le=(it=xe.canonical)==null?void 0:it.Id;if(!Le)return;const ze=(xe.duplicates||[]).map(ko=>ko.Id).filter(Boolean);if(!ze.length)return;const We=((Wt=xe.canonical)==null?void 0:Wt.Name)||xe.name||`Stack ${Le}`;if(!(typeof window<"u"&&!window.confirm(`Bereinigung für "${We}" starten?
Es werden ${ze.length} Duplikate entfernt: ${ze.join(", ")}`))){Ir(String(Le));try{const Wr=(await lt.post("/api/maintenance/duplicates/cleanup",{canonicalId:Le,duplicateIds:ze})).data??{};if(Wr.success===!1)throw new Error(Wr.error||"Bereinigung fehlgeschlagen");const Pi=Array.isArray(Wr.results)?Wr.results.filter(za=>za.status==="deleted").map(za=>za.id):ze;r({variant:"success",title:"Bereinigung abgeschlossen",description:`${We} – entfernte IDs: ${Pi.join(", ")}`}),await Or({silent:!0})}catch(ko){const Wr=((rn=(Vt=ko.response)==null?void 0:Vt.data)==null?void 0:rn.error)||ko.message||"Bereinigung fehlgeschlagen";r({variant:"error",title:"Bereinigung fehlgeschlagen",description:Wr})}finally{Ir(null)}}},[q,Or,z,r,X]);n!=null&&n.activatedAt&&Ua(n.activatedAt),n!=null&&n.updatedAt&&Ua(n.updatedAt);const Ln=z||X,Fa=(o==null?void 0:o.status)??(X?"running":"idle"),Us=tV[Fa]??Fa,np=dl[Fa]??dl.idle,Fu=o!=null&&o.stage?rV[o.stage]??o.stage:"–",zu=(o==null?void 0:o.logs)??[],op=(o==null?void 0:o.targetVersion)??"-",ap=(o==null?void 0:o.resultVersion)??"-",ip=o!=null&&o.startedAt?Ua(o.startedAt):"-",sp=o!=null&&o.finishedAt?Ua(o.finishedAt):"-",Bu=(o==null?void 0:o.message)??(X?"Update läuft…":""),lo=X||ee||c;return w?y.jsxs("div",{className:"mt-12 mb-8 flex flex-col gap-12",children:[(z||X)&&y.jsx("div",{className:"rounded-lg border border-cyan-500/60 bg-cyan-900/30 px-4 py-3 text-sm text-bluegray-100",children:y.jsxs("div",{className:"flex flex-col gap-1",children:[y.jsxs("span",{children:["Wartungsmodus aktiv",$?` – ${$}`:X?" – Portainer-Update läuft":"","."]}),X&&y.jsxs("span",{className:"text-xs text-indigo-900",children:["Phase: ",Fu]})]})}),w&&y.jsxs(H.Card,{children:[y.jsx(H.CardHeader,{variant:"gradient",color:"gray",className:"mb-5 p-4",children:y.jsx(H.Typography,{variant:"h6",color:"white",className:"flex items-center justify-between",children:y.jsx("span",{children:"Wartungsmodus"})})}),y.jsxs(H.CardBody,{className:"flex flex-col gap-4 p-4",children:[y.jsxs("div",{className:"flex items-start justify-between gap-4",children:[y.jsx("div",{className:"space-y-1",children:y.jsx("p",{className:"text-sm antialiased font-sans text-sm font-light leading-normal text-inherit",children:"Schaltet StackPulse für Benutzer in den Wartungsmodus."})}),y.jsx(H.Switch,{checked:z,disabled:!j||Pe||c||X,onChange:xe=>Oi(xe.target.checked),ripple:!1,color:"amber"})]}),Pe&&y.jsx("p",{className:"text-xs text-stormGrey-500",children:"Wartungsmodus wird aktualisiert…"})]})]}),N&&y.jsxs(H.Card,{children:[y.jsx(H.CardHeader,{variant:"gradient",color:"gray",className:"mb-5 p-4",children:y.jsx(H.Typography,{variant:"h6",color:"white",className:"flex items-center justify-between",children:y.jsx("span",{children:"Server & API-Keys"})})}),y.jsxs(H.CardBody,{className:"flex flex-col gap-4 p-4",children:[dt&&y.jsx("p",{className:"text-sm text-stormGrey-500",children:"Daten werden geladen…"}),!dt&&Ft&&y.jsx(H.Alert,{color:"red",className:"border border-red-200 bg-red-50 text-red-700",children:Ft}),!dt&&!Ft&&y.jsxs("div",{className:"flex flex-col gap-6",children:[y.jsxs("div",{className:"flex flex-col gap-3",children:[y.jsxs("div",{className:"flex items-center justify-between",children:[y.jsx(H.Typography,{variant:"small",color:"blue-gray",className:"font-semibold uppercase",children:"Server"}),y.jsxs(H.Typography,{variant:"small",color:"blue-gray",children:[tn.length," vorhanden"]})]}),tn.length===0?y.jsx("p",{className:"text-sm text-stormGrey-500",children:"Es sind derzeit keine Server hinterlegt."}):y.jsx("div",{className:"flex flex-col gap-3",children:tn.map(xe=>{const Le=so.get(xe.id)||null,ze=!!(Le!=null&&Le.hasKey),We=Le!=null&&Le.updatedAt?new Date(Le.updatedAt).toLocaleString("de-DE",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"}):null;return y.jsxs("div",{className:"flex flex-col gap-3 rounded-md border border-blue-gray-100 p-3 md:flex-row md:items-start md:justify-between",children:[y.jsxs("div",{className:"flex flex-col gap-1",children:[y.jsx("p",{className:"text-sm font-medium text-stormGrey-900",children:xe.name||"Ohne Namen"}),y.jsx("p",{className:"text-xs text-stormGrey-500 break-all",children:xe.url}),y.jsxs("p",{className:"text-xs text-stormGrey-500",children:["API-Key: ",ze?"vorhanden":"nicht hinterlegt",We?` (aktualisiert am ${We})`:""]})]}),y.jsxs("div",{className:"flex w-full flex-col gap-2 md:max-w-xs",children:[y.jsx(H.Input,{type:"password",label:"Neuer API-Key",value:Pt[xe.id]??"",onChange:it=>Mu(xe.id,it.target.value),disabled:!k||dt||hr===xe.id}),y.jsxs("div",{className:"flex items-center gap-2 justify-end",children:[y.jsx(H.Button,{variant:"text",color:"red",size:"sm",onClick:()=>Ru(xe.id),disabled:te||!A||dt||Jr===xe.id||hr===xe.id,children:Jr===xe.id?"Lösche…":"Löschen"}),y.jsx(H.Button,{color:"blue",size:"sm",onClick:()=>Vs(xe.id),disabled:te||!k||dt||hr===xe.id,children:hr===xe.id?"Speichere…":"API-Key speichern"})]})]})]},xe.id)})})]}),y.jsxs("div",{clas Line truncated
`)},kV=(e=[])=>{if(!Array.isArray(e))return[];const t=[];for(let r=0;r<e.length;r+=4)t.push(e.slice(r,r+4));return t};function NV(){const e=So(),{user:t,setSession:r,refreshSession:n}=Ur(),{maintenance:o,update:a}=Aa(),[i,c]=P.useState(!0),[f,g]=P.useState(""),[b,S]=P.useState([]),[O,T]=P.useState(!1),[C,x]=P.useState(!1),m=!!(t!=null&&t.requiresSecurityPhraseDownload),d=!!(o!=null&&o.active);o==null||o.message;const s=!!(a!=null&&a.running);a!=null&&a.stage&&(OV[a.stage]??a.stage);const u=d||s,l=P.useMemo(()=>kV(b),[b]),h=P.useCallback(N=>{if(!N)return;const k=new Blob([N],{type:"text/plain;charset=utf-8"}),A=URL.createObjectURL(k),M=document.createElement("a");M.href=A;const D=t!=null&&t.username?`${t.username}-sicherheitsschluessel`:"security-phrase";M.download=`${D}.txt`,document.body.appendChild(M),M.click(),document.body.removeChild(M),URL.revokeObjectURL(A)},[t==null?void 0:t.username]),v=P.useCallback(async()=>{var N,k,A,M,D,L,B,U;c(!0),g("");try{const Y=await lt.get("/api/users/me/security-phrase"),q=Array.isArray((k=(N=Y.data)==null?void 0:N.item)==null?void 0:k.words)?Y.data.item.words:[];if(!q.length)throw new Error("EMPTY_SECURITY_PHRASE");S(q),x(!1)}catch(Y){if(((A=Y==null?void 0:Y.response)==null?void 0:A.status)===409||((D=(M=Y==null?void 0:Y.response)==null?void 0:M.data)==null?void 0:D.error)==="SECURITY_PHRASE_ALREADY_DOWNLOADED"){await n(),e("/dashboard/stacks",{replace:!0});return}if(((L=Y==null?void 0:Y.response)==null?void 0:L.status)===401){await n(),e("/auth/sign-in",{replace:!0});return}const q=((U=(B=Y==null?void 0:Y.response)==null?void 0:B.data)==null?void 0:U.error)||(Y==null?void 0:Y.message)||"Sicherheitsschlüssel konnte nicht geladen werden.";g(q)}finally{c(!1)}},[e,n]);P.useEffect(()=>{if(!t){e("/auth/sign-in",{replace:!0});return}if(!m){e("/dashboard/stacks",{replace:!0});return}v()},[t,m,v,e]);const w=P.useCallback(()=>{v()},[v]),j=P.useCallback(async()=>{var N,k,A,M,D,L;if(!(O||!Array.isArray(b)||b.length===0)){T(!0),g("");try{const B=PV(b);h(B);const U=await lt.post("/api/users/me/security-phrase/downloaded"),Y=((k=(N=U.data)==null?void 0:N.item)==null?void 0:k.downloadedAt)||null,q=(A=U.data)==null?void 0:A.session;q!=null&&q.user?r(q):await n(),x(!!Y),e("/dashboard/stacks",{replace:!0})}catch(B){if(((M=B==null?void 0:B.response)==null?void 0:M.status)===401){await n(),e("/auth/sign-in",{replace:!0});return}const U=((L=(D=B==null?void 0:B.response)==null?void 0:D.data)==null?void 0:L.error)||(B==null?void 0:B.message)||"Download konnte nicht bestätigt werden.";g(U)}finally{T(!1)}}},[b,O,h,n,e,r]);return m?y.jsxs("div",{className:"flex min-h-screen flex-col items-center justify-center bg-blue-gray-50/50 px-4 py-10",children:[y.jsxs("div",{className:"mb-6 text-center",children:[y.jsx(H.Typography,{variant:"h2",className:"font-semibold text-blue-gray-800",children:"Sicherheitsschlüssel sichern"}),y.jsx(H.Typography,{color:"blue-gray",className:"mt-2 max-w-3xl text-base",children:"Bitte lade den einmaligen Sicherheitsschlüssel herunter und bewahre ihn sicher auf. Er wird benötigt, um dein Passwort zurückzusetzen. Ohne den Download kannst du die Anwendung nicht weiter nutzen."})]}),y.jsx(H.Card,{className:"w-full max-w-3xl shadow-lg",children:y.jsxs(H.CardBody,{className:"space-y-6",children:[i&&y.jsx("div",{className:"flex items-center justify-center py-10",children:y.jsx(H.Spinner,{className:"h-10 w-10 text-blue-gray-500"})}),!i&&f&&y.jsx(H.Alert,{color:"red",className:"border border-red-200 bg-red-50 text-red-700",icon:y.jsx(zz,{className:"h-5 w-5"}),children:y.jsxs("div",{className:"flex items-start justify-between gap-4",children:[y.jsxs("div",{children:[y.jsx(H.Typography,{variant:"h6",color:"red",children:"Fehler"}),y.jsx(H.Typography,{color:"red",className:"text-sm",children:f})]}),y.jsx(H.Button,{size:"sm",color:"red",variant:"text",onClick:w,className:"normal-case",children:"Erneut versuchen"})]})}),!i&&!f&&y.jsxs(y.Fragment,{children:[y.jsxs("div",{className:"rounded-lg border border-blue-gray-100 bg-blue-gray-50/60 p-6",children:[y.jsx(H.Typography,{variant:"h5",className:"mb-4 text-blue-gray-800",children:"Deine Sicherheitsphrase"}),y.jsx("div",{className:"space-y-3",children:l.map((N,k)=>y.jsx("div",{className:"flex flex-wrap items-center justify-center gap-4 text-lg font-mono tracking-wide text-blue-gray-900 md:text-xl",children:N.map((A,M)=>y.jsx("span",{className:"uppercase",children:A},`phrase-${k}-${M}`))},`phrase-row-${k}`))})]}),y.jsxs("div",{className:"flex flex-col items-center justify-between gap-4 md:flex-row",children:[y.jsx("div",{className:"text-sm text-blue-gray-600",children:"Der Download startet im TXT-Format. Bewahre die Datei sicher auf – sie kann später nicht erneut angezeigt werden."}),y.jsxs(H.Button,{color:"blue",size:"md",className:"flex items-center gap-2 normal-case",onClick:j,disabled:u||O||C||b.length===0,children:[y.jsx(Dz,{className:"h-5 w-5"}),"Sicherheitsschlüssel herunterladen"]})]})]})]})})]}):null}const EV="/assets Line truncated
+1
View File
@@ -0,0 +1 @@
*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#eee}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Roboto,sans-serif;font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#bdbdbd}input::placeholder,textarea::placeholder{opacity:1;color:#bdbdbd}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(33 150 243 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(33 150 243 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width: 540px){.container{max-width:540px}}@media (min-width: 720px){.container{max-width:720px}}@media (min-width: 960px){.container{max-width:960px}}@media (min-width: 1140px){.container{max-width:1140px}}@media (min-width: 1320px){.container{max-width:1320px}}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.visible{visibility:visible}.\!invisible{visibility:hidden!important}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.\!absolute{position:absolute!important}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bLine truncated
+2 -2
View File
@@ -34,8 +34,8 @@
<script defer data-site="YOUR_DOMAIN_HERE" src="https://api.nepcha.com/js/nepcha-analytics.js"></script>
<script type="module" crossorigin src="/assets/index-65b35a5f.js"></script>
<link rel="stylesheet" href="/assets/index-328d6adf.css">
<script type="module" crossorigin src="/assets/index-71db1823.js"></script>
<link rel="stylesheet" href="/assets/index-dad5f6fa.css">
</head>
<body>
<div id="root"></div>
+12 -288
View File
@@ -15,26 +15,6 @@ const updateServer = db.prepare(`
WHERE id = ?
`);
const selectAllEndpoints = db.prepare(`
SELECT e.*, s.name as server_name, s.url as server_url
FROM endpoints e
INNER JOIN servers s ON s.id = e.server_id
ORDER BY e.id ASC
`);
const selectEndpointById = db.prepare('SELECT * FROM endpoints WHERE id = ?');
const selectEndpointByExternalId = db.prepare('SELECT * FROM endpoints WHERE external_id = ?');
const selectEndpointByServerAndExternal = db.prepare('SELECT * FROM endpoints WHERE server_id = ? AND external_id = ?');
const selectEndpointsByServerId = db.prepare('SELECT * FROM endpoints WHERE server_id = ?');
const insertEndpoint = db.prepare(`
INSERT INTO endpoints (server_id, name, external_id, is_default)
VALUES (?, ?, ?, ?)
`);
const updateEndpoint = db.prepare(`
UPDATE endpoints
SET name = ?, external_id = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`);
const deleteEndpointStmt = db.prepare('DELETE FROM endpoints WHERE id = ?');
const deleteServerStmt = db.prepare('DELETE FROM servers WHERE id = ?');
const selectApiKeyByServerId = db.prepare('SELECT * FROM server_api_keys WHERE server_id = ?');
@@ -51,29 +31,8 @@ const upsertApiKey = db.prepare(`
`);
const deleteApiKeyByServerId = db.prepare('DELETE FROM server_api_keys WHERE server_id = ?');
const clearDefaultEndpointStmt = db.prepare('UPDATE endpoints SET is_default = 0 WHERE is_default != 0');
const setDefaultEndpointStmt = db.prepare('UPDATE endpoints SET is_default = 1, updated_at = CURRENT_TIMESTAMP WHERE id = ?');
const selectDefaultEndpointStmt = db.prepare(`
SELECT e.*, s.name as server_name, s.url as server_url
FROM endpoints e
INNER JOIN servers s ON s.id = e.server_id
WHERE e.is_default = 1
LIMIT 1
`);
const countServersStmt = db.prepare('SELECT COUNT(*) as count FROM servers');
const countEndpointsStmt = db.prepare('SELECT COUNT(*) as count FROM endpoints');
const selectFirstServerStmt = db.prepare('SELECT * FROM servers ORDER BY id ASC LIMIT 1');
const selectFirstEndpointStmt = db.prepare(`
SELECT e.*, s.name as server_name, s.url as server_url
FROM endpoints e
INNER JOIN servers s ON s.id = e.server_id
ORDER BY e.id ASC
LIMIT 1
`);
const transactionalSetDefaultEndpoint = db.transaction((endpointId) => {
clearDefaultEndpointStmt.run();
setDefaultEndpointStmt.run(endpointId);
});
const API_KEY_SECRET = crypto.createHash('sha256')
.update(process.env.PORTAINER_API_SECRET || process.env.PORTAINER_API_KEY || 'stackpulse-portainer-api-key')
@@ -167,89 +126,6 @@ function ensureServer({ name, url }) {
return created;
}
function ensureEndpoint({ serverId, name, externalId, makeDefault = false }) {
if (!serverId) {
const error = new Error('SERVER_REQUIRED');
error.code = 'SERVER_REQUIRED';
throw error;
}
const server = selectServerById.get(serverId);
if (!server) {
const error = new Error('SERVER_NOT_FOUND');
error.code = 'SERVER_NOT_FOUND';
throw error;
}
const trimmedExternal = String(externalId ?? '').trim();
if (!trimmedExternal) {
const error = new Error('ENDPOINT_EXTERNAL_ID_REQUIRED');
error.code = 'ENDPOINT_EXTERNAL_ID_REQUIRED';
throw error;
}
const normalizedName = String(name || `Endpoint ${trimmedExternal}`).trim();
if (!normalizedName) {
const error = new Error('ENDPOINT_NAME_REQUIRED');
error.code = 'ENDPOINT_NAME_REQUIRED';
throw error;
}
let existing = selectEndpointByServerAndExternal.get(serverId, trimmedExternal);
if (existing) {
if (existing.name !== normalizedName || existing.external_id !== trimmedExternal) {
updateEndpoint.run(normalizedName, trimmedExternal, existing.id);
existing = selectEndpointById.get(existing.id);
console.log(`ℹ️ [Setup] Endpoint aktualisiert: ${existing.name} (${existing.id}) für Server ${server.name} (${server.id})`);
}
} else {
const isDefault = makeDefault || countEndpointsStmt.get().count === 0 ? 1 : 0;
const result = insertEndpoint.run(serverId, normalizedName, trimmedExternal, isDefault);
existing = selectEndpointById.get(result.lastInsertRowid);
console.log(`✅ [Setup] Endpoint angelegt: ${existing.name} (${existing.id}) für Server ${server.name} (${server.id})`);
}
if (makeDefault && existing && !existing.is_default) {
transactionalSetDefaultEndpoint(existing.id);
existing = selectEndpointById.get(existing.id);
}
return existing;
}
function removeEndpoint(endpointId) {
const id = Number(endpointId);
if (!Number.isFinite(id)) {
const error = new Error('ENDPOINT_ID_INVALID');
error.code = 'ENDPOINT_ID_INVALID';
throw error;
}
const endpoint = selectEndpointById.get(id);
if (!endpoint) {
const error = new Error('ENDPOINT_NOT_FOUND');
error.code = 'ENDPOINT_NOT_FOUND';
throw error;
}
const wasDefault = Boolean(endpoint.is_default);
deleteEndpointStmt.run(id);
if (wasDefault) {
const fallback = selectFirstEndpointStmt.get();
if (fallback) {
transactionalSetDefaultEndpoint(fallback.id);
console.log(`ℹ️ [Setup] Neuer Standard-Endpoint gesetzt: ${fallback.name} (${fallback.id})`);
}
}
console.log(`🗑️ [Setup] Endpoint entfernt: ${endpoint.name} (${endpoint.id}) für Server ${endpoint.server_id}`);
return {
endpoint,
removed: true
};
}
function setServerApiKey({ serverId, apiKey }) {
const id = Number(serverId);
if (!Number.isFinite(id)) {
@@ -289,43 +165,7 @@ function setServerApiKey({ serverId, apiKey }) {
};
}
function setDefaultEndpoint(endpointId) {
if (!endpointId) return null;
const endpoint = selectEndpointById.get(endpointId);
if (!endpoint) {
const error = new Error('ENDPOINT_NOT_FOUND');
error.code = 'ENDPOINT_NOT_FOUND';
throw error;
}
transactionalSetDefaultEndpoint(endpointId);
return selectEndpointById.get(endpointId);
}
function getDefaultEndpoint() {
let endpoint = selectDefaultEndpointStmt.get();
if (!endpoint) {
endpoint = selectFirstEndpointStmt.get();
if (endpoint) {
transactionalSetDefaultEndpoint(endpoint.id);
endpoint = selectDefaultEndpointStmt.get();
}
}
return endpoint || null;
}
function getActiveEndpointExternalId() {
const endpoint = getDefaultEndpoint();
return endpoint ? endpoint.external_id : null;
}
function getActiveApiKey() {
const defaultEndpoint = getDefaultEndpoint();
if (defaultEndpoint) {
const row = selectApiKeyByServerId.get(defaultEndpoint.server_id);
const key = decryptApiKey(row);
if (key) return key;
}
const firstServer = selectFirstServerStmt.get();
if (firstServer) {
const row = selectApiKeyByServerId.get(firstServer.id);
@@ -338,21 +178,6 @@ function getActiveApiKey() {
}
function getActiveServerUrl() {
const endpoint = getDefaultEndpoint();
if (endpoint) {
const normalizedEndpointUrl = endpoint.server_url ? normalizeUrl(endpoint.server_url) : '';
if (normalizedEndpointUrl) {
return normalizedEndpointUrl;
}
const server = selectServerById.get(endpoint.server_id);
if (server?.url) {
const normalizedServerUrl = normalizeUrl(server.url);
if (normalizedServerUrl) {
return normalizedServerUrl;
}
}
}
const firstServer = selectFirstServerStmt.get();
if (firstServer?.url) {
const normalizedUrl = normalizeUrl(firstServer.url);
@@ -386,8 +211,6 @@ function removeServer(serverId) {
throw error;
}
const relatedEndpoints = selectEndpointsByServerId.all(id);
const hadDefaultEndpoint = relatedEndpoints.some((endpoint) => endpoint.is_default);
const existingApiKey = selectApiKeyByServerId.get(id);
deleteServerStmt.run(id);
@@ -396,41 +219,26 @@ function removeServer(serverId) {
console.log(`🗑️ [Setup] API-Key entfernt: Server ${server.name} (${server.id})`);
}
if (hadDefaultEndpoint) {
const fallback = getDefaultEndpoint();
if (fallback) {
console.log(`ℹ️ [Setup] Neuer Standard-Endpoint gesetzt: ${fallback.name} (${fallback.id})`);
}
}
console.log(`🗑️ [Setup] Server entfernt: ${server.name} (${server.id}) – entfernte Endpoints: ${relatedEndpoints.length}`);
console.log(`🗑️ [Setup] Server entfernt: ${server.name} (${server.id})`);
return {
server,
removed: true,
endpointsRemoved: relatedEndpoints.length
removed: true
};
}
function hasEndpoint() {
const { count } = countEndpointsStmt.get();
return count > 0;
}
function hasApiKey() {
const { count } = countApiKeysStmt.get();
return count > 0;
}
function hasCompleteSetup() {
return hasSuperuser() && hasServer() && hasEndpoint() && hasApiKey();
return hasSuperuser() && hasServer() && hasApiKey();
}
function ensureDefaultsFromEnv() {
const envServerUrlRaw = process.env.PORTAINER_URL;
const envServerName = process.env.PORTAINER_SERVER_NAME;
const envEndpointIdRaw = process.env.PORTAINER_ENDPOINT_ID;
const envEndpointName = process.env.PORTAINER_ENDPOINT_NAME;
let server = null;
if (envServerUrlRaw) {
try {
@@ -440,31 +248,6 @@ function ensureDefaultsFromEnv() {
}
}
const trimmedEndpointId = typeof envEndpointIdRaw === 'string' ? envEndpointIdRaw.trim() : '';
if (trimmedEndpointId) {
const existingEndpoint = selectEndpointByExternalId.get(trimmedEndpointId);
if (!existingEndpoint) {
const targetServer = server || selectFirstServerStmt.get();
if (targetServer) {
try {
const endpointName = envEndpointName || `Endpoint ${trimmedEndpointId}`;
ensureEndpoint({
serverId: targetServer.id,
name: endpointName,
externalId: trimmedEndpointId,
makeDefault: true
});
} catch (error) {
console.error('⚠️ [Setup] Konnte Endpoint aus Umgebungsvariablen nicht anlegen:', error.message);
}
} else {
console.warn('⚠️ [Setup] Endpoint aus Umgebungsvariablen benötigt einen vorhandenen Server.');
}
} else if (!existingEndpoint.is_default) {
transactionalSetDefaultEndpoint(existingEndpoint.id);
}
}
const envApiKeyRaw = typeof process.env.PORTAINER_API_KEY === 'string' ? process.env.PORTAINER_API_KEY : '';
if (envApiKeyRaw.trim()) {
const targetServer = server || selectFirstServerStmt.get();
@@ -480,8 +263,6 @@ function ensureDefaultsFromEnv() {
function getSetupStatus() {
const servers = selectAllServers.all();
const endpoints = selectAllEndpoints.all();
const defaultEndpoint = getDefaultEndpoint();
const apiKeyRecords = selectAllApiKeys.all();
const apiKeyMap = new Map(apiKeyRecords.map((entry) => [entry.server_id, entry]));
@@ -489,10 +270,6 @@ function getSetupStatus() {
const envServerName = rawEnvServerName.trim();
const rawEnvServerUrl = typeof process.env.PORTAINER_URL === 'string' ? process.env.PORTAINER_URL : '';
const envServerUrl = rawEnvServerUrl.trim();
const rawEnvEndpointName = typeof process.env.PORTAINER_ENDPOINT_NAME === 'string' ? process.env.PORTAINER_ENDPOINT_NAME : '';
const envEndpointName = rawEnvEndpointName.trim();
const rawEnvEndpointId = typeof process.env.PORTAINER_ENDPOINT_ID === 'string' ? process.env.PORTAINER_ENDPOINT_ID : '';
const envEndpointId = rawEnvEndpointId.trim();
const rawEnvApiKey = typeof process.env.PORTAINER_API_KEY === 'string' ? process.env.PORTAINER_API_KEY : '';
const envApiKeyTrimmed = rawEnvApiKey.trim();
const envApiKeyProvided = Boolean(envApiKeyTrimmed);
@@ -503,7 +280,6 @@ function getSetupStatus() {
const envSuperuserEmail = envSuperuserEmailRaw.trim();
const serverRequired = servers.length === 0;
const endpointRequired = endpoints.length === 0;
const apiKeyItems = servers.map((server) => {
const keyMeta = apiKeyMap.get(server.id) || null;
@@ -518,7 +294,7 @@ function getSetupStatus() {
const apiKeyRequired = servers.length > 0 && apiKeyCount === 0;
const superuserExists = hasSuperuser();
const setupComplete = superuserExists && !serverRequired && !endpointRequired && !apiKeyRequired;
const setupComplete = superuserExists && !serverRequired && !apiKeyRequired;
return {
superuser: {
@@ -536,13 +312,6 @@ function getSetupStatus() {
requireInput: serverRequired,
envProvided: Boolean(envServerUrl)
},
endpoints: {
count: endpoints.length,
items: endpoints,
requireInput: endpointRequired,
envProvided: Boolean(envEndpointId),
default: defaultEndpoint
},
apiKeys: {
count: apiKeyCount,
items: apiKeyItems,
@@ -553,7 +322,6 @@ function getSetupStatus() {
requirements: {
superuser: !superuserExists,
server: serverRequired,
endpoint: endpointRequired,
apiKey: apiKeyRequired
},
setupComplete,
@@ -561,9 +329,6 @@ function getSetupStatus() {
serverName: envServerName || (envServerUrl ? deriveServerName(envServerUrl) : ''),
serverNameFromEnv: rawEnvServerName,
serverUrl: envServerUrl,
endpointName: envEndpointName || (envEndpointId ? `Endpoint ${envEndpointId}` : ''),
endpointNameFromEnv: rawEnvEndpointName,
endpointExternalId: envEndpointId,
apiKeyProvided: envApiKeyProvided,
apiKeyValue: rawEnvApiKey,
superuserUsername: envSuperuserUsernameRaw,
@@ -573,77 +338,36 @@ function getSetupStatus() {
};
}
const createEndpointWithDefaultTransaction = db.transaction(({ serverInput, endpointInput }) => {
let server = null;
function completeSetup({ server: serverInput }) {
let serverRecord = null;
if (endpointInput?.serverId) {
const byId = selectServerById.get(endpointInput.serverId);
if (!byId) {
const error = new Error('SERVER_NOT_FOUND');
error.code = 'SERVER_NOT_FOUND';
throw error;
}
server = byId;
if (serverInput && typeof serverInput.url === 'string' && serverInput.url.trim()) {
serverRecord = ensureServer(serverInput);
} else {
serverRecord = selectFirstServerStmt.get();
}
if (serverInput) {
server = ensureServer(serverInput);
}
if (!server) {
server = selectFirstServerStmt.get();
}
if (!server) {
if (!serverRecord) {
const error = new Error('SERVER_DETAILS_REQUIRED');
error.code = 'SERVER_DETAILS_REQUIRED';
throw error;
}
if (!endpointInput) {
const error = new Error('ENDPOINT_DETAILS_REQUIRED');
error.code = 'ENDPOINT_DETAILS_REQUIRED';
throw error;
}
const endpoint = ensureEndpoint({
serverId: server.id,
name: endpointInput.name,
externalId: endpointInput.externalId,
makeDefault: true
});
return {
server,
endpoint
};
});
function completeSetup({ server: serverInput, endpoint: endpointInput }) {
const result = createEndpointWithDefaultTransaction({ serverInput, endpointInput });
return {
server: result.server,
endpoint: result.endpoint,
defaultEndpoint: getDefaultEndpoint()
server: serverRecord
};
}
export {
ensureDefaultsFromEnv,
ensureServer,
ensureEndpoint,
setServerApiKey,
setDefaultEndpoint,
getDefaultEndpoint,
getActiveEndpointExternalId,
getActiveApiKey,
getActiveServerUrl,
hasServer,
hasEndpoint,
hasApiKey,
hasCompleteSetup,
getSetupStatus,
completeSetup,
removeEndpoint,
removeServer
};
+7 -10
View File
@@ -8,16 +8,13 @@ services:
- "4001:4001"
volumes:
- stackpulse_data:/app/backend/data
environment:
PORTAINER_URL: "Your_Portainer_Server_Address"
PORTAINER_API_KEY: "Your_Portainer_API_Key"
PORTAINER_ENDPOINT_ID: "Your_Portainer_Endpoint_ID"
SELF_STACK_ID: "Stackpulse ID"
SUPERUSER_USERNAME: "Your_Superuser_Username" (optional)
SUPERUSER_EMAIL: "Your_Superuser_Email" (optional)
SUPERUSER_PASSWORD: "Your_Superuser_Password" (optional)
SELF_STACK_ID: "Your_StackPulse_Stack_ID"
restart: unless-stopped
environment:
- PORTAINER_URL=Your_Portainer_Server_Adress (optional)
- PORTAINER_API_KEY=Your_Portainer_API_Key (optional)
- SUPERUSER_USERNAME=Your_Superuser_Username (optional)
- SUPERUSER_EMAIL=Your_Superuser_Email (optional)
- SUPERUSER_PASSWORD=Your_Superuser_Password (optional)
- SELF_STACK_ID=Your_StackPulse_Stack_ID (optional)
volumes:
stackpulse_data:
+30 -13
View File
@@ -1,5 +1,6 @@
import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
import axios from "axios";
import { useAuth } from "@/components/AuthProvider.jsx";
const MaintenanceContext = createContext(null);
@@ -14,6 +15,8 @@ const INITIAL_STATE = {
};
export default function MaintenanceProvider({ children }) {
const { isAuthenticated, hasPermission, user } = useAuth();
const canAccessMaintenance = Boolean(isAuthenticated && (user?.isSuperuser || hasPermission("maintenance-access", "read")));
const [state, setState] = useState(INITIAL_STATE);
const pollingRef = useRef(null);
const wasRunningRef = useRef(false);
@@ -26,6 +29,10 @@ export default function MaintenanceProvider({ children }) {
}, []);
const fetchConfig = useCallback(async () => {
if (!canAccessMaintenance) {
applyState({ loading: false, error: "" });
return null;
}
applyState({ loading: true, error: "" });
try {
const response = await axios.get("/api/maintenance/config");
@@ -43,9 +50,12 @@ export default function MaintenanceProvider({ children }) {
const message = err.response?.data?.error || err.message || "Fehler beim Laden der Wartungsdaten";
applyState({ loading: false, error: message });
}
}, [applyState]);
}, [applyState, canAccessMaintenance]);
const refreshUpdateStatus = useCallback(async () => {
if (!canAccessMaintenance) {
return;
}
try {
const response = await axios.get("/api/maintenance/update-status");
const data = response.data ?? {};
@@ -59,13 +69,20 @@ export default function MaintenanceProvider({ children }) {
const message = err.response?.data?.error || err.message || "Fehler beim Aktualisieren des Wartungsstatus";
setState((prev) => ({ ...prev, error: message }));
}
}, []);
}, [canAccessMaintenance]);
useEffect(() => {
if (!canAccessMaintenance) {
applyState({ loading: false, error: "" });
return;
}
fetchConfig();
}, [fetchConfig]);
}, [applyState, fetchConfig, canAccessMaintenance]);
useEffect(() => {
if (!canAccessMaintenance) {
return undefined;
}
if (state.update?.running) {
refreshUpdateStatus();
const intervalId = setInterval(() => {
@@ -79,15 +96,15 @@ export default function MaintenanceProvider({ children }) {
pollingRef.current = null;
}
return undefined;
}, [state.update?.running, refreshUpdateStatus]);
}, [canAccessMaintenance, state.update?.running, refreshUpdateStatus]);
useEffect(() => {
const currentlyRunning = Boolean(state.update?.running);
if (!currentlyRunning && wasRunningRef.current) {
if (canAccessMaintenance && !currentlyRunning && wasRunningRef.current) {
fetchConfig();
}
wasRunningRef.current = currentlyRunning;
}, [state.update?.running, fetchConfig]);
}, [canAccessMaintenance, state.update?.running, fetchConfig]);
const triggerUpdate = useCallback(async (payload = {}) => {
await axios.post("/api/maintenance/portainer-update", payload);
@@ -145,11 +162,6 @@ export default function MaintenanceProvider({ children }) {
return response.data ?? {};
}, []);
const deleteSetupEndpoint = useCallback(async (endpointId) => {
const response = await axios.delete(`/api/setup/endpoints/${endpointId}`);
return response.data ?? { success: false };
}, []);
const deleteSetupServer = useCallback(async (serverId) => {
const response = await axios.delete(`/api/setup/servers/${serverId}`);
return response.data ?? { success: false };
@@ -160,6 +172,11 @@ export default function MaintenanceProvider({ children }) {
return response.data ?? { success: false };
}, []);
const updateSelfStackId = useCallback(async (selfStackId) => {
const response = await axios.put("/api/setup/self-stack", { selfStackId });
return response.data ?? { success: false };
}, []);
const removeSuperuserAccount = useCallback(async () => {
const response = await axios.delete("/api/auth/superuser");
return response.data ?? { success: false };
@@ -184,11 +201,11 @@ export default function MaintenanceProvider({ children }) {
testSshConnection,
fetchSuperuserStatus,
fetchSetupStatus,
deleteSetupEndpoint,
deleteSetupServer,
updateSetupApiKey,
updateSelfStackId,
removeSuperuserAccount
}), [state, fetchConfig, refreshUpdateStatus, setMaintenanceMode, triggerUpdate, saveScript, resetScript, saveSshConfig, deleteSshConfig, testSshConnection, fetchSuperuserStatus, fetchSetupStatus, deleteSetupEndpoint, deleteSetupServer, updateSetupApiKey, removeSuperuserAccount]);
}), [state, fetchConfig, refreshUpdateStatus, setMaintenanceMode, triggerUpdate, saveScript, resetScript, saveSshConfig, deleteSshConfig, testSshConnection, fetchSuperuserStatus, fetchSetupStatus, deleteSetupServer, updateSetupApiKey, updateSelfStackId, removeSuperuserAccount]);
return (
<MaintenanceContext.Provider value={value}>
+9 -1
View File
@@ -1,4 +1,4 @@
import React, { useCallback, useMemo, useState } from "react";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import axios from "axios";
import {
Card,
@@ -12,6 +12,7 @@ import {
} from "@material-tailwind/react";
import { useNavigate } from "react-router-dom";
import { useToast } from "@/components/ToastProvider.jsx";
import { useAuth } from "@/components/AuthProvider.jsx";
const PHRASE_WORD_COUNT = 8;
@@ -36,6 +37,7 @@ const buildPhrasePayload = (words = []) => {
export function ForgotPassword() {
const navigate = useNavigate();
const { showToast } = useToast();
const { user } = useAuth();
const [username, setUsername] = useState("");
const [phraseInputs, setPhraseInputs] = useState(Array(PHRASE_WORD_COUNT).fill(""));
@@ -50,6 +52,12 @@ export function ForgotPassword() {
const [resetting, setResetting] = useState(false);
const [resetError, setResetError] = useState("");
useEffect(() => {
if (user) {
navigate("/dashboard/stacks", { replace: true });
}
}, [user, navigate]);
const phraseWords = useMemo(() => {
if (fileContent.trim()) {
return extractWordsFromText(fileContent);
+32 -1
View File
@@ -26,10 +26,41 @@ import {
ordersOverviewData,
} from "@/data";
import { CheckCircleIcon, ClockIcon } from "@heroicons/react/24/solid";
import { useMaintenance } from "@/components/MaintenanceProvider.jsx";
const UPDATE_STAGE_LABELS = {
initializing: "Vorbereitung",
"activating-maintenance": "Wartungsmodus aktivieren",
"executing-script": "Skript wird ausgeführt",
waiting: "Warte auf Portainer",
completed: "Abgeschlossen",
failed: "Fehlgeschlagen"
};
export function Home() {
const { maintenance: maintenanceMeta, update: updateState } = useMaintenance();
const maintenanceActive = Boolean(maintenanceMeta?.active);
const maintenanceMessage = maintenanceMeta?.message;
const updateRunning = Boolean(updateState?.running);
const updateStageLabel = updateState?.stage ? (UPDATE_STAGE_LABELS[updateState.stage] ?? updateState.stage) : "–";
const maintenanceLocked = maintenanceActive || updateRunning;
return (
<div className="mt-12">
{(maintenanceActive || updateRunning) && (
<div className="rounded-lg border border-cyan-500/60 bg-cyan-900/30 px-4 py-3 text-sm text-bluegray-100 mb-8">
<div className="flex flex-col gap-1">
<span>
Wartungsmodus aktiv{maintenanceMessage ? ` – ${maintenanceMessage}` : updateRunning ? " – Portainer-Update läuft" : ""}.
</span>
{updateRunning && (
<span className="text-xs text-indigo-900">
Phase: {updateStageLabel}
</span>
)}
</div>
</div>
)}
<div className="mb-12 grid gap-y-10 gap-x-6 md:grid-cols-2 xl:grid-cols-4">
{statisticsCardsData.map(({ icon, title, footer, ...rest }) => (
<StatisticsCard
@@ -87,7 +118,7 @@ export function Home() {
</div>
<Menu placement="left-start">
<MenuHandler>
<IconButton size="sm" variant="text" color="blue-gray">
<IconButton size="sm" variant="text" color="blue-gray" disabled={maintenanceLocked}>
<EllipsisVerticalIcon
strokeWidth={3}
fill="currenColor"
+17 -16
View File
@@ -693,18 +693,19 @@ export function Logs() {
const toggleOpen = () => setOpen((cur) => !cur);
return (
<div className="mt-12 mb-8 flex flex-col gap-12">
{(maintenanceActive || updateRunning) && (<div className="rounded-lg border border-cyan-500/60 bg-cyan-900/30 px-4 py-3 text-sm text-bluegray-100">
<div className="flex flex-col gap-1">
<span>
Wartungsmodus aktiv{maintenanceMessage ? ` – ${maintenanceMessage}` : updateRunning ? " – Portainer-Update läuft" : ""}.
</span>
{updateRunning && (
<span className="text-xs text-indigo-900">
Phase: {updateStageLabel}
{(maintenanceActive || updateRunning) && (
<div className="rounded-lg border border-cyan-500/60 bg-cyan-900/30 px-4 py-3 text-sm text-bluegray-100">
<div className="flex flex-col gap-1">
<span>
Wartungsmodus aktiv{maintenanceMessage ? ` – ${maintenanceMessage}` : updateRunning ? " – Portainer-Update läuft" : ""}.
</span>
)}
{updateRunning && (
<span className="text-xs text-indigo-900">
Phase: {updateStageLabel}
</span>
)}
</div>
</div>
</div>
)}
<Card>
<CardHeader variant="gradient" color="gray" className="mb-5 p-4">
@@ -738,12 +739,12 @@ export function Logs() {
<>
<Button
onClick={() => handleExport('txt')}
disabled={actionLoading || loading}
disabled={maintenanceLocked || actionLoading || loading}
className="w-full md:flex-1">
Export TXT</Button>
<Button
onClick={() => handleExport('sql')}
disabled={actionLoading || loading}
disabled={maintenanceLocked || actionLoading || loading}
className="w-full md:flex-1">
Export SQL</Button>
</>
@@ -751,7 +752,7 @@ export function Logs() {
{canDeleteLogs && (
<Button
onClick={handleDeleteFiltered}
disabled={actionLoading || loading || logs.length === 0}
disabled={maintenanceLocked || actionLoading || loading || logs.length === 0}
className="w-full md:flex-1 bg-sunsetCoral-500 hover:bg-sunsetCoral-600">
Angezeigte löschen</Button>
)}
@@ -1075,7 +1076,7 @@ export function Logs() {
}}
variant="static"
label="Kontext-ID"
placeholder="z. B. Endpoint-ID"
placeholder="z. B. Server-ID"
/>
</div>
</div>
@@ -1141,7 +1142,7 @@ export function Logs() {
<div className="flex-1 min-w-[180px]">
<Button
onClick={handleResetFilters}
disabled={actionLoading || loading}
disabled={maintenanceLocked || actionLoading || loading}
className="w-full"
>
Zurücksetzen
@@ -1298,7 +1299,7 @@ export function Logs() {
<Typography variant="small">
<button
onClick={() => handleDeleteLog(log.id)}
disabled={actionLoading}
disabled={maintenanceLocked || actionLoading}
className="rounded-md border border-sunsetCoral-600 px-3 py-1 text-xs text-sunsetCoral-800 transition hover:bg-sunsetCoral-600/20 disabled:opacity-60"
>
Löschen
+168 -112
View File
@@ -10,7 +10,8 @@ import {
CardBody,
Button,
Switch,
Input
Input,
Alert
} from "@material-tailwind/react";
const UPDATE_STATUS_LABELS = {
@@ -128,9 +129,9 @@ export function Maintenance() {
testSshConnection,
fetchSuperuserStatus,
fetchSetupStatus,
deleteSetupEndpoint,
deleteSetupServer,
updateSetupApiKey,
updateSelfStackId,
removeSuperuserAccount
} = useMaintenance();
@@ -140,6 +141,7 @@ export function Maintenance() {
const canViewServers = Boolean(isSuperuserAccount || hasPermission("maintenance-server-manage", "read"));
const canEditServers = Boolean(isSuperuserAccount || hasPermission("maintenance-server-manage", "full"));
const canDeleteServers = Boolean(isSuperuserAccount || hasPermission("maintenance-server-delete", "full"));
const canDeleteSuperuser = Boolean(isSuperuserAccount || hasPermission("maintenance-superuser-delete", "full"));
const canViewPortainer = Boolean(isSuperuserAccount || hasPermission("maintenance-portainer", "read"));
const canManagePortainerUpdate = Boolean(isSuperuserAccount || hasPermission("maintenance-update", "full"));
const canViewSsh = Boolean(isSuperuserAccount || hasPermission("maintenance-ssh-update", "read"));
@@ -151,6 +153,7 @@ export function Maintenance() {
const maintenanceMessage = maintenanceMeta?.message;
const maintenanceExtraType = maintenanceMeta?.extra?.type;
const updateRunning = Boolean(updateState?.running);
const maintenanceLocked = maintenanceActive || updateRunning;
const [scriptDraft, setScriptDraft] = useState("");
const [scriptSaving, setScriptSaving] = useState(false);
@@ -214,7 +217,9 @@ export function Maintenance() {
const [apiKeyDrafts, setApiKeyDrafts] = useState({});
const [apiKeyUpdatingId, setApiKeyUpdatingId] = useState(null);
const [serverDeleteId, setServerDeleteId] = useState(null);
const [endpointDeleteId, setEndpointDeleteId] = useState(null);
const [selfStackDraft, setSelfStackDraft] = useState("");
const [selfStackSaving, setSelfStackSaving] = useState(false);
const [selfStackError, setSelfStackError] = useState("");
const [duplicates, setDuplicates] = useState([]);
const [duplicatesLoading, setDuplicatesLoading] = useState(true);
@@ -381,12 +386,21 @@ export function Maintenance() {
}, [fetchDuplicates]);
const setupServers = useMemo(() => setupResources?.servers?.items ?? [], [setupResources]);
const setupEndpoints = useMemo(() => setupResources?.endpoints?.items ?? [], [setupResources]);
const apiKeyInfoMap = useMemo(() => {
const items = setupResources?.apiKeys?.items ?? [];
return new Map(items.map((entry) => [entry.serverId, entry]));
}, [setupResources]);
const setupComplete = useMemo(() => Boolean(setupResources?.setupComplete), [setupResources]);
const selfStackInfo = setupResources?.selfStack ?? null;
const currentSelfStackValue = selfStackInfo?.current ?? "";
const envSelfStackValue = setupResources?.envDefaults?.selfStackId ?? "";
useEffect(() => {
setSelfStackDraft(currentSelfStackValue || "");
setSelfStackError("");
}, [currentSelfStackValue]);
const selfStackDirty = selfStackDraft !== currentSelfStackValue;
const totals = useMemo(() => {
const groups = Array.isArray(duplicates) ? duplicates.length : 0;
@@ -525,7 +539,7 @@ export function Maintenance() {
}, [canManageSsh, deleteSshConfig, showToast]);
const handleSuperuserDelete = useCallback(async () => {
if (superuserDeleteLoading || superuserStatusLoading || !superuserExists) {
if (!canDeleteSuperuser || superuserDeleteLoading || superuserStatusLoading || !superuserExists) {
return;
}
@@ -566,48 +580,7 @@ export function Maintenance() {
} finally {
setSuperuserDeleteLoading(false);
}
}, [superuserDeleteLoading, superuserStatusLoading, superuserExists, removeSuperuserAccount, loadSuperuserStatus, showToast]);
const handleDeleteEndpoint = useCallback(async (endpointId) => {
if (!canDeleteServers) {
return;
}
if (endpointDeleteId === endpointId) {
return;
}
const target = setupEndpoints.find((entry) => entry.id === endpointId);
if (!target) {
return;
}
const label = target.name || `Endpoint ${target.external_id}`;
const confirmMessage = `Endpoint "${label}" wirklich löschen?${target.is_default ? "\nDieser Endpoint ist aktuell als Standard markiert." : ""}`;
if (typeof window !== "undefined") {
const confirmed = window.confirm(confirmMessage);
if (!confirmed) {
return;
}
}
setEndpointDeleteId(endpointId);
try {
await deleteSetupEndpoint(endpointId);
showToast({
variant: "success",
title: "Endpoint gelöscht",
description: `Endpoint "${label}" wurde entfernt.`
});
await loadSetupResources({ silent: true });
} catch (err) {
const message = err.response?.data?.error || err.message || "Endpoint konnte nicht gelöscht werden.";
showToast({
variant: "error",
title: "Endpoint löschen fehlgeschlagen",
description: message
});
} finally {
setEndpointDeleteId(null);
}
}, [canDeleteServers, deleteSetupEndpoint, endpointDeleteId, loadSetupResources, setupEndpoints, showToast]);
}, [canDeleteSuperuser, superuserDeleteLoading, superuserStatusLoading, superuserExists, removeSuperuserAccount, loadSuperuserStatus, showToast]);
const handleDeleteServer = useCallback(async (serverId) => {
if (!canDeleteServers) {
@@ -620,12 +593,10 @@ export function Maintenance() {
if (!target) {
return;
}
const linkedEndpoints = setupEndpoints.filter((entry) => entry.server_id === serverId);
const label = target.name || target.url || `Server ${serverId}`;
const confirmMessage = [
`Server "${label}" wirklich löschen?`,
linkedEndpoints.length ? `Dabei werden ${linkedEndpoints.length} zugeordnete Endpoint(s) entfernt.` : null,
"Das System benötigt anschließend erneut einen gültigen Server/Endpoint im Setup."
"Das System benötigt anschließend erneut einen gültigen Server im Setup."
].filter(Boolean).join("\n");
if (typeof window !== "undefined") {
@@ -654,7 +625,7 @@ export function Maintenance() {
} finally {
setServerDeleteId(null);
}
}, [canDeleteServers, deleteSetupServer, loadSetupResources, serverDeleteId, setupEndpoints, setupServers, showToast]);
}, [canDeleteServers, deleteSetupServer, loadSetupResources, serverDeleteId, setupServers, showToast]);
const handleApiKeyDraftChange = useCallback((serverId, value) => {
if (!canEditServers) return;
@@ -706,6 +677,79 @@ export function Maintenance() {
}
}, [apiKeyDrafts, apiKeyUpdatingId, canEditServers, loadSetupResources, showToast, updateSetupApiKey]);
const handleSelfStackDraftChange = useCallback((value) => {
if (!canEditServers) return;
setSelfStackDraft(value);
setSelfStackError("");
}, [canEditServers]);
const handleSelfStackSave = useCallback(async () => {
if (!canEditServers || !selfStackDirty) {
return;
}
setSelfStackSaving(true);
setSelfStackError("");
const normalizedValue = typeof selfStackDraft === "string" ? selfStackDraft.trim() : "";
try {
await updateSelfStackId(normalizedValue);
showToast({
variant: "success",
title: "Self-Stack-ID gespeichert",
description: normalizedValue
? `Self-Stack-ID wurde auf "${normalizedValue}" gesetzt.`
: "Self-Stack-ID wurde entfernt."
});
await loadSetupResources({ silent: true });
} catch (err) {
const message = err.response?.data?.error || err.message || "Self-Stack-ID konnte nicht aktualisiert werden.";
setSelfStackError(message);
showToast({
variant: "error",
title: "Self-Stack-ID",
description: message
});
} finally {
setSelfStackSaving(false);
}
}, [canEditServers, loadSetupResources, selfStackDirty, selfStackDraft, showToast, updateSelfStackId]);
const handleSelfStackRemove = useCallback(async () => {
if (!canEditServers) {
return;
}
if (!selfStackDraft && !currentSelfStackValue) {
return;
}
if (typeof window !== "undefined") {
const confirmed = window.confirm("Self-Stack-ID wirklich entfernen?");
if (!confirmed) {
return;
}
}
setSelfStackSaving(true);
setSelfStackError("");
try {
await updateSelfStackId("");
setSelfStackDraft("");
showToast({
variant: "success",
title: "Self-Stack-ID entfernt",
description: "Die Self-Stack-ID wurde gelöscht."
});
await loadSetupResources({ silent: true });
} catch (err) {
const message = err.response?.data?.error || err.message || "Self-Stack-ID konnte nicht entfernt werden.";
setSelfStackError(message);
showToast({
variant: "error",
title: "Self-Stack-ID",
description: message
});
} finally {
setSelfStackSaving(false);
}
}, [canEditServers, currentSelfStackValue, loadSetupResources, selfStackDraft, showToast, updateSelfStackId]);
const handleMaintenanceToggle = useCallback(async (nextActive) => {
if (!canControlMaintenance) return;
if (maintenanceLoading || maintenanceToggleLoading) return;
@@ -935,7 +979,7 @@ export function Maintenance() {
color="white"
className="flex items-center justify-between"
>
<span>Server &amp; Endpoints</span>
<span>Server &amp; API-Keys</span>
</Typography>
</CardHeader>
@@ -1008,7 +1052,7 @@ export function Maintenance() {
color="red"
size="sm"
onClick={() => handleDeleteServer(server.id)}
disabled={!canDeleteServers || setupResourcesLoading || serverDeleteId === server.id || endpointDeleteId !== null || apiKeyUpdatingId === server.id}
disabled={maintenanceLocked || !canDeleteServers || setupResourcesLoading || serverDeleteId === server.id || apiKeyUpdatingId === server.id}
>
{serverDeleteId === server.id ? "Lösche…" : "Löschen"}
</Button>
@@ -1016,7 +1060,7 @@ export function Maintenance() {
color="blue"
size="sm"
onClick={() => handleApiKeyUpdate(server.id)}
disabled={!canEditServers || setupResourcesLoading || apiKeyUpdatingId === server.id}
disabled={maintenanceLocked || !canEditServers || setupResourcesLoading || apiKeyUpdatingId === server.id}
>
{apiKeyUpdatingId === server.id ? "Speichere…" : "API-Key speichern"}
</Button>
@@ -1029,61 +1073,74 @@ export function Maintenance() {
)}
</div>
<div className="flex flex-col gap-3">
<div className="flex items-center justify-between">
<div className="flex flex-col gap-3 border-t border-blue-gray-50 pt-4">
<div className="flex flex-col gap-1">
<Typography variant="small" color="blue-gray" className="font-semibold uppercase">
Endpoints
Self-Stack-ID
</Typography>
<Typography variant="small" color="blue-gray">
{setupEndpoints.length} vorhanden
</Typography>
</div>
{setupEndpoints.length === 0 ? (
<p className="text-sm text-stormGrey-500">
Es sind derzeit keine Endpoints hinterlegt.
<p className="text-xs text-stormGrey-500">
Optional – lass das Feld leer, wenn keine Self-Stack-ID benötigt wird.
</p>
) : (
<div className="flex flex-col gap-3">
{setupEndpoints.map((endpoint) => (
<div
key={endpoint.id}
className="flex flex-col gap-2 rounded-md border border-blue-gray-100 p-3 md:flex-row md:items-center md:justify-between"
>
<div>
<div className="flex items-center gap-2">
<p className="text-sm font-medium text-stormGrey-900">
{endpoint.name || `Endpoint ${endpoint.external_id}`}
</p>
{endpoint.is_default ? (
<span className="rounded-full bg-arcticBlue-100 px-2 py-0.5 text-[10px] font-semibold text-arcticBlue-700">
Standard
</span>
) : null}
</div>
<p className="text-xs text-stormGrey-500">
ID: {endpoint.external_id} · Server: {endpoint.server_name || endpoint.server_url}
</p>
</div>
<div className="flex items-center gap-2">
<Button
variant="text"
color="red"
size="sm"
onClick={() => handleDeleteEndpoint(endpoint.id)}
disabled={!canDeleteServers || setupResourcesLoading || endpointDeleteId === endpoint.id || serverDeleteId !== null}
>
{endpointDeleteId === endpoint.id ? "Lösche…" : "Löschen"}
</Button>
</div>
</div>
))}
</div>
{selfStackInfo?.envProvided && (
<p className="text-xs text-stormGrey-500">
Hinweis: Eine Umgebungsvariable liefert bereits einen Standardwert.
</p>
)}
<div className="flex flex-col gap-2 md:flex-row md:items-end">
<div className="flex-1">
<Input
label="Self-Stack-ID"
value={selfStackDraft}
onChange={(event) => handleSelfStackDraftChange(event.target.value)}
disabled={!canEditServers || setupResourcesLoading || selfStackSaving}
/>
</div>
{canEditServers && (
<div className="flex items-center justify-end gap-2">
<Button
variant="text"
color="red"
size="sm"
onClick={handleSelfStackRemove}
disabled={maintenanceLocked || selfStackSaving || (!currentSelfStackValue && !selfStackDraft)}
>
Entfernen
</Button>
<Button
color="blue"
size="sm"
onClick={handleSelfStackSave}
disabled={maintenanceLocked || selfStackSaving || setupResourcesLoading || !selfStackDirty}
>
{selfStackSaving ? "Speichere…" : "Self-Stack-ID speichern"}
</Button>
</div>
)}
</div>
{!canEditServers && (
<p className="text-xs text-stormGrey-500">
Bearbeitung benötigt das Gruppenrecht „Server-Sektion“.
</p>
)}
<p className="text-xs text-stormGrey-500">
Aktuell gespeichert: {currentSelfStackValue || "nicht gesetzt"}
</p>
{envSelfStackValue && (
<p className="text-xs text-stormGrey-500">
Vorgabe aus Umgebung: {envSelfStackValue}
</p>
)}
{selfStackError && (
<Alert color="red" className="border border-red-200 bg-red-50 text-red-700">
{selfStackError}
</Alert>
)}
</div>
{!setupComplete && (
<Alert color="amber" className="border border-amber-200 bg-amber-50 text-amber-800">
Das Setup ist aktuell unvollständig. Bitte öffne den Setup-Bereich, um Server, Endpoint und API-Key erneut festzulegen.
Das Setup ist aktuell unvollständig. Bitte öffne den Setup-Bereich, um Server und API-Key erneut festzulegen.
</Alert>
)}
@@ -1093,7 +1150,7 @@ export function Maintenance() {
color="gray"
size="sm"
onClick={() => loadSetupResources()}
disabled={setupResourcesLoading}
disabled={maintenanceLocked || setupResourcesLoading}
>
Aktualisieren
</Button>
@@ -1142,7 +1199,7 @@ export function Maintenance() {
variant="outlined"
color="gray"
onClick={() => loadSuperuserStatus()}
disabled={superuserStatusLoading || superuserDeleteLoading}
disabled={maintenanceLocked || superuserStatusLoading || superuserDeleteLoading}
className="w-full sm:w-auto"
>
Status aktualisieren
@@ -1150,7 +1207,7 @@ export function Maintenance() {
<Button
color="red"
onClick={handleSuperuserDelete}
disabled={superuserStatusLoading || superuserDeleteLoading || !superuserExists}
disabled={maintenanceLocked || !canDeleteSuperuser || superuserStatusLoading || superuserDeleteLoading || !superuserExists}
className="w-full sm:w-auto"
>
{superuserDeleteLoading ? "Wird gelöscht…" : "Superuser löschen"}
@@ -1203,7 +1260,7 @@ export function Maintenance() {
<div className="flex items-center gap-3">
<Button
onClick={() => fetchPortainerStatus({ silent: false })}
disabled={portainerLoading || portainerRefreshing}
disabled={maintenanceLocked || portainerLoading || portainerRefreshing}
className="w-full">
Status aktualisieren
</Button>
@@ -1424,14 +1481,14 @@ export function Maintenance() {
<Button
onClick={handleSshSaveConfig}
disabled={!canManageSsh || sshSaving || sshTesting || sshDeleting || updateRunning}
disabled={!canManageSsh || sshSaving || sshTesting || sshDeleting || maintenanceLocked}
className="mt-3 w-full">
{sshSaving ? 'Speichern…' : 'SSH-Konfiguration speichern'}
</Button>
<Button
onClick={handleSshTestConnection}
disabled={!canManageSsh || sshSaving || sshTesting || sshDeleting || updateRunning}
disabled={!canManageSsh || sshSaving || sshTesting || sshDeleting || maintenanceLocked}
className="w-full bg-arcticBlue-500 hover:bg-arcticBlue-600">
{sshTesting ? 'Test läuft…' : 'Verbindung testen'}
</Button>
@@ -1439,7 +1496,7 @@ export function Maintenance() {
<Button
onClick={handleSshDeleteConfig}
disabled={!canManageSsh || sshSaving || sshTesting || sshDeleting || updateRunning}
disabled={!canManageSsh || sshSaving || sshTesting || sshDeleting || maintenanceLocked}
className="w-full hover:bg-sunsetCoral-600 bg-sunsetCoral-500"
>
{sshDeleting ? 'Löschen…' : 'SSH-Einstellungen löschen'}
@@ -1469,14 +1526,14 @@ export function Maintenance() {
<div className="mt-3 grid gap-2">
<Button
onClick={handleScriptSave}
disabled={!canManageSsh || !scriptIsDirty || scriptSaving || updateRunning}
disabled={!canManageSsh || !scriptIsDirty || scriptSaving || maintenanceLocked}
className="mt-3 w-full">
Speichern
</Button>
<Button
color="purple"
onClick={handleScriptReset}
disabled={!canManageSsh || !scriptConfig || scriptConfig.source !== "custom" || scriptSaving || updateRunning}
disabled={!canManageSsh || !scriptConfig || scriptConfig.source !== "custom" || scriptSaving || maintenanceLocked}
className="w-full hover:bg-sunsetCoral-600 bg-sunsetCoral-500">
Standard wiederherstellen
</Button>
@@ -1552,7 +1609,7 @@ export function Maintenance() {
<Button
onClick={handleTriggerUpdate}
disabled={!canManagePortainerUpdate || disableUpdateButton || updateActionLoading}
disabled={maintenanceLocked || !canManagePortainerUpdate || disableUpdateButton || updateActionLoading}
className="mt-3 w-full">
{updateActionLoading ? "Update wird gestartet…" : "Portainer aktualisieren"}
</Button>
@@ -1649,7 +1706,7 @@ export function Maintenance() {
</span>
</div>
<p className="text-sm text-gray-300">
Behaltener Stack: ID {canonicalId} (Endpoint {entry?.canonical?.EndpointId ?? "-"})
Behaltener Stack: ID {canonicalId}
</p>
<p className="text-xs text-gray-500">
Typ: {resolveStackType(entry?.canonical?.Type)} • Erstellt: {formatCreatedAt(entry?.canonical?.Created)}
@@ -1674,7 +1731,6 @@ export function Maintenance() {
>
<div className="flex flex-wrap items-center justify-between gap-2">
<span className="font-semibold text-white">ID: {duplicate.Id}</span>
<span>Endpoint: {duplicate.EndpointId ?? "-"}</span>
<span>Typ: {resolveStackType(duplicate.Type)}</span>
<span>Erstellt: {formatCreatedAt(duplicate.Created)}</span>
</div>
@@ -11,6 +11,15 @@ import {
import { useNavigate } from "react-router-dom";
import { ArrowDownTrayIcon, ArrowPathIcon } from "@heroicons/react/24/outline";
import { useAuth } from "@/components/AuthProvider.jsx";
import { useMaintenance } from "@/components/MaintenanceProvider.jsx";
const UPDATE_STAGE_LABELS = {
initializing: "Vorbereitung",
"activating-maintenance": "Wartungsmodus aktivieren",
"executing-script": "Skript wird ausgeführt",
waiting: "Warte auf Portainer",
completed: "Abgeschlossen",
failed: "Fehlgeschlagen"
};
const formatWordsAsText = (words = []) => {
if (!Array.isArray(words) || words.length === 0) {
@@ -37,6 +46,7 @@ const groupWords = (words = []) => {
export function SecurityPhrase() {
const navigate = useNavigate();
const { user, setSession, refreshSession } = useAuth();
const { maintenance: maintenanceMeta, update: updateState } = useMaintenance();
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
const [words, setWords] = useState([]);
@@ -44,6 +54,11 @@ export function SecurityPhrase() {
const [downloaded, setDownloaded] = useState(false);
const requiresDownload = Boolean(user?.requiresSecurityPhraseDownload);
const maintenanceActive = Boolean(maintenanceMeta?.active);
const maintenanceMessage = maintenanceMeta?.message;
const updateRunning = Boolean(updateState?.running);
const updateStageLabel = updateState?.stage ? (UPDATE_STAGE_LABELS[updateState.stage] ?? updateState.stage) : "–";
const maintenanceLocked = maintenanceActive || updateRunning;
const groupedWords = useMemo(() => groupWords(words), [words]);
@@ -83,6 +98,11 @@ export function SecurityPhrase() {
navigate("/dashboard/stacks", { replace: true });
return;
}
if (err?.response?.status === 401) {
await refreshSession();
navigate("/auth/sign-in", { replace: true });
return;
}
const message = err?.response?.data?.error || err?.message || "Sicherheitsschlüssel konnte nicht geladen werden.";
setError(message);
@@ -133,6 +153,11 @@ export function SecurityPhrase() {
setDownloaded(Boolean(downloadedAt));
navigate("/dashboard/stacks", { replace: true });
} catch (err) {
if (err?.response?.status === 401) {
await refreshSession();
navigate("/auth/sign-in", { replace: true });
return;
}
const message = err?.response?.data?.error || err?.message || "Download konnte nicht bestätigt werden.";
setError(message);
} finally {
@@ -223,7 +248,7 @@ export function SecurityPhrase() {
size="md"
className="flex items-center gap-2 normal-case"
onClick={handleDownload}
disabled={saving || downloaded || words.length === 0}
disabled={maintenanceLocked || saving || downloaded || words.length === 0}
>
<ArrowDownTrayIcon className="h-5 w-5" />
Sicherheitsschlüssel herunterladen
+12 -11
View File
@@ -819,18 +819,19 @@ export function Stacks() {
<div className="mt-12 mb-8 flex flex-col gap-12">
{(maintenanceActive || updateRunning) && (<div className="rounded-lg border border-cyan-500/60 bg-cyan-900/30 px-4 py-3 text-sm text-bluegray-100">
<div className="flex flex-col gap-1">
<span>
Wartungsmodus aktiv{maintenanceMessage ? ` – ${maintenanceMessage}` : updateRunning ? " – Portainer-Update läuft" : ""}.
</span>
{updateRunning && (
<span className="text-xs text-indigo-900">
Phase: {updateStageLabel}
{(maintenanceActive || updateRunning) && (
<div className="rounded-lg border border-cyan-500/60 bg-cyan-900/30 px-4 py-3 text-sm text-bluegray-100">
<div className="flex flex-col gap-1">
<span>
Wartungsmodus aktiv{maintenanceMessage ? ` – ${maintenanceMessage}` : updateRunning ? " – Portainer-Update läuft" : ""}.
</span>
)}
{updateRunning && (
<span className="text-xs text-indigo-900">
Phase: {updateStageLabel}
</span>
)}
</div>
</div>
</div>
)}
<Card>
@@ -1072,7 +1073,7 @@ export function Stacks() {
) : (canRedeploySingle ? (
<Button
onClick={() => handleRedeploy(stack.Id)}
disabled={isBusy}
disabled={maintenanceLocked || isBusy}
className="disabled:opacity-50 disabled:cursor-not-allowed"
>
Redeploy
+52 -16
View File
@@ -21,6 +21,15 @@ import { useAuth } from "@/components/AuthProvider.jsx";
const _ = AVATAR_COLORS.join(" ");
const UPDATE_STAGE_LABELS = {
initializing: "Vorbereitung",
"activating-maintenance": "Wartungsmodus aktivieren",
"executing-script": "Skript wird ausgeführt",
waiting: "Warte auf Portainer",
completed: "Abgeschlossen",
failed: "Fehlgeschlagen"
};
const normalizeUserGroups = (rawGroups) => {
if (!Array.isArray(rawGroups)) {
return [];
@@ -95,7 +104,7 @@ const buildInitialFormValues = (user) => {
export function UserDetails() {
const { userId } = useParams();
const { showToast } = useToast();
const { maintenance } = useMaintenance();
const { maintenance: maintenanceMeta, update: updateState } = useMaintenance();
const { hasPermission, user: authUser } = useAuth();
const [user, setUser] = useState(null);
@@ -115,7 +124,11 @@ export function UserDetails() {
const [securityPhraseError, setSecurityPhraseError] = useState("");
const [renewingSecurityPhrase, setRenewingSecurityPhrase] = useState(false);
const maintenanceActive = Boolean(maintenance?.active);
const maintenanceActive = Boolean(maintenanceMeta?.active);
const maintenanceMessage = maintenanceMeta?.message;
const updateRunning = Boolean(updateState?.running);
const updateStageLabel = updateState?.stage ? (UPDATE_STAGE_LABELS[updateState.stage] ?? updateState.stage) : "–";
const maintenanceLocked = maintenanceActive || updateRunning;
const canEditUsers = Boolean(authUser?.isSuperuser || hasPermission("users-edit", "full"));
const canReadUsers = Boolean(authUser?.isSuperuser || hasPermission("users-edit", "read"));
@@ -132,6 +145,8 @@ export function UserDetails() {
}
return user.groups.some((group) => (group?.name || "").toLowerCase() === "superuser");
}, [user]);
const canCurrentUserEditSuperuser = authUser?.isSuperuser && user?.id && Number(authUser.id) === Number(user.id);
const superuserFieldsLocked = isSuperuserUser && !canCurrentUserEditSuperuser;
const numericUserId = useMemo(() => {
const asNumber = Number(userId);
@@ -161,7 +176,7 @@ export function UserDetails() {
setSecurityPhraseWords([]);
setSecurityPhraseDownloadedAt(item.securityPhraseDownloadedAt || null);
setSecurityPhraseError("");
const initialValues = buildInitialFormValues(item);
const initialValues = buildInitialFormValues(item);
initialFormValuesRef.current = { ...initialValues };
setFormValues(initialValues);
setSaveError("");
@@ -436,7 +451,7 @@ export function UserDetails() {
}, [canRenewSecurityPhrase, fetchSecurityPhrase, renewingSecurityPhrase, securityPhraseLoading]);
const handleRenewSecurityPhrase = useCallback(async () => {
if (!canRenewSecurityPhrase || !numericUserId || maintenanceActive || renewingSecurityPhrase) {
if (!canRenewSecurityPhrase || !numericUserId || maintenanceLocked || renewingSecurityPhrase) {
return;
}
@@ -472,7 +487,7 @@ export function UserDetails() {
}, [
canRenewSecurityPhrase,
numericUserId,
maintenanceActive,
maintenanceLocked,
renewingSecurityPhrase,
showToast
]);
@@ -560,17 +575,26 @@ export function UserDetails() {
fetchSecurityPhrase();
}, [hasLoaded, canManageSecurityPhrase, fetchSecurityPhrase, numericUserId]);
const inputDisabled = maintenanceActive || savingUser || !user || !canEditUsers;
const selectDisabled = maintenanceActive || savingUser || !user || groupsLoading || !canEditUsers || isSuperuserUser;
const avatarSelectDisabled = maintenanceActive || savingUser || !user || !canEditUsers;
const inputDisabled = maintenanceLocked || savingUser || !user || !canEditUsers;
const selectDisabled = maintenanceLocked || savingUser || !user || groupsLoading || !canEditUsers || isSuperuserUser;
const avatarSelectDisabled = maintenanceLocked || savingUser || !user || !canEditUsers || superuserFieldsLocked;
const groupSelectValue = formValues.groupId ? String(formValues.groupId) : "";
return (
<>
<div className="mt-12 flex flex-col gap-12">
{maintenanceActive && (
<div className="mb-3 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
Wartungsmodus aktiv – Änderungen sind deaktiviert.
{(maintenanceActive || updateRunning) && (
<div className="rounded-lg border border-cyan-500/60 bg-cyan-900/30 px-4 py-3 text-sm text-bluegray-100">
<div className="flex flex-col gap-1">
<span>
Wartungsmodus aktiv{maintenanceMessage ? ` – ${maintenanceMessage}` : updateRunning ? " – Portainer-Update läuft" : ""}.
</span>
{updateRunning && (
<span className="text-xs text-indigo-900">
Phase: {updateStageLabel}
</span>
)}
</div>
</div>
)}
<div className="relative h-72 w-full overflow-hidden rounded-xl bg-[url('/img/background-image.png')] bg-cover\tbg-center">
@@ -600,7 +624,7 @@ export function UserDetails() {
color="green"
className="normal-case"
onClick={handleSaveUser}
disabled={maintenanceActive || savingUser}
disabled={maintenanceLocked || savingUser}
>
{savingUser ? "Speichert ..." : "Änderungen speichern"}
</Button>
@@ -627,6 +651,16 @@ export function UserDetails() {
<Typography variant="h6" color="blue-gray" className="mb-4">
Allgemeine Einstellungen
</Typography>
{(isSuperuserUser) && (
<div className="mb-4 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
Systemgruppe – Name und Beschreibung sind geschützt.
{superuserFieldsLocked && (
<span className="mt-1 block text-xs text-amber-600">
Nur der Superuser selbst kann Benutzername, E-Mail-Adresse und Passwort ändern.
</span>
)}
</div>
)}
<div className="mb-6">
<Typography className="mb-2 block text-xs font-semibold uppercase text-blue-gray-500">
Benutzername
@@ -635,7 +669,7 @@ export function UserDetails() {
value={formValues.username}
onChange={handleUsernameChange}
placeholder="Benutzername"
disabled={inputDisabled}
disabled={inputDisabled || superuserFieldsLocked}
className=" !border-t-blue-gray-200 focus:!border-t-gray-900"
labelProps={{
className: "before:content-none after:content-none"
@@ -651,7 +685,7 @@ export function UserDetails() {
value={formValues.email}
onChange={handleEmailChange}
placeholder="benutzer@example.com"
disabled={inputDisabled}
disabled={inputDisabled || superuserFieldsLocked}
className=" !border-t-blue-gray-200 focus:!border-t-gray-900"
labelProps={{
className: "before:content-none after:content-none"
@@ -667,7 +701,7 @@ export function UserDetails() {
value={formValues.password}
onChange={handlePasswordChange}
placeholder="Passwort setzen"
disabled={inputDisabled}
disabled={inputDisabled || superuserFieldsLocked}
className=" !border-t-blue-gray-200 focus:!border-t-gray-900"
labelProps={{
className: "before:content-none after:content-none"
@@ -787,6 +821,7 @@ export function UserDetails() {
onClick={handleReloadSecurityPhrase}
disabled={
!canRenewSecurityPhrase ||
maintenanceLocked ||
securityPhraseLoading ||
renewingSecurityPhrase
}
@@ -799,7 +834,7 @@ export function UserDetails() {
onClick={handleRenewSecurityPhrase}
disabled={
!canRenewSecurityPhrase ||
maintenanceActive ||
maintenanceLocked ||
securityPhraseLoading ||
renewingSecurityPhrase
}
@@ -824,6 +859,7 @@ export function UserDetails() {
onClick={handleReloadSecurityPhrase}
disabled={
!canRenewSecurityPhrase ||
maintenanceLocked ||
securityPhraseLoading ||
renewingSecurityPhrase
}
+174 -112
View File
@@ -24,6 +24,15 @@ import { AVATAR_COLORS } from "@/data/avatarColors.js";
const _ = AVATAR_COLORS.join(" ");
const UPDATE_STAGE_LABELS = {
initializing: "Vorbereitung",
"activating-maintenance": "Wartungsmodus aktivieren",
"executing-script": "Skript wird ausgeführt",
waiting: "Warte auf Portainer",
completed: "Abgeschlossen",
failed: "Fehlgeschlagen"
};
const mapGroup = (item) => ({
id: item?.id ?? null,
name: item?.name || "",
@@ -63,11 +72,19 @@ const LEVEL_OPTIONS = [
{ value: "read", label: "Nur lesen" },
{ value: "none", label: "Kein Zugriff" }
];
const LEVEL_PRIORITY = {
none: 0,
read: 1,
full: 2
};
const normalizePermissionLevel = (key, value) =>
key === "maintenance-server-delete" && value !== "full" ? "none" : value;
export function UserGroupDetail() {
const { groupId } = useParams();
const { showToast } = useToast();
const { maintenance } = useMaintenance();
const { maintenance: maintenanceMeta, update: updateState } = useMaintenance();
const { hasPermission, user: authUser } = useAuth();
const [group, setGroup] = useState(null);
@@ -85,12 +102,17 @@ export function UserGroupDetail() {
const [permissionsLoading, setPermissionsLoading] = useState(false);
const [permissionsError, setPermissionsError] = useState("");
const maintenanceActive = Boolean(maintenance?.active);
const maintenanceActive = Boolean(maintenanceMeta?.active);
const maintenanceMessage = maintenanceMeta?.message;
const updateRunning = Boolean(updateState?.running);
const updateStageLabel = updateState?.stage ? (UPDATE_STAGE_LABELS[updateState.stage] ?? updateState.stage) : "–";
const maintenanceLocked = maintenanceActive || updateRunning;
const isSuperuserGroup = useMemo(() => (group?.name || "").toLowerCase() === "superuser", [group]);
const isSuperuserAccount = Boolean(authUser?.isSuperuser);
const canEditGroupDetails = Boolean(isSuperuserAccount || hasPermission("user-groups-edit", "full"));
const canViewPermissionSettings = Boolean(isSuperuserAccount || hasPermission("user-groups-edit", "read"));
const canAdjustPermissions = Boolean(isSuperuserAccount || hasPermission("user-groups-edit", "full"));
const superuserGroupLocked = isSuperuserGroup && !isSuperuserAccount;
const numericGroupId = useMemo(() => {
const candidate = Number(groupId);
@@ -162,15 +184,15 @@ export function UserGroupDetail() {
const sectionGroups = Array.isArray(section.groups)
? section.groups.map((group, groupIdx) => {
const groupItems = Array.isArray(group.items)
? group.items.map((item, itemIdx) => normalizeItem(item, itemIdx)).filter(Boolean)
: [];
return {
...group,
sortOrder: typeof group.sortOrder === "number" ? group.sortOrder : groupIdx,
items: groupItems
};
})
const groupItems = Array.isArray(group.items)
? group.items.map((item, itemIdx) => normalizeItem(item, itemIdx)).filter(Boolean)
: [];
return {
...group,
sortOrder: typeof group.sortOrder === "number" ? group.sortOrder : groupIdx,
items: groupItems
};
})
: [];
return {
@@ -485,22 +507,33 @@ export function UserGroupDetail() {
return group?.avatarColor || "";
}, [formValues.avatarColor, group]);
const inputDisabled = maintenanceActive || savingGroup || !group || isSuperuserGroup || !canEditGroupDetails;
const selectDisabled = maintenanceActive || savingGroup || !group || !canEditGroupDetails;
const inputDisabled = maintenanceLocked || savingGroup || !group || superuserGroupLocked || !canEditGroupDetails;
const selectDisabled = maintenanceLocked || savingGroup || !group || superuserGroupLocked || !canEditGroupDetails;
const selectValue = formValues.avatarColor || "";
const handlePermissionChange = useCallback((permissionKey, value) => {
if (!permissionKey || !canAdjustPermissions || isSuperuserGroup) {
return;
}
const nextValue = normalizePermissionLevel(permissionKey, value);
setPermissionSelection((prev) => {
if (prev[permissionKey] === value) {
const shouldResetDelete =
permissionKey === "maintenance-server-manage" && nextValue !== "full";
if (!shouldResetDelete && prev[permissionKey] === nextValue) {
return prev;
}
return {
const nextState = {
...prev,
[permissionKey]: value
[permissionKey]: nextValue
};
if (shouldResetDelete) {
nextState["maintenance-server-delete"] = "none";
}
return nextState;
});
}, [canAdjustPermissions, isSuperuserGroup]);
@@ -509,13 +542,16 @@ export function UserGroupDetail() {
if (!permissionKey) {
return null;
}
let value = null;
if (Object.prototype.hasOwnProperty.call(permissionSelection, permissionKey)) {
return permissionSelection[permissionKey];
value = permissionSelection[permissionKey];
} else if (Object.prototype.hasOwnProperty.call(permissionDefaults, permissionKey)) {
value = permissionDefaults[permissionKey];
}
if (Object.prototype.hasOwnProperty.call(permissionDefaults, permissionKey)) {
return permissionDefaults[permissionKey];
if (typeof value === "string") {
return normalizePermissionLevel(permissionKey, value);
}
return null;
return value;
},
[permissionSelection, permissionDefaults]
);
@@ -571,6 +607,8 @@ export function UserGroupDetail() {
const rowName = `permission-${ownerKey}-${row.key}`;
const currentValue = getPermissionValue(row.key) ?? "none";
const displayValue = normalizePermissionLevel(row.key, currentValue);
const serverManageLevel = getPermissionValue("maintenance-server-manage") ?? "none";
const { addTopBorder = true } = options;
const rowClasses = [
@@ -580,10 +618,13 @@ export function UserGroupDetail() {
.filter(Boolean)
.join(" ");
const baseLevels = Array.isArray(row.availableLevels) && row.availableLevels.length
? row.availableLevels
: LEVEL_OPTIONS.map((option) => option.value);
const availableLevels = new Set(
Array.isArray(row.availableLevels) && row.availableLevels.length
? row.availableLevels
: LEVEL_OPTIONS.map((option) => option.value)
row.key === "maintenance-server-delete"
? baseLevels.filter((level) => level === "full" || level === "none")
: baseLevels
);
const levelOptions = LEVEL_OPTIONS;
@@ -595,13 +636,18 @@ export function UserGroupDetail() {
<List className="flex-col gap-1.5 md:ml-auto md:flex-row md:flex-nowrap md:w-1/2 md:items-center md:justify-end md:gap-3">
{levelOptions.map((option) => {
const optionId = `${rowName}-${option.value}`;
const checked = currentValue === option.value;
const checked = displayValue === option.value;
const requiresManageFull =
row.key === "maintenance-server-delete" &&
option.value === "full" &&
(LEVEL_PRIORITY[serverManageLevel] ?? 0) < LEVEL_PRIORITY.full;
const disabled =
!availableLevels.has(option.value) ||
permissionsLoading ||
savingGroup ||
!canAdjustPermissions ||
isSuperuserGroup;
isSuperuserGroup ||
requiresManageFull;
return (
<ListItem
@@ -610,9 +656,8 @@ export function UserGroupDetail() {
>
<label
htmlFor={optionId}
className={`inline-flex w-full items-center gap-2 rounded-lg px-3 py-2 min-h-[38px] ${
disabled ? "cursor-not-allowed bg-blue-gray-50/40" : "cursor-pointer hover:bg-blue-gray-50/60"
}`}
className={`inline-flex w-full items-center gap-2 rounded-lg px-3 py-2 min-h-[38px] ${disabled ? "cursor-not-allowed bg-blue-gray-50/40" : "cursor-pointer hover:bg-blue-gray-50/60"
}`}
>
<ListItemPrefix className="flex-shrink-0">
<Radio
@@ -633,9 +678,8 @@ export function UserGroupDetail() {
</ListItemPrefix>
<Typography
color="blue-gray"
className={`text-sm font-medium leading-5 whitespace-nowrap ${
disabled ? "text-blue-gray-300" : "text-blue-gray-600"
}`}
className={`text-sm font-medium leading-5 whitespace-nowrap ${disabled ? "text-blue-gray-300" : "text-blue-gray-600"
}`}
>
{option.label}
</Typography>
@@ -651,9 +695,18 @@ export function UserGroupDetail() {
return (
<>
<div className="mt-12 flex flex-col gap-12">
{maintenanceActive && (
<div className="mb-3 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
Wartungsmodus aktiv – Änderungen sind deaktiviert.
{(maintenanceActive || updateRunning) && (
<div className="rounded-lg border border-cyan-500/60 bg-cyan-900/30 px-4 py-3 text-sm text-bluegray-100">
<div className="flex flex-col gap-1">
<span>
Wartungsmodus aktiv{maintenanceMessage ? ` – ${maintenanceMessage}` : updateRunning ? " – Portainer-Update läuft" : ""}.
</span>
{updateRunning && (
<span className="text-xs text-indigo-900">
Phase: {updateStageLabel}
</span>
)}
</div>
</div>
)}
<div className="relative h-72 w-full overflow-hidden rounded-xl bg-[url('/img/background-image.png')] bg-cover\tbg-center">
@@ -683,7 +736,7 @@ export function UserGroupDetail() {
color="green"
className="normal-case"
onClick={handleSaveGroup}
disabled={maintenanceActive || savingGroup}
disabled={maintenanceLocked || savingGroup}
>
{savingGroup ? "Speichert ..." : "Änderungen speichern"}
</Button>
@@ -710,11 +763,14 @@ export function UserGroupDetail() {
<Typography variant="h6" color="blue-gray" className="mb-4">
Gruppendaten
</Typography>
{(maintenanceActive || isSuperuserGroup) && (
{isSuperuserGroup && (
<div className="mb-4 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
{maintenanceActive
? "Wartungsmodus aktiv – Änderungen sind deaktiviert."
: "Systemgruppe – Name und Beschreibung sind geschützt."}
Systemgruppe – Name und Beschreibung sind geschützt.
{superuserGroupLocked && (
<span className="mt-1 block text-xs text-amber-600">
Nur der Superuser darf die Superuser-Gruppe bearbeiten.
</span>
)}
</div>
)}
<div className="mb-6">
@@ -810,81 +866,87 @@ export function UserGroupDetail() {
</div>
</div>
<div className="lg:col-span-2 xl:col-span-3">
<Typography variant="h6" color="blue-gray" className="mb-4">
Berechtigungen (Ansicht)
</Typography>
{isSuperuserGroup ? (
<div className="rounded-xl border border-blue-gray-100 bg-white px-4 py-4 text-sm text-blue-gray-600 shadow-sm">
Die Superuser-Gruppe besitzt automatisch Vollzugriff auf alle Bereiche. Berechtigungen können hier nicht angepasst werden.
</div>
) : !canViewPermissionSettings ? null : (
<div className="rounded-xl border border-blue-gray-100 bg-white shadow-sm">
{permissionsLoading && (
<div className="border-b border-blue-gray-100 px-4 py-4">
<div className="flex items-center gap-3 text-sm text-blue-gray-500">
<Spinner className="h-4 w-4" />
<span>Berechtigungen werden geladen ...</span>
</div>
{(!maintenanceActive && !updateRunning) && (
<>
<Typography variant="h6" color="blue-gray" className="mb-4">
Berechtigungen (Ansicht)
</Typography>
{isSuperuserGroup ? (
<div className="rounded-xl border border-blue-gray-100 bg-white px-4 py-4 text-sm text-blue-gray-600 shadow-sm">
Die Superuser-Gruppe besitzt automatisch Vollzugriff auf alle Bereiche. Berechtigungen können hier nicht angepasst werden.
</div>
)}
{!permissionsLoading && permissionsError && (
<div className="border-b border-blue-gray-100 px-4 py-4 text-sm text-red-700">
{permissionsError}
</div>
)}
{!permissionsLoading && !permissionsError &&
(permissionSections.length === 0 ? (
<div className="border-b border-blue-gray-100 px-4 py-4 text-sm text-blue-gray-500">
Für diese Gruppe sind keine Berechtigungen definiert.
</div>
) : (
permissionSections.map((section, sectionIndex) => {
const sectionItems = Array.isArray(section.items) ? section.items : [];
const sectionGroups = Array.isArray(section.groups) ? section.groups : [];
return (
<div
key={section.key || sectionIndex}
className={sectionIndex === 0 ? "" : "border-t border-blue-gray-100"}
>
<div className="border-b border-blue-gray-100 px-4 py-4">
<Typography variant="h6" className="text-lg font-semibold text-blue-gray-700">
{section.title}
</Typography>
</div>
{sectionItems.map((row, rowIndex) =>
renderPermissionRow(row, section.key || sectionIndex, {
addTopBorder: rowIndex !== 0
})
)}
{sectionGroups.map((group, groupIdx) => {
const groupItems = Array.isArray(group.items) ? group.items : [];
const hasVisibleRows = groupItems.some(isPermissionRowVisible);
if (!hasVisibleRows) {
return null;
}
return (
<div key={group.key || `${section.key || sectionIndex}-${groupIdx}`} className="border-t border-blue-gray-100">
<div className="border-b border-blue-gray-100 px-4 py-3">
<Typography className="text-sm font-semibold uppercase tracking-wide text-blue-gray-600">
{group.title}
</Typography>
</div>
{groupItems.map((row, rowIndex) =>
renderPermissionRow(row, group.key || `${section.key || sectionIndex}-${groupIdx}`, {
addTopBorder: rowIndex !== 0
})
)}
</div>
);
})}
) : !canViewPermissionSettings ? null : (
<div className="rounded-xl border border-blue-gray-100 bg-white shadow-sm">
{permissionsLoading && (
<div className="border-b border-blue-gray-100 px-4 py-4">
<div className="flex items-center gap-3 text-sm text-blue-gray-500">
<Spinner className="h-4 w-4" />
<span>Berechtigungen werden geladen ...</span>
</div>
);
})
))}
</div>
</div>
)}
{!permissionsLoading && permissionsError && (
<div className="border-b border-blue-gray-100 px-4 py-4 text-sm text-red-700">
{permissionsError}
</div>
)}
{!permissionsLoading && !permissionsError &&
(permissionSections.length === 0 ? (
<div className="border-b border-blue-gray-100 px-4 py-4 text-sm text-blue-gray-500">
Für diese Gruppe sind keine Berechtigungen definiert.
</div>
) : (
permissionSections.map((section, sectionIndex) => {
const sectionItems = Array.isArray(section.items) ? section.items : [];
const sectionGroups = Array.isArray(section.groups) ? section.groups : [];
return (
<div
key={section.key || sectionIndex}
className={sectionIndex === 0 ? "" : "border-t border-blue-gray-100"}
>
<div className="border-b border-blue-gray-100 px-4 py-4">
<Typography variant="h6" className="text-lg font-semibold text-blue-gray-700">
{section.title}
</Typography>
</div>
{sectionItems.map((row, rowIndex) =>
renderPermissionRow(row, section.key || sectionIndex, {
addTopBorder: rowIndex !== 0
})
)}
{sectionGroups.map((group, groupIdx) => {
const groupItems = Array.isArray(group.items) ? group.items : [];
const hasVisibleRows = groupItems.some(isPermissionRowVisible);
if (!hasVisibleRows) {
return null;
}
return (
<div key={group.key || `${section.key || sectionIndex}-${groupIdx}`} className="border-t border-blue-gray-100">
<div className="border-b border-blue-gray-100 px-4 py-3">
<Typography className="text-sm font-semibold uppercase tracking-wide text-blue-gray-600">
{group.title}
</Typography>
</div>
{groupItems.map((row, rowIndex) =>
renderPermissionRow(row, group.key || `${section.key || sectionIndex}-${groupIdx}`, {
addTopBorder: rowIndex !== 0
})
)}
</div>
);
})}
</div>
);
})
))}
</div>
)}
</>
)}
</div>
</div>
+40 -12
View File
@@ -17,6 +17,15 @@ import { useMaintenance } from "@/components/MaintenanceProvider.jsx";
import { useToast } from "@/components/ToastProvider.jsx";
import { useAuth } from "@/components/AuthProvider.jsx";
const UPDATE_STAGE_LABELS = {
initializing: "Vorbereitung",
"activating-maintenance": "Wartungsmodus aktivieren",
"executing-script": "Skript wird ausgeführt",
waiting: "Warte auf Portainer",
completed: "Abgeschlossen",
failed: "Fehlgeschlagen"
};
export function Usergroups() {
const [groups, setGroups] = useState([]);
const [loading, setLoading] = useState(false);
@@ -29,8 +38,12 @@ export function Usergroups() {
const [deletingGroupId, setDeletingGroupId] = useState(null);
const { showToast } = useToast();
const { maintenance } = useMaintenance();
const maintenanceActive = Boolean(maintenance?.active);
const { maintenance: maintenanceMeta, update: updateState } = useMaintenance();
const maintenanceActive = Boolean(maintenanceMeta?.active);
const maintenanceMessage = maintenanceMeta?.message;
const updateRunning = Boolean(updateState?.running);
const updateStageLabel = updateState?.stage ? (UPDATE_STAGE_LABELS[updateState.stage] ?? updateState.stage) : "–";
const maintenanceLocked = maintenanceActive || updateRunning;
const navigate = useNavigate();
const { hasPermission } = useAuth();
@@ -190,7 +203,8 @@ export function Usergroups() {
}, []);
const handleCreateGroup = useCallback(async () => {
if (maintenanceActive) {
if (maintenanceLocked) {
setCreateGroupError("Im Wartungsmodus oder während eines Updates können keine Gruppen angelegt werden.");
return;
}
if (!canEditGroups) {
@@ -236,7 +250,7 @@ export function Usergroups() {
setCreatingGroup(false);
}
}, [
maintenanceActive,
maintenanceLocked,
canEditGroups,
newGroupName,
newGroupDescription,
@@ -245,10 +259,10 @@ export function Usergroups() {
fetchGroups
]);
const createGroupDisabled = maintenanceActive || creatingGroup;
const createGroupDisabled = maintenanceLocked || creatingGroup;
const handleDeleteGroup = useCallback(async (group) => {
if (maintenanceActive || !group?.id) {
if (maintenanceLocked || !group?.id) {
return;
}
@@ -322,13 +336,22 @@ export function Usergroups() {
} finally {
setDeletingGroupId(null);
}
}, [maintenanceActive, canDeleteGroups, showToast, fetchGroups]);
}, [maintenanceLocked, canDeleteGroups, showToast, fetchGroups]);
return (
<div className="mt-12 mb-8 flex flex-col gap-12">
{maintenanceActive && (
<div className="mb-4 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
Wartungsmodus aktiv – Änderungen sind deaktiviert. Die Liste kann dennoch angezeigt werden.
{(maintenanceActive || updateRunning) && (
<div className="rounded-lg border border-cyan-500/60 bg-cyan-900/30 px-4 py-3 text-sm text-bluegray-100">
<div className="flex flex-col gap-1">
<span>
Wartungsmodus aktiv{maintenanceMessage ? ` – ${maintenanceMessage}` : updateRunning ? " – Portainer-Update läuft" : ""}.
</span>
{updateRunning && (
<span className="text-xs text-indigo-900">
Phase: {updateStageLabel}
</span>
)}
</div>
</div>
)}
<Card className="border border-blue-gray-100 shadow-sm">
@@ -374,7 +397,12 @@ export function Usergroups() {
<Button color="green" onClick={handleCreateGroup} disabled={createGroupDisabled}>
{creatingGroup ? "Speichert ..." : "Gruppe anlegen"}
</Button>
<Button variant="text" color="blue-gray" onClick={resetNewGroupForm} disabled={creatingGroup}>
<Button
variant="text"
color="blue-gray"
onClick={resetNewGroupForm}
disabled={creatingGroup || maintenanceLocked}
>
Formular zurücksetzen
</Button>
</div>
@@ -498,7 +526,7 @@ export function Usergroups() {
color="red"
onClick={() => handleDeleteGroup(group)}
disabled={
maintenanceActive ||
maintenanceLocked ||
deletingGroupId === group.id ||
Number(group.memberCount) > 0
}
+49 -21
View File
@@ -17,6 +17,15 @@ import { useMaintenance } from "@/components/MaintenanceProvider.jsx";
import { useToast } from "@/components/ToastProvider.jsx";
import { useAuth } from "@/components/AuthProvider.jsx";
const UPDATE_STAGE_LABELS = {
initializing: "Vorbereitung",
"activating-maintenance": "Wartungsmodus aktivieren",
"executing-script": "Skript wird ausgeführt",
waiting: "Warte auf Portainer",
completed: "Abgeschlossen",
failed: "Fehlgeschlagen"
};
const normalizeUserGroups = (rawGroups) => {
if (!Array.isArray(rawGroups)) {
return [];
@@ -61,8 +70,12 @@ export function Users() {
const [togglingUserId, setTogglingUserId] = useState(null);
const { showToast } = useToast();
const { maintenance } = useMaintenance();
const maintenanceActive = Boolean(maintenance?.active);
const { maintenance: maintenanceMeta, update: updateState } = useMaintenance();
const maintenanceActive = Boolean(maintenanceMeta?.active);
const maintenanceMessage = maintenanceMeta?.message;
const updateRunning = Boolean(updateState?.running);
const updateStageLabel = updateState?.stage ? (UPDATE_STAGE_LABELS[updateState.stage] ?? updateState.stage) : "–";
const maintenanceLocked = maintenanceActive || updateRunning;
const navigate = useNavigate();
const noop = useCallback(() => { }, []);
const { hasPermission } = useAuth();
@@ -253,7 +266,7 @@ export function Users() {
}, []);
const handleDeleteUser = useCallback(async (user) => {
if (maintenanceActive || !user?.id) {
if (maintenanceLocked || !user?.id) {
return;
}
if (!canDeleteUsers) {
@@ -314,7 +327,7 @@ export function Users() {
} finally {
setDeletingUserId(null);
}
}, [maintenanceActive, canDeleteUsers, showToast, fetchUsers]);
}, [maintenanceLocked, canDeleteUsers, showToast, fetchUsers]);
const handleToggleUserStatus = useCallback(async (user) => {
if (!user?.id) {
@@ -323,11 +336,11 @@ export function Users() {
if (!canManageUsers) {
return;
}
if (maintenanceActive) {
if (maintenanceLocked) {
showToast({
variant: "error",
title: "Aktion nicht möglich",
description: "Im Wartungsmodus können keine Statusänderungen durchgeführt werden."
description: "Im Wartungsmodus oder während eines Updates sind Statusänderungen gesperrt."
});
return;
}
@@ -386,10 +399,11 @@ export function Users() {
} finally {
setTogglingUserId(null);
}
}, [maintenanceActive, canManageUsers, showToast, fetchUsers]);
}, [maintenanceLocked, canManageUsers, showToast, fetchUsers]);
const handleCreateUser = useCallback(async () => {
if (maintenanceActive) {
if (maintenanceLocked) {
setCreateError("Im Wartungsmodus oder während eines Updates können keine Benutzer angelegt werden.");
return;
}
if (!canManageUsers) {
@@ -467,7 +481,7 @@ export function Users() {
setCreatingUser(false);
}
}, [
maintenanceActive,
maintenanceLocked,
canManageUsers,
newUsername,
newEmail,
@@ -479,8 +493,8 @@ export function Users() {
]);
const hasSelectableGroups = availableGroups.length > 0;
const createDisabled = maintenanceActive || creatingUser || groupsLoading || !hasSelectableGroups;
const groupSelectDisabled = maintenanceActive || creatingUser || groupsLoading || !hasSelectableGroups;
const createDisabled = maintenanceLocked || creatingUser || groupsLoading || !hasSelectableGroups;
const groupSelectDisabled = maintenanceLocked || creatingUser || groupsLoading || !hasSelectableGroups;
const renderSelectedGroup = useCallback(
(element) => {
if (element && element.props && element.props.children) {
@@ -497,9 +511,18 @@ export function Users() {
return (
<div className="mt-12 mb-8 flex flex-col gap-12">
{maintenanceActive && (
<div className="mb-4 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
Wartungsmodus aktiv – Änderungen sind deaktiviert. Die Liste kann dennoch angezeigt werden.
{(maintenanceActive || updateRunning) && (
<div className="rounded-lg border border-cyan-500/60 bg-cyan-900/30 px-4 py-3 text-sm text-bluegray-100">
<div className="flex flex-col gap-1">
<span>
Wartungsmodus aktiv{maintenanceMessage ? ` – ${maintenanceMessage}` : updateRunning ? " – Portainer-Update läuft" : ""}.
</span>
{updateRunning && (
<span className="text-xs text-indigo-900">
Phase: {updateStageLabel}
</span>
)}
</div>
</div>
)}
<Card>
@@ -539,14 +562,14 @@ export function Users() {
label="Benutzername"
value={newUsername}
onChange={(event) => setNewUsername(event.target.value)}
disabled={maintenanceActive || creatingUser}
disabled={maintenanceLocked || creatingUser}
crossOrigin=""
/>
<Input
label="E-Mail (optional)"
value={newEmail}
onChange={(event) => setNewEmail(event.target.value)}
disabled={maintenanceActive || creatingUser}
disabled={maintenanceLocked || creatingUser}
crossOrigin=""
/>
<Input
@@ -554,7 +577,7 @@ export function Users() {
label="Passwort"
value={newPassword}
onChange={(event) => setNewPassword(event.target.value)}
disabled={maintenanceActive || creatingUser}
disabled={maintenanceLocked || creatingUser}
crossOrigin=""
/>
<Select
@@ -582,7 +605,12 @@ export function Users() {
<Button color="green" onClick={handleCreateUser} disabled={createDisabled}>
{creatingUser ? "Speichert ..." : "Benutzer anlegen"}
</Button>
<Button variant="text" color="blue-gray" onClick={resetNewUserForm} disabled={creatingUser}>
<Button
variant="text"
color="blue-gray"
onClick={resetNewUserForm}
disabled={creatingUser || maintenanceLocked}
>
Formular zurücksetzen
</Button>
</div>
@@ -694,10 +722,10 @@ export function Users() {
{canManageUsers ? (
<button
type="button"
className={`inline-flex items-center rounded-full px-1 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 ${maintenanceActive ? "cursor-not-allowed" : "cursor-pointer"}`}
className={`inline-flex items-center rounded-full px-1 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 ${maintenanceLocked ? "cursor-not-allowed" : "cursor-pointer"}`}
onClick={() => handleToggleUserStatus(user)}
disabled={
maintenanceActive ||
maintenanceLocked ||
togglingUserId === user.id ||
(Array.isArray(user.groups) &&
user.groups.some((group) => (group?.name || "").toLowerCase() === "superuser" && user.isActive))
@@ -746,7 +774,7 @@ export function Users() {
variant="text"
color="red"
onClick={() => handleDeleteUser(user)}
disabled={maintenanceActive || deletingUserId === user.id}
disabled={maintenanceLocked || deletingUserId === user.id}
>
{deletingUserId === user.id ? "Löscht ..." : "Löschen"}
</Button>
File diff suppressed because it is too large. Load diff
+1
View File
@@ -89,6 +89,7 @@ export const routes = [
name: "passwort vergessen",
path: "/forgot-password",
element: <ForgotPassword />,
hidden: true,
},
{
icon: <ArrowLeftOnRectangleIcon {...icon} />,
+5 -2
View File
@@ -22,7 +22,7 @@ export function Sidenav({ brandImg, brandName, routes }) {
} fixed inset-0 z-50 my-4 ml-4 h-[calc(100vh-32px)] w-72 rounded-xl transition-transform duration-300 xl:translate-x-0 border border-blue-gray-100`}
>
<div className="relative py-6 px-8 text-right">
<img src={logo} alt="StackPulse" className="w-auto" /><span className="mt-1 text-xs text-stormGrey-500 block antialiased font-sans">v0.4</span>
<img src={logo} alt="StackPulse" className="w-auto" /><span className="mt-1 text-xs text-stormGrey-500 block antialiased font-sans">v0.5</span>
<IconButton
variant="text"
@@ -36,7 +36,10 @@ export function Sidenav({ brandImg, brandName, routes }) {
</div>
<div className="m-4">
{routes.map(({ layout, title, pages }, key) => {
const visiblePages = pages.filter(({ permission }) => {
const visiblePages = pages.filter(({ permission, hidden }) => {
if (hidden) {
return false;
}
if (!permission || !permission.key) {
return true;
}