Update
This commit is contained in:
1 parent
f0b1ca4b68
commit
a76567969a
27 files changed
+2611
-377
No files matched your search
@@ -18,6 +18,12 @@ const selectSuperuser = db.prepare(`
|
|||||||
const selectUserByUsername = db.prepare('SELECT * FROM users WHERE username = ?');
|
const selectUserByUsername = db.prepare('SELECT * FROM users WHERE username = ?');
|
||||||
const selectUserByEmail = db.prepare('SELECT * FROM users WHERE email = ?');
|
const selectUserByEmail = db.prepare('SELECT * FROM users WHERE email = ?');
|
||||||
const selectUserById = db.prepare('SELECT * FROM users WHERE id = ?');
|
const selectUserById = db.prepare('SELECT * FROM users WHERE id = ?');
|
||||||
|
const updateLastLogin = db.prepare(`
|
||||||
|
UPDATE users
|
||||||
|
SET last_login = CURRENT_TIMESTAMP,
|
||||||
|
updated_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE id = ?
|
||||||
|
`);
|
||||||
|
|
||||||
const insertUser = db.prepare(`
|
const insertUser = db.prepare(`
|
||||||
INSERT INTO users (username, email, password_hash, password_salt, is_active, created_at, updated_at)
|
INSERT INTO users (username, email, password_hash, password_salt, is_active, created_at, updated_at)
|
||||||
@@ -31,8 +37,8 @@ const updateUser = db.prepare(`
|
|||||||
`);
|
`);
|
||||||
|
|
||||||
const insertMembership = db.prepare(`
|
const insertMembership = db.prepare(`
|
||||||
INSERT OR IGNORE INTO user_group_memberships (user_id, group_id, role)
|
INSERT OR IGNORE INTO user_group_memberships (user_id, group_id)
|
||||||
VALUES (?, ?, ?)
|
VALUES (?, ?)
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const removeMembershipsForGroup = db.prepare(`
|
const removeMembershipsForGroup = db.prepare(`
|
||||||
@@ -70,24 +76,48 @@ const creationTransaction = db.transaction(({ username, email, passwordHash, pas
|
|||||||
}
|
}
|
||||||
|
|
||||||
removeMembershipsForGroup.run(groupId);
|
removeMembershipsForGroup.run(groupId);
|
||||||
insertMembership.run(userId, groupId, SUPERUSER_GROUP_NAME);
|
insertMembership.run(userId, groupId);
|
||||||
|
|
||||||
return selectUserById.get(userId);
|
return selectUserById.get(userId);
|
||||||
});
|
});
|
||||||
|
|
||||||
function hashPassword(password) {
|
export function hashPassword(password) {
|
||||||
if (!password || typeof password !== 'string') {
|
if (!password || typeof password !== 'string') {
|
||||||
throw new Error('INVALID_PASSWORD');
|
const err = new Error('INVALID_PASSWORD');
|
||||||
|
err.code = 'INVALID_PASSWORD';
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
const trimmed = password.trim();
|
const trimmed = password.trim();
|
||||||
|
if (!trimmed) {
|
||||||
|
const err = new Error('INVALID_PASSWORD');
|
||||||
|
err.code = 'INVALID_PASSWORD';
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
if (trimmed.length < 8) {
|
if (trimmed.length < 8) {
|
||||||
throw new Error('PASSWORD_TOO_SHORT');
|
const err = new Error('PASSWORD_TOO_SHORT');
|
||||||
|
err.code = 'PASSWORD_TOO_SHORT';
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
const salt = crypto.randomBytes(16).toString('hex');
|
const salt = crypto.randomBytes(16).toString('hex');
|
||||||
const hash = crypto.pbkdf2Sync(trimmed, salt, 120000, 64, 'sha512').toString('hex');
|
const hash = crypto.pbkdf2Sync(trimmed, salt, 120000, 64, 'sha512').toString('hex');
|
||||||
return { salt, hash };
|
return { salt, hash };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function verifyPassword(password, hash, salt) {
|
||||||
|
if (!hash || !salt) return false;
|
||||||
|
if (!password || typeof password !== 'string') return false;
|
||||||
|
const trimmed = password.trim();
|
||||||
|
if (!trimmed) return false;
|
||||||
|
|
||||||
|
const derived = crypto.pbkdf2Sync(trimmed, salt, 120000, 64, 'sha512').toString('hex');
|
||||||
|
const storedBuffer = Buffer.from(hash, 'hex');
|
||||||
|
const derivedBuffer = Buffer.from(derived, 'hex');
|
||||||
|
if (storedBuffer.length !== derivedBuffer.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return crypto.timingSafeEqual(storedBuffer, derivedBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
function ensureGroup(name, description = null) {
|
function ensureGroup(name, description = null) {
|
||||||
let group = selectGroupByName.get(name);
|
let group = selectGroupByName.get(name);
|
||||||
if (group) return group;
|
if (group) return group;
|
||||||
@@ -101,6 +131,25 @@ export function hasSuperuser() {
|
|||||||
return count > 0;
|
return count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function findUserByIdentifier(identifier) {
|
||||||
|
const value = String(identifier || '').trim();
|
||||||
|
if (!value) return null;
|
||||||
|
if (value.includes('@')) {
|
||||||
|
return selectUserByEmail.get(value.toLowerCase());
|
||||||
|
}
|
||||||
|
return selectUserByUsername.get(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findUserById(id) {
|
||||||
|
if (!id) return null;
|
||||||
|
return selectUserById.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function markUserLogin(userId) {
|
||||||
|
if (!userId) return;
|
||||||
|
updateLastLogin.run(userId);
|
||||||
|
}
|
||||||
|
|
||||||
function createOrUpdateSuperuser({ username, email, password }) {
|
function createOrUpdateSuperuser({ username, email, password }) {
|
||||||
const normalizedUsername = String(username || '').trim();
|
const normalizedUsername = String(username || '').trim();
|
||||||
const normalizedEmail = String(email || '').trim().toLowerCase();
|
const normalizedEmail = String(email || '').trim().toLowerCase();
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -58,6 +58,45 @@ CREATE TABLE IF NOT EXISTS user_group_memberships (
|
|||||||
);
|
);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
const createServersTable = `
|
||||||
|
CREATE TABLE IF NOT EXISTS servers (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
url TEXT NOT NULL UNIQUE,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const createEndpointsTable = `
|
||||||
|
CREATE TABLE IF NOT EXISTS 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)
|
||||||
|
);
|
||||||
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
const createServerApiKeysTable = `
|
||||||
|
CREATE TABLE IF NOT EXISTS server_api_keys (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
server_id INTEGER NOT NULL UNIQUE,
|
||||||
|
key_cipher TEXT NOT NULL,
|
||||||
|
key_iv TEXT NOT NULL,
|
||||||
|
key_tag TEXT NOT NULL,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (server_id) REFERENCES servers(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
`;
|
||||||
|
|
||||||
const createUserSettingsTable = `
|
const createUserSettingsTable = `
|
||||||
CREATE TABLE IF NOT EXISTS user_settings (
|
CREATE TABLE IF NOT EXISTS user_settings (
|
||||||
user_id INTEGER NOT NULL,
|
user_id INTEGER NOT NULL,
|
||||||
@@ -97,6 +136,15 @@ console.log('✅ user_groups table ready');
|
|||||||
db.exec(createUserGroupMembershipsTable);
|
db.exec(createUserGroupMembershipsTable);
|
||||||
console.log('✅ user_group_memberships table ready');
|
console.log('✅ user_group_memberships table ready');
|
||||||
|
|
||||||
|
db.exec(createServersTable);
|
||||||
|
console.log('✅ servers table ready');
|
||||||
|
|
||||||
|
db.exec(createEndpointsTable);
|
||||||
|
console.log('✅ endpoints table ready');
|
||||||
|
|
||||||
|
db.exec(createServerApiKeysTable);
|
||||||
|
console.log('✅ server_api_keys table ready');
|
||||||
|
|
||||||
db.exec(createUserSettingsTable);
|
db.exec(createUserSettingsTable);
|
||||||
console.log('✅ user_settings table ready');
|
console.log('✅ user_settings table ready');
|
||||||
|
|
||||||
|
|||||||
+586
-26
@@ -11,7 +11,17 @@ import os from 'os';
|
|||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import { db } from './db/index.js';
|
import { db } from './db/index.js';
|
||||||
import { ensureSuperuserFromEnv, getSuperuserSummary, hasSuperuser, registerSuperuser, removeSuperuser } from './auth/superuser.js';
|
import {
|
||||||
|
ensureSuperuserFromEnv,
|
||||||
|
getSuperuserSummary,
|
||||||
|
hasSuperuser,
|
||||||
|
registerSuperuser,
|
||||||
|
removeSuperuser,
|
||||||
|
findUserByIdentifier,
|
||||||
|
findUserById,
|
||||||
|
markUserLogin,
|
||||||
|
verifyPassword
|
||||||
|
} from './auth/superuser.js';
|
||||||
import {
|
import {
|
||||||
logRedeployEvent,
|
logRedeployEvent,
|
||||||
buildLogFilter,
|
buildLogFilter,
|
||||||
@@ -21,10 +31,25 @@ import {
|
|||||||
} from './db/redeployLogs.js';
|
} from './db/redeployLogs.js';
|
||||||
import { getSetting, setSetting, deleteSetting } from './db/settings.js';
|
import { getSetting, setSetting, deleteSetting } from './db/settings.js';
|
||||||
import { activateMaintenanceMode, deactivateMaintenanceMode, getMaintenanceState, isMaintenanceModeActive } from './maintenance/state.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';
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
ensureSuperuserFromEnv();
|
ensureSuperuserFromEnv();
|
||||||
|
ensureDefaultsFromEnv();
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
@@ -39,13 +64,46 @@ app.get('*', (req, res, next) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const PORT = 4001;
|
const PORT = 4001;
|
||||||
const ENDPOINT_ID = Number(process.env.PORTAINER_ENDPOINT_ID);
|
|
||||||
|
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 agent = new https.Agent({ rejectUnauthorized: false });
|
||||||
|
|
||||||
|
const resolvePortainerBaseUrl = () => {
|
||||||
|
const envUrl = typeof process.env.PORTAINER_URL === 'string' ? process.env.PORTAINER_URL.trim() : '';
|
||||||
|
if (envUrl) return envUrl;
|
||||||
|
const activeUrl = getActiveServerUrl();
|
||||||
|
return activeUrl ? activeUrl.trim() : '';
|
||||||
|
};
|
||||||
|
|
||||||
const axiosInstance = axios.create({
|
const axiosInstance = axios.create({
|
||||||
httpsAgent: agent,
|
httpsAgent: agent
|
||||||
headers: { "X-API-Key": process.env.PORTAINER_API_KEY },
|
});
|
||||||
baseURL: process.env.PORTAINER_URL,
|
|
||||||
|
axiosInstance.interceptors.request.use((config) => {
|
||||||
|
const currentBase = typeof config.baseURL === 'string' ? config.baseURL.trim() : '';
|
||||||
|
const effectiveBase = currentBase || resolvePortainerBaseUrl();
|
||||||
|
if (!effectiveBase) {
|
||||||
|
throw new Error('PORTAINER_URL_NOT_CONFIGURED');
|
||||||
|
}
|
||||||
|
config.baseURL = effectiveBase;
|
||||||
|
|
||||||
|
const apiKey = getActiveApiKey();
|
||||||
|
if (apiKey) {
|
||||||
|
config.headers = config.headers || {};
|
||||||
|
config.headers["X-API-Key"] = apiKey;
|
||||||
|
} else if (config.headers?.["X-API-Key"]) {
|
||||||
|
delete config.headers["X-API-Key"];
|
||||||
|
}
|
||||||
|
return config;
|
||||||
});
|
});
|
||||||
|
|
||||||
const REDEPLOY_PHASES = {
|
const REDEPLOY_PHASES = {
|
||||||
@@ -58,6 +116,132 @@ const REDEPLOY_PHASES = {
|
|||||||
|
|
||||||
const redeployingStacks = new Map();
|
const redeployingStacks = new Map();
|
||||||
|
|
||||||
|
const AUTH_COOKIE_NAME = process.env.AUTH_COOKIE_NAME || 'sp_auth_token';
|
||||||
|
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 AUTH_COOKIE_OPTIONS = {
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: 'lax',
|
||||||
|
secure: process.env.NODE_ENV === 'production',
|
||||||
|
path: '/'
|
||||||
|
};
|
||||||
|
|
||||||
|
const activeSessions = new Map();
|
||||||
|
|
||||||
|
const PUBLIC_API_ROUTES = [
|
||||||
|
{ method: 'GET', matcher: /^\/api\/auth\/superuser\/status$/ },
|
||||||
|
{ method: 'POST', matcher: /^\/api\/auth\/superuser\/register$/ },
|
||||||
|
{ method: 'POST', matcher: /^\/api\/auth\/login$/ },
|
||||||
|
{ method: 'POST', matcher: /^\/api\/auth\/logout$/ },
|
||||||
|
{ method: 'GET', matcher: /^\/api\/auth\/session$/ },
|
||||||
|
{ method: 'GET', matcher: /^\/api\/setup\/status$/ },
|
||||||
|
{ method: 'POST', matcher: /^\/api\/setup\/complete$/ }
|
||||||
|
];
|
||||||
|
|
||||||
|
const sanitizeUser = (user) => ({
|
||||||
|
id: user.id,
|
||||||
|
username: user.username,
|
||||||
|
email: user.email
|
||||||
|
});
|
||||||
|
|
||||||
|
const cleanupExpiredSessions = () => {
|
||||||
|
const now = Date.now();
|
||||||
|
for (const [token, session] of activeSessions.entries()) {
|
||||||
|
if (!session || session.expiresAt <= now) {
|
||||||
|
activeSessions.delete(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeSessionsForUser = (userId) => {
|
||||||
|
if (!userId) return;
|
||||||
|
for (const [token, session] of activeSessions.entries()) {
|
||||||
|
if (session?.userId === userId) {
|
||||||
|
activeSessions.delete(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createSessionForUser = (user) => {
|
||||||
|
cleanupExpiredSessions();
|
||||||
|
removeSessionsForUser(user.id);
|
||||||
|
const token = crypto.randomBytes(48).toString('hex');
|
||||||
|
const expiresAt = Date.now() + AUTH_SESSION_TTL_MS;
|
||||||
|
activeSessions.set(token, { userId: user.id, expiresAt });
|
||||||
|
return { token, expiresAt };
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSessionRecord = (token) => {
|
||||||
|
if (!token) return null;
|
||||||
|
cleanupExpiredSessions();
|
||||||
|
const record = activeSessions.get(token);
|
||||||
|
if (!record) return null;
|
||||||
|
if (record.expiresAt <= Date.now()) {
|
||||||
|
activeSessions.delete(token);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return record;
|
||||||
|
};
|
||||||
|
|
||||||
|
const touchSession = (token) => {
|
||||||
|
if (!token) return;
|
||||||
|
const record = activeSessions.get(token);
|
||||||
|
if (!record) return;
|
||||||
|
record.expiresAt = Date.now() + AUTH_SESSION_TTL_MS;
|
||||||
|
activeSessions.set(token, record);
|
||||||
|
};
|
||||||
|
|
||||||
|
const extractAuthToken = (req) => {
|
||||||
|
const rawCookie = req.headers?.cookie;
|
||||||
|
if (rawCookie && typeof rawCookie === 'string') {
|
||||||
|
const parts = rawCookie.split(';');
|
||||||
|
for (const part of parts) {
|
||||||
|
const [name, ...rest] = part.trim().split('=');
|
||||||
|
if (name === AUTH_COOKIE_NAME) {
|
||||||
|
return rest.join('=');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const authHeader = req.headers?.authorization || req.headers?.Authorization;
|
||||||
|
if (authHeader && typeof authHeader === 'string') {
|
||||||
|
const lower = authHeader.toLowerCase();
|
||||||
|
if (lower.startsWith('bearer ')) {
|
||||||
|
return authHeader.slice(7).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setAuthCookie = (res, token) => {
|
||||||
|
if (!token || !res) return;
|
||||||
|
if (typeof res.cookie === 'function') {
|
||||||
|
res.cookie(AUTH_COOKIE_NAME, token, {
|
||||||
|
...AUTH_COOKIE_OPTIONS,
|
||||||
|
maxAge: AUTH_SESSION_TTL_MS
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.setHeader('Set-Cookie', `${AUTH_COOKIE_NAME}=${token}; Path=/; HttpOnly; SameSite=Lax`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearAuthCookie = (res) => {
|
||||||
|
if (!res) return;
|
||||||
|
if (typeof res.clearCookie === 'function') {
|
||||||
|
res.clearCookie(AUTH_COOKIE_NAME, {
|
||||||
|
...AUTH_COOKIE_OPTIONS,
|
||||||
|
maxAge: 0
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.setHeader('Set-Cookie', `${AUTH_COOKIE_NAME}=; Path=/; Max-Age=0`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isPublicApiRoute = (req) => {
|
||||||
|
return PUBLIC_API_ROUTES.some(({ method, matcher }) => req.method === method && matcher.test(req.path));
|
||||||
|
};
|
||||||
|
|
||||||
const isActiveRedeployPhase = (phase) => phase === REDEPLOY_PHASES.QUEUED || phase === REDEPLOY_PHASES.STARTED;
|
const isActiveRedeployPhase = (phase) => phase === REDEPLOY_PHASES.QUEUED || phase === REDEPLOY_PHASES.STARTED;
|
||||||
|
|
||||||
const resolveRedeployPhase = (phase, message) => {
|
const resolveRedeployPhase = (phase, message) => {
|
||||||
@@ -461,7 +645,8 @@ const testSshConnection = async (configOverride = null) => {
|
|||||||
|
|
||||||
const detectPortainerContainer = async () => {
|
const detectPortainerContainer = async () => {
|
||||||
try {
|
try {
|
||||||
const containersRes = await axiosInstance.get(`/api/endpoints/${ENDPOINT_ID}/docker/containers/json`, {
|
const endpointId = requireActiveEndpointId();
|
||||||
|
const containersRes = await axiosInstance.get(`/api/endpoints/${endpointId}/docker/containers/json`, {
|
||||||
params: { all: true }
|
params: { all: true }
|
||||||
});
|
});
|
||||||
const containers = Array.isArray(containersRes.data) ? containersRes.data : [];
|
const containers = Array.isArray(containersRes.data) ? containersRes.data : [];
|
||||||
@@ -487,7 +672,7 @@ const detectPortainerContainer = async () => {
|
|||||||
return { summary: null, error: 'Portainer-Container nicht gefunden' };
|
return { summary: null, error: 'Portainer-Container nicht gefunden' };
|
||||||
}
|
}
|
||||||
|
|
||||||
const inspectRes = await axiosInstance.get(`/api/endpoints/${ENDPOINT_ID}/docker/containers/${matchedContainer.Id}/json`);
|
const inspectRes = await axiosInstance.get(`/api/endpoints/${endpointId}/docker/containers/${matchedContainer.Id}/json`);
|
||||||
const inspect = inspectRes.data ?? {};
|
const inspect = inspectRes.data ?? {};
|
||||||
|
|
||||||
const trimName = (value) => (typeof value === 'string' ? value.replace(/^\//, '') : value);
|
const trimName = (value) => (typeof value === 'string' ? value.replace(/^\//, '') : value);
|
||||||
@@ -732,6 +917,7 @@ let currentPortainerUpdatePromise = null;
|
|||||||
|
|
||||||
const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) => {
|
const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) => {
|
||||||
let maintenanceActivated = false;
|
let maintenanceActivated = false;
|
||||||
|
const endpointId = requireActiveEndpointId();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
addUpdateLog(`Portainer-Update gestartet (Quelle: ${scriptSource})`, 'info');
|
addUpdateLog(`Portainer-Update gestartet (Quelle: ${scriptSource})`, 'info');
|
||||||
@@ -758,7 +944,7 @@ const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) =
|
|||||||
stackName: 'Portainer',
|
stackName: 'Portainer',
|
||||||
status: 'started',
|
status: 'started',
|
||||||
message: `Portainer Update gestartet (Ziel: ${targetVersion ?? 'unbekannt'})`,
|
message: `Portainer Update gestartet (Ziel: ${targetVersion ?? 'unbekannt'})`,
|
||||||
endpoint: ENDPOINT_ID,
|
endpoint: endpointId,
|
||||||
redeployType: REDEPLOY_TYPES.MAINTENANCE
|
redeployType: REDEPLOY_TYPES.MAINTENANCE
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -767,7 +953,7 @@ const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) =
|
|||||||
stackName: 'StackPulse Wartung',
|
stackName: 'StackPulse Wartung',
|
||||||
status: 'started',
|
status: 'started',
|
||||||
message: 'Wartungsmodus aktiviert (Portainer Update)',
|
message: 'Wartungsmodus aktiviert (Portainer Update)',
|
||||||
endpoint: ENDPOINT_ID,
|
endpoint: endpointId,
|
||||||
redeployType: REDEPLOY_TYPES.MAINTENANCE
|
redeployType: REDEPLOY_TYPES.MAINTENANCE
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -797,7 +983,7 @@ const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) =
|
|||||||
stackName: 'Portainer',
|
stackName: 'Portainer',
|
||||||
status: 'success',
|
status: 'success',
|
||||||
message: `Portainer Update abgeschlossen (Version: ${finalVersion ?? 'unbekannt'})`,
|
message: `Portainer Update abgeschlossen (Version: ${finalVersion ?? 'unbekannt'})`,
|
||||||
endpoint: ENDPOINT_ID,
|
endpoint: endpointId,
|
||||||
redeployType: REDEPLOY_TYPES.MAINTENANCE
|
redeployType: REDEPLOY_TYPES.MAINTENANCE
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -821,7 +1007,7 @@ const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) =
|
|||||||
stackName: 'StackPulse Wartung',
|
stackName: 'StackPulse Wartung',
|
||||||
status: 'success',
|
status: 'success',
|
||||||
message: 'Wartungsmodus deaktiviert',
|
message: 'Wartungsmodus deaktiviert',
|
||||||
endpoint: ENDPOINT_ID,
|
endpoint: endpointId,
|
||||||
redeployType: REDEPLOY_TYPES.MAINTENANCE
|
redeployType: REDEPLOY_TYPES.MAINTENANCE
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -832,7 +1018,7 @@ const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) =
|
|||||||
stackName: 'Portainer',
|
stackName: 'Portainer',
|
||||||
status: 'error',
|
status: 'error',
|
||||||
message,
|
message,
|
||||||
endpoint: ENDPOINT_ID,
|
endpoint: endpointId,
|
||||||
redeployType: REDEPLOY_TYPES.MAINTENANCE
|
redeployType: REDEPLOY_TYPES.MAINTENANCE
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -854,7 +1040,7 @@ const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) =
|
|||||||
stackName: 'StackPulse Wartung',
|
stackName: 'StackPulse Wartung',
|
||||||
status: 'error',
|
status: 'error',
|
||||||
message: 'Wartungsmodus deaktiviert (Fehler)',
|
message: 'Wartungsmodus deaktiviert (Fehler)',
|
||||||
endpoint: ENDPOINT_ID,
|
endpoint: endpointId,
|
||||||
redeployType: REDEPLOY_TYPES.MAINTENANCE
|
redeployType: REDEPLOY_TYPES.MAINTENANCE
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -864,8 +1050,9 @@ const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) =
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fetchPortainerStacks = async () => {
|
const fetchPortainerStacks = async () => {
|
||||||
|
const endpointId = requireActiveEndpointId();
|
||||||
const stacksRes = await axiosInstance.get('/api/stacks');
|
const stacksRes = await axiosInstance.get('/api/stacks');
|
||||||
return stacksRes.data.filter((stack) => stack.EndpointId === ENDPOINT_ID);
|
return stacksRes.data.filter((stack) => String(stack.EndpointId) === String(endpointId));
|
||||||
};
|
};
|
||||||
|
|
||||||
const buildStackCollections = (stacks = []) => {
|
const buildStackCollections = (stacks = []) => {
|
||||||
@@ -982,13 +1169,14 @@ const shouldFallbackToStackFile = (message) => {
|
|||||||
const redeployStackById = async (stackId, redeployType) => {
|
const redeployStackById = async (stackId, redeployType) => {
|
||||||
let stack;
|
let stack;
|
||||||
const messages = getRedeployMessages(redeployType);
|
const messages = getRedeployMessages(redeployType);
|
||||||
|
const endpointId = requireActiveEndpointId();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const stackRes = await axiosInstance.get(`/api/stacks/${stackId}`);
|
const stackRes = await axiosInstance.get(`/api/stacks/${stackId}`);
|
||||||
stack = stackRes.data;
|
stack = stackRes.data;
|
||||||
|
|
||||||
if (stack.EndpointId !== ENDPOINT_ID) {
|
if (String(stack.EndpointId) !== String(endpointId)) {
|
||||||
throw new Error(`Stack gehört nicht zum Endpoint ${ENDPOINT_ID}`);
|
throw new Error(`Stack gehört nicht zum Endpoint ${endpointId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetId = stack.Id || stackId;
|
const targetId = stack.Id || stackId;
|
||||||
@@ -1107,7 +1295,7 @@ const redeployStackById = async (stackId, redeployType) => {
|
|||||||
stackName: fallbackName,
|
stackName: fallbackName,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
message: errorMessage,
|
message: errorMessage,
|
||||||
endpoint: stack?.EndpointId || ENDPOINT_ID,
|
endpoint: stack?.EndpointId || endpointId,
|
||||||
redeployType
|
redeployType
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1116,8 +1304,373 @@ const redeployStackById = async (stackId, redeployType) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
if (!req.path.startsWith('/api')) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.method === 'OPTIONS' || isPublicApiRoute(req)) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasSuperuser()) {
|
||||||
|
return res.status(403).json({ error: 'SUPERUSER_REQUIRED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasServer() || !hasEndpoint() || !hasApiKey()) {
|
||||||
|
return res.status(403).json({ error: 'SETUP_REQUIRED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = extractAuthToken(req);
|
||||||
|
const session = getSessionRecord(token);
|
||||||
|
if (!session) {
|
||||||
|
clearAuthCookie(res);
|
||||||
|
return res.status(401).json({ error: 'UNAUTHORIZED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = findUserById(session.userId);
|
||||||
|
if (!user || !user.is_active) {
|
||||||
|
activeSessions.delete(token);
|
||||||
|
clearAuthCookie(res);
|
||||||
|
return res.status(401).json({ error: 'UNAUTHORIZED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
req.user = sanitizeUser(user);
|
||||||
|
req.authToken = token;
|
||||||
|
touchSession(token);
|
||||||
|
setAuthCookie(res, token);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
// --- API Endpoints ---
|
// --- API Endpoints ---
|
||||||
|
|
||||||
|
app.get('/api/setup/status', (req, res) => {
|
||||||
|
try {
|
||||||
|
const status = getSetupStatus();
|
||||||
|
res.json(status);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('⚠️ [Setup] Statusabfrage fehlgeschlagen:', error);
|
||||||
|
res.status(500).json({ error: 'SETUP_STATUS_FAILED' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/setup/complete', (req, res) => {
|
||||||
|
const { superuser: superuserInput, server: serverInput, endpoint: endpointInput, apiKey: apiKeyInput } = req.body ?? {};
|
||||||
|
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 = {};
|
||||||
|
|
||||||
|
const normalizeServerInput = (input) => {
|
||||||
|
if (!input) return null;
|
||||||
|
const name = typeof input.name === 'string' ? input.name.trim() : '';
|
||||||
|
const url = typeof input.url === 'string' ? input.url.trim() : '';
|
||||||
|
if (!url) {
|
||||||
|
return { name: name || '', url: '' };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
name: name || null,
|
||||||
|
url
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
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') {
|
||||||
|
const value = input.trim();
|
||||||
|
return { value, serverId: null };
|
||||||
|
}
|
||||||
|
if (typeof input === 'object') {
|
||||||
|
const rawValue = typeof input.value === 'string' ? input.value : typeof input.key === 'string' ? input.key : '';
|
||||||
|
const value = rawValue.trim();
|
||||||
|
const serverIdRaw = input.serverId !== undefined && input.serverId !== null ? Number(input.serverId) : null;
|
||||||
|
const serverId = Number.isFinite(serverIdRaw) ? serverIdRaw : null;
|
||||||
|
return { value, serverId };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
let serverPayload = normalizeServerInput(serverInput);
|
||||||
|
let endpointPayload = normalizeEndpointInput(endpointInput);
|
||||||
|
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' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const setupResult = completeSetup({
|
||||||
|
server: serverPayload && serverPayload.url ? serverPayload : null,
|
||||||
|
endpoint: endpointInputPayload
|
||||||
|
});
|
||||||
|
|
||||||
|
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() : '';
|
||||||
|
const password = typeof superuserInput?.password === 'string' ? superuserInput.password : '';
|
||||||
|
|
||||||
|
if (!username || !email || !password) {
|
||||||
|
return res.status(400).json({ error: 'SUPERUSER_DETAILS_REQUIRED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = registerSuperuser({ username, email, password });
|
||||||
|
created.superuser = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusSnapshot = getSetupStatus();
|
||||||
|
|
||||||
|
if (!targetServerId) {
|
||||||
|
targetServerId = statusSnapshot.servers.items?.[0]?.id ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apiKeyPayload && apiKeyPayload.value) {
|
||||||
|
if (!targetServerId) {
|
||||||
|
return res.status(400).json({ error: 'SERVER_DETAILS_REQUIRED' });
|
||||||
|
}
|
||||||
|
const apiKeyResult = setServerApiKey({ serverId: targetServerId, apiKey: apiKeyPayload.value });
|
||||||
|
created.apiKey = apiKeyResult;
|
||||||
|
} else if (needsApiKey) {
|
||||||
|
return res.status(400).json({ error: 'API_KEY_REQUIRED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalStatus = getSetupStatus();
|
||||||
|
created.defaultEndpoint = finalStatus.endpoints.default;
|
||||||
|
res.status(201).json({
|
||||||
|
success: finalStatus.setupComplete,
|
||||||
|
status: finalStatus,
|
||||||
|
created
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
const code = error.code || error.message;
|
||||||
|
switch (code) {
|
||||||
|
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':
|
||||||
|
case 'INVALID_PASSWORD':
|
||||||
|
case 'API_KEY_REQUIRED':
|
||||||
|
return res.status(400).json({ error: code });
|
||||||
|
case 'API_KEY_ENCRYPT_FAILED':
|
||||||
|
console.error('⚠️ [Setup] API-Key Verschlüsselung fehlgeschlagen:', error);
|
||||||
|
return res.status(500).json({ error: 'API_KEY_ENCRYPT_FAILED' });
|
||||||
|
case 'SERVER_NOT_FOUND':
|
||||||
|
return res.status(404).json({ error: 'SERVER_NOT_FOUND' });
|
||||||
|
case 'SUPERUSER_ALREADY_EXISTS':
|
||||||
|
return res.status(409).json({ error: 'SUPERUSER_EXISTS' });
|
||||||
|
default:
|
||||||
|
console.error('⚠️ [Setup] Fehler beim Abschluss:', error);
|
||||||
|
return res.status(500).json({ error: 'SETUP_FAILED' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete('/api/setup/endpoints/:id', (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', (req, res) => {
|
||||||
|
const { id } = req.params;
|
||||||
|
const numericId = Number(id);
|
||||||
|
if (!Number.isFinite(numericId)) {
|
||||||
|
return res.status(400).json({ error: 'SERVER_ID_INVALID' });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = removeServer(numericId);
|
||||||
|
const status = getSetupStatus();
|
||||||
|
res.json({ success: true, removed: result, status });
|
||||||
|
} catch (error) {
|
||||||
|
const code = error.code || error.message;
|
||||||
|
switch (code) {
|
||||||
|
case 'SERVER_ID_INVALID':
|
||||||
|
return res.status(400).json({ error: 'SERVER_ID_INVALID' });
|
||||||
|
case 'SERVER_NOT_FOUND':
|
||||||
|
return res.status(404).json({ error: 'SERVER_NOT_FOUND' });
|
||||||
|
default:
|
||||||
|
console.error('⚠️ [Setup] Server konnte nicht gelöscht werden:', error);
|
||||||
|
return res.status(500).json({ error: 'SERVER_DELETE_FAILED' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.put('/api/setup/servers/:id/api-key', (req, res) => {
|
||||||
|
const { id } = req.params;
|
||||||
|
const numericId = Number(id);
|
||||||
|
if (!Number.isFinite(numericId)) {
|
||||||
|
return res.status(400).json({ error: 'SERVER_ID_INVALID' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawValue = typeof req.body?.apiKey === 'string' ? req.body.apiKey : typeof req.body?.key === 'string' ? req.body.key : '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = setServerApiKey({ serverId: numericId, apiKey: rawValue });
|
||||||
|
const status = getSetupStatus();
|
||||||
|
res.json({ success: true, updated: result, status });
|
||||||
|
} catch (error) {
|
||||||
|
const code = error.code || error.message;
|
||||||
|
switch (code) {
|
||||||
|
case 'SERVER_ID_INVALID':
|
||||||
|
return res.status(400).json({ error: 'SERVER_ID_INVALID' });
|
||||||
|
case 'SERVER_NOT_FOUND':
|
||||||
|
return res.status(404).json({ error: 'SERVER_NOT_FOUND' });
|
||||||
|
case 'API_KEY_REQUIRED':
|
||||||
|
return res.status(400).json({ error: 'API_KEY_REQUIRED' });
|
||||||
|
case 'API_KEY_ENCRYPT_FAILED':
|
||||||
|
console.error('⚠️ [Setup] API-Key Verschlüsselung fehlgeschlagen:', error);
|
||||||
|
return res.status(500).json({ error: 'API_KEY_ENCRYPT_FAILED' });
|
||||||
|
default:
|
||||||
|
console.error('⚠️ [Setup] API-Key konnte nicht aktualisiert werden:', error);
|
||||||
|
return res.status(500).json({ error: 'API_KEY_UPDATE_FAILED' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/auth/login', (req, res) => {
|
||||||
|
if (!hasSuperuser()) {
|
||||||
|
return res.status(403).json({ error: 'SUPERUSER_REQUIRED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasServer() || !hasEndpoint() || !hasApiKey()) {
|
||||||
|
return res.status(403).json({ error: 'SETUP_REQUIRED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { identifier, password } = req.body ?? {};
|
||||||
|
if (!identifier || !password) {
|
||||||
|
return res.status(400).json({ error: 'MISSING_CREDENTIALS' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = findUserByIdentifier(identifier);
|
||||||
|
if (!user || !user.is_active) {
|
||||||
|
return res.status(401).json({ error: 'INVALID_CREDENTIALS' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const passwordValid = verifyPassword(password, user.password_hash, user.password_salt);
|
||||||
|
if (!passwordValid) {
|
||||||
|
return res.status(401).json({ error: 'INVALID_CREDENTIALS' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const session = createSessionForUser(user);
|
||||||
|
markUserLogin(user.id);
|
||||||
|
setAuthCookie(res, session.token);
|
||||||
|
|
||||||
|
res.json({ user: sanitizeUser(user) });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/auth/logout', (req, res) => {
|
||||||
|
const token = extractAuthToken(req);
|
||||||
|
if (token) {
|
||||||
|
activeSessions.delete(token);
|
||||||
|
}
|
||||||
|
clearAuthCookie(res);
|
||||||
|
res.json({ success: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/api/auth/session', (req, res) => {
|
||||||
|
if (!hasSuperuser()) {
|
||||||
|
return res.status(403).json({ error: 'SUPERUSER_REQUIRED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasServer() || !hasEndpoint() || !hasApiKey()) {
|
||||||
|
return res.status(403).json({ error: 'SETUP_REQUIRED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = extractAuthToken(req);
|
||||||
|
const session = getSessionRecord(token);
|
||||||
|
if (!session) {
|
||||||
|
clearAuthCookie(res);
|
||||||
|
return res.status(401).json({ error: 'UNAUTHORIZED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = findUserById(session.userId);
|
||||||
|
if (!user || !user.is_active) {
|
||||||
|
activeSessions.delete(token);
|
||||||
|
clearAuthCookie(res);
|
||||||
|
return res.status(401).json({ error: 'UNAUTHORIZED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
touchSession(token);
|
||||||
|
setAuthCookie(res, token);
|
||||||
|
res.json({ user: sanitizeUser(user) });
|
||||||
|
});
|
||||||
|
|
||||||
// Superuser Setup
|
// Superuser Setup
|
||||||
app.get('/api/auth/superuser/status', (req, res) => {
|
app.get('/api/auth/superuser/status', (req, res) => {
|
||||||
const exists = hasSuperuser();
|
const exists = hasSuperuser();
|
||||||
@@ -1225,6 +1778,9 @@ app.get('/api/stacks', maintenanceGuard, async (req, res) => {
|
|||||||
res.json(stacksWithStatus);
|
res.json(stacksWithStatus);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`❌ Fehler beim Abrufen der Stacks:`, err.message);
|
console.error(`❌ Fehler beim Abrufen der Stacks:`, err.message);
|
||||||
|
if (err.message === 'PORTAINER_URL_NOT_CONFIGURED') {
|
||||||
|
return res.status(503).json({ error: 'PORTAINER_URL_NOT_CONFIGURED' });
|
||||||
|
}
|
||||||
res.status(500).json({ error: err.message });
|
res.status(500).json({ error: err.message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1237,6 +1793,9 @@ app.get('/api/maintenance/portainer-status', async (req, res) => {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
const message = err.response?.data?.message || err.message || 'Unbekannter Fehler';
|
const message = err.response?.data?.message || err.message || 'Unbekannter Fehler';
|
||||||
console.error(`❌ [Maintenance] Fehler beim Prüfen des Portainer-Status: ${message}`);
|
console.error(`❌ [Maintenance] Fehler beim Prüfen des Portainer-Status: ${message}`);
|
||||||
|
if (message === 'PORTAINER_URL_NOT_CONFIGURED') {
|
||||||
|
return res.status(503).json({ error: 'PORTAINER_URL_NOT_CONFIGURED' });
|
||||||
|
}
|
||||||
res.status(500).json({ error: message });
|
res.status(500).json({ error: message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1737,8 +2296,9 @@ app.put('/api/stacks/redeploy-all', maintenanceGuard, async (req, res) => {
|
|||||||
console.log(`🚀 PUT /api/stacks/redeploy-all: Redeploy ALL gestartet`);
|
console.log(`🚀 PUT /api/stacks/redeploy-all: Redeploy ALL gestartet`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const endpointId = requireActiveEndpointId();
|
||||||
const stacksRes = await axiosInstance.get('/api/stacks');
|
const stacksRes = await axiosInstance.get('/api/stacks');
|
||||||
const filteredStacks = stacksRes.data.filter(stack => stack.EndpointId === ENDPOINT_ID);
|
const filteredStacks = stacksRes.data.filter(stack => String(stack.EndpointId) === String(endpointId));
|
||||||
|
|
||||||
console.log("📦 Redeploy ALL für folgende Stacks:");
|
console.log("📦 Redeploy ALL für folgende Stacks:");
|
||||||
filteredStacks.forEach(s => console.log(` - ${s.Name}`));
|
filteredStacks.forEach(s => console.log(` - ${s.Name}`));
|
||||||
@@ -1758,7 +2318,7 @@ app.put('/api/stacks/redeploy-all', maintenanceGuard, async (req, res) => {
|
|||||||
stackName: '---',
|
stackName: '---',
|
||||||
status: 'started',
|
status: 'started',
|
||||||
message: `Redeploy ALL gestartet für: ${stackSummary}`,
|
message: `Redeploy ALL gestartet für: ${stackSummary}`,
|
||||||
endpoint: ENDPOINT_ID,
|
endpoint: endpointId,
|
||||||
redeployType: REDEPLOY_TYPES.ALL
|
redeployType: REDEPLOY_TYPES.ALL
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1789,7 +2349,7 @@ app.put('/api/stacks/redeploy-all', maintenanceGuard, async (req, res) => {
|
|||||||
stackName: '---',
|
stackName: '---',
|
||||||
status: 'success',
|
status: 'success',
|
||||||
message: 'Redeploy ALL abgeschlossen',
|
message: 'Redeploy ALL abgeschlossen',
|
||||||
endpoint: ENDPOINT_ID,
|
endpoint: endpointId,
|
||||||
redeployType: REDEPLOY_TYPES.ALL
|
redeployType: REDEPLOY_TYPES.ALL
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1801,7 +2361,7 @@ app.put('/api/stacks/redeploy-all', maintenanceGuard, async (req, res) => {
|
|||||||
stackName: '---',
|
stackName: '---',
|
||||||
status: 'error',
|
status: 'error',
|
||||||
message,
|
message,
|
||||||
endpoint: ENDPOINT_ID,
|
endpoint: endpointId,
|
||||||
redeployType: REDEPLOY_TYPES.ALL
|
redeployType: REDEPLOY_TYPES.ALL
|
||||||
});
|
});
|
||||||
console.error('❌ Fehler bei Redeploy ALL:', message);
|
console.error('❌ Fehler bei Redeploy ALL:', message);
|
||||||
@@ -1820,6 +2380,7 @@ app.put('/api/stacks/redeploy-selection', maintenanceGuard, async (req, res) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const endpointId = requireActiveEndpointId();
|
||||||
const normalizedIds = stackIds.map((id) => String(id));
|
const normalizedIds = stackIds.map((id) => String(id));
|
||||||
const { filteredStacks } = await loadStackCollections();
|
const { filteredStacks } = await loadStackCollections();
|
||||||
const stacksById = new Map(filteredStacks.map((stack) => [String(stack.Id), stack]));
|
const stacksById = new Map(filteredStacks.map((stack) => [String(stack.Id), stack]));
|
||||||
@@ -1850,7 +2411,7 @@ app.put('/api/stacks/redeploy-selection', maintenanceGuard, async (req, res) =>
|
|||||||
stackName: `Auswahl (${stackIds.length})`,
|
stackName: `Auswahl (${stackIds.length})`,
|
||||||
status: 'started',
|
status: 'started',
|
||||||
message: `Redeploy Auswahl gestartet für: ${summaryText}`,
|
message: `Redeploy Auswahl gestartet für: ${summaryText}`,
|
||||||
endpoint: ENDPOINT_ID,
|
endpoint: endpointId,
|
||||||
redeployType: REDEPLOY_TYPES.SELECTION
|
redeployType: REDEPLOY_TYPES.SELECTION
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1860,7 +2421,7 @@ app.put('/api/stacks/redeploy-selection', maintenanceGuard, async (req, res) =>
|
|||||||
stackName: `Auswahl (${stackIds.length})`,
|
stackName: `Auswahl (${stackIds.length})`,
|
||||||
status: 'success',
|
status: 'success',
|
||||||
message: 'Redeploy Auswahl übersprungen: keine veralteten Stacks',
|
message: 'Redeploy Auswahl übersprungen: keine veralteten Stacks',
|
||||||
endpoint: ENDPOINT_ID,
|
endpoint: endpointId,
|
||||||
redeployType: REDEPLOY_TYPES.SELECTION
|
redeployType: REDEPLOY_TYPES.SELECTION
|
||||||
});
|
});
|
||||||
return res.json({ success: true, message: 'Keine veralteten Stacks in der Auswahl' });
|
return res.json({ success: true, message: 'Keine veralteten Stacks in der Auswahl' });
|
||||||
@@ -1888,7 +2449,7 @@ app.put('/api/stacks/redeploy-selection', maintenanceGuard, async (req, res) =>
|
|||||||
stackName: `Auswahl (${stackIds.length})`,
|
stackName: `Auswahl (${stackIds.length})`,
|
||||||
status: 'success',
|
status: 'success',
|
||||||
message: 'Redeploy Auswahl abgeschlossen',
|
message: 'Redeploy Auswahl abgeschlossen',
|
||||||
endpoint: ENDPOINT_ID,
|
endpoint: endpointId,
|
||||||
redeployType: REDEPLOY_TYPES.SELECTION
|
redeployType: REDEPLOY_TYPES.SELECTION
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1900,7 +2461,7 @@ app.put('/api/stacks/redeploy-selection', maintenanceGuard, async (req, res) =>
|
|||||||
stackName: `Auswahl (${Array.isArray(stackIds) ? stackIds.length : 0})`,
|
stackName: `Auswahl (${Array.isArray(stackIds) ? stackIds.length : 0})`,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
message,
|
message,
|
||||||
endpoint: ENDPOINT_ID,
|
endpoint: endpointId,
|
||||||
redeployType: REDEPLOY_TYPES.SELECTION
|
redeployType: REDEPLOY_TYPES.SELECTION
|
||||||
});
|
});
|
||||||
console.error('❌ Fehler bei Redeploy Auswahl:', message);
|
console.error('❌ Fehler bei Redeploy Auswahl:', message);
|
||||||
@@ -1911,4 +2472,3 @@ app.put('/api/stacks/redeploy-selection', maintenanceGuard, async (req, res) =>
|
|||||||
server.listen(PORT, () => {
|
server.listen(PORT, () => {
|
||||||
console.log(`🚀 Server läuft auf Port ${PORT}`);
|
console.log(`🚀 Server läuft auf Port ${PORT}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -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
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
function EN(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 Tx(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function Ps(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 Cx={exports:{}},dd={},Ox={exports:{}},ut={};/**
|
|
||||||
* @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 ks=Symbol.for("react.element"),NN=Symbol.for("react.portal"),RN=Symbol.for("react.fragment"),jN=Symbol.for("react.strict_mode"),AN=Symbol.for("react.profiler"),MN=Symbol.for("react.provider"),IN=Symbol.for("react.context"),DN=Symbol.for("react.forward_ref"),LN=Symbol.for("react.suspense"),FN=Symbol.for("react.memo"),VN=Symbol.for("react.lazy"),Mv=Symbol.iterator;function zN(e){return e===null||typeof e!="object"?null:(e=Mv&&e[Mv]||e["@@iterator"],typeof e=="function"?e:null)}var Sx={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Px=Object.assign,kx={};function Ui(e,t,r){this.props=e,this.context=t,this.refs=kx,this.updater=r||Sx}Ui.prototype.isReactComponent={};Ui.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")};Ui.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function Ex(){}Ex.prototype=Ui.prototype;function Oh(e,t,r){this.props=e,this.context=t,this.refs=kx,this.updater=r||Sx}var Sh=Oh.prototype=new Ex;Sh.constructor=Oh;Px(Sh,Ui.prototype);Sh.isPureReactComponent=!0;var Iv=Array.isArray,Nx=Object.prototype.hasOwnProperty,Ph={current:null},Rx={key:!0,ref:!0,__self:!0,__source:!0};function jx(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)Nx.call(t,n)&&!Rx.hasOwnProperty(n)&&(o[n]=t[n]);var c=arguments.length-2;if(c===1)o.children=r;else if(1<c){for(var p=Array(c),g=0;g<c;g++)p[g]=arguments[g+2];o.children=p}if(e&&e.defaultProps)for(n in c=e.defaultProps,c)o[n]===void 0&&(o[n]=c[n]);return{$$typeof:ks,type:e,key:a,ref:i,props:o,_owner:Ph.current}}function BN(e,t){return{$$typeof:ks,type:e.type,key:t,ref:e.ref,props:e.props,_owner:e._owner}}function kh(e){return typeof e=="object"&&e!==null&&e.$$typeof===ks}function WN(e){var t={"=":"=0",":":"=2"};return"$"+e.replace(/[=:]/g,function(r){return t[r]})}var Dv=/\/+/g;function qf(e,t){return typeof e=="object"&&e!==null&&e.key!=null?WN(""+e.key):t.toString(36)}function Fu(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 ks:case NN:i=!0}}if(i)return i=e,o=o(i),e=n===""?"."+qf(i,0):n,Iv(o)?(r="",e!=null&&(r=e.replace(Dv,"$&/")+"/"),Fu(o,t,r,"",function(g){return g})):o!=null&&(kh(o)&&(o=BN(o,r+(!o.key||i&&i.key===o.key?"":(""+o.key).replace(Dv,"$&/")+"/")+e)),t.push(o)),1;if(i=0,n=n===""?".":n+":",Iv(e))for(var c=0;c<e.length;c++){a=e[c];var p=n+qf(a,c);i+=Fu(a,t,r,p,o)}else if(p=zN(e),typeof p=="function")for(e=p.call(e),c=0;!(a=e.next()).done;)a=a.value,p=n+qf(a,c++),i+=Fu(a,t,r,p,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 uu(e,t,r){if(e==null)return e;var n=[],o=0;return Fu(e,n,"","",function(a){return t.call(r,a,o++)}),n}function UN(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 Cr={current:null},Vu={transition:null},HN={ReactCurrentDispatcher:Cr,ReactCurrentBatchConfig:Vu,ReactCurrentOwner:Ph};ut.Children={map:uu,forEach:function(e,t,r){uu(e,function(){t.apply(this,arguments)},r)},count:function(e){var t=0;return uu(e,function(){t++}),t},toArray:function(e){return uu(e,function(t){return t})||[]},only:function(e){if(!kh(e))throw Error("React.Children.only expected to receive a single React element child.");return e}};ut.Component=Ui;ut.Fragment=RN;ut.Profiler=AN;ut.PureComponent=Oh;ut.StrictMode=jN;ut.Suspense=LN;ut.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=HN;ut.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=Px({},e.props),o=e.key,a=e.ref,i=e._owner;if(t!=null){if(t.ref!==void 0&&(a=t.ref,i=Ph.current),t.key!==void 0&&(o=""+t.key),e.type&&e.type.defaultProps)var c=e.type.defaultProps;for(p in t)Nx.call(t,p)&&!Rx.hasOwnProperty(p)&&(n[p]=t[p]===void 0&&c!==void 0?c[p]:t[p])}var p=arguments.length-2;if(p===1)n.children=r;else if(1<p){c=Array(p);for(var g=0;g<p;g++)c[g]=arguments[g+2];n.children=c}return{$$typeof:ks,type:e.type,key:o,ref:a,props:n,_owner:i}};ut.createContext=function(e){return e={$$typeof:IN,_currentValue:e,_currentValue2:e,_threadCount:0,Provider:null,Consumer:null,_defaultValue:null,_globalName:null},e.Provider={$$typeof:MN,_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 $N=j,qN=Symbol.for("react.element"),GN=Symbol.for("react.fragment"),KN=Object.prototype.hasOwnProperty,YN=$N.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,XN={key:!0,ref:!0,__self:!0,__source:!0};function Ax(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)KN.call(t,n)&&!XN.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:qN,type:e,key:a,ref:i,props:o,_owner:YN.current}}dd.Fragment=GN;dd.jsx=Ax;dd.jsxs=Ax;Cx.exports=dd;var M=Cx.exports,$p={},Mx={exports:{}},Yr={},Ix={exports:{}},Dx={};/**
|
|
||||||
* @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($,B){var U=$.length;$.push(B);e:for(;0<U;){var K=U-1>>>1,Q=$[K];if(0<o(Q,B))$[K]=B,$[U]=Q,U=K;else break e}}function r($){return $.length===0?null:$[0]}function n($){if($.length===0)return null;var B=$[0],U=$.pop();if(U!==B){$[0]=U;e:for(var K=0,Q=$.length,ne=Q>>>1;K<ne;){var le=2*(K+1)-1,ce=$[le],re=le+1,me=$[re];if(0>o(ce,U))re<Q&&0>o(me,ce)?($[K]=me,$[re]=U,K=re):($[K]=ce,$[le]=U,K=le);else if(re<Q&&0>o(me,U))$[K]=me,$[re]=U,K=re;else break e}}return B}function o($,B){var U=$.sortIndex-B.sortIndex;return U!==0?U:$.id-B.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 p=[],g=[],m=1,_=null,O=3,C=!1,T=!1,x=!1,b=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 u($){for(var B=r(g);B!==null;){if(B.callback===null)n(g);else if(B.startTime<=$)n(g),B.sortIndex=B.expirationTime,t(p,B);else break;B=r(g)}}function s($){if(x=!1,u($),!T)if(r(p)!==null)T=!0,G(h);else{var B=r(g);B!==null&&X(s,B.startTime-$)}}function h($,B){T=!1,x&&(x=!1,d(E),E=-1),C=!0;var U=O;try{for(u(B),_=r(p);_!==null&&(!(_.expirationTime>B)||$&&!R());){var K=_.callback;if(typeof K=="function"){_.callback=null,O=_.priorityLevel;var Q=K(_.expirationTime<=B);B=e.unstable_now(),typeof Q=="function"?_.callback=Q:_===r(p)&&n(p),u(B)}else n(p);_=r(p)}if(_!==null)var ne=!0;else{var le=r(g);le!==null&&X(s,le.startTime-B),ne=!1}return ne}finally{_=null,O=U,C=!1}}var v=!1,y=null,E=-1,P=5,k=-1;function R(){return!(e.unstable_now()-k<P)}function A(){if(y!==null){var $=e.unstable_now();k=$;var B=!0;try{B=y(!0,$)}finally{B?D():(v=!1,y=null)}}else v=!1}var D;if(typeof l=="function")D=function(){l(A)};else if(typeof MessageChannel<"u"){var L=new MessageChannel,W=L.port2;L.port1.onmessage=A,D=function(){W.postMessage(null)}}else D=function(){b(A,0)};function G($){y=$,v||(v=!0,D())}function X($,B){E=b(function(){$(e.unstable_now())},B)}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($){$.callback=null},e.unstable_continueExecution=function(){T||C||(T=!0,G(h))},e.unstable_forceFrameRate=function($){0>$||125<$?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):P=0<$?Math.floor(1e3/$):5},e.unstable_getCurrentPriorityLevel=function(){return O},e.unstable_getFirstCallbackNode=function(){return r(p)},e.unstable_next=function($){switch(O){case 1:case 2:case 3:var B=3;break;default:B=O}var U=O;O=B;try{return $()}finally{O=U}},e.unstable_pauseExecution=function(){},e.unstable_requestPaint=function(){},e.unstable_runWithPriority=function($,B){switch($){case 1:case 2:case 3:case 4:case 5:break;default:$=3}var U=O;O=$;try{return B()}finally{O=U}},e.unstable_scheduleCallback=function($,B,U){var K=e.unstable_now();switch(typeof U=="object"&&U!==null?(U=U.delay,U=typeof U=="number"&&0<U?K+U:K):U=K,$){case 1:var Q=-1;break;case 2:Q=250;break;case 5:Q=1073741823;break;case 4:Q=1e4;break;default:Q=5e3}return Q=U+Q,$={id:m++,callback:B,priorityLevel:$,startTime:U,expirationTime:Q,sortIndex:-1},U>K?($.sortIndex=U,t(g,$),r(p)===null&&$===r(g)&&(x?(d(E),E=-1):x=!0,X(s,U-K))):($.sortIndex=Q,t(p,$),T||C||(T=!0,G(h))),$},e.unstable_shouldYield=R,e.unstable_wrapCallback=function($){var B=O;return function(){var U=O;O=B;try{return $.apply(this,arguments)}finally{O=U}}}})(Dx);Ix.exports=Dx;var QN=Ix.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 Lx=j,Gr=QN;function we(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 Fx=new Set,Zl={};function Va(e,t){ki(e,t),ki(e+"Capture",t)}function ki(e,t){for(Zl[e]=t,e=0;e<t.length;e++)Fx.add(t[e])}var to=!(typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),qp=Object.prototype.hasOwnProperty,JN=/^[: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]*$/,Lv={},Fv={};function ZN(e){return qp.call(Fv,e)?!0:qp.call(Lv,e)?!1:JN.test(e)?Fv[e]=!0:(Lv[e]=!0,!1)}function eR(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 tR(e,t,r,n){if(t===null||typeof t>"u"||eR(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 Or(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 sr={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){sr[e]=new Or(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];sr[t]=new Or(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){sr[e]=new Or(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){sr[e]=new Or(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){sr[e]=new Or(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){sr[e]=new Or(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){sr[e]=new Or(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){sr[e]=new Or(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){sr[e]=new Or(e,5,!1,e.toLowerCase(),null,!1,!1)});var Nh=/[\-:]([a-z])/g;function Rh(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(Nh,Rh);sr[t]=new Or(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(Nh,Rh);sr[t]=new Or(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(Nh,Rh);sr[t]=new Or(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){sr[e]=new Or(e,1,!1,e.toLowerCase(),null,!1,!1)});sr.xlinkHref=new Or("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){sr[e]=new Or(e,1,!1,e.toLowerCase(),null,!0,!0)});function jh(e,t,r,n){var o=sr.hasOwnProperty(t)?sr[t]:null;(o!==null?o.type!==0:n||!(2<t.length)||t[0]!=="o"&&t[0]!==
Line truncated
|
|
||||||
`+Gf+e}var Kf=!1;function Yf(e,t){if(!e||Kf)return"";Kf=!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 p=`
|
|
||||||
`+o[i].replace(" at new "," at ");return e.displayName&&p.includes("<anonymous>")&&(p=p.replace("<anonymous>",e.displayName)),p}while(1<=i&&0<=c);break}}}finally{Kf=!1,Error.prepareStackTrace=r}return(e=e?e.displayName||e.name:"")?Rl(e):""}function rR(e){switch(e.tag){case 5:return Rl(e.type);case 16:return Rl("Lazy");case 13:return Rl("Suspense");case 19:return Rl("SuspenseList");case 0:case 2:case 15:return e=Yf(e.type,!1),e;case 11:return e=Yf(e.type.render,!1),e;case 1:return e=Yf(e.type,!0),e;default:return""}}function Xp(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 oi:return"Fragment";case ni:return"Portal";case Gp:return"Profiler";case Ah:return"StrictMode";case Kp:return"Suspense";case Yp:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case zx:return(e.displayName||"Context")+".Consumer";case Vx:return(e._context.displayName||"Context")+".Provider";case Mh:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case Ih:return t=e.displayName||null,t!==null?t:Xp(e.type)||"Memo";case Eo:t=e._payload,e=e._init;try{return Xp(e(t))}catch{}}return null}function nR(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 Xp(t);case 8:return t===Ah?"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 Ko(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function Wx(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function oR(e){var t=Wx(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 du(e){e._valueTracker||(e._valueTracker=oR(e))}function Ux(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var r=t.getValue(),n="";return e&&(n=Wx(e)?e.checked?"true":"false":e.value),e=n,e!==r?(t.setValue(e),!0):!1}function gc(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 Qp(e,t){var r=t.checked;return Bt({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:r??e._wrapperState.initialChecked})}function zv(e,t){var r=t.defaultValue==null?"":t.defaultValue,n=t.checked!=null?t.checked:t.defaultChecked;r=Ko(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 Hx(e,t){t=t.checked,t!=null&&jh(e,"checked",t,!1)}function Jp(e,t){Hx(e,t);var r=Ko(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")?Zp(e,t.type,r):t.hasOwnProperty("defaultValue")&&Zp(e,t.type,Ko(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function Bv(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 Zp(e,t,r){(t!=="number"||gc(e.ownerDocument)!==e)&&(r==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+r&&(e.defaultValue=""+r))}var jl=Array.isArray;function mi(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=""+Ko(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(hj,"")}function yu(e,t,r){if(t=cy(t),cy(e)!==t&&r)throw Error(we(425))}function Tc(){}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 b0=typeof setTimeout=="function"?setTimeout:void 0,gj=typeof clearTimeout=="function"?clearTimeout:void 0,dy=typeof Promise=="function"?Promise:void 0,bj=typeof queueMicrotask=="function"?queueMicrotask:typeof dy<"u"?function(e){return dy.resolve(null).then(e).catch(mj)}:b0;function mj(e){setTimeout(function(){throw e})}function sp(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),os(t);return}n--}else r!=="$"&&r!=="$?"&&r!=="$!"||n++;r=o}while(r);os(t)}function zo(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 fy(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 $i=Math.random().toString(36).slice(2),Fn="__reactFiber$"+$i,us="__reactProps$"+$i,ro="__reactContainer$"+$i,m0="__reactEvents$"+$i,vj="__reactListeners$"+$i,yj="__reactHandles$"+$i;function wa(e){var t=e[Fn];if(t)return t;for(var r=e.parentNode;r;){if(t=r[ro]||r[Fn]){if(r=t.alternate,t.child!==null||r!==null&&r.child!==null)for(e=fy(e);e!==null;){if(r=e[Fn])return r;e=fy(e)}return t}e=r,r=e.parentNode}return null}function Rs(e){return e=e[Fn]||e[ro],!e||e.tag!==5&&e.tag!==6&&e.tag!==13&&e.tag!==3?null:e}function si(e){if(e.tag===5||e.tag===6)return e.stateNode;throw Error(we(33))}function bd(e){return e[us]||null}var v0=[],ui=-1;function Jo(e){return{current:e}}function Rt(e){0>ui||(e.current=v0[ui],v0[ui]=null,ui--)}function St(e,t){ui++,v0[ui]=e.current,e.current=t}var Yo={},mr=Jo(Yo),Ir=Jo(!1),Ea=Yo;function Ei(e,t){var r=e.type.contextTypes;if(!r)return Yo;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 Dr(e){return e=e.childContextTypes,e!=null}function Cc(){Rt(Ir),Rt(mr)}function py(e,t,r){if(mr.current!==Yo)throw Error(we(168));St(mr,t),St(Ir,r)}function j_(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(we(108,nR(e)||"Unknown",o));return Bt({},r,n)}function Oc(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||Yo,Ea=mr.current,St(mr,e),St(Ir,Ir.current),!0}function hy(e,t,r){var n=e.stateNode;if(!n)throw Error(we(169));r?(e=j_(e,t,Ea),n.__reactInternalMemoizedMergedChildContext=e,Rt(Ir),Rt(mr),St(mr,e)):Rt(Ir),St(Ir,r)}var Yn=null,md=!1,up=!1;function A_(e){Yn===null?Yn=[e]:Yn.push(e)}function wj(e){md=!0,A_(e)}function Zo(){if(!up&&Yn!==null){up=!0;var e=0,t=xt;try{var r=Yn;for(xt=1;e<r.length;e++){var n=r[e];do n=n(!0);while(n!==null)}Yn=null,md=!1}catch(o){throw Yn!==null&&(Yn=Yn.slice(e+1)),o_(Lh,Zo),o}finally{xt=t,up=!1}}return null}var ci=[],di=0,Sc=null,Pc=0,nn=[],on=0,Na=null,Xn=1,Qn="";function va(e,t){ci[di++]=Pc,ci[di++]=Sc,Sc=e,Pc=t}function M_(e,t,r){nn[on++]=Xn,nn[on++]=Qn,nn[on++]=Na,Na=e;var n=Xn;e=Qn;var o=32-Tn(n)-1;n&=~(1<<o),r+=1;var a=32-Tn(t)+o;if(30<a){var i=o-o%5;a=(n&(1<<i)-1).toString(32),n>>=i,o-=i,Xn=1<<32-Tn(t)+o|r<<o|n,Qn=a+e}else Xn=1<<a|r<<o|n,Qn=e}function qh(e){e.return!==null&&(va(e,1),M_(e,1,0))}function Gh(e){for(;e===Sc;)Sc=ci[--di],ci[di]=null,Pc=ci[--di],ci[di]=null;for(;e===Na;)Na=nn[--on],nn[on]=null,Qn=nn[--on],nn[on]=null,Xn=nn[--on],nn[on]=null}var Ur=null,Wr=null,Mt=!1,wn=null;function I_(e,t){var r=ln(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 gy(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,Ur=e,Wr=zo(t.firstChild),!0):!1;case 6:return t=e.pendingProps===""||t.nodeType!==3?null:t,t!==null?(e.stateNode=t,Ur=e,Wr=null,!0):!1;case 13:return t=t.nodeType!==8?null:t,t!==null?(r=Na!==null?{id:Xn,overflow:Qn}:null,e.memoizedState={dehydrated:t,treeContext:r,retryLane:1073741824},r=ln(18,null,null,0),r.stateNode=t,r.return=e,e.child=r,Ur=e,Wr=null,!0):!1;default:return!1}}function y0(e){return(e.mode&1)!==0&&(e.flags&128)===0}function w0(e){if(Mt){var t=Wr;if(t){var r=t;if(!gy(e,t)){if(y0(e))throw Error(we(418));t=zo(r.nextSibling);var n=Ur;t&&gy(e,t)?I_(n,r):(e.flags=e.flags&-4097|2,Mt=!1,Ur=e)}}else{if(y0(e))throw Error(we(418));e.flags=e.flags&-4097|2,Mt=!1,Ur=e}}}function by(e){
Line truncated
|
|
||||||
Error generating stack: `+a.message+`
|
|
||||||
`+a.stack}return{value:e,source:t,stack:o,digest:null}}function hp(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 Ej=typeof WeakMap=="function"?WeakMap:Map;function u1(e,t,r){r=Jn(-1,r),r.tag=3,r.payload={element:null};var n=t.value;return r.callback=function(){Ic||(Ic=!0,M0=n),C0(e,t)},r}function c1(e,t,r){r=Jn(-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"&&(Wo===null?Wo=new Set([this]):Wo.add(this));var i=t.stack;this.componentDidCatch(t.value,{componentStack:i!==null?i:""})}),r}function Cy(e,t,r){var n=e.pingCache;if(n===null){n=e.pingCache=new Ej;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=Uj.bind(null,e,t,r),t.then(e,e))}function Oy(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 Sy(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=Jn(-1,1),t.tag=2,Bo(r,t,1))),r.lanes|=1),e)}var Nj=lo.ReactCurrentOwner,Ar=!1;function _r(e,t,r,n){t.child=e===null?W_(t,null,r,n):Ri(t,e.child,r,n)}function Py(e,t,r,n,o){r=r.render;var a=t.ref;return xi(t,o),n=og(e,t,r,n,a,o),r=ag(),e!==null&&!Ar?(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~o,oo(e,t,o)):(Mt&&r&&qh(t),t.flags|=1,_r(e,t,n,o),t.child)}function ky(e,t,r,n,o){if(e===null){var a=r.type;return typeof a=="function"&&!gg(a)&&a.defaultProps===void 0&&r.compare===null&&r.defaultProps===void 0?(t.tag=15,t.type=a,d1(e,t,a,n,o)):(e=Yu(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:is,r(i,n)&&e.ref===t.ref)return oo(e,t,o)}return t.flags|=1,e=Ho(a,n),e.ref=t.ref,e.return=t,t.child=e}function d1(e,t,r,n,o){if(e!==null){var a=e.memoizedProps;if(is(a,n)&&e.ref===t.ref)if(Ar=!1,t.pendingProps=n=a,(e.lanes&o)!==0)e.flags&131072&&(Ar=!0);else return t.lanes=e.lanes,oo(e,t,o)}return O0(e,t,r,n,o)}function f1(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},St(hi,Br),Br|=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,St(hi,Br),Br|=e,null;t.memoizedState={baseLanes:0,cachePool:null,transitions:null},n=a!==null?a.baseLanes:r,St(hi,Br),Br|=n}else a!==null?(n=a.baseLanes|r,t.memoizedState=null):n=r,St(hi,Br),Br|=n;return _r(e,t,o,r),t.child}function p1(e,t){var r=t.ref;(e===null&&r!==null||e!==null&&e.ref!==r)&&(t.flags|=512,t.flags|=2097152)}function O0(e,t,r,n,o){var a=Dr(r)?Ea:mr.current;return a=Ei(t,a),xi(t,o),r=og(e,t,r,n,a,o),n=ag(),e!==null&&!Ar?(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~o,oo(e,t,o)):(Mt&&n&&qh(t),t.flags|=1,_r(e,t,r,o),t.child)}function Ey(e,t,r,n,o){if(Dr(r)){var a=!0;Oc(t)}else a=!1;if(xi(t,o),t.stateNode===null)qu(e,t),z_(t,r,n),T0(t,r,n,o),n=!0;else if(e===null){var i=t.stateNode,c=t.memoizedProps;i.props=c;var p=i.context,g=r.contextType;typeof g=="object"&&g!==null?g=dn(g):(g=Dr(r)?Ea:mr.current,g=Ei(t,g));var m=r.getDerivedStateFromProps,_=typeof m=="function"||typeof i.getSnapshotBeforeUpdate=="function";_||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(c!==n||p!==g)&&wy(t,i,n,g),No=!1;var O=t.memoizedState;i.state=O,Nc(t,n,i,o),p=t.memoizedState,c!==n||O!==p||Ir.current||No?(typeof m=="function"&&(_0(t,r,m,n),p=t.memoizedState),(c=No||yy(t,r,c,n,O,p,g))?(_||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=p),i.props=n,i.state=p,i.context=g,n=c):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),n=!1)}else{i=t.stateNode,F_(e,t),c=t.memoizedProps,g=t.type===t.elementType?c:mn(t.type,c),i.props=g,_=t.pendingProps,O=i.context,p=r.contextType,typeof p=="object"&&p!==null?p=dn(p):(p=Dr(r)?Ea:mr.current,p=Ei(t,p));var C=r.getDerivedStateFromProps;(m=typeof C=="function"||typeof i.getSnapshotBeforeUpdate=="function")||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(c!==_||O!==p)&&wy(t,i,n,p),No=!1,O=t.memoizedState,i.state=O,Nc(t,n,i,o);var T=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 bs(){return bs=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},bs.apply(this,arguments)}var Do;(function(e){e.Pop="POP",e.Push="PUSH",e.Replace="REPLACE"})(Do||(Do={}));const Uy="popstate";function eA(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:Vc(o)}return rA(t,r,null,e)}function zt(e,t){if(e===!1||e===null||typeof e>"u")throw new Error(t)}function wg(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function tA(){return Math.random().toString(36).substr(2,8)}function Hy(e,t){return{usr:e.state,key:e.key,idx:t}}function F0(e,t,r,n){return r===void 0&&(r=null),bs({pathname:typeof e=="string"?e:e.pathname,search:"",hash:""},typeof t=="string"?qi(t):t,{state:r,key:t&&t.key||n||tA()})}function Vc(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 qi(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 rA(e,t,r,n){n===void 0&&(n={});let{window:o=document.defaultView,v5Compat:a=!1}=n,i=o.history,c=Do.Pop,p=null,g=m();g==null&&(g=0,i.replaceState(bs({},i.state,{idx:g}),""));function m(){return(i.state||{idx:null}).idx}function _(){c=Do.Pop;let b=m(),d=b==null?null:b-g;g=b,p&&p({action:c,location:x.location,delta:d})}function O(b,d){c=Do.Push;let l=F0(x.location,b,d);r&&r(l,b),g=m()+1;let u=Hy(l,g),s=x.createHref(l);try{i.pushState(u,"",s)}catch(h){if(h instanceof DOMException&&h.name==="DataCloneError")throw h;o.location.assign(s)}a&&p&&p({action:c,location:x.location,delta:1})}function C(b,d){c=Do.Replace;let l=F0(x.location,b,d);r&&r(l,b),g=m();let u=Hy(l,g),s=x.createHref(l);i.replaceState(u,"",s),a&&p&&p({action:c,location:x.location,delta:0})}function T(b){let d=o.location.origin!=="null"?o.location.origin:o.location.href,l=typeof b=="string"?b:Vc(b);return zt(d,"No window.location.(origin|href) available to create URL for href: "+l),new URL(l,d)}let x={get action(){return c},get location(){return e(o,i)},listen(b){if(p)throw new Error("A history only accepts one active listener");return o.addEventListener(Uy,_),p=b,()=>{o.removeEventListener(Uy,_),p=null}},createHref(b){return t(o,b)},createURL:T,encodeLocation(b){let d=T(b);return{pathname:d.pathname,search:d.search,hash:d.hash}},push:O,replace:C,go(b){return i.go(b)}};return x}var $y;(function(e){e.data="data",e.deferred="deferred",e.redirect="redirect",e.error="error"})($y||($y={}));function nA(e,t,r){r===void 0&&(r="/");let n=typeof t=="string"?qi(t):t,o=ms(n.pathname||"/",r);if(o==null)return null;let a=L1(e);oA(a);let i=null;for(let c=0;i==null&&c<a.length;++c)i=pA(a[c],gA(o));return i}function L1(e,t,r,n){t===void 0&&(t=[]),r===void 0&&(r=[]),n===void 0&&(n="");let o=(a,i,c)=>{let p={relativePath:c===void 0?a.path||"":c,caseSensitive:a.caseSensitive===!0,childrenIndex:i,route:a};p.relativePath.startsWith("/")&&(zt(p.relativePath.startsWith(n),'Absolute route path "'+p.relativePath+'" nested under path '+('"'+n+'" is not valid. An absolute child route path ')+"must start with the combined path of all its parent routes."),p.relativePath=p.relativePath.slice(n.length));let g=$o([n,p.relativePath]),m=r.concat(p);a.children&&a.children.length>0&&(zt(a.index!==!0,"Index routes must not have child routes. Please remove "+('all child routes from route path "'+g+'".')),L1(a.children,t,m,g)),!(a.path==null&&!a.index)&&t.push({path:g,score:dA(g,a.index),routesMeta:m})};return e.forEach((a,i)=>{var c;if(a.path===""||!((c=a.path)!=null&&c.includes("?")))o(a,i);else for(let p of F1(a.path))o(a,i,p)}),t}function F1(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=F1(n.join("/")),c=[];return c.push(...i.map(p=>p===""?a:[a,p].join("/"))),o&&c.push(...i),c.map(p=>e.startsWith("/")&&p===""?"/":p)}function oA(e){e.sort((t,r)=>t.score!==r.score?r.score-t.score:fA(t.routesMeta.map(n=>n.childrenIndex),r.routesMeta.map(n=>n.childrenIndex)))}const aA=/^:\w+$/,iA=3,lA=2,sA=1,uA=10,cA=-2,qy=e=>e==="*";function dA(e,t){let r=e.split("/"),n=r.length;return r.some(qy)&&(n+=cA),t&&(n+=lA),r.filter(o=>!qy(o)).reduce((o,a)=>o+(aA.test(a)?iA:a===""?sA:uA),n)}function fA(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 pA(e,t){let{routesMeta:r}=e,n={},o="/",a=[];for(let i=0;i<r.length;++i){let c=r[i],p=i===r.length-1,g=o==="/"?t:t.slice(o.length)||"/",m=V0({path:c.relativePath,caseSensitive:c.caseSensitive,end:p},g);if(!m)return null;Object.assign(n,m.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 zc(){return zc=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},zc.apply(this,arguments)}const Pd=j.createContext(null),z1=j.createContext(null),Ba=j.createContext(null),kd=j.createContext(null),ea=j.createContext({outlet:null,matches:[],isDataRoute:!1}),B1=j.createContext(null);function CA(e,t){let{relative:r}=t===void 0?{}:t;Gi()||zt(!1);let{basename:n,navigator:o}=j.useContext(Ba),{hash:a,pathname:i,search:c}=Nd(e,{relative:r}),p=i;return n!=="/"&&(p=i==="/"?n:$o([n,i])),o.createHref({pathname:p,search:c,hash:a})}function Gi(){return j.useContext(kd)!=null}function ta(){return Gi()||zt(!1),j.useContext(kd).location}function W1(e){j.useContext(Ba).static||j.useLayoutEffect(e)}function Ed(){let{isDataRoute:e}=j.useContext(ea);return e?LA():OA()}function OA(){Gi()||zt(!1);let e=j.useContext(Pd),{basename:t,navigator:r}=j.useContext(Ba),{matches:n}=j.useContext(ea),{pathname:o}=ta(),a=JSON.stringify(xg(n).map(p=>p.pathnameBase)),i=j.useRef(!1);return W1(()=>{i.current=!0}),j.useCallback(function(p,g){if(g===void 0&&(g={}),!i.current)return;if(typeof p=="number"){r.go(p);return}let m=_g(p,JSON.parse(a),o,g.relative==="path");e==null&&t!=="/"&&(m.pathname=m.pathname==="/"?t:$o([t,m.pathname])),(g.replace?r.replace:r.push)(m,g.state,g)},[t,r,a,o,e])}function Nd(e,t){let{relative:r}=t===void 0?{}:t,{matches:n}=j.useContext(ea),{pathname:o}=ta(),a=JSON.stringify(xg(n).map(i=>i.pathnameBase));return j.useMemo(()=>_g(e,JSON.parse(a),o,r==="path"),[e,a,o,r])}function SA(e,t){return PA(e,t)}function PA(e,t,r){Gi()||zt(!1);let{navigator:n}=j.useContext(Ba),{matches:o}=j.useContext(ea),a=o[o.length-1],i=a?a.params:{};a&&a.pathname;let c=a?a.pathnameBase:"/";a&&a.route;let p=ta(),g;if(t){var m;let x=typeof t=="string"?qi(t):t;c==="/"||(m=x.pathname)!=null&&m.startsWith(c)||zt(!1),g=x}else g=p;let _=g.pathname||"/",O=c==="/"?_:_.slice(c.length)||"/",C=nA(e,{pathname:O}),T=jA(C&&C.map(x=>Object.assign({},x,{params:Object.assign({},i,x.params),pathname:$o([c,n.encodeLocation?n.encodeLocation(x.pathname).pathname:x.pathname]),pathnameBase:x.pathnameBase==="/"?c:$o([c,n.encodeLocation?n.encodeLocation(x.pathnameBase).pathname:x.pathnameBase])})),o,r);return t&&T?j.createElement(kd.Provider,{value:{location:zc({pathname:"/",search:"",hash:"",state:null,key:"default"},g),navigationType:Do.Pop}},T):T}function kA(){let e=DA(),t=_A(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 j.createElement(j.Fragment,null,j.createElement("h2",null,"Unexpected Application Error!"),j.createElement("h3",{style:{fontStyle:"italic"}},t),r?j.createElement("pre",{style:o},r):null,a)}const EA=j.createElement(kA,null);class NA extends j.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?j.createElement(ea.Provider,{value:this.props.routeContext},j.createElement(B1.Provider,{value:this.state.error,children:this.props.component})):this.props.children}}function RA(e){let{routeContext:t,match:r,children:n}=e,o=j.useContext(Pd);return o&&o.static&&o.staticContext&&(r.route.errorElement||r.route.ErrorBoundary)&&(o.staticContext._deepestRenderedBoundaryId=r.route.id),j.createElement(ea.Provider,{value:t},n)}function jA(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(p=>p.route.id&&(i==null?void 0:i[p.route.id]));c>=0||zt(!1),a=a.slice(0,Math.min(a.length,c+1))}return a.reduceRight((c,p,g)=>{let m=p.route.id?i==null?void 0:i[p.route.id]:null,_=null;r&&(_=p.route.errorElement||EA);let O=t.concat(a.slice(0,g+1)),C=()=>{let T;return m?T=_:p.route.Component?T=j.createElement(p.route.Component,null):p.route.element?T=p.route.element:T=c,j.createElement(RA,{match:p,routeContext:{outlet:c,matches:O,isDataRoute:r!=null},children:T})};return r&&(p.route.ErrorBoundary||p.route.errorElement||g===0)?j.createElement(NA,{location:r.location,revalidation:r.revalidation,component:_,error:m,children:C(),routeContext:{outlet:null,matches:O,isDataRoute:!0}}):C()},null)}var U1=function(e){return e.UseBlocker="useBlocker",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e}(U1||{}),Bc=function(e){return e.UseBlocker="useBlocker",e.UseLoadeLine 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 Wc(){return Wc=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},Wc.apply(this,arguments)}function $1(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 zA(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function BA(e,t){return e.button===0&&(!t||t==="_self")&&!zA(e)}const WA=["onClick","relative","reloadDocument","replace","state","target","to","preventScrollReset","unstable_viewTransition"],UA=["aria-current","caseSensitive","className","end","style","to","unstable_viewTransition","children"],HA=j.createContext({isTransitioning:!1}),$A="startTransition",Gy=Eh[$A];function qA(e){let{basename:t,children:r,future:n,window:o}=e,a=j.useRef();a.current==null&&(a.current=eA({window:o,v5Compat:!0}));let i=a.current,[c,p]=j.useState({action:i.action,location:i.location}),{v7_startTransition:g}=n||{},m=j.useCallback(_=>{g&&Gy?Gy(()=>p(_)):p(_)},[p,g]);return j.useLayoutEffect(()=>i.listen(m),[i,m]),j.createElement(VA,{basename:t,children:r,location:c.location,navigationType:c.action,navigator:i})}const GA=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",KA=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,q1=j.forwardRef(function(t,r){let{onClick:n,relative:o,reloadDocument:a,replace:i,state:c,target:p,to:g,preventScrollReset:m,unstable_viewTransition:_}=t,O=$1(t,WA),{basename:C}=j.useContext(Ba),T,x=!1;if(typeof g=="string"&&KA.test(g)&&(T=g,GA))try{let u=new URL(window.location.href),s=g.startsWith("//")?new URL(u.protocol+g):new URL(g),h=ms(s.pathname,C);s.origin===u.origin&&h!=null?g=h+s.search+s.hash:x=!0}catch{}let b=CA(g,{relative:o}),d=QA(g,{replace:i,state:c,target:p,preventScrollReset:m,relative:o,unstable_viewTransition:_});function l(u){n&&n(u),u.defaultPrevented||d(u)}return j.createElement("a",Wc({},O,{href:T||b,onClick:x||a?n:l,ref:r,target:p}))}),YA=j.forwardRef(function(t,r){let{"aria-current":n="page",caseSensitive:o=!1,className:a="",end:i=!1,style:c,to:p,unstable_viewTransition:g,children:m}=t,_=$1(t,UA),O=Nd(p,{relative:_.relative}),C=ta(),T=j.useContext(z1),{navigator:x}=j.useContext(Ba),b=T!=null&&JA(O)&&g===!0,d=x.encodeLocation?x.encodeLocation(O).pathname:O.pathname,l=C.pathname,u=T&&T.navigation&&T.navigation.location?T.navigation.location.pathname:null;o||(l=l.toLowerCase(),u=u?u.toLowerCase():null,d=d.toLowerCase());let s=l===d||!i&&l.startsWith(d)&&l.charAt(d.length)==="/",h=u!=null&&(u===d||!i&&u.startsWith(d)&&u.charAt(d.length)==="/"),v={isActive:s,isPending:h,isTransitioning:b},y=s?n:void 0,E;typeof a=="function"?E=a(v):E=[a,s?"active":null,h?"pending":null,b?"transitioning":null].filter(Boolean).join(" ");let P=typeof c=="function"?c(v):c;return j.createElement(q1,Wc({},_,{"aria-current":y,className:E,ref:r,style:P,to:p,unstable_viewTransition:g}),typeof m=="function"?m(v):m)});var B0;(function(e){e.UseScrollRestoration="useScrollRestoration",e.UseSubmit="useSubmit",e.UseSubmitFetcher="useSubmitFetcher",e.UseFetcher="useFetcher",e.useViewTransitionState="useViewTransitionState"})(B0||(B0={}));var Ky;(function(e){e.UseFetchers="useFetchers",e.UseScrollRestoration="useScrollRestoration"})(Ky||(Ky={}));function XA(e){let t=j.useContext(Pd);return t||zt(!1),t}function QA(e,t){let{target:r,replace:n,state:o,preventScrollReset:a,relative:i,unstable_viewTransition:c}=t===void 0?{}:t,p=Ed(),g=ta(),m=Nd(e,{relative:i});return j.useCallback(_=>{if(BA(_,r)){_.preventDefault();let O=n!==void 0?n:Vc(g)===Vc(m);p(e,{replace:O,state:o,preventScrollReset:a,relative:i,unstable_viewTransition:c})}},[g,p,m,n,o,r,e,a,i,c])}function JA(e,t){t===void 0&&(t={});let r=j.useContext(HA);r==null&&zt(!1);let{basename:n}=XA(B0.useViewTransitionState),o=Nd(e,{relative:t.relative});if(!r.isTransitioning)return!1;let a=ms(r.currentLocation.pathname,n)||r.currentLocation.pathname,i=ms(r.nextLocation.pathname,n)||r.nextLocation.pathname;return V0(o.pathname,i)!=null||V0(o.pathname,a)!=null}function ZA({title:e,titleId:t,...r},n){return j.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?j.createElement("title",{id:t},e):null,j.createElement("path",{fillRule:"evenodd",d:"M3 6.75A.75.75 0 013.75 6h16.5a.75.75 0 010 1.5H3.75A.75.75 0 013 6.75zM3 12a.75.75 0 01.75-.75h16.5a.75.75 0 010 1.5H3.75A.75.75 0 013 12zm0 5.25a.75.75 0 01.75-.75h16.5a.75.75 0 010 1.5H3.75a.75.75 0 01-.75-.75z",clipRule:"evenodd"}))}const eM=j.forwardRef(ZA),tM=eM;function rM({title:e,titleId:t,...r},n){return j.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?j.createElement("title",{id:t},e):null,j.createElement("path",{fillRule:"evenodd",d:"M11.078 2.25c-.917 0-1.69Line 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 p in a)t.call(a,p)&&a[p]&&n.push(p)}}}return n.join(" ")}e.exports?(r.default=r,e.exports=r):window.classNames=r})()})(Y1);var He=Y1.exports,X1={exports:{}},bn={};function Cg(){for(var e,t,r=0,n="";r<arguments.length;)(e=arguments[r++])&&(t=Q1(e))&&(n&&(n+=" "),n+=t);return n}function Q1(e){if(typeof e=="string")return e;for(var t,r="",n=0;n<e.length;n++)e[n]&&(t=Q1(e[n]))&&(r&&(r+=" "),r+=t);return r}function W0(){return W0=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},W0.apply(this,arguments)}function yM(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(p){return[p[0],p[1].map(function(g){return typeof g=="string"?c+g:typeof g=="object"?Object.fromEntries(Object.entries(g).map(function(m){return[c+m[0],m[1]]})):g})]}):i}(Object.entries(r.classGroups),o).forEach(function(i){U0(i[1],a,i[0],n)}),a}(e);return{getClassGroupId:function(r){var n=r.split("-");return n[0]===""&&n.length!==1&&n.shift(),J1(n,t)||function(o){if(Yy.test(o)){var a=Yy.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 J1(e,t){var r;if(e.length===0)return t.classGroupId;var n=t.nextPart.get(e[0]),o=n?J1(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 Yy=/^\[(.+)\]$/;function U0(e,t,r,n){e.forEach(function(o){if(typeof o!="string"){if(typeof o=="function")return o.isThemeGetter?void U0(o(n),t,r,n):void t.validators.push({validator:o,classGroupId:r});Object.entries(o).forEach(function(a){U0(a[1],Xy(t,a[0]),r,n)})}else(o===""?t:Xy(t,o)).classGroupId=r})}function Xy(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 wM(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 xM(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 p=o.length===0?r:r.substring(a),g=p.startsWith("!");return{modifiers:o,hasImportantModifier:g,baseClassName:g?p.substring(1):p}}}function _M(e){return W0({cache:wM(e.cacheSize),splitModifiers:xM(e)},yM(e))}var TM=/\s+/;function CM(e,t){var r=t.splitModifiers,n=t.getClassGroupId,o=t.getConflictingClassGroupIds,a=new Set;return e.trim().split(TM).map(function(i){var c=r(i),p=c.modifiers,g=c.hasImportantModifier,m=n(c.baseClassName);if(!m)return{isTailwindClass:!1,originalClassName:i};var _=function(O){if(O.length<=1)return O;var C=[],T=[];return O.forEach(function(x){x[0]==="["?(C.push.apply(C,T.sort().concat([x])),T=[]):T.push(x)}),C.push.apply(C,T.sort()),C}(p).join(":");return{isTailwindClass:!0,modifierId:g?_+"!":_,classGroupId:m,originalClassName:i}}).reverse().filter(function(i){if(!i.isTailwindClass)return!0;var c=i.modifierId,p=i.classGroupId,g=c+p;return!a.has(g)&&(a.add(g),o(p).forEach(function(m){return a.add(c+m)}),!0)}).reverse().map(function(i){return i.originalClassName}).join(" ")}function H0(){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 m=t[0],_=t.slice(1).reduce(function(O,C){return C(O)},m());return n=_M(_),o=n.cache.get,a=n.cache.set,i=p,p(g)}function p(g){var m=o(g);if(m)return m;var _=CM(g,n);return a(g,_),_}return function(){return i(Cg.apply(null,arguments))}}function Et(e){var t=function(r){return r[e]||[]};return t.isThemeGetter=!0,t}var ra=/^\[(.+)\]$/,OM=/^\d+\/\d+$/,SM=new Set(["px","full","screen"]),PM=/^(\d+)?(xs|sm|md|lg|xl)$/,kM=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh)/,EM=/^-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/;function tn(e){return!Number.isNaN(Number(e))||SM.has(e)||OM.test(e)||Ro(e)}function Ro(e){var t,r=(t=ra.exec(e))==null?void 0:t[1];return!!r&&(r.startsWith("length:")||kM.test(r))}function Z1(e){var t,r=(t=ra.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 yL=["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])"],ah=yL.join(","),iS=typeof Element>"u",Ts=iS?function(){}:Element.prototype.matches||Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector,Jc=!iS&&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},Zc=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},wL=function(t){var r,n=t==null||(r=t.getAttribute)===null||r===void 0?void 0:r.call(t,"contenteditable");return n===""||n==="true"},xL=function(t,r,n){if(Zc(t))return[];var o=Array.prototype.slice.apply(t.querySelectorAll(ah));return r&&Ts.call(t,ah)&&o.unshift(t),o=o.filter(n),o},_L=function e(t,r,n){for(var o=[],a=Array.from(t);a.length;){var i=a.shift();if(!Zc(i,!1))if(i.tagName==="SLOT"){var c=i.assignedElements(),p=c.length?c:i.children,g=e(p,!0,n);n.flatten?o.push.apply(o,g):o.push({scopeParent:i,candidates:g})}else{var m=Ts.call(i,ah);m&&n.filter(i)&&(r||!t.includes(i))&&o.push(i);var _=i.shadowRoot||typeof n.getShadowRoot=="function"&&n.getShadowRoot(i),O=!Zc(_,!1)&&(!n.shadowRootFilter||n.shadowRootFilter(i));if(_&&O){var C=e(_===!0?i.children:_.children,!0,n);n.flatten?o.push.apply(o,C):o.push({scopeParent:i,candidates:C})}else a.unshift.apply(a,i.children)}}return o},lS=function(t){return!isNaN(parseInt(t.getAttribute("tabindex"),10))},sS=function(t){if(!t)throw new Error("No node provided");return t.tabIndex<0&&(/^(AUDIO|VIDEO|DETAILS)$/.test(t.tagName)||wL(t))&&!lS(t)?0:t.tabIndex},TL=function(t,r){var n=sS(t);return n<0&&r&&!lS(t)?0:n},CL=function(t,r){return t.tabIndex===r.tabIndex?t.documentOrder-r.documentOrder:t.tabIndex-r.tabIndex},uS=function(t){return t.tagName==="INPUT"},OL=function(t){return uS(t)&&t.type==="hidden"},SL=function(t){var r=t.tagName==="DETAILS"&&Array.prototype.slice.apply(t.children).some(function(n){return n.tagName==="SUMMARY"});return r},PL=function(t,r){for(var n=0;n<t.length;n++)if(t[n].checked&&t[n].form===r)return t[n]},kL=function(t){if(!t.name)return!0;var r=t.form||Jc(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=PL(o,t.form);return!a||a===t},EL=function(t){return uS(t)&&t.type==="radio"},NL=function(t){return EL(t)&&!kL(t)},RL=function(t){var r,n=t&&Jc(t),o=(r=n)===null||r===void 0?void 0:r.host,a=!1;if(n&&n!==t){var i,c,p;for(a=!!((i=o)!==null&&i!==void 0&&(c=i.ownerDocument)!==null&&c!==void 0&&c.contains(o)||t!=null&&(p=t.ownerDocument)!==null&&p!==void 0&&p.contains(t));!a&&o;){var g,m,_;n=Jc(o),o=(g=n)===null||g===void 0?void 0:g.host,a=!!((m=o)!==null&&m!==void 0&&(_=m.ownerDocument)!==null&&_!==void 0&&_.contains(o))}}return a},Cw=function(t){var r=t.getBoundingClientRect(),n=r.width,o=r.height;return n===0&&o===0},jL=function(t,r){var n=r.displayCheck,o=r.getShadowRoot;if(getComputedStyle(t).visibility==="hidden")return!0;var a=Ts.call(t,"details>summary:first-of-type"),i=a?t.parentElement:t;if(Ts.call(i,"details:not([open]) *"))return!0;if(!n||n==="full"||n==="legacy-full"){if(typeof o=="function"){for(var c=t;t;){var p=t.parentElement,g=Jc(t);if(p&&!p.shadowRoot&&o(p)===!0)return Cw(t);t.assignedSlot?t=t.assignedSlot:!p&&g!==t.ownerDocument?t=g.host:t=p}t=c}if(RL(t))return!t.getClientRects().length;if(n!=="legacy-full")return!0}else if(n==="non-zero-area")return Cw(t);return!1},AL=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 Ts.call(r,"fieldset[disabled] *")?!0:!o.contains(t)}return!0}r=r.parentElement}return!1},ML=function(t,r){return!(r.disabled||Zc(r)||OL(r)||jL(r,t)||SL(r)||AL(r))},Ow=function(t,r){return!(NL(r)||sS(r)<0||!ML(t,r))},IL=function(t){var r=parseInt(t.getAttribute("tabindex"),10);return!!(isNaN(r)||r>=0)},DL=function e(t){var r=[],n=[];return t.forEach(function(o,a){var i=!!o.scopeParent,c=i?o.scopeParent:o,p=TL(c,i),g=i?e(o.candidates):c;p===0?i?r.push.apply(r,g):r.push(c):n.push({documentOrder:a,tabIndex:p,item:o,isScope:i,content:g})}),n.sort(CL).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),k=(0,g.useTheme)().navbar,R=k.styles,A=R.base.mobileNav;s=s??{},h=h??"";var D=(0,c.twMerge)((0,i.default)((0,p.default)(A)),h),L={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]"}}},W=(0,a.default)(L,s),G=n.AnimatePresence,X=(0,o.useMergeRefs)([l,P]);return r.default.createElement(n.LazyMotion,{features:n.domAnimation},r.default.createElement(G,null,r.default.createElement(n.m.div,_({},y,{ref:X,className:D,initial:"unmount",exit:"unmount",animate:u?"mount":"unmount",variants:W}),v)))});x.displayName="MaterialTailwind.MobileNav",x.propTypes={open:m.propTypesOpen,animate:m.propTypesAnimate,className:m.propTypesClassName,children:m.propTypesChildren};var b=x})(JS);(function(e){Object.defineProperty(e,"__esModule",{value:!0});function t(l,u){for(var s in u)Object.defineProperty(l,s,{enumerable:!0,get:u[s]})}t(e,{Navbar:function(){return b},MobileNav:function(){return g.MobileNav},default:function(){return d}});var r=C(j),n=C(ze),o=C(He),a=Ve,i=C(It),c=C(De),p=je,g=JS,m=Rd;function _(l,u,s){return u in l?Object.defineProperty(l,u,{value:s,enumerable:!0,configurable:!0,writable:!0}):l[u]=s,l}function O(){return O=Object.assign||function(l){for(var u=1;u<arguments.length;u++){var s=arguments[u];for(var h in s)Object.prototype.hasOwnProperty.call(s,h)&&(l[h]=s[h])}return l},O.apply(this,arguments)}function C(l){return l&&l.__esModule?l:{default:l}}function T(l,u){if(l==null)return{};var s=x(l,u),h,v;if(Object.getOwnPropertySymbols){var y=Object.getOwnPropertySymbols(l);for(v=0;v<y.length;v++)h=y[v],!(u.indexOf(h)>=0)&&Object.prototype.propertyIsEnumerable.call(l,h)&&(s[h]=l[h])}return s}function x(l,u){if(l==null)return{};var s={},h=Object.keys(l),v,y;for(y=0;y<h.length;y++)v=h[y],!(u.indexOf(v)>=0)&&(s[v]=l[v]);return s}var b=r.default.forwardRef(function(l,u){var s=l.variant,h=l.color,v=l.shadow,y=l.blurred,E=l.fullWidth,P=l.className,k=l.children,R=T(l,["variant","color","shadow","blurred","fullWidth","className","children"]),A=(0,p.useTheme)().navbar,D=A.defaultProps,L=A.valid,W=A.styles,G=W.base,X=W.variants;s=s??D.variant,h=h??D.color,v=v??D.shadow,y=y??D.blurred,E=E??D.fullWidth,P=P??D.className;var $,B=(0,o.default)((0,c.default)(G.navbar.initial),($={},_($,(0,c.default)(G.navbar.shadow),v),_($,(0,c.default)(G.navbar.blurred),y&&h==="white"),_($,(0,c.default)(G.navbar.fullWidth),E),$)),U=(0,o.default)((0,c.default)(X[(0,i.default)(L.variants,s,"filled")][(0,i.default)(L.colors,h,"white")])),K=(0,a.twMerge)((0,o.default)(B,U),P);return r.default.createElement("nav",O({},R,{ref:u,className:K}),k)});b.propTypes={variant:n.default.oneOf(m.propTypesVariant),color:n.default.oneOf(m.propTypesColor),shadow:m.propTypesShadow,blurred:m.propTypesBlurred,fullWidth:m.propTypesFullWidth,className:m.propTypesClassName,children:m.propTypesChildren},b.displayName="MaterialTailwind.Navbar";var d=Object.assign(b,{MobileNav:g.MobileNav})})(QS);var ZS={},Jd={},tl={};(function(e){Object.defineProperty(e,"__esModule",{value:!0});function t(b,d){for(var l in d)Object.defineProperty(b,l,{enumerable:!0,get:d[l]})}t(e,{propTypesOpen:function(){return a},propTypesHandler:function(){return i},propTypesPlacement:function(){return c},propTypesOffset:function(){return p},propTypesDismiss:function(){return g},propTypesAnimate:function(){return m},propTypesContent:function(){return _},propTypesInteractive:function(){return O},propTypesClassName:function(){return C},propTypesChildren:function(){return T},propTypesContextValue:function(){return x}});var r=o(ze),n=jt;function o(b){return b&&b.__esModule?b:{default:b}}var a=r.default.bool,i=r.default.func,c=n.propTypesPlacements,p=n.propTypesOffsetType,g=n.propTypesDismissType,m=n.propTypesAnimation,_=r.default.node,O=r.default.bool,C=r.default.string,T=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:m.isRequired,labelId:r.default.string.isRequired,descriptionId:r.default.string.isRequired}).isRequired})(tl);(function(e){Object.defineProperty(e,"__esModule",{value:!0});function t(p,g){for(var m in g)Object.defineProperty(p,m,{enumerable:!0,get:g[m]})}t(e,{PopoverContext:function(){return a},usePopover:function(){return i},PopoverContextProvider:function(){return c}});var r=o(j),n=tl;function o(p){return p&&p.__esModule?p:{default:p}}var a=r.default.createContext(null);a.displayName="MaterialTailwind.PopoverContext";function i(){var p=r.default.useContext(a);if(!p)throw new Error("usePopover() must be used within a Popover. It happens when you us
Line truncated
|
|
||||||
`).forEach(function(i){o=i.indexOf(":"),r=i.substring(0,o).trim().toLowerCase(),n=i.substring(o+1).trim(),!(!r||t[r]&&V6[r])&&(r==="set-cookie"?t[r]?t[r].push(n):t[r]=[n]:t[r]=t[r]?t[r]+", "+n:n)}),t},ox=Symbol("internals");function Pl(e){return e&&String(e).trim().toLowerCase()}function sc(e){return e===!1||e==null?e:de.isArray(e)?e.map(sc):String(e)}function B6(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 W6=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function Dp(e,t,r,n,o){if(de.isFunction(n))return n.call(this,t,r);if(o&&(t=r),!!de.isString(t)){if(de.isString(n))return t.indexOf(n)!==-1;if(de.isRegExp(n))return n.test(t)}}function U6(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(t,r,n)=>r.toUpperCase()+n)}function H6(e,t){const r=de.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 af{constructor(t){t&&this.set(t)}set(t,r,n){const o=this;function a(c,p,g){const m=Pl(p);if(!m)throw new Error("header name must be a non-empty string");const _=de.findKey(o,m);(!_||o[_]===void 0||g===!0||g===void 0&&o[_]!==!1)&&(o[_||p]=sc(c))}const i=(c,p)=>de.forEach(c,(g,m)=>a(g,m,p));if(de.isPlainObject(t)||t instanceof this.constructor)i(t,r);else if(de.isString(t)&&(t=t.trim())&&!W6(t))i(z6(t),r);else if(de.isHeaders(t))for(const[c,p]of t.entries())a(p,c,n);else t!=null&&a(r,t,n);return this}get(t,r){if(t=Pl(t),t){const n=de.findKey(this,t);if(n){const o=this[n];if(!r)return o;if(r===!0)return B6(o);if(de.isFunction(r))return r.call(this,o,n);if(de.isRegExp(r))return r.exec(o);throw new TypeError("parser must be boolean|regexp|function")}}}has(t,r){if(t=Pl(t),t){const n=de.findKey(this,t);return!!(n&&this[n]!==void 0&&(!r||Dp(this,this[n],n,r)))}return!1}delete(t,r){const n=this;let o=!1;function a(i){if(i=Pl(i),i){const c=de.findKey(n,i);c&&(!r||Dp(n,n[c],c,r))&&(delete n[c],o=!0)}}return de.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||Dp(this,this[a],a,t,!0))&&(delete this[a],o=!0)}return o}normalize(t){const r=this,n={};return de.forEach(this,(o,a)=>{const i=de.findKey(n,a);if(i){r[i]=sc(o),delete r[a];return}const c=t?U6(a):String(a).trim();c!==a&&delete r[a],r[c]=sc(o),n[c]=!0}),this}concat(...t){return this.constructor.concat(this,...t)}toJSON(t){const r=Object.create(null);return de.forEach(this,(n,o)=>{n!=null&&n!==!1&&(r[o]=t&&de.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[ox]=this[ox]={accessors:{}}).accessors,o=this.prototype;function a(i){const c=Pl(i);n[c]||(H6(o,i),n[c]=!0)}return de.isArray(t)?t.forEach(a):a(t),this}}af.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);de.reduceDescriptors(af.prototype,({value:e},t)=>{let r=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(n){this[r]=n}}});de.freezeMethods(af);const Sn=af;function Lp(e,t){const r=this||Ab,n=t||r,o=Sn.from(n.headers);let a=n.data;return de.forEach(e,function(c){a=c.call(r,a,o.normalize(),t?t.status:void 0)}),o.normalize(),a}function rk(e){return!!(e&&e.__CANCEL__)}function ol(e,t,r){Qe.call(this,e??"canceled",Qe.ERR_CANCELED,t,r),this.name="CanceledError"}de.inherits(ol,Qe,{__CANCEL__:!0});function nk(e,t,r){const n=r.config.validateStatus;!r.status||!n||n(r.status)?e(r):t(new Qe("Request failed with status code "+r.status,[Qe.ERR_BAD_REQUEST,Qe.ERR_BAD_RESPONSE][Math.floor(r.status/100)-4],r.config,r.request,r))}function $6(e){const t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||""}function q6(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(p){const g=Date.now(),m=n[a];i||(i=g),r[o]=p,n[o]=g;let _=a,O=0;for(;_!==o;)O+=r[_++],_=_%e;if(o=(o+1)%e,o===a&&(a=(a+1)%e),g-i<t)return;const C=m&&g-m;return C?Math.round(O*1e3/C):void 0}}function G6(e,t){let r=0,n=1e3/t,o,a;const i=(g,m=Date.now())=>{r=m,o=null,a&&(clearTimeout(a),a=null),e.apply(null,g)};return[(...g)=>{const m=Date.now(),_=m-r;_>=n?i(g,m):(o=g,a||(a=setTimeout(()=>{a=null,i(o)},n-_)))},()=>o&&i(o)]}const sd=(e,t,r=3)=>{let n=0;const o=q6(50,250);return G6(a=>{const i=a.loaded,c=a.lengthComputable?a.total:void 0,p=i-n,g=o(p),m=i<=c;n=i;const _={loaded:i,total:c,progress:c?i/c:void 0,bytes:p,rate:g||void 0,estimated:g&&c&&m?(c-i)/g:void 0,event:a,lengthComputable:c!=null,[t?"download":"upload"]:!0};e(_)},r)},ax=(e,t)=>{const r=e!=null;return[n=>t[0]({lengthComputable:r,total:e,loaded:n}),t[1]]},ix=e=>(...t)=>de.asap(()=>e(...t)),K6=qr.hasStandardBrowserEnv?function(){const t=qr.navigator&&/(msie|trident)/i.test(qr.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=de.isString(i)?o(i):i;return c.protocol===n.protocol&&c.host===n.host}}():function(){return function(){return!0}}(),Y6=qr.hasStandardBrowserEnv?{write(e,t,r,n,o,a){const i=[e+"="+encodeURIComponent(t)];de.isNumber(r)&&i.push("expires="+new Date(r).toGMTString()),de.isString(n)&&i.push("path="+n),de.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 X6(e){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e)}function Q6(e,t){return t?e.replace(/\/?\/$/,"")+"/"+t.replace(/^\/+/,""):e}function ok(e,t){return e&&!X6(t)?Q6(e,t):t}const lx=e=>e instanceof Sn?{...e}:e;function Fa(e,t){t=t||{};const r={};function n(g,m,_){return de.isPlainObject(g)&&de.isPlainObject(m)?de.merge.call({caseless:_},g,m):de.isPlainObject(m)?de.merge({},m):de.isArray(m)?m.slice():m}function o(g,m,_){if(de.isUndefined(m)){if(!de.isUndefined(g))return n(void 0,g,_)}else return n(g,m,_)}function a(g,m){if(!de.isUndefined(m))return n(void 0,m)}function i(g,m){if(de.isUndefined(m)){if(!de.isUndefined(g))return n(void 0,g)}else return n(void 0,m)}function c(g,m,_){if(_ in t)return n(g,m);if(_ in e)return n(void 0,g)}const p={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,m)=>o(lx(g),lx(m),!0)};return de.forEach(Object.keys(Object.assign({},e,t)),function(m){const _=p[m]||o,O=_(e[m],t[m],m);de.isUndefined(O)&&_!==c||(r[m]=O)}),r}const ak=e=>{const t=Fa({},e);let{data:r,withXSRFToken:n,xsrfHeaderName:o,xsrfCookieName:a,headers:i,auth:c}=t;t.headers=i=Sn.from(i),t.url=ZP(ok(t.baseURL,t.url),e.params,e.paramsSerializer),c&&i.set("Authorization","Basic "+btoa((c.username||"")+":"+(c.password?unescape(enco
Line truncated
|
|
||||||
`+a.map(cx).join(`
|
|
||||||
`):" "+cx(a[0]):"as no adapter specified";throw new Qe("There is no suitable adapter to dispatch the request "+i,"ERR_NOT_SUPPORT")}return n},adapters:gh};function Fp(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new ol(null,e)}function dx(e){return Fp(e),e.headers=Sn.from(e.headers),e.data=Lp.call(e,e.transformRequest),["post","put","patch"].indexOf(e.method)!==-1&&e.headers.setContentType("application/x-www-form-urlencoded",!1),sk.getAdapter(e.adapter||Ab.adapter)(e).then(function(n){return Fp(e),n.data=Lp.call(e,e.transformResponse,n),n.headers=Sn.from(n.headers),n},function(n){return rk(n)||(Fp(e),n&&n.response&&(n.response.data=Lp.call(e,e.transformResponse,n.response),n.response.headers=Sn.from(n.response.headers))),Promise.reject(n)})}const uk="1.7.5",Mb={};["object","boolean","number","function","string","symbol"].forEach((e,t)=>{Mb[e]=function(n){return typeof n===e||"a"+(t<1?"n ":" ")+e}});const fx={};Mb.transitional=function(t,r,n){function o(a,i){return"[Axios v"+uk+"] Transitional option '"+a+"'"+i+(n?". "+n:"")}return(a,i,c)=>{if(t===!1)throw new Qe(o(i," has been removed"+(r?" in "+r:"")),Qe.ERR_DEPRECATED);return r&&!fx[i]&&(fx[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 uV(e,t,r){if(typeof e!="object")throw new Qe("options must be an object",Qe.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],p=c===void 0||i(c,a,e);if(p!==!0)throw new Qe("option "+a+" must be "+p,Qe.ERR_BAD_OPTION_VALUE);continue}if(r!==!0)throw new Qe("Unknown option "+a,Qe.ERR_BAD_OPTION)}}const bh={assertOptions:uV,validators:Mb},So=bh.validators;class cd{constructor(t){this.defaults=t,this.interceptors={request:new nx,response:new nx}}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=Fa(this.defaults,r);const{transitional:n,paramsSerializer:o,headers:a}=r;n!==void 0&&bh.assertOptions(n,{silentJSONParsing:So.transitional(So.boolean),forcedJSONParsing:So.transitional(So.boolean),clarifyTimeoutError:So.transitional(So.boolean)},!1),o!=null&&(de.isFunction(o)?r.paramsSerializer={serialize:o}:bh.assertOptions(o,{encode:So.function,serialize:So.function},!0)),r.method=(r.method||this.defaults.method||"get").toLowerCase();let i=a&&de.merge(a.common,a[r.method]);a&&de.forEach(["delete","get","head","post","put","patch","common"],T=>{delete a[T]}),r.headers=Sn.concat(i,a);const c=[];let p=!0;this.interceptors.request.forEach(function(x){typeof x.runWhen=="function"&&x.runWhen(r)===!1||(p=p&&x.synchronous,c.unshift(x.fulfilled,x.rejected))});const g=[];this.interceptors.response.forEach(function(x){g.push(x.fulfilled,x.rejected)});let m,_=0,O;if(!p){const T=[dx.bind(this),void 0];for(T.unshift.apply(T,c),T.push.apply(T,g),O=T.length,m=Promise.resolve(r);_<O;)m=m.then(T[_++],T[_++]);return m}O=c.length;let C=r;for(_=0;_<O;){const T=c[_++],x=c[_++];try{C=T(C)}catch(b){x.call(this,b);break}}try{m=dx.call(this,C)}catch(T){return Promise.reject(T)}for(_=0,O=g.length;_<O;)m=m.then(g[_++],g[_++]);return m}getUri(t){t=Fa(this.defaults,t);const r=ok(t.baseURL,t.url);return ZP(r,t.params,t.paramsSerializer)}}de.forEach(["delete","get","head","options"],function(t){cd.prototype[t]=function(r,n){return this.request(Fa(n||{},{method:t,url:r,data:(n||{}).data}))}});de.forEach(["post","put","patch"],function(t){function r(n){return function(a,i,c){return this.request(Fa(c||{},{method:t,headers:n?{"Content-Type":"multipart/form-data"}:{},url:a,data:i}))}}cd.prototype[t]=r(),cd.prototype[t+"Form"]=r(!0)});const uc=cd;class Ib{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 ol(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 Ib(function(o){t=o}),cancel:t}}}const cV=Ib;function dV(e){return function(r){return e.apply(null,r)}}function fV(e){return de.isObject(e)&&e.isAxiosError===!0}const mh={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(mh).forEach(([e,t])=>{mh[t]=e});const pV=mh;function ck(e){const t=new uc(e),r=BP(uc.prototype.request,t);return de.extend(r,uc.prototype,t,{allOwnKeys:!0}),de.extend(r,t,null,{allOwnKeys:!0}),r.create=function(o){return ck(Fa(e,o))},r}const Xt=ck(Ab);Xt.Axios=uc;Xt.CanceledError=ol;Xt.CancelToken=cV;Xt.isCancel=rk;Xt.VERSION=uk;Xt.toFormData=of;Xt.AxiosError=Qe;Xt.Cancel=Xt.CanceledError;Xt.all=function(t){return Promise.all(t)};Xt.spread=dV;Xt.isAxiosError=fV;Xt.mergeConfig=Fa;Xt.AxiosHeaders=Sn;Xt.formToJSON=e=>tk(de.isHTMLForm(e)?new FormData(e):e);Xt.getAdapter=sk.getAdapter;Xt.HttpStatusCode=pV;Xt.default=Xt;const Nt=Xt,$n=Object.create(null);$n.open="0";$n.close="1";$n.ping="2";$n.pong="3";$n.message="4";$n.upgrade="5";$n.noop="6";const cc=Object.create(null);Object.keys($n).forEach(e=>{cc[$n[e]]=e});const vh={type:"error",data:"parser error"},dk=typeof Blob=="function"||typeof Blob<"u"&&Object.prototype.toString.call(Blob)==="[object BlobConstructor]",fk=typeof Array
Line truncated
|
|
||||||
${Te?"border-purple-500 ring-1 ring-purple-500/40":"border-transparent"}
|
|
||||||
${!mt||ye?"opacity-75 bg-stormGrey-200/20":""}`,children:[M.jsxs("div",{className:"flex items-center space-x-4",id:"left",children:[M.jsx("input",{type:"checkbox",checked:Te,onChange:()=>Se(ae.Id,!mt),className:`h-5 w-5 text-purple-500 focus:ring-purple-400 border-gray-600 bg-gray-900 rounded ${mt?"":"opacity-40 cursor-not-allowed"}`,disabled:!mt}),M.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"}`}),M.jsxs("div",{children:[M.jsx("p",{className:"text-lg antialiased font-sans font-semibold leading-normal text-inherit",children:ae.Name}),M.jsxs("p",{className:"text-sm antialiased font-sans font-light leading-normal text-inherit",children:["ID: ",ae.Id]}),ae.duplicateName&&M.jsx("p",{className:"text-xs text-amber-700",children:"⚠️ Doppelter Name erkannt"})]})]}),M.jsx("div",{className:"ml-auto flex flex-col items-end gap-1 text-sm",id:"right",children:Re?M.jsxs(M.Fragment,{children:[M.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-warmAmberGlow-600",children:"Redeploy"}),M.jsx("span",{className:"text-warmAmberGlow-500",children:"läuft…"})]}):Ae?M.jsxs(M.Fragment,{children:[M.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-warmAmberGlow-600",children:"Redeploy"}),M.jsx("span",{className:"text-warmAmberGlow-500",children:"in Warteliste…"})]}):bt?M.jsxs(M.Fragment,{children:[M.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-stormGrey-500",children:"System"}),M.jsx("span",{className:"text-gray-300",children:"Redeploy deaktiviert"})]}):$e?M.jsxs(M.Fragment,{children:[M.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-stormGrey-500",children:"Status"}),M.jsx("span",{className:"text-mossGreen-600",children:"Aktuell"})]}):M.jsx(pe.Button,{onClick:()=>qe(ae.Id),disabled:ye,className:"disabled:opacity-50 disabled:cursor-not-allowed",children:"Redeploy"})})]})},ae.Id)})}),M.jsx(Pk,{disabled:r||!!o})]})}const wz={idle:"Bereit",running:"Läuft",success:"Erfolgreich",error:"Fehlgeschlagen"},El={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"},xz={initializing:"Vorbereitung","activating-maintenance":"Wartungsmodus aktivieren","executing-script":"Skript wird ausgeführt",waiting:"Warte auf Portainer",completed:"Abgeschlossen",failed:"Fehlgeschlagen"},_z={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"},Tz=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"})},ma=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"})},wx=e=>e===1?"Git":e===2?"Compose":e??"-",Up=()=>({host:"",port:"22",username:"",password:"",extraSshArgs:""});function Cz(){var An,bo,da;const{showToast:e}=Bb(),{maintenance:t,update:r,script:n,ssh:o,loading:a,error:i,setMaintenanceMode:c,triggerUpdate:p,saveScript:g,resetScript:m,saveSshConfig:_,deleteSshConfig:O,testSshConnection:C,fetchSuperuserStatus:T,removeSuperuserAccount:x}=Wb(),b=!!(t!=null&&t.active),d=t==null?void 0:t.message,l=(An=t==null?void 0:t.extra)==null?void 0:An.type,u=!!(r!=null&&r.running),[s,h]=j.useState(""),[v,y]=j.useState(!1),[E,P]=j.useState(!1),[k,R]=j.useState(""),[A,D]=j.useState(!1),[L,W]=j.useState(()=>Up()),[G,X]=j.useState(!1),[$,B]=j.useState(!1),[U,K]=j.useState(!1),[Q,ne]=j.useState(!1),[le,ce]=j.useState(!1),[re,me]=j.useState(null);j.useEffect(()=>{if(!n)return;const ve=n.custom??n.default??n.effective??"";h(ve)},[n]),j.useEffect(()=>{if(!o){W(Up()),X(!1),B(!1),me(null);return}W({host:o.host??"",port:String(o.port??"22"),username:o.username??"",password:"",extraSshArgs:Array.isArray(o.extraSshArgs)?o.extraSshArgs.join(`
|
|
||||||
`):""}),X(!!o.passwordStored),B(!1),me(null)},[o]);const fe=j.useMemo(()=>n?n.source==="custom"&&typeof n.custom=="string"?n.custom:n.default??n.effective??"":"",[n]),ie=n?s!==fe:!1,he=(n==null?void 0:n.source)==="custom"?"Benutzerdefiniert":"Standard",[ue,Se]=j.useState(!0),[Ce,Oe]=j.useState(!1),[_e,Me]=j.useState(null),[qe,at]=j.useState(""),[it,nt]=j.useState(!1),[ft,vt]=j.useState([]),[yt,Ke]=j.useState(!0),[ae,be]=j.useState(""),[Ae,Re]=j.useState(!1),[ye,Te]=j.useState(null),[$e,bt]=j.useState(null),mt=j.useRef(null),[Xe,tt]=j.useState(null),[Gt,vr]=j.useState(!0),[Wt,Fr]=j.useState(""),[oe,ge]=j.useState(!1),[ke,Je]=j.useState(null),et=j.useRef(null),ct=j.useCallback(async({silent:ve=!1}={})=>{var Be,Ie;Se(!0),at("");try{const Le=await T(),ot=!!(Le!=null&&Le.exists);Oe(ot),Me(ot?Le.user??null:null)}catch(Le){const ot=((Ie=(Be=Le.response)==null?void 0:Be.data)==null?void 0:Ie.error)||Le.message||"Superuser-Status konnte nicht geladen werden";Oe(!1),Me(null),at(ot),ve||e({variant:"error",title:"Statusabfrage fehlgeschlagen",description:ot})}finally{Se(!1)}},[T,e]),lt=j.useCallback(async({silent:ve=!1}={})=>{if(et.current)return et.current;const Be=(async()=>{var Ie,Le;ve?ge(!0):vr(!0),Fr("");try{const $t=(await Nt.get("/api/maintenance/portainer-status")).data??{};tt($t),Je(new Date)}catch(ot){const $t=((Le=(Ie=ot.response)==null?void 0:Ie.data)==null?void 0:Le.error)||ot.message||"Fehler beim Prüfen des Portainer-Status";Fr($t),e({variant:"error",title:"Statusaktualisierung fehlgeschlagen",description:$t})}finally{ve?ge(!1):vr(!1),et.current=null}})();return et.current=Be,Be},[e]),Tt=j.useCallback(async({silent:ve=!1}={})=>{if(b||u){vt([]),be("Wartungsmodus aktiv – Duplikat-Verwaltung ist vorübergehend deaktiviert."),Ke(!1),Re(!1);return}if(mt.current)return mt.current;const Be=(async()=>{var Ie,Le,ot;ve?Re(!0):Ke(!0),be("");try{const Dt=(await Nt.get("/api/maintenance/duplicates")).data,yr=Array.isArray(Dt)?Dt:(Dt==null?void 0:Dt.items)??[];vt(yr),bt(new Date)}catch($t){if(((Ie=$t.response)==null?void 0:Ie.status)===423){const Dt="Wartungsmodus aktiv – Duplikat-Verwaltung ist vorübergehend deaktiviert.";vt([]),be(Dt),e({variant:"warning",title:"Duplikat-Verwaltung gesperrt",description:Dt})}else{const Dt=((ot=(Le=$t.response)==null?void 0:Le.data)==null?void 0:ot.error)||$t.message||"Fehler beim Laden der Wartungsdaten";be(Dt),e({variant:"error",title:"Duplikate konnten nicht geladen werden",description:Dt})}}finally{ve?Re(!1):Ke(!1),mt.current=null}})();return mt.current=Be,Be},[b,u,e]);j.useEffect(()=>{ct({silent:!0})},[ct]),j.useEffect(()=>{lt()},[lt]),j.useEffect(()=>{Tt()},[Tt]);const Ht=j.useMemo(()=>{const ve=Array.isArray(ft)?ft.length:0,Be=Array.isArray(ft)?ft.reduce((Ie,Le)=>{var ot;return Ie+(((ot=Le==null?void 0:Le.duplicates)==null?void 0:ot.length)||0)},0):0;return{groups:ve,duplicateCount:Be}},[ft]),pn=j.useCallback((ve,Be)=>{W(Ie=>({...Ie,[ve]:Be})),ve==="password"&&X(!1)},[]),cr=j.useMemo(()=>{const ve={host:L.host.trim(),port:Number.parseInt(L.port,10)||22,username:L.username.trim(),extraSshArgs:(L.extraSshArgs||"").split(/\r?\n/).map(Ie=>Ie.trim()).filter(Boolean)},Be=L.password??"";return Be?ve.password=Be:G||(ve.password=""),ve},[L,G]),uo=j.useCallback(async()=>{var ve,Be;if(n)try{y(!0),await g(s),e({variant:"success",title:"Skript gespeichert",description:"Das benutzerdefinierte Portainer-Update-Skript wurde aktualisiert."})}catch(Ie){const Le=((Be=(ve=Ie.response)==null?void 0:ve.data)==null?void 0:Be.error)||Ie.message||"Skript konnte nicht gespeichert werden";e({variant:"error",title:"Speichern fehlgeschlagen",description:Le})}finally{y(!1)}},[g,s,n,e]),co=j.useCallback(async()=>{var ve,Be;try{y(!0),await m(),e({variant:"info",title:"Standardskript wiederhergestellt",description:"Es wird wieder das Standard-Update-Skript verwendet."})}catch(Ie){const Le=((Be=(ve=Ie.response)==null?void 0:ve.data)==null?void 0:Be.error)||Ie.message||"Standardskript konnte nicht wiederhergestellt werden";e({variant:"error",title:"Zurücksetzen fehlgeschlagen",description:Le})}finally{y(!1)}},[m,e]),Ka=j.useCallback(async()=>{var ve,Be;try{K(!0),await _(cr),me(null),B(!1),e({variant:"success",title:"SSH-Konfiguration gespeichert",description:"Verbindungseinstellungen wurden aktualisiert."})}catch(Ie){const Le=((Be=(ve=Ie.response)==null?void 0:ve.data)==null?void 0:Be.error)||Ie.message||"SSH-Konfiguration konnte nicht gespeichert werden";e({variant:"error",title:"Speichern fehlgeschlagen",description:Le})}finally{K(!1)}},[cr,_,e]),fo=j.useCallback(async()=>{var ve,Be;try{ne(!0);const Ie=await C(cr);me({success:!0,timestamp:new Date,details:Ie==null?void 0:Ie.result}),e({variant:"success",title:"SSH-Verbindung erfolgreich",description:"Verbindung zum Portainer-Host wurde hergestellt."})}catch(Ie){const Le=((Be=(ve=Ie.response)==null?void 0:ve.data)==null?void 0:Be.error)||Ie.message||"SSH-Verbindung fehlgeschlagen";me({success:!1,timestamp:new Date,error:Le}),e({variant:"error",title:"
Line truncated
|
|
||||||
Dies setzt die Superuser-Einrichtung zurück.`)))try{nt(!0);const Le=await x(),ot=Number((Le==null?void 0:Le.usersRemoved)??0);e({variant:"success",title:"Superuser gelöscht",description:ot>1?`${ot} Konten aus der Superuser-Gruppe wurden entfernt.`:"Superuser-Konto wurde entfernt."}),await ct({silent:!0})}catch(Le){if(((ve=Le.response)==null?void 0:ve.status)===404)await ct({silent:!0}),e({variant:"info",title:"Kein Superuser vorhanden",description:"Es ist kein Superuser mehr vorhanden."});else{const $t=((Ie=(Be=Le.response)==null?void 0:Be.data)==null?void 0:Ie.error)||Le.message||"Superuser konnte nicht gelöscht werden";e({variant:"error",title:"Löschen fehlgeschlagen",description:$t})}}finally{nt(!1)}},[it,ue,Ce,x,ct,e]),na=j.useCallback(async ve=>{var Be,Ie;if(!(a||A)&&ve!==b){if(u){e({variant:"warning",title:"Aktion nicht möglich",description:"Während eines laufenden Updates kann der Wartungsmodus nicht geändert werden."});return}D(!0);try{const Le=ve&&!!l,ot={active:ve};Le&&d?ot.message=d:ot.message=null,await c(ot),e({variant:ve?"info":"success",title:ve?"Wartungsmodus aktiviert":"Wartungsmodus deaktiviert",description:ve?"Benutzer sehen jetzt die Wartungsseite.":"StackPulse steht wieder zur Verfügung."})}catch(Le){const ot=((Ie=(Be=Le.response)==null?void 0:Be.data)==null?void 0:Ie.error)||Le.message||"Wartungsmodus konnte nicht geändert werden";e({variant:"error",title:"Änderung fehlgeschlagen",description:ot})}finally{D(!1)}}},[a,A,b,u,c,d,l,e]),ll=j.useCallback(async()=>{var Be,Ie;if(b||u){e({variant:"warning",title:"Update nicht möglich",description:"Während eines aktiven Wartungsmodus kann kein weiteres Update gestartet werden."});return}const ve=(Xe==null?void 0:Xe.latestVersion)??(Xe==null?void 0:Xe.currentVersion)??"unbekannt";if(!(typeof window<"u"&&!window.confirm(`Portainer-Update starten?
|
|
||||||
Zielversion: ${ve}.
|
|
||||||
Während des Updates befindet sich StackPulse im Wartungsmodus.
|
|
||||||
Fortfahren?`)))try{R(""),P(!0),await p(),e({variant:"info",title:"Update gestartet",description:"Das Portainer-Update wurde gestartet."})}catch(Le){const ot=((Ie=(Be=Le.response)==null?void 0:Be.data)==null?void 0:Ie.error)||Le.message||"Portainer-Update konnte nicht gestartet werden";R(ot),e({variant:"error",title:"Update fehlgeschlagen",description:ot})}finally{P(!1)}},[b,u,Xe,p,e]),oa=j.useCallback(async ve=>{var ot,$t,Dt,yr;if(!ve)return;if(b||u){e({variant:"warning",title:"Aktion nicht möglich",description:"Während des Wartungsmodus sind Bereinigungen deaktiviert."});return}const Be=(ot=ve.canonical)==null?void 0:ot.Id;if(!Be)return;const Ie=(ve.duplicates||[]).map(st=>st.Id).filter(Boolean);if(!Ie.length)return;const Le=(($t=ve.canonical)==null?void 0:$t.Name)||ve.name||`Stack ${Be}`;if(!(typeof window<"u"&&!window.confirm(`Bereinigung für "${Le}" starten?
|
|
||||||
Es werden ${Ie.length} Duplikate entfernt: ${Ie.join(", ")}`))){Te(String(Be));try{const Ct=(await Nt.post("/api/maintenance/duplicates/cleanup",{canonicalId:Be,duplicateIds:Ie})).data??{};if(Ct.success===!1)throw new Error(Ct.error||"Bereinigung fehlgeschlagen");const Pt=Array.isArray(Ct.results)?Ct.results.filter(Pr=>Pr.status==="deleted").map(Pr=>Pr.id):Ie;e({variant:"success",title:"Bereinigung abgeschlossen",description:`${Le} – entfernte IDs: ${Pt.join(", ")}`}),await Tt({silent:!0})}catch(st){const Ct=((yr=(Dt=st.response)==null?void 0:Dt.data)==null?void 0:yr.error)||st.message||"Bereinigung fehlgeschlagen";e({variant:"error",title:"Bereinigung fehlgeschlagen",description:Ct})}finally{Te(null)}}},[Tt,b,e,u]);t!=null&&t.activatedAt&&ma(t.activatedAt),t!=null&&t.updatedAt&&ma(t.updatedAt);const Vr=b||u,aa=(r==null?void 0:r.status)??(u?"running":"idle"),Sr=wz[aa]??aa,po=El[aa]??El.idle,ia=r!=null&&r.stage?xz[r.stage]??r.stage:"–",ho=(r==null?void 0:r.logs)??[],Ya=(r==null?void 0:r.targetVersion)??"-",la=(r==null?void 0:r.resultVersion)??"-",sa=r!=null&&r.startedAt?ma(r.startedAt):"-",ua=r!=null&&r.finishedAt?ma(r.finishedAt):"-",go=(r==null?void 0:r.message)??(u?"Update läuft…":""),ca=u||v||a;return M.jsxs("div",{className:"mt-12 mb-8 flex flex-col gap-12",children:[(b||u)&&M.jsx("div",{className:"rounded-lg border border-cyan-500/60 bg-cyan-900/30 px-4 py-3 text-sm text-bluegray-100",children:M.jsxs("div",{className:"flex flex-col gap-1",children:[M.jsxs("span",{children:["Wartungsmodus aktiv",d?` – ${d}`:u?" – Portainer-Update läuft":"","."]}),u&&M.jsxs("span",{className:"text-xs text-indigo-900",children:["Phase: ",ia]})]})}),M.jsxs(pe.Card,{children:[M.jsx(pe.CardHeader,{variant:"gradient",color:"gray",className:"mb-5 p-4",children:M.jsx(pe.Typography,{variant:"h6",color:"white",className:"flex items-center justify-between",children:M.jsx("span",{children:"Wartungsmodus"})})}),M.jsxs(pe.CardBody,{className:"flex flex-col gap-4 p-4",children:[M.jsxs("div",{className:"flex items-start justify-between gap-4",children:[M.jsx("div",{className:"space-y-1",children:M.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."})}),M.jsx(pe.Switch,{checked:b,disabled:A||a||u,onChange:ve=>na(ve.target.checked),ripple:!1,color:"amber"})]}),A&&M.jsx("p",{className:"text-xs text-stormGrey-500",children:"Wartungsmodus wird aktualisiert…"})]})]}),M.jsxs(pe.Card,{children:[M.jsx(pe.CardHeader,{variant:"gradient",color:"gray",className:"mb-5 p-4",children:M.jsx(pe.Typography,{variant:"h6",color:"white",className:"flex items-center justify-between",children:M.jsx("span",{children:"Superuser"})})}),M.jsxs(pe.CardBody,{className:"flex flex-col gap-4 p-4",children:[M.jsxs("div",{className:"space-y-2",children:[ue&&M.jsx("p",{className:"text-sm text-stormGrey-500",children:"Status wird geladen…"}),!ue&&qe&&M.jsx("p",{className:"text-sm text-sunsetCoral-600",children:qe}),!ue&&!qe&&Ce&&M.jsxs("div",{className:"space-y-1",children:[M.jsx("p",{className:"text-sm",children:"Superuser ist angelegt."}),M.jsxs("p",{className:"text-xs text-stormGrey-500",children:["Benutzername: ",M.jsx("span",{className:"font-medium text-stormGrey-900",children:(_e==null?void 0:_e.username)??"unbekannt"}),_e!=null&&_e.email?` - ${_e.email}`:""]})]}),!ue&&!qe&&!Ce&&M.jsx("p",{className:"text-sm",children:"Es ist kein Superuser vorhanden."})]}),M.jsxs("div",{className:"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between",children:[M.jsx(pe.Button,{variant:"outlined",color:"gray",onClick:()=>ct(),disabled:ue||it,className:"w-full sm:w-auto",children:"Status aktualisieren"}),M.jsx(pe.Button,{color:"red",onClick:il,disabled:ue||it||!Ce,className:"w-full sm:w-auto",children:it?"Wird gelöscht…":"Superuser löschen"})]}),M.jsx("p",{className:"text-xs text-stormGrey-500",children:"Entfernt das Superuser-Konto samt zugehöriger Gruppe. Danach kann der Erstbenutzer bei Bedarf erneut angelegt werden."})]})]}),M.jsxs(pe.Card,{children:[M.jsx(pe.CardHeader,{variant:"gradient",color:"gray",className:"mb-5 p-4",children:M.jsx(pe.Typography,{variant:"h6",color:"white",className:"flex items-center justify-between",children:M.jsx("span",{children:"Portainer"})})}),M.jsxs(pe.CardBody,{className:"flex flex-col gap-4 p-4 pt-0",children:[M.jsxs("div",{className:"flex flex-col gap-4 md:flex-row md:items-center md:justify-between",children:[M.jsxs("div",{children:[M.jsx("h2",{className:"block antialiased font-sans text-lg font-semibold",children:"Portainer Status"}),M.jsx("p",{className:"block antialiased font-sans text-sm font-light leading-normal text-inherit",children:Gt?"Status wird geprüft…":Wt||(u?"Portainer wird aktuell aktualisiert.":(Xe==null?void 0:Xe.updateAvailable)===!0?"Es ist ein Update für Portainer verfügbar.":"Portainer ist auf dem aktuellen Stand.")}),ke&&!Gt&&M.jsxs("p",{className:"mt-1 text-xs text-stormG
Line truncated
|
|
||||||
@@ -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
|
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
function kN(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 Ex(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function Hs(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 Nx={exports:{}},kd={},jx={exports:{}},dt={};/**
|
||||||
|
* @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 $s=Symbol.for("react.element"),EN=Symbol.for("react.portal"),NN=Symbol.for("react.fragment"),jN=Symbol.for("react.strict_mode"),RN=Symbol.for("react.profiler"),AN=Symbol.for("react.provider"),MN=Symbol.for("react.context"),IN=Symbol.for("react.forward_ref"),DN=Symbol.for("react.suspense"),LN=Symbol.for("react.memo"),FN=Symbol.for("react.lazy"),zv=Symbol.iterator;function VN(e){return e===null||typeof e!="object"?null:(e=zv&&e[zv]||e["@@iterator"],typeof e=="function"?e:null)}var Rx={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Ax=Object.assign,Mx={};function ol(e,t,r){this.props=e,this.context=t,this.refs=Mx,this.updater=r||Rx}ol.prototype.isReactComponent={};ol.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")};ol.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function Ix(){}Ix.prototype=ol.prototype;function zh(e,t,r){this.props=e,this.context=t,this.refs=Mx,this.updater=r||Rx}var Bh=zh.prototype=new Ix;Bh.constructor=zh;Ax(Bh,ol.prototype);Bh.isPureReactComponent=!0;var Bv=Array.isArray,Dx=Object.prototype.hasOwnProperty,Wh={current:null},Lx={key:!0,ref:!0,__self:!0,__source:!0};function Fx(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)Dx.call(t,n)&&!Lx.hasOwnProperty(n)&&(o[n]=t[n]);var u=arguments.length-2;if(u===1)o.children=r;else if(1<u){for(var p=Array(u),g=0;g<u;g++)p[g]=arguments[g+2];o.children=p}if(e&&e.defaultProps)for(n in u=e.defaultProps,u)o[n]===void 0&&(o[n]=u[n]);return{$$typeof:$s,type:e,key:a,ref:i,props:o,_owner:Wh.current}}function zN(e,t){return{$$typeof:$s,type:e.type,key:t,ref:e.ref,props:e.props,_owner:e._owner}}function Uh(e){return typeof e=="object"&&e!==null&&e.$$typeof===$s}function BN(e){var t={"=":"=0",":":"=2"};return"$"+e.replace(/[=:]/g,function(r){return t[r]})}var Wv=/\/+/g;function lp(e,t){return typeof e=="object"&&e!==null&&e.key!=null?BN(""+e.key):t.toString(36)}function tc(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 $s:case EN:i=!0}}if(i)return i=e,o=o(i),e=n===""?"."+lp(i,0):n,Bv(o)?(r="",e!=null&&(r=e.replace(Wv,"$&/")+"/"),tc(o,t,r,"",function(g){return g})):o!=null&&(Uh(o)&&(o=zN(o,r+(!o.key||i&&i.key===o.key?"":(""+o.key).replace(Wv,"$&/")+"/")+e)),t.push(o)),1;if(i=0,n=n===""?".":n+":",Bv(e))for(var u=0;u<e.length;u++){a=e[u];var p=n+lp(a,u);i+=tc(a,t,r,p,o)}else if(p=VN(e),typeof p=="function")for(e=p.call(e),u=0;!(a=e.next()).done;)a=a.value,p=n+lp(a,u++),i+=tc(a,t,r,p,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 Ou(e,t,r){if(e==null)return e;var n=[],o=0;return tc(e,n,"","",function(a){return t.call(r,a,o++)}),n}function WN(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 kr={current:null},rc={transition:null},UN={ReactCurrentDispatcher:kr,ReactCurrentBatchConfig:rc,ReactCurrentOwner:Wh};dt.Children={map:Ou,forEach:function(e,t,r){Ou(e,function(){t.apply(this,arguments)},r)},count:function(e){var t=0;return Ou(e,function(){t++}),t},toArray:function(e){return Ou(e,function(t){return t})||[]},only:function(e){if(!Uh(e))throw Error("React.Children.only expected to receive a single React element child.");return e}};dt.Component=ol;dt.Fragment=NN;dt.Profiler=RN;dt.PureComponent=zh;dt.StrictMode=jN;dt.Suspense=DN;dt.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=UN;dt.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=Ax({},e.props),o=e.key,a=e.ref,i=e._owner;if(t!=null){if(t.ref!==void 0&&(a=t.ref,i=Wh.current),t.key!==void 0&&(o=""+t.key),e.type&&e.type.defaultProps)var u=e.type.defaultProps;for(p in t)Dx.call(t,p)&&!Lx.hasOwnProperty(p)&&(n[p]=t[p]===void 0&&u!==void 0?u[p]:t[p])}var p=arguments.length-2;if(p===1)n.children=r;else if(1<p){u=Array(p);for(var g=0;g<p;g++)u[g]=arguments[g+2];n.children=u}return{$$typeof:$s,type:e.type,key:o,ref:a,props:n,_owner:i}};dt.createContext=function(e){return e={$$typeof:MN,_currentValue:e,_currentValue2:e,_threadCount:0,Provider:null,Consumer:null,_defaultValue:null,_globalName:null},e.Provider={$$typeof:AN,_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 HN=A,$N=Symbol.for("react.element"),qN=Symbol.for("react.fragment"),GN=Object.prototype.hasOwnProperty,KN=HN.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,YN={key:!0,ref:!0,__self:!0,__source:!0};function Vx(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)GN.call(t,n)&&!YN.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:$N,type:e,key:a,ref:i,props:o,_owner:KN.current}}kd.Fragment=qN;kd.jsx=Vx;kd.jsxs=Vx;Nx.exports=kd;var R=Nx.exports;function zx(e,t){return function(){return e.apply(t,arguments)}}const{toString:XN}=Object.prototype,{getPrototypeOf:$h}=Object,Ed=(e=>t=>{const r=XN.call(t);return e[r]||(e[r]=r.slice(8,-1).toLowerCase())})(Object.create(null)),Bn=e=>(e=e.toLowerCase(),t=>Ed(t)===e),Nd=e=>t=>typeof t===e,{isArray:al}=Array,bs=Nd("undefined");function QN(e){return e!==null&&!bs(e)&&e.constructor!==null&&!bs(e.constructor)&&Yr(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}const Bx=Bn("ArrayBuffer");function JN(e){let t;return typeof ArrayBuffer<"u"&&ArrayBuffer.isView?t=ArrayBuffer.isView(e):t=e&&e.buffer&&Bx(e.buffer),t}const ZN=Nd("string"),Yr=Nd("function"),Wx=Nd("number"),jd=e=>e!==null&&typeof e=="object",ej=e=>e===!0||e===!1,nc=e=>{if(Ed(e)!=="object")return!1;const t=$h(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(Symbol.toStringTag in e)&&!(Symbol.iterator in e)},tj=Bn("Date"),rj=Bn("File"),nj=Bn("Blob"),oj=Bn("FileList"),aj=e=>jd(e)&&Yr(e.pipe),ij=e=>{let t;return e&&(typeof FormData=="function"&&e instanceof FormData||Yr(e.append)&&((t=Ed(e))==="formdata"||t==="object"&&Yr(e.toString)&&e.toString()==="[object FormData]"))},lj=Bn("URLSearchParams"),[sj,uj,cj,dj]=["ReadableStream","Request","Response","Headers"].map(Bn),fj=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"");function qs(e,t,{allOwnKeys:r=!1}={}){if(e===null||typeof e>"u")return;let n,o;if(typeof e!="object"&&(e=[e]),al(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 u;for(n=0;n<i;n++)u=a[n],t.call(null,e[u],u,e)}}function Ux(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 Ra=(()=>typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:global)(),Hx=e=>!bs(e)&&e!==Ra;function i0(){const{caseless:e}=Hx(this)&&this||{},t={},r=(n,o)=>{const a=e&&Ux(t,o)||o;nc(t[a])&&nc(n)?t[a]=i0(t[a],n):nc(n)?t[a]=i0({},n):al(n)?t[a]=n.slice():t[a]=n};for(let n=0,o=arguments.length;n<o;n++)arguments[n]&&qs(arguments[n],r);return t}const pj=(e,t,r,{allOwnKeys:n}={})=>(qs(t,(o,a)=>{r&&Yr(o)?e[a]=zx(o,r):e[a]=o},{allOwnKeys:n}),e),hj=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),gj=(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)},bj=(e,t,r,n)=>{let o,a,i;const u={};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))&&!u[i]&&(t[i]=e[i],u[i]=!0);e=r!==!1&&$h(e)}while(e&&(!r||r(e,t))&&e!==Object.prototype);return t},mj=(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},vj=e=>{if(!e)return null;if(al(e))return e;let t=e.length;if(!Wx(t))return null;const r=new Array(t);for(;t-- >0;)r[t]=e[t];return r},yj=(e=>t=>e&&t instanceof e)(typeof Uint8Array<"u"&&$h(Uint8Array)),wj=(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])}},xj=(e,t)=>{let r;const n=[];for(;(r=e.exec(t))!==null;)n.push(r);return n},_j=Bn("HTMLFormElement"),Cj=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(r,n,o){return n.toUpperCase()+o}),Uv=(({hasOwnProperty:e})=>(t,r)=>e.call(t,r))(Object.prototype),Tj=Bn("RegExp"),$x=(e,t)=>{const r=Object.getOwnPropertyDescriptors(e),n={};qs(r,(o,a)=>{let i;(i=t(o,a,e))!==!1&&(n[a]=i||o)}),Object.defineProperties(e,n)},Sj=e=>{$x(e,(t,r)=>{if(Yr(e)&&["arguments","caller","callee"].indexOf(r)!==-1)return!1;const n=e[r];if(Yr(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+"'")})}})},Oj=(e,t)=>{const r={},n=o=>{o.forEach(a=>{r[a]=!0})};return al(e)?n(e):n(String(e).split(t)),r},Pj=()=>{},kj=(e,t)=>e!=null&&Number.isFinite(e=+e)?e:t,sp="abcdefghijklmnopqrstuvwxyz",Hv="0123456789",qx={DIGIT:Hv,ALPHA:sp,ALPHA_DIGIT:sp+sp.toUpperCase()+Hv},Ej=(e=16,t=qx.ALPHA_DIGIT)=>{let r="";const{length:n}=t;for(;e--;)r+=t[Math.random()*n|0];return r};function Nj(e){return!!(e&&Yr(e.append)&&e[Symbol.toStringTag]==="FormData"&&e[Symbol.iterator])}const jj=e=>{const t=new Array(10),r=(n,o)=>{if(jd(n)){if(t.indexOf(n)>=0)return;if(!("toJSON"in n)){t[o]=n;const a=aLine truncated
|
||||||
|
`).forEach(function(i){o=i.indexOf(":"),r=i.substring(0,o).trim().toLowerCase(),n=i.substring(o+1).trim(),!(!r||t[r]&&Jj[r])&&(r==="set-cookie"?t[r]?t[r].push(n):t[r]=[n]:t[r]=t[r]?t[r]+", "+n:n)}),t},Kv=Symbol("internals");function Il(e){return e&&String(e).trim().toLowerCase()}function oc(e){return e===!1||e==null?e:he.isArray(e)?e.map(oc):String(e)}function eR(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 tR=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function up(e,t,r,n,o){if(he.isFunction(n))return n.call(this,t,r);if(o&&(t=r),!!he.isString(t)){if(he.isString(n))return t.indexOf(n)!==-1;if(he.isRegExp(n))return n.test(t)}}function rR(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(t,r,n)=>r.toUpperCase()+n)}function nR(e,t){const r=he.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 Ad{constructor(t){t&&this.set(t)}set(t,r,n){const o=this;function a(u,p,g){const m=Il(p);if(!m)throw new Error("header name must be a non-empty string");const _=he.findKey(o,m);(!_||o[_]===void 0||g===!0||g===void 0&&o[_]!==!1)&&(o[_||p]=oc(u))}const i=(u,p)=>he.forEach(u,(g,m)=>a(g,m,p));if(he.isPlainObject(t)||t instanceof this.constructor)i(t,r);else if(he.isString(t)&&(t=t.trim())&&!tR(t))i(Zj(t),r);else if(he.isHeaders(t))for(const[u,p]of t.entries())a(p,u,n);else t!=null&&a(r,t,n);return this}get(t,r){if(t=Il(t),t){const n=he.findKey(this,t);if(n){const o=this[n];if(!r)return o;if(r===!0)return eR(o);if(he.isFunction(r))return r.call(this,o,n);if(he.isRegExp(r))return r.exec(o);throw new TypeError("parser must be boolean|regexp|function")}}}has(t,r){if(t=Il(t),t){const n=he.findKey(this,t);return!!(n&&this[n]!==void 0&&(!r||up(this,this[n],n,r)))}return!1}delete(t,r){const n=this;let o=!1;function a(i){if(i=Il(i),i){const u=he.findKey(n,i);u&&(!r||up(n,n[u],u,r))&&(delete n[u],o=!0)}}return he.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||up(this,this[a],a,t,!0))&&(delete this[a],o=!0)}return o}normalize(t){const r=this,n={};return he.forEach(this,(o,a)=>{const i=he.findKey(n,a);if(i){r[i]=oc(o),delete r[a];return}const u=t?rR(a):String(a).trim();u!==a&&delete r[a],r[u]=oc(o),n[u]=!0}),this}concat(...t){return this.constructor.concat(this,...t)}toJSON(t){const r=Object.create(null);return he.forEach(this,(n,o)=>{n!=null&&n!==!1&&(r[o]=t&&he.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[Kv]=this[Kv]={accessors:{}}).accessors,o=this.prototype;function a(i){const u=Il(i);n[u]||(nR(o,i),n[u]=!0)}return he.isArray(t)?t.forEach(a):a(t),this}}Ad.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);he.reduceDescriptors(Ad.prototype,({value:e},t)=>{let r=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(n){this[r]=n}}});he.freezeMethods(Ad);const Rn=Ad;function cp(e,t){const r=this||Yh,n=t||r,o=Rn.from(n.headers);let a=n.data;return he.forEach(e,function(u){a=u.call(r,a,o.normalize(),t?t.status:void 0)}),o.normalize(),a}function t_(e){return!!(e&&e.__CANCEL__)}function il(e,t,r){rt.call(this,e??"canceled",rt.ERR_CANCELED,t,r),this.name="CanceledError"}he.inherits(il,rt,{__CANCEL__:!0});function r_(e,t,r){const n=r.config.validateStatus;!r.status||!n||n(r.status)?e(r):t(new rt("Request failed with status code "+r.status,[rt.ERR_BAD_REQUEST,rt.ERR_BAD_RESPONSE][Math.floor(r.status/100)-4],r.config,r.request,r))}function oR(e){const t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||""}function aR(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(p){const g=Date.now(),m=n[a];i||(i=g),r[o]=p,n[o]=g;let _=a,S=0;for(;_!==o;)S+=r[_++],_=_%e;if(o=(o+1)%e,o===a&&(a=(a+1)%e),g-i<t)return;const T=m&&g-m;return T?Math.round(S*1e3/T):void 0}}function iR(e,t){let r=0,n=1e3/t,o,a;const i=(g,m=Date.now())=>{r=m,o=null,a&&(clearTimeout(a),a=null),e.apply(null,g)};return[(...g)=>{const m=Date.now(),_=m-r;_>=n?i(g,m):(o=g,a||(a=setTimeout(()=>{a=null,i(o)},n-_)))},()=>o&&i(o)]}const Rc=(e,t,r=3)=>{let n=0;const o=aR(50,250);return iR(a=>{const i=a.loaded,u=a.lengthComputable?a.total:void 0,p=i-n,g=o(p),m=i<=u;n=i;const _={loaded:i,total:u,progress:u?i/u:void 0,bytes:p,rate:g||void 0,estimated:g&&u&&m?(u-i)/g:void 0,event:a,lengthComputable:u!=null,[t?"download":"upload"]:!0};e(_)},r)},Yv=(e,t)=>{const r=e!=null;return[n=>t[0]({lengthComputable:r,total:e,loaded:n}),t[1]]},Xv=e=>(...t)=>he.asap(()=>e(...t)),lR=Xr.hasStandardBrowserEnv?function(){const t=Xr.navigator&&/(msie|trident)/i.test(Xr.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 u=he.isString(i)?o(i):i;return u.protocol===n.protocol&&u.host===n.host}}():function(){return function(){return!0}}(),sR=Xr.hasStandardBrowserEnv?{write(e,t,r,n,o,a){const i=[e+"="+encodeURIComponent(t)];he.isNumber(r)&&i.push("expires="+new Date(r).toGMTString()),he.isString(n)&&i.push("path="+n),he.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 uR(e){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e)}function cR(e,t){return t?e.replace(/\/?\/$/,"")+"/"+t.replace(/^\/+/,""):e}function n_(e,t){return e&&!uR(t)?cR(e,t):t}const Qv=e=>e instanceof Rn?{...e}:e;function Ba(e,t){t=t||{};const r={};function n(g,m,_){return he.isPlainObject(g)&&he.isPlainObject(m)?he.merge.call({caseless:_},g,m):he.isPlainObject(m)?he.merge({},m):he.isArray(m)?m.slice():m}function o(g,m,_){if(he.isUndefined(m)){if(!he.isUndefined(g))return n(void 0,g,_)}else return n(g,m,_)}function a(g,m){if(!he.isUndefined(m))return n(void 0,m)}function i(g,m){if(he.isUndefined(m)){if(!he.isUndefined(g))return n(void 0,g)}else return n(void 0,m)}function u(g,m,_){if(_ in t)return n(g,m);if(_ in e)return n(void 0,g)}const p={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:u,headers:(g,m)=>o(Qv(g),Qv(m),!0)};return he.forEach(Object.keys(Object.assign({},e,t)),function(m){const _=p[m]||o,S=_(e[m],t[m],m);he.isUndefined(S)&&_!==u||(r[m]=S)}),r}const o_=e=>{const t=Ba({},e);let{data:r,withXSRFToken:n,xsrfHeaderName:o,xsrfCookieName:a,headers:i,auth:u}=t;t.headers=i=Rn.from(i),t.url=Jx(n_(t.baseURL,t.url),e.params,e.paramsSerializer),u&&i.set("Authorization","Basic "+btoa((u.username||"")+":"+(u.password?unescape(enco
Line truncated
|
||||||
|
`+a.map(ey).join(`
|
||||||
|
`):" "+ey(a[0]):"as no adapter specified";throw new rt("There is no suitable adapter to dispatch the request "+i,"ERR_NOT_SUPPORT")}return n},adapters:d0};function dp(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new il(null,e)}function ty(e){return dp(e),e.headers=Rn.from(e.headers),e.data=cp.call(e,e.transformRequest),["post","put","patch"].indexOf(e.method)!==-1&&e.headers.setContentType("application/x-www-form-urlencoded",!1),l_.getAdapter(e.adapter||Yh.adapter)(e).then(function(n){return dp(e),n.data=cp.call(e,e.transformResponse,n),n.headers=Rn.from(n.headers),n},function(n){return t_(n)||(dp(e),n&&n.response&&(n.response.data=cp.call(e,e.transformResponse,n.response),n.response.headers=Rn.from(n.response.headers))),Promise.reject(n)})}const s_="1.7.5",Xh={};["object","boolean","number","function","string","symbol"].forEach((e,t)=>{Xh[e]=function(n){return typeof n===e||"a"+(t<1?"n ":" ")+e}});const ry={};Xh.transitional=function(t,r,n){function o(a,i){return"[Axios v"+s_+"] Transitional option '"+a+"'"+i+(n?". "+n:"")}return(a,i,u)=>{if(t===!1)throw new rt(o(i," has been removed"+(r?" in "+r:"")),rt.ERR_DEPRECATED);return r&&!ry[i]&&(ry[i]=!0,console.warn(o(i," has been deprecated since v"+r+" and will be removed in the near future"))),t?t(a,i,u):!0}};function _R(e,t,r){if(typeof e!="object")throw new rt("options must be an object",rt.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 u=e[a],p=u===void 0||i(u,a,e);if(p!==!0)throw new rt("option "+a+" must be "+p,rt.ERR_BAD_OPTION_VALUE);continue}if(r!==!0)throw new rt("Unknown option "+a,rt.ERR_BAD_OPTION)}}const f0={assertOptions:_R,validators:Xh},Vo=f0.validators;class Mc{constructor(t){this.defaults=t,this.interceptors={request:new Gv,response:new Gv}}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=Ba(this.defaults,r);const{transitional:n,paramsSerializer:o,headers:a}=r;n!==void 0&&f0.assertOptions(n,{silentJSONParsing:Vo.transitional(Vo.boolean),forcedJSONParsing:Vo.transitional(Vo.boolean),clarifyTimeoutError:Vo.transitional(Vo.boolean)},!1),o!=null&&(he.isFunction(o)?r.paramsSerializer={serialize:o}:f0.assertOptions(o,{encode:Vo.function,serialize:Vo.function},!0)),r.method=(r.method||this.defaults.method||"get").toLowerCase();let i=a&&he.merge(a.common,a[r.method]);a&&he.forEach(["delete","get","head","post","put","patch","common"],C=>{delete a[C]}),r.headers=Rn.concat(i,a);const u=[];let p=!0;this.interceptors.request.forEach(function(w){typeof w.runWhen=="function"&&w.runWhen(r)===!1||(p=p&&w.synchronous,u.unshift(w.fulfilled,w.rejected))});const g=[];this.interceptors.response.forEach(function(w){g.push(w.fulfilled,w.rejected)});let m,_=0,S;if(!p){const C=[ty.bind(this),void 0];for(C.unshift.apply(C,u),C.push.apply(C,g),S=C.length,m=Promise.resolve(r);_<S;)m=m.then(C[_++],C[_++]);return m}S=u.length;let T=r;for(_=0;_<S;){const C=u[_++],w=u[_++];try{T=C(T)}catch(b){w.call(this,b);break}}try{m=ty.call(this,T)}catch(C){return Promise.reject(C)}for(_=0,S=g.length;_<S;)m=m.then(g[_++],g[_++]);return m}getUri(t){t=Ba(this.defaults,t);const r=n_(t.baseURL,t.url);return Jx(r,t.params,t.paramsSerializer)}}he.forEach(["delete","get","head","options"],function(t){Mc.prototype[t]=function(r,n){return this.request(Ba(n||{},{method:t,url:r,data:(n||{}).data}))}});he.forEach(["post","put","patch"],function(t){function r(n){return function(a,i,u){return this.request(Ba(u||{},{method:t,headers:n?{"Content-Type":"multipart/form-data"}:{},url:a,data:i}))}}Mc.prototype[t]=r(),Mc.prototype[t+"Form"]=r(!0)});const ac=Mc;class Qh{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(u=>{n.subscribe(u),a=u}).then(o);return i.cancel=function(){n.unsubscribe(a)},i},t(function(a,i,u){n.reason||(n.reason=new il(a,i,u),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 Qh(function(o){t=o}),cancel:t}}}const CR=Qh;function TR(e){return function(r){return e.apply(null,r)}}function SR(e){return he.isObject(e)&&e.isAxiosError===!0}const p0={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(p0).forEach(([e,t])=>{p0[t]=e});const OR=p0;function u_(e){const t=new ac(e),r=zx(ac.prototype.request,t);return he.extend(r,ac.prototype,t,{allOwnKeys:!0}),he.extend(r,t,null,{allOwnKeys:!0}),r.create=function(o){return u_(Ba(e,o))},r}const Zt=u_(Yh);Zt.Axios=ac;Zt.CanceledError=il;Zt.CancelToken=CR;Zt.isCancel=t_;Zt.VERSION=s_;Zt.toFormData=Rd;Zt.AxiosError=rt;Zt.Cancel=Zt.CanceledError;Zt.all=function(t){return Promise.all(t)};Zt.spread=TR;Zt.isAxiosError=SR;Zt.mergeConfig=Ba;Zt.AxiosHeaders=Rn;Zt.formToJSON=e=>e_(he.isHTMLForm(e)?new FormData(e):e);Zt.getAdapter=l_.getAdapter;Zt.HttpStatusCode=OR;Zt.default=Zt;const wt=Zt;var h0={},c_={exports:{}},tn={},d_={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(U,B){var H=U.length;U.push(B);e:for(;0<H;){var K=H-1>>>1,Q=U[K];if(0<o(Q,B))U[K]=B,U[H]=Q,H=K;else break e}}function r(U){return U.length===0?null:U[0]}function n(U){if(U.length===0)return null;var B=U[0],H=U.pop();if(H!==B){U[0]=H;e:for(var K=0,Q=U.length,oe=Q>>>1;K<oe;){var ue=2*(K+1)-1,ce=U[ue],te=ue+1,ve=U[te];if(0>o(ce,H))te<Q&&0>o(ve,ce)?(U[K]=ve,U[te]=H,K=te):(U[K]=ce,U[ue]=H,K=ue);else if(te<Q&&0>o(ve,H))U[K]=ve,U[te]=H,K=te;else break e}}return B}function o(U,B){var H=U.sortIndex-B.sortIndex;return H!==0?H:U.id-B.id}if(typeof performance=="object"&&typeof performance.now=="function"){var a=performance;e.unstable_now=function(){return a.now()}}else{var i=Date,u=i.now();e.unstable_now=function(){return i.now()-u}}var p=[],g=[],m=1,_=null,S=3,T=!1,C=!1,w=!1,b=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(U){for(var B=r(g);B!==null;){if(B.callback===null)n(g);else if(B.startTime<=U)n(g),B.sortIndex=B.expirationTime,t(p,B);else break;B=r(g)}}function s(U){if(w=!1,c(U),!C)if(r(p)!==null)C=!0,G(h);else{var B=r(g);B!==null&&X(s,B.startTime-U)}}function h(U,B){C=!1,w&&(w=!1,d(k),k=-1),T=!0;var H=S;try{for(c(B),_=r(p);_!==null&&(!(_.expirationTime>B)||U&&!N());){var K=_.callback;if(typeof K=="function"){_.callback=null,S=_.priorityLevel;var Q=K(_.expirationTime<=B);B=e.unstable_now(),typeof Q=="function"?_.callback=Q:_===r(p)&&n(p),c(B)}else n(p);_=r(p)}if(_!==null)var oe=!0;else{var ue=r(g);ue!==null&&X(s,ue.startTime-B),oe=!1}return oe}finally{_=null,S=H,T=!1}}var v=!1,y=null,k=-1,P=5,E=-1;function N(){return!(e.unstable_now()-E<P)}function M(){if(y!==null){var U=e.unstable_now();E=U;var B=!0;try{B=y(!0,U)}finally{B?D():(v=!1,y=null)}}else v=!1}var D;if(typeof l=="function")D=function(){l(M)};else if(typeof MessageChannel<"u"){var F=new MessageChannel,W=F.port2;F.port1.onmessage=M,D=function(){W.postMessage(null)}}else D=function(){b(M,0)};function G(U){y=U,v||(v=!0,D())}function X(U,B){k=b(function(){U(e.unstable_now())},B)}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(U){U.callback=null},e.unstable_continueExecution=function(){C||T||(C=!0,G(h))},e.unstable_forceFrameRate=function(U){0>U||125<U?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):P=0<U?Math.floor(1e3/U):5},e.unstable_getCurrentPriorityLevel=function(){return S},e.unstable_getFirstCallbackNode=function(){return r(p)},e.unstable_next=function(U){switch(S){case 1:case 2:case 3:var B=3;break;default:B=S}var H=S;S=B;try{return U()}finally{S=H}},e.unstable_pauseExecution=function(){},e.unstable_requestPaint=function(){},e.unstable_runWithPriority=function(U,B){switch(U){case 1:case 2:case 3:case 4:case 5:break;default:U=3}var H=S;S=U;try{return B()}finally{S=H}},e.unstable_scheduleCallback=function(U,B,H){var K=e.unstable_now();switch(typeof H=="object"&&H!==null?(H=H.delay,H=typeof H=="number"&&0<H?K+H:K):H=K,U){case 1:var Q=-1;break;case 2:Q=250;break;case 5:Q=1073741823;break;case 4:Q=1e4;break;default:Q=5e3}return Q=H+Q,U={id:m++,callback:B,priorityLevel:U,startTime:H,expirationTime:Q,sortIndex:-1},H>K?(U.sortIndex=H,t(g,U),r(p)===null&&U===r(g)&&(w?(d(k),k=-1):w=!0,X(s,H-K))):(U.sortIndex=Q,t(p,U),C||T||(C=!0,G(h))),U},e.unstable_shouldYield=N,e.unstable_wrapCallback=function(U){var B=S;return function(){var H=S;S=B;try{return U.apply(this,arguments)}finally{S=H}}}})(f_);d_.exports=f_;var PR=d_.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 p_=A,Zr=PR;function we(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 h_=new Set,ms={};function Qa(e,t){Ui(e,t),Ui(e+"Capture",t)}function Ui(e,t){for(ms[e]=t,e=0;e<t.length;e++)h_.add(t[e])}var vo=!(typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),g0=Object.prototype.hasOwnProperty,kR=/^[: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]*$/,ny={},oy={};function ER(e){return g0.call(oy,e)?!0:g0.call(ny,e)?!1:kR.test(e)?oy[e]=!0:(ny[e]=!0,!1)}function NR(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 jR(e,t,r,n){if(t===null||typeof t>"u"||NR(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 Er(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 pr={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){pr[e]=new Er(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];pr[t]=new Er(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){pr[e]=new Er(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){pr[e]=new Er(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){pr[e]=new Er(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){pr[e]=new Er(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){pr[e]=new Er(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){pr[e]=new Er(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){pr[e]=new Er(e,5,!1,e.toLowerCase(),null,!1,!1)});var Jh=/[\-:]([a-z])/g;function Zh(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(Jh,Zh);pr[t]=new Er(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(Jh,Zh);pr[t]=new Er(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(Jh,Zh);pr[t]=new Er(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){pr[e]=new Er(e,1,!1,e.toLowerCase(),null,!1,!1)});pr.xlinkHref=new Er("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){pr[e]=new Er(e,1,!1,e.toLowerCase(),null,!0,!0)});function eg(e,t,r,n){var o=pr.hasOwnProperty(t)?pr[t]:null;(o!==null?o.type!==0:n||!(2<t.length)||t[0]!=="o"&&t[0]!==
Line truncated
|
||||||
|
`+fp+e}var pp=!1;function hp(e,t){if(!e||pp)return"";pp=!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,u=a.length-1;1<=i&&0<=u&&o[i]!==a[u];)u--;for(;1<=i&&0<=u;i--,u--)if(o[i]!==a[u]){if(i!==1||u!==1)do if(i--,u--,0>u||o[i]!==a[u]){var p=`
|
||||||
|
`+o[i].replace(" at new "," at ");return e.displayName&&p.includes("<anonymous>")&&(p=p.replace("<anonymous>",e.displayName)),p}while(1<=i&&0<=u);break}}}finally{pp=!1,Error.prepareStackTrace=r}return(e=e?e.displayName||e.name:"")?Kl(e):""}function RR(e){switch(e.tag){case 5:return Kl(e.type);case 16:return Kl("Lazy");case 13:return Kl("Suspense");case 19:return Kl("SuspenseList");case 0:case 2:case 15:return e=hp(e.type,!1),e;case 11:return e=hp(e.type.render,!1),e;case 1:return e=hp(e.type,!0),e;default:return""}}function y0(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 yi:return"Fragment";case vi:return"Portal";case b0:return"Profiler";case tg:return"StrictMode";case m0:return"Suspense";case v0:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case b_:return(e.displayName||"Context")+".Consumer";case g_: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 ng:return t=e.displayName||null,t!==null?t:y0(e.type)||"Memo";case Uo:t=e._payload,e=e._init;try{return y0(e(t))}catch{}}return null}function AR(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 y0(t);case 8:return t===tg?"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 sa(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function v_(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function MR(e){var t=v_(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 ku(e){e._valueTracker||(e._valueTracker=MR(e))}function y_(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var r=t.getValue(),n="";return e&&(n=v_(e)?e.checked?"true":"false":e.value),e=n,e!==r?(t.setValue(e),!0):!1}function Ic(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 w0(e,t){var r=t.checked;return Wt({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:r??e._wrapperState.initialChecked})}function iy(e,t){var r=t.defaultValue==null?"":t.defaultValue,n=t.checked!=null?t.checked:t.defaultChecked;r=sa(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 w_(e,t){t=t.checked,t!=null&&eg(e,"checked",t,!1)}function x0(e,t){w_(e,t);var r=sa(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")?_0(e,t.type,r):t.hasOwnProperty("defaultValue")&&_0(e,t.type,sa(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function ly(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 _0(e,t,r){(t!=="number"||Ic(e.ownerDocument)!==e)&&(r==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+r&&(e.defaultValue=""+r))}var Yl=Array.isArray;function Ri(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=""+sa(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 C0(e,t){if(t.dan
Line truncated
|
||||||
|
`).replace(HA,"")}function Du(e,t,r){if(t=Ny(t),Ny(e)!==t&&r)throw Error(we(425))}function Uc(){}var D0=null,L0=null;function F0(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 V0=typeof setTimeout=="function"?setTimeout:void 0,$A=typeof clearTimeout=="function"?clearTimeout:void 0,jy=typeof Promise=="function"?Promise:void 0,qA=typeof queueMicrotask=="function"?queueMicrotask:typeof jy<"u"?function(e){return jy.resolve(null).then(e).catch(GA)}:V0;function GA(e){setTimeout(function(){throw e})}function Pp(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),_s(t);return}n--}else r!=="$"&&r!=="$?"&&r!=="$!"||n++;r=o}while(r);_s(t)}function ea(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 Ry(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),Xn="__reactFiber$"+sl,Ps="__reactProps$"+sl,yo="__reactContainer$"+sl,z0="__reactEvents$"+sl,KA="__reactListeners$"+sl,YA="__reactHandles$"+sl;function Aa(e){var t=e[Xn];if(t)return t;for(var r=e.parentNode;r;){if(t=r[yo]||r[Xn]){if(r=t.alternate,t.child!==null||r!==null&&r.child!==null)for(e=Ry(e);e!==null;){if(r=e[Xn])return r;e=Ry(e)}return t}e=r,r=e.parentNode}return null}function Ys(e){return e=e[Xn]||e[yo],!e||e.tag!==5&&e.tag!==6&&e.tag!==13&&e.tag!==3?null:e}function Ci(e){if(e.tag===5||e.tag===6)return e.stateNode;throw Error(we(33))}function Vd(e){return e[Ps]||null}var B0=[],Ti=-1;function fa(e){return{current:e}}function jt(e){0>Ti||(e.current=B0[Ti],B0[Ti]=null,Ti--)}function Pt(e,t){Ti++,B0[Ti]=e.current,e.current=t}var ua={},_r=fa(ua),Fr=fa(!1),Wa=ua;function Hi(e,t){var r=e.type.contextTypes;if(!r)return ua;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 Vr(e){return e=e.childContextTypes,e!=null}function Hc(){jt(Fr),jt(_r)}function Ay(e,t,r){if(_r.current!==ua)throw Error(we(168));Pt(_r,t),Pt(Fr,r)}function s1(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(we(108,AR(e)||"Unknown",o));return Wt({},r,n)}function $c(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||ua,Wa=_r.current,Pt(_r,e),Pt(Fr,Fr.current),!0}function My(e,t,r){var n=e.stateNode;if(!n)throw Error(we(169));r?(e=s1(e,t,Wa),n.__reactInternalMemoizedMergedChildContext=e,jt(Fr),jt(_r),Pt(_r,e)):jt(Fr),Pt(Fr,r)}var co=null,zd=!1,kp=!1;function u1(e){co===null?co=[e]:co.push(e)}function XA(e){zd=!0,u1(e)}function pa(){if(!kp&&co!==null){kp=!0;var e=0,t=_t;try{var r=co;for(_t=1;e<r.length;e++){var n=r[e];do n=n(!0);while(n!==null)}co=null,zd=!1}catch(o){throw co!==null&&(co=co.slice(e+1)),M_(ag,pa),o}finally{_t=t,kp=!1}}return null}var Si=[],Oi=0,qc=null,Gc=0,dn=[],fn=0,Ua=null,fo=1,po="";function Na(e,t){Si[Oi++]=Gc,Si[Oi++]=qc,qc=e,Gc=t}function c1(e,t,r){dn[fn++]=fo,dn[fn++]=po,dn[fn++]=Ua,Ua=e;var n=fo;e=po;var o=32-An(n)-1;n&=~(1<<o),r+=1;var a=32-An(t)+o;if(30<a){var i=o-o%5;a=(n&(1<<i)-1).toString(32),n>>=i,o-=i,fo=1<<32-An(t)+o|r<<o|n,po=a+e}else fo=1<<a|r<<o|n,po=e}function hg(e){e.return!==null&&(Na(e,1),c1(e,1,0))}function gg(e){for(;e===qc;)qc=Si[--Oi],Si[Oi]=null,Gc=Si[--Oi],Si[Oi]=null;for(;e===Ua;)Ua=dn[--fn],dn[fn]=null,po=dn[--fn],dn[fn]=null,fo=dn[--fn],dn[fn]=null}var Qr=null,Kr=null,Dt=!1,En=null;function d1(e,t){var r=hn(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 Iy(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,Qr=e,Kr=ea(t.firstChild),!0):!1;case 6:return t=e.pendingProps===""||t.nodeType!==3?null:t,t!==null?(e.stateNode=t,Qr=e,Kr=null,!0):!1;case 13:return t=t.nodeType!==8?null:t,t!==null?(r=Ua!==null?{id:fo,overflow:po}:null,e.memoizedState={dehydrated:t,treeContext:r,retryLane:1073741824},r=hn(18,null,null,0),r.stateNode=t,r.return=e,e.child=r,Qr=e,Kr=null,!0):!1;default:return!1}}function W0(e){return(e.mode&1)!==0&&(e.flags&128)===0}function U0(e){if(Dt){var t=Kr;if(t){var r=t;if(!Iy(e,t)){if(W0(e))throw Error(we(418));t=ea(r.nextSibling);var n=Qr;t&&Iy(e,t)?d1(n,r):(e.flags=e.flags&-4097|2,Dt=!1,Qr=e)}}else{if(W0(e))throw Error(we(418));e.flags=e.flags&-4097|2,Dt=!1,Qr=e}}}function Dy(e){
Line truncated
|
||||||
|
Error generating stack: `+a.message+`
|
||||||
|
`+a.stack}return{value:e,source:t,stack:o,digest:null}}function Ap(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 aM=typeof WeakMap=="function"?WeakMap:Map;function V1(e,t,r){r=go(-1,r),r.tag=3,r.payload={element:null};var n=t.value;return r.callback=function(){td||(td=!0,nh=n),G0(e,t)},r}function z1(e,t,r){r=go(-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"&&(ra===null?ra=new Set([this]):ra.add(this));var i=t.stack;this.componentDidCatch(t.value,{componentStack:i!==null?i:""})}),r}function Hy(e,t,r){var n=e.pingCache;if(n===null){n=e.pingCache=new aM;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=yM.bind(null,e,t,r),t.then(e,e))}function $y(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 qy(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=go(-1,1),t.tag=2,ta(r,t,1))),r.lanes|=1),e)}var iM=To.ReactCurrentOwner,Dr=!1;function Or(e,t,r,n){t.child=e===null?v1(t,null,r,n):qi(t,e.child,r,n)}function Gy(e,t,r,n,o){r=r.render;var a=t.ref;return Di(t,o),n=Og(e,t,r,n,a,o),r=Pg(),e!==null&&!Dr?(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~o,xo(e,t,o)):(Dt&&r&&hg(t),t.flags|=1,Or(e,t,n,o),t.child)}function Ky(e,t,r,n,o){if(e===null){var a=r.type;return typeof a=="function"&&!Lg(a)&&a.defaultProps===void 0&&r.compare===null&&r.defaultProps===void 0?(t.tag=15,t.type=a,B1(e,t,a,n,o)):(e=gc(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:Ts,r(i,n)&&e.ref===t.ref)return xo(e,t,o)}return t.flags|=1,e=oa(a,n),e.ref=t.ref,e.return=t,t.child=e}function B1(e,t,r,n,o){if(e!==null){var a=e.memoizedProps;if(Ts(a,n)&&e.ref===t.ref)if(Dr=!1,t.pendingProps=n=a,(e.lanes&o)!==0)e.flags&131072&&(Dr=!0);else return t.lanes=e.lanes,xo(e,t,o)}return K0(e,t,r,n,o)}function W1(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},Pt(Ei,Gr),Gr|=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,Pt(Ei,Gr),Gr|=e,null;t.memoizedState={baseLanes:0,cachePool:null,transitions:null},n=a!==null?a.baseLanes:r,Pt(Ei,Gr),Gr|=n}else a!==null?(n=a.baseLanes|r,t.memoizedState=null):n=r,Pt(Ei,Gr),Gr|=n;return Or(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 K0(e,t,r,n,o){var a=Vr(r)?Wa:_r.current;return a=Hi(t,a),Di(t,o),r=Og(e,t,r,n,a,o),n=Pg(),e!==null&&!Dr?(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~o,xo(e,t,o)):(Dt&&n&&hg(t),t.flags|=1,Or(e,t,r,o),t.child)}function Yy(e,t,r,n,o){if(Vr(r)){var a=!0;$c(t)}else a=!1;if(Di(t,o),t.stateNode===null)fc(e,t),b1(t,r,n),q0(t,r,n,o),n=!0;else if(e===null){var i=t.stateNode,u=t.memoizedProps;i.props=u;var p=i.context,g=r.contextType;typeof g=="object"&&g!==null?g=vn(g):(g=Vr(r)?Wa:_r.current,g=Hi(t,g));var m=r.getDerivedStateFromProps,_=typeof m=="function"||typeof i.getSnapshotBeforeUpdate=="function";_||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(u!==n||p!==g)&&zy(t,i,n,g),Ho=!1;var S=t.memoizedState;i.state=S,Xc(t,n,i,o),p=t.memoizedState,u!==n||S!==p||Fr.current||Ho?(typeof m=="function"&&($0(t,r,m,n),p=t.memoizedState),(u=Ho||Vy(t,r,u,n,S,p,g))?(_||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=p),i.props=n,i.state=p,i.context=g,n=u):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),n=!1)}else{i=t.stateNode,h1(e,t),u=t.memoizedProps,g=t.type===t.elementType?u:On(t.type,u),i.props=g,_=t.pendingProps,S=i.context,p=r.contextType,typeof p=="object"&&p!==null?p=vn(p):(p=Vr(r)?Wa:_r.current,p=Hi(t,p));var T=r.getDerivedStateFromProps;(m=typeof T=="function"||typeof i.getSnapshotBeforeUpdate=="function")||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(u!==_||S!==p)&&zy(t,i,n,p),Ho=!1,S=t.memoizedState,i.state=S,Xc(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 Ms(){return Ms=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},Ms.apply(this,arguments)}var Xo;(function(e){e.Pop="POP",e.Push="PUSH",e.Replace="REPLACE"})(Xo||(Xo={}));const uw="popstate";function NM(e){e===void 0&&(e={});function t(n,o){let{pathname:a,search:i,hash:u}=n.location;return lh("",{pathname:a,search:i,hash:u},o.state&&o.state.usr||null,o.state&&o.state.key||"default")}function r(n,o){return typeof o=="string"?o:ad(o)}return RM(t,r,null,e)}function Bt(e,t){if(e===!1||e===null||typeof e>"u")throw new Error(t)}function Wg(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function jM(){return Math.random().toString(36).substr(2,8)}function cw(e,t){return{usr:e.state,key:e.key,idx:t}}function lh(e,t,r,n){return r===void 0&&(r=null),Ms({pathname:typeof e=="string"?e:e.pathname,search:"",hash:""},typeof t=="string"?ul(t):t,{state:r,key:t&&t.key||n||jM()})}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 ul(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 RM(e,t,r,n){n===void 0&&(n={});let{window:o=document.defaultView,v5Compat:a=!1}=n,i=o.history,u=Xo.Pop,p=null,g=m();g==null&&(g=0,i.replaceState(Ms({},i.state,{idx:g}),""));function m(){return(i.state||{idx:null}).idx}function _(){u=Xo.Pop;let b=m(),d=b==null?null:b-g;g=b,p&&p({action:u,location:w.location,delta:d})}function S(b,d){u=Xo.Push;let l=lh(w.location,b,d);r&&r(l,b),g=m()+1;let c=cw(l,g),s=w.createHref(l);try{i.pushState(c,"",s)}catch(h){if(h instanceof DOMException&&h.name==="DataCloneError")throw h;o.location.assign(s)}a&&p&&p({action:u,location:w.location,delta:1})}function T(b,d){u=Xo.Replace;let l=lh(w.location,b,d);r&&r(l,b),g=m();let c=cw(l,g),s=w.createHref(l);i.replaceState(c,"",s),a&&p&&p({action:u,location:w.location,delta:0})}function C(b){let d=o.location.origin!=="null"?o.location.origin:o.location.href,l=typeof b=="string"?b:ad(b);return Bt(d,"No window.location.(origin|href) available to create URL for href: "+l),new URL(l,d)}let w={get action(){return u},get location(){return e(o,i)},listen(b){if(p)throw new Error("A history only accepts one active listener");return o.addEventListener(uw,_),p=b,()=>{o.removeEventListener(uw,_),p=null}},createHref(b){return t(o,b)},createURL:C,encodeLocation(b){let d=C(b);return{pathname:d.pathname,search:d.search,hash:d.hash}},push:S,replace:T,go(b){return i.go(b)}};return w}var dw;(function(e){e.data="data",e.deferred="deferred",e.redirect="redirect",e.error="error"})(dw||(dw={}));function AM(e,t,r){r===void 0&&(r="/");let n=typeof t=="string"?ul(t):t,o=Is(n.pathname||"/",r);if(o==null)return null;let a=p5(e);MM(a);let i=null;for(let u=0;i==null&&u<a.length;++u)i=UM(a[u],$M(o));return i}function p5(e,t,r,n){t===void 0&&(t=[]),r===void 0&&(r=[]),n===void 0&&(n="");let o=(a,i,u)=>{let p={relativePath:u===void 0?a.path||"":u,caseSensitive:a.caseSensitive===!0,childrenIndex:i,route:a};p.relativePath.startsWith("/")&&(Bt(p.relativePath.startsWith(n),'Absolute route path "'+p.relativePath+'" nested under path '+('"'+n+'" is not valid. An absolute child route path ')+"must start with the combined path of all its parent routes."),p.relativePath=p.relativePath.slice(n.length));let g=aa([n,p.relativePath]),m=r.concat(p);a.children&&a.children.length>0&&(Bt(a.index!==!0,"Index routes must not have child routes. Please remove "+('all child routes from route path "'+g+'".')),p5(a.children,t,m,g)),!(a.path==null&&!a.index)&&t.push({path:g,score:BM(g,a.index),routesMeta:m})};return e.forEach((a,i)=>{var u;if(a.path===""||!((u=a.path)!=null&&u.includes("?")))o(a,i);else for(let p of h5(a.path))o(a,i,p)}),t}function h5(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=h5(n.join("/")),u=[];return u.push(...i.map(p=>p===""?a:[a,p].join("/"))),o&&u.push(...i),u.map(p=>e.startsWith("/")&&p===""?"/":p)}function MM(e){e.sort((t,r)=>t.score!==r.score?r.score-t.score:WM(t.routesMeta.map(n=>n.childrenIndex),r.routesMeta.map(n=>n.childrenIndex)))}const IM=/^:\w+$/,DM=3,LM=2,FM=1,VM=10,zM=-2,fw=e=>e==="*";function BM(e,t){let r=e.split("/"),n=r.length;return r.some(fw)&&(n+=zM),t&&(n+=LM),r.filter(o=>!fw(o)).reduce((o,a)=>o+(IM.test(a)?DM:a===""?FM:VM),n)}function WM(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 UM(e,t){let{routesMeta:r}=e,n={},o="/",a=[];for(let i=0;i<r.length;++i){let u=r[i],p=i===r.length-1,g=o==="/"?t:t.slice(o.length)||"/",m=sh({path:u.relativePath,caseSensitive:u.caseSensitive,end:p},g);if(!m)return null;Object.assign(n,m.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 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)}const Xd=A.createContext(null),b5=A.createContext(null),Za=A.createContext(null),Qd=A.createContext(null),ha=A.createContext({outlet:null,matches:[],isDataRoute:!1}),m5=A.createContext(null);function eI(e,t){let{relative:r}=t===void 0?{}:t;cl()||Bt(!1);let{basename:n,navigator:o}=A.useContext(Za),{hash:a,pathname:i,search:u}=Jd(e,{relative:r}),p=i;return n!=="/"&&(p=i==="/"?n:aa([n,i])),o.createHref({pathname:p,search:u,hash:a})}function cl(){return A.useContext(Qd)!=null}function ga(){return cl()||Bt(!1),A.useContext(Qd).location}function v5(e){A.useContext(Za).static||A.useLayoutEffect(e)}function Qs(){let{isDataRoute:e}=A.useContext(ha);return e?pI():tI()}function tI(){cl()||Bt(!1);let e=A.useContext(Xd),{basename:t,navigator:r}=A.useContext(Za),{matches:n}=A.useContext(ha),{pathname:o}=ga(),a=JSON.stringify(Ug(n).map(p=>p.pathnameBase)),i=A.useRef(!1);return v5(()=>{i.current=!0}),A.useCallback(function(p,g){if(g===void 0&&(g={}),!i.current)return;if(typeof p=="number"){r.go(p);return}let m=Hg(p,JSON.parse(a),o,g.relative==="path");e==null&&t!=="/"&&(m.pathname=m.pathname==="/"?t:aa([t,m.pathname])),(g.replace?r.replace:r.push)(m,g.state,g)},[t,r,a,o,e])}function Jd(e,t){let{relative:r}=t===void 0?{}:t,{matches:n}=A.useContext(ha),{pathname:o}=ga(),a=JSON.stringify(Ug(n).map(i=>i.pathnameBase));return A.useMemo(()=>Hg(e,JSON.parse(a),o,r==="path"),[e,a,o,r])}function rI(e,t){return nI(e,t)}function nI(e,t,r){cl()||Bt(!1);let{navigator:n}=A.useContext(Za),{matches:o}=A.useContext(ha),a=o[o.length-1],i=a?a.params:{};a&&a.pathname;let u=a?a.pathnameBase:"/";a&&a.route;let p=ga(),g;if(t){var m;let w=typeof t=="string"?ul(t):t;u==="/"||(m=w.pathname)!=null&&m.startsWith(u)||Bt(!1),g=w}else g=p;let _=g.pathname||"/",S=u==="/"?_:_.slice(u.length)||"/",T=AM(e,{pathname:S}),C=sI(T&&T.map(w=>Object.assign({},w,{params:Object.assign({},i,w.params),pathname:aa([u,n.encodeLocation?n.encodeLocation(w.pathname).pathname:w.pathname]),pathnameBase:w.pathnameBase==="/"?u:aa([u,n.encodeLocation?n.encodeLocation(w.pathnameBase).pathname:w.pathnameBase])})),o,r);return t&&C?A.createElement(Qd.Provider,{value:{location:id({pathname:"/",search:"",hash:"",state:null,key:"default"},g),navigationType:Xo.Pop}},C):C}function oI(){let e=fI(),t=JM(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 A.createElement(A.Fragment,null,A.createElement("h2",null,"Unexpected Application Error!"),A.createElement("h3",{style:{fontStyle:"italic"}},t),r?A.createElement("pre",{style:o},r):null,a)}const aI=A.createElement(oI,null);class iI extends A.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?A.createElement(ha.Provider,{value:this.props.routeContext},A.createElement(m5.Provider,{value:this.state.error,children:this.props.component})):this.props.children}}function lI(e){let{routeContext:t,match:r,children:n}=e,o=A.useContext(Xd);return o&&o.static&&o.staticContext&&(r.route.errorElement||r.route.ErrorBoundary)&&(o.staticContext._deepestRenderedBoundaryId=r.route.id),A.createElement(ha.Provider,{value:t},n)}function sI(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 u=a.findIndex(p=>p.route.id&&(i==null?void 0:i[p.route.id]));u>=0||Bt(!1),a=a.slice(0,Math.min(a.length,u+1))}return a.reduceRight((u,p,g)=>{let m=p.route.id?i==null?void 0:i[p.route.id]:null,_=null;r&&(_=p.route.errorElement||aI);let S=t.concat(a.slice(0,g+1)),T=()=>{let C;return m?C=_:p.route.Component?C=A.createElement(p.route.Component,null):p.route.element?C=p.route.element:C=u,A.createElement(lI,{match:p,routeContext:{outlet:u,matches:S,isDataRoute:r!=null},children:C})};return r&&(p.route.ErrorBoundary||p.route.errorElement||g===0)?A.createElement(iI,{location:r.location,revalidation:r.revalidation,component:_,error:m,children:T(),routeContext:{outlet:null,matches:S,isDataRoute:!0}}):T()},null)}var y5=function(e){return e.UseBlocker="useBlocker",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e}(y5||{}),ld=function(e){return e.UseBlocker="useBlocker",e.UseLoadeLine 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 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)}function x5(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 bI(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function mI(e,t){return e.button===0&&(!t||t==="_self")&&!bI(e)}const vI=["onClick","relative","reloadDocument","replace","state","target","to","preventScrollReset","unstable_viewTransition"],yI=["aria-current","caseSensitive","className","end","style","to","unstable_viewTransition","children"],wI=A.createContext({isTransitioning:!1}),xI="startTransition",pw=Hh[xI];function _I(e){let{basename:t,children:r,future:n,window:o}=e,a=A.useRef();a.current==null&&(a.current=NM({window:o,v5Compat:!0}));let i=a.current,[u,p]=A.useState({action:i.action,location:i.location}),{v7_startTransition:g}=n||{},m=A.useCallback(_=>{g&&pw?pw(()=>p(_)):p(_)},[p,g]);return A.useLayoutEffect(()=>i.listen(m),[i,m]),A.createElement(gI,{basename:t,children:r,location:u.location,navigationType:u.action,navigator:i})}const CI=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",TI=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,_5=A.forwardRef(function(t,r){let{onClick:n,relative:o,reloadDocument:a,replace:i,state:u,target:p,to:g,preventScrollReset:m,unstable_viewTransition:_}=t,S=x5(t,vI),{basename:T}=A.useContext(Za),C,w=!1;if(typeof g=="string"&&TI.test(g)&&(C=g,CI))try{let c=new URL(window.location.href),s=g.startsWith("//")?new URL(c.protocol+g):new URL(g),h=Is(s.pathname,T);s.origin===c.origin&&h!=null?g=h+s.search+s.hash:w=!0}catch{}let b=eI(g,{relative:o}),d=PI(g,{replace:i,state:u,target:p,preventScrollReset:m,relative:o,unstable_viewTransition:_});function l(c){n&&n(c),c.defaultPrevented||d(c)}return A.createElement("a",sd({},S,{href:C||b,onClick:w||a?n:l,ref:r,target:p}))}),SI=A.forwardRef(function(t,r){let{"aria-current":n="page",caseSensitive:o=!1,className:a="",end:i=!1,style:u,to:p,unstable_viewTransition:g,children:m}=t,_=x5(t,yI),S=Jd(p,{relative:_.relative}),T=ga(),C=A.useContext(b5),{navigator:w}=A.useContext(Za),b=C!=null&&kI(S)&&g===!0,d=w.encodeLocation?w.encodeLocation(S).pathname:S.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 s=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:s,isPending:h,isTransitioning:b},y=s?n:void 0,k;typeof a=="function"?k=a(v):k=[a,s?"active":null,h?"pending":null,b?"transitioning":null].filter(Boolean).join(" ");let P=typeof u=="function"?u(v):u;return A.createElement(_5,sd({},_,{"aria-current":y,className:k,ref:r,style:P,to:p,unstable_viewTransition:g}),typeof m=="function"?m(v):m)});var ch;(function(e){e.UseScrollRestoration="useScrollRestoration",e.UseSubmit="useSubmit",e.UseSubmitFetcher="useSubmitFetcher",e.UseFetcher="useFetcher",e.useViewTransitionState="useViewTransitionState"})(ch||(ch={}));var hw;(function(e){e.UseFetchers="useFetchers",e.UseScrollRestoration="useScrollRestoration"})(hw||(hw={}));function OI(e){let t=A.useContext(Xd);return t||Bt(!1),t}function PI(e,t){let{target:r,replace:n,state:o,preventScrollReset:a,relative:i,unstable_viewTransition:u}=t===void 0?{}:t,p=Qs(),g=ga(),m=Jd(e,{relative:i});return A.useCallback(_=>{if(mI(_,r)){_.preventDefault();let S=n!==void 0?n:ad(g)===ad(m);p(e,{replace:S,state:o,preventScrollReset:a,relative:i,unstable_viewTransition:u})}},[g,p,m,n,o,r,e,a,i,u])}function kI(e,t){t===void 0&&(t={});let r=A.useContext(wI);r==null&&Bt(!1);let{basename:n}=OI(ch.useViewTransitionState),o=Jd(e,{relative:t.relative});if(!r.isTransitioning)return!1;let a=Is(r.currentLocation.pathname,n)||r.currentLocation.pathname,i=Is(r.nextLocation.pathname,n)||r.nextLocation.pathname;return sh(o.pathname,i)!=null||sh(o.pathname,a)!=null}function EI({title:e,titleId:t,...r},n){return A.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?A.createElement("title",{id:t},e):null,A.createElement("path",{fillRule:"evenodd",d:"M3 6.75A.75.75 0 013.75 6h16.5a.75.75 0 010 1.5H3.75A.75.75 0 013 6.75zM3 12a.75.75 0 01.75-.75h16.5a.75.75 0 010 1.5H3.75A.75.75 0 013 12zm0 5.25a.75.75 0 01.75-.75h16.5a.75.75 0 010 1.5H3.75a.75.75 0 01-.75-.75z",clipRule:"evenodd"}))}const NI=A.forwardRef(EI),jI=NI;function RI({title:e,titleId:t,...r},n){return A.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?A.createElement("title",{id:t},e):null,A.createElement("path",{fillRule:"evenodd",d:"M11.078 2.25c-.917 0-1.69Line 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 u=r.apply(null,a);u&&n.push(u)}}else if(i==="object"){if(a.toString!==Object.prototype.toString&&!a.toString.toString().includes("[native code]")){n.push(a.toString());continue}for(var p in a)t.call(a,p)&&a[p]&&n.push(p)}}}return n.join(" ")}e.exports?(r.default=r,e.exports=r):window.classNames=r})()})(S5);var Ye=S5.exports,O5={exports:{}},Sn={};function qg(){for(var e,t,r=0,n="";r<arguments.length;)(e=arguments[r++])&&(t=P5(e))&&(n&&(n+=" "),n+=t);return n}function P5(e){if(typeof e=="string")return e;for(var t,r="",n=0;n<e.length;n++)e[n]&&(t=P5(e[n]))&&(r&&(r+=" "),r+=t);return r}function dh(){return dh=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},dh.apply(this,arguments)}function t4(e){var t=function(r){var n=r.theme,o=r.prefix,a={nextPart:new Map,validators:[]};return function(i,u){return u?i.map(function(p){return[p[0],p[1].map(function(g){return typeof g=="string"?u+g:typeof g=="object"?Object.fromEntries(Object.entries(g).map(function(m){return[u+m[0],m[1]]})):g})]}):i}(Object.entries(r.classGroups),o).forEach(function(i){fh(i[1],a,i[0],n)}),a}(e);return{getClassGroupId:function(r){var n=r.split("-");return n[0]===""&&n.length!==1&&n.shift(),k5(n,t)||function(o){if(gw.test(o)){var a=gw.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 k5(e,t){var r;if(e.length===0)return t.classGroupId;var n=t.nextPart.get(e[0]),o=n?k5(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(Sn,"__esModule",{value:!0});var gw=/^\[(.+)\]$/;function fh(e,t,r,n){e.forEach(function(o){if(typeof o!="string"){if(typeof o=="function")return o.isThemeGetter?void fh(o(n),t,r,n):void t.validators.push({validator:o,classGroupId:r});Object.entries(o).forEach(function(a){fh(a[1],bw(t,a[0]),r,n)})}else(o===""?t:bw(t,o)).classGroupId=r})}function bw(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 r4(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 n4(e){var t=e.separator||":";return function(r){for(var n=0,o=[],a=0,i=0;i<r.length;i++){var u=r[i];n===0&&u===t[0]&&(t.length!==1&&r.slice(i,i+t.length)!==t||(o.push(r.slice(a,i)),a=i+t.length)),u==="["?n++:u==="]"&&n--}var p=o.length===0?r:r.substring(a),g=p.startsWith("!");return{modifiers:o,hasImportantModifier:g,baseClassName:g?p.substring(1):p}}}function o4(e){return dh({cache:r4(e.cacheSize),splitModifiers:n4(e)},t4(e))}var a4=/\s+/;function i4(e,t){var r=t.splitModifiers,n=t.getClassGroupId,o=t.getConflictingClassGroupIds,a=new Set;return e.trim().split(a4).map(function(i){var u=r(i),p=u.modifiers,g=u.hasImportantModifier,m=n(u.baseClassName);if(!m)return{isTailwindClass:!1,originalClassName:i};var _=function(S){if(S.length<=1)return S;var T=[],C=[];return S.forEach(function(w){w[0]==="["?(T.push.apply(T,C.sort().concat([w])),C=[]):C.push(w)}),T.push.apply(T,C.sort()),T}(p).join(":");return{isTailwindClass:!0,modifierId:g?_+"!":_,classGroupId:m,originalClassName:i}}).reverse().filter(function(i){if(!i.isTailwindClass)return!0;var u=i.modifierId,p=i.classGroupId,g=u+p;return!a.has(g)&&(a.add(g),o(p).forEach(function(m){return a.add(u+m)}),!0)}).reverse().map(function(i){return i.originalClassName}).join(" ")}function ph(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];var n,o,a,i=u;function u(g){var m=t[0],_=t.slice(1).reduce(function(S,T){return T(S)},m());return n=o4(_),o=n.cache.get,a=n.cache.set,i=p,p(g)}function p(g){var m=o(g);if(m)return m;var _=i4(g,n);return a(g,_),_}return function(){return i(qg.apply(null,arguments))}}function Nt(e){var t=function(r){return r[e]||[]};return t.isThemeGetter=!0,t}var ba=/^\[(.+)\]$/,l4=/^\d+\/\d+$/,s4=new Set(["px","full","screen"]),u4=/^(\d+)?(xs|sm|md|lg|xl)$/,c4=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh)/,d4=/^-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/;function un(e){return!Number.isNaN(Number(e))||s4.has(e)||l4.test(e)||$o(e)}function $o(e){var t,r=(t=ba.exec(e))==null?void 0:t[1];return!!r&&(r.startsWith("length:")||c4.test(r))}function E5(e){var t,r=(t=ba.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 tF=["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])"],kh=tF.join(","),DO=typeof Element>"u",Bs=DO?function(){}:Element.prototype.matches||Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector,vd=!DO&&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},yd=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},rF=function(t){var r,n=t==null||(r=t.getAttribute)===null||r===void 0?void 0:r.call(t,"contenteditable");return n===""||n==="true"},nF=function(t,r,n){if(yd(t))return[];var o=Array.prototype.slice.apply(t.querySelectorAll(kh));return r&&Bs.call(t,kh)&&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(!yd(i,!1))if(i.tagName==="SLOT"){var u=i.assignedElements(),p=u.length?u:i.children,g=e(p,!0,n);n.flatten?o.push.apply(o,g):o.push({scopeParent:i,candidates:g})}else{var m=Bs.call(i,kh);m&&n.filter(i)&&(r||!t.includes(i))&&o.push(i);var _=i.shadowRoot||typeof n.getShadowRoot=="function"&&n.getShadowRoot(i),S=!yd(_,!1)&&(!n.shadowRootFilter||n.shadowRootFilter(i));if(_&&S){var T=e(_===!0?i.children:_.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},LO=function(t){return!isNaN(parseInt(t.getAttribute("tabindex"),10))},FO=function(t){if(!t)throw new Error("No node provided");return t.tabIndex<0&&(/^(AUDIO|VIDEO|DETAILS)$/.test(t.tagName)||rF(t))&&!LO(t)?0:t.tabIndex},aF=function(t,r){var n=FO(t);return n<0&&r&&!LO(t)?0:n},iF=function(t,r){return t.tabIndex===r.tabIndex?t.documentOrder-r.documentOrder:t.tabIndex-r.tabIndex},VO=function(t){return t.tagName==="INPUT"},lF=function(t){return VO(t)&&t.type==="hidden"},sF=function(t){var r=t.tagName==="DETAILS"&&Array.prototype.slice.apply(t.children).some(function(n){return n.tagName==="SUMMARY"});return r},uF=function(t,r){for(var n=0;n<t.length;n++)if(t[n].checked&&t[n].form===r)return t[n]},cF=function(t){if(!t.name)return!0;var r=t.form||vd(t),n=function(u){return r.querySelectorAll('input[type="radio"][name="'+u+'"]')},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=uF(o,t.form);return!a||a===t},dF=function(t){return VO(t)&&t.type==="radio"},fF=function(t){return dF(t)&&!cF(t)},pF=function(t){var r,n=t&&vd(t),o=(r=n)===null||r===void 0?void 0:r.host,a=!1;if(n&&n!==t){var i,u,p;for(a=!!((i=o)!==null&&i!==void 0&&(u=i.ownerDocument)!==null&&u!==void 0&&u.contains(o)||t!=null&&(p=t.ownerDocument)!==null&&p!==void 0&&p.contains(t));!a&&o;){var g,m,_;n=vd(o),o=(g=n)===null||g===void 0?void 0:g.host,a=!!((m=o)!==null&&m!==void 0&&(_=m.ownerDocument)!==null&&_!==void 0&&_.contains(o))}}return a},Hw=function(t){var r=t.getBoundingClientRect(),n=r.width,o=r.height;return n===0&&o===0},hF=function(t,r){var n=r.displayCheck,o=r.getShadowRoot;if(getComputedStyle(t).visibility==="hidden")return!0;var a=Bs.call(t,"details>summary:first-of-type"),i=a?t.parentElement:t;if(Bs.call(i,"details:not([open]) *"))return!0;if(!n||n==="full"||n==="legacy-full"){if(typeof o=="function"){for(var u=t;t;){var p=t.parentElement,g=vd(t);if(p&&!p.shadowRoot&&o(p)===!0)return Hw(t);t.assignedSlot?t=t.assignedSlot:!p&&g!==t.ownerDocument?t=g.host:t=p}t=u}if(pF(t))return!t.getClientRects().length;if(n!=="legacy-full")return!0}else if(n==="non-zero-area")return Hw(t);return!1},gF=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 Bs.call(r,"fieldset[disabled] *")?!0:!o.contains(t)}return!0}r=r.parentElement}return!1},bF=function(t,r){return!(r.disabled||yd(r)||lF(r)||hF(r,t)||sF(r)||gF(r))},$w=function(t,r){return!(fF(r)||FO(r)<0||!bF(t,r))},mF=function(t){var r=parseInt(t.getAttribute("tabindex"),10);return!!(isNaN(r)||r>=0)},vF=function e(t){var r=[],n=[];return t.forEach(function(o,a){var i=!!o.scopeParent,u=i?o.scopeParent:o,p=aF(u,i),g=i?e(o.candidates):u;p===0?i?r.push.apply(r,g):r.push(u):n.push({documentOrder:a,tabIndex:p,item:o,isScope:i,content:g})}),n.sort(iF).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),E=(0,g.useTheme)().navbar,N=E.styles,M=N.base.mobileNav;s=s??{},h=h??"";var D=(0,u.twMerge)((0,i.default)((0,p.default)(M)),h),F={unmount:{height:0,opacity:0,transition:{duration:.3,times:"[0.4, 0, 0.2, 1]"}},mount:{opacity:1,height:"".concat((k=P.current)===null||k===void 0?void 0:k.scrollHeight,"px"),transition:{duration:.3,times:"[0.4, 0, 0.2, 1]"}}},W=(0,a.default)(F,s),G=n.AnimatePresence,X=(0,o.useMergeRefs)([l,P]);return r.default.createElement(n.LazyMotion,{features:n.domAnimation},r.default.createElement(G,null,r.default.createElement(n.m.div,_({},y,{ref:X,className:D,initial:"unmount",exit:"unmount",animate:c?"mount":"unmount",variants:W}),v)))});w.displayName="MaterialTailwind.MobileNav",w.propTypes={open:m.propTypesOpen,animate:m.propTypesAnimate,className:m.propTypesClassName,children:m.propTypesChildren};var b=w})(kP);(function(e){Object.defineProperty(e,"__esModule",{value:!0});function t(l,c){for(var s in c)Object.defineProperty(l,s,{enumerable:!0,get:c[s]})}t(e,{Navbar:function(){return b},MobileNav:function(){return g.MobileNav},default:function(){return d}});var r=T(A),n=T(Be),o=T(Ye),a=ze,i=T(Lt),u=T(Fe),p=Le,g=kP,m=Zd;function _(l,c,s){return c in l?Object.defineProperty(l,c,{value:s,enumerable:!0,configurable:!0,writable:!0}):l[c]=s,l}function S(){return S=Object.assign||function(l){for(var c=1;c<arguments.length;c++){var s=arguments[c];for(var h in s)Object.prototype.hasOwnProperty.call(s,h)&&(l[h]=s[h])}return l},S.apply(this,arguments)}function T(l){return l&&l.__esModule?l:{default:l}}function C(l,c){if(l==null)return{};var s=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)&&(s[h]=l[h])}return s}function w(l,c){if(l==null)return{};var s={},h=Object.keys(l),v,y;for(y=0;y<h.length;y++)v=h[y],!(c.indexOf(v)>=0)&&(s[v]=l[v]);return s}var b=r.default.forwardRef(function(l,c){var s=l.variant,h=l.color,v=l.shadow,y=l.blurred,k=l.fullWidth,P=l.className,E=l.children,N=C(l,["variant","color","shadow","blurred","fullWidth","className","children"]),M=(0,p.useTheme)().navbar,D=M.defaultProps,F=M.valid,W=M.styles,G=W.base,X=W.variants;s=s??D.variant,h=h??D.color,v=v??D.shadow,y=y??D.blurred,k=k??D.fullWidth,P=P??D.className;var U,B=(0,o.default)((0,u.default)(G.navbar.initial),(U={},_(U,(0,u.default)(G.navbar.shadow),v),_(U,(0,u.default)(G.navbar.blurred),y&&h==="white"),_(U,(0,u.default)(G.navbar.fullWidth),k),U)),H=(0,o.default)((0,u.default)(X[(0,i.default)(F.variants,s,"filled")][(0,i.default)(F.colors,h,"white")])),K=(0,a.twMerge)((0,o.default)(B,H),P);return r.default.createElement("nav",S({},N,{ref:c,className:K}),E)});b.propTypes={variant:n.default.oneOf(m.propTypesVariant),color:n.default.oneOf(m.propTypesColor),shadow:m.propTypesShadow,blurred:m.propTypesBlurred,fullWidth:m.propTypesFullWidth,className:m.propTypesClassName,children:m.propTypesChildren},b.displayName="MaterialTailwind.Navbar";var d=Object.assign(b,{MobileNav:g.MobileNav})})(PP);var EP={},xf={},vl={};(function(e){Object.defineProperty(e,"__esModule",{value:!0});function t(b,d){for(var l in d)Object.defineProperty(b,l,{enumerable:!0,get:d[l]})}t(e,{propTypesOpen:function(){return a},propTypesHandler:function(){return i},propTypesPlacement:function(){return u},propTypesOffset:function(){return p},propTypesDismiss:function(){return g},propTypesAnimate:function(){return m},propTypesContent:function(){return _},propTypesInteractive:function(){return S},propTypesClassName:function(){return T},propTypesChildren:function(){return C},propTypesContextValue:function(){return w}});var r=o(Be),n=Rt;function o(b){return b&&b.__esModule?b:{default:b}}var a=r.default.bool,i=r.default.func,u=n.propTypesPlacements,p=n.propTypesOffsetType,g=n.propTypesDismissType,m=n.propTypesAnimation,_=r.default.node,S=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:m.isRequired,labelId:r.default.string.isRequired,descriptionId:r.default.string.isRequired}).isRequired})(vl);(function(e){Object.defineProperty(e,"__esModule",{value:!0});function t(p,g){for(var m in g)Object.defineProperty(p,m,{enumerable:!0,get:g[m]})}t(e,{PopoverContext:function(){return a},usePopover:function(){return i},PopoverContextProvider:function(){return u}});var r=o(A),n=vl;function o(p){return p&&p.__esModule?p:{default:p}}var a=r.default.createContext(null);a.displayName="MaterialTailwind.PopoverContext";function i(){var p=r.default.useContext(a);if(!p)throw new Error("usePopover() must be used within a Popover. It happens when you us
Line truncated
|
||||||
|
${Ce?"border-purple-500 ring-1 ring-purple-500/40":"border-transparent"}
|
||||||
|
${!ct||ye?"opacity-75 bg-stormGrey-200/20":""}`,children:[R.jsxs("div",{className:"flex items-center space-x-4",id:"left",children:[R.jsx("input",{type:"checkbox",checked:Ce,onChange:()=>Pe(ee.Id,!ct),className:`h-5 w-5 text-purple-500 focus:ring-purple-400 border-gray-600 bg-gray-900 rounded ${ct?"":"opacity-40 cursor-not-allowed"}`,disabled:!ct}),R.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"}`}),R.jsxs("div",{children:[R.jsx("p",{className:"text-lg antialiased font-sans font-semibold leading-normal text-inherit",children:ee.Name}),R.jsxs("p",{className:"text-sm antialiased font-sans font-light leading-normal text-inherit",children:["ID: ",ee.Id]}),ee.duplicateName&&R.jsx("p",{className:"text-xs text-amber-700",children:"⚠️ Doppelter Name erkannt"})]})]}),R.jsx("div",{className:"ml-auto flex flex-col items-end gap-1 text-sm",id:"right",children:Ae?R.jsxs(R.Fragment,{children:[R.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-warmAmberGlow-600",children:"Redeploy"}),R.jsx("span",{className:"text-warmAmberGlow-500",children:"läuft…"})]}):Te?R.jsxs(R.Fragment,{children:[R.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-warmAmberGlow-600",children:"Redeploy"}),R.jsx("span",{className:"text-warmAmberGlow-500",children:"in Warteliste…"})]}):st?R.jsxs(R.Fragment,{children:[R.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-stormGrey-500",children:"System"}),R.jsx("span",{className:"text-gray-300",children:"Redeploy deaktiviert"})]}):He?R.jsxs(R.Fragment,{children:[R.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-stormGrey-500",children:"Status"}),R.jsx("span",{className:"text-mossGreen-600",children:"Aktuell"})]}):R.jsx(le.Button,{onClick:()=>$e(ee.Id),disabled:ye,className:"disabled:opacity-50 disabled:cursor-not-allowed",children:"Redeploy"})})]})},ee.Id)})}),R.jsx(Ak,{disabled:r||!!o})]})}const Sz={idle:"Bereit",running:"Läuft",success:"Erfolgreich",error:"Fehlgeschlagen"},Gl={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"},Oz={initializing:"Vorbereitung","activating-maintenance":"Wartungsmodus aktivieren","executing-script":"Skript wird ausgeführt",waiting:"Warte auf Portainer",completed:"Abgeschlossen",failed:"Fehlgeschlagen"},Pz={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"},kz=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"})},ka=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"})},Ox=e=>e===1?"Git":e===2?"Compose":e??"-",o0=()=>({host:"",port:"22",username:"",password:"",extraSshArgs:""});function Ez(){var $n,Ca,Sl;const{showToast:e}=rm(),{maintenance:t,update:r,script:n,ssh:o,loading:a,error:i,setMaintenanceMode:u,triggerUpdate:p,saveScript:g,resetScript:m,saveSshConfig:_,deleteSshConfig:S,testSshConnection:T,fetchSuperuserStatus:C,fetchSetupStatus:w,deleteSetupEndpoint:b,deleteSetupServer:d,updateSetupApiKey:l,removeSuperuserAccount:c}=nm(),s=!!(t!=null&&t.active),h=t==null?void 0:t.message,v=($n=t==null?void 0:t.extra)==null?void 0:$n.type,y=!!(r!=null&&r.running),[k,P]=A.useState(""),[E,N]=A.useState(!1),[M,D]=A.useState(!1),[F,W]=A.useState(""),[G,X]=A.useState(!1),[U,B]=A.useState(()=>o0()),[H,K]=A.useState(!1),[Q,oe]=A.useState(!1),[ue,ce]=A.useState(!1),[te,ve]=A.useState(!1),[pe,se]=A.useState(!1),[be,fe]=A.useState(null);A.useEffect(()=>{if(!n)return;const ie=n.custom??n.default??n.effective??"";P(ie)},[n]),A.useEffect(()=>{if(!o){B(o0()),K(!1),oe(!1),fe(null);return}B({host:o.host??"",port:String(o.port??"22"),username:o.username??"",password:"",extraSshArgs:Array.isArray(o.extraSshArgs)?o.extraSshArgs.join(`
|
||||||
|
`):""}),K(!!o.passwordStored),oe(!1),fe(null)},[o]);const Pe=A.useMemo(()=>n?n.source==="custom"&&typeof n.custom=="string"?n.custom:n.default??n.effective??"":"",[n]),ke=n?k!==Pe:!1,Oe=(n==null?void 0:n.source)==="custom"?"Benutzerdefiniert":"Standard",[xe,De]=A.useState(!0),[$e,at]=A.useState(!1),[et,nt]=A.useState(null),[ft,Ge]=A.useState(""),[Je,qe]=A.useState(!1),[ee,ge]=A.useState(null),[Te,Ae]=A.useState(!0),[ye,Ce]=A.useState(""),[He,st]=A.useState({}),[ct,Ut]=A.useState(null),[Xe,Gt]=A.useState(null),[Kt,At]=A.useState(null),[Xt,ae]=A.useState([]),[me,je]=A.useState(!0),[tt,it]=A.useState(""),[vt,ut]=A.useState(!1),[Mt,er]=A.useState(null),[Oo,Br]=A.useState(null),wn=A.useRef(null),[mt,ii]=A.useState(null),[xn,Wr]=A.useState(!0),[ma,Po]=A.useState(""),[xl,ko]=A.useState(!1),[Eo,_l]=A.useState(null),cr=A.useRef(null),Ur=A.useCallback(async({silent:ie=!1}={})=>{var Se,Ee;De(!0),Ge("");try{const Ne=await C(),Ue=!!(Ne!=null&&Ne.exists);at(Ue),nt(Ue?Ne.user??null:null)}catch(Ne){const Ue=((Ee=(Se=Ne.response)==null?void 0:Se.data)==null?void 0:Ee.error)||Ne.message||"Superuser-Status konnte nicht geladen werden";at(!1),nt(null),Ge(Ue),ie||e({variant:"error",title:"Statusabfrage fehlgeschlagen",description:Ue})}finally{De(!1)}},[C,e]),Nr=A.useCallback(async({silent:ie=!1}={})=>{var Se,Ee;ie||Ae(!0),Ce("");try{const Ne=await w();ge(Ne),st({})}catch(Ne){const Ue=((Ee=(Se=Ne.response)==null?void 0:Se.data)==null?void 0:Ee.error)||Ne.message||"Setup-Daten konnten nicht geladen werden";Ce(Ue),ie||e({variant:"error",title:"Setup-Daten",description:Ue})}finally{Ae(!1)}},[w,e]),oo=A.useCallback(async({silent:ie=!1}={})=>{if(cr.current)return cr.current;const Se=(async()=>{var Ee,Ne;ie?ko(!0):Wr(!0),Po("");try{const Tt=(await wt.get("/api/maintenance/portainer-status")).data??{};ii(Tt),_l(new Date)}catch(Ue){const Tt=((Ne=(Ee=Ue.response)==null?void 0:Ee.data)==null?void 0:Ne.error)||Ue.message||"Fehler beim Prüfen des Portainer-Status";Po(Tt),e({variant:"error",title:"Statusaktualisierung fehlgeschlagen",description:Tt})}finally{ie?ko(!1):Wr(!1),cr.current=null}})();return cr.current=Se,Se},[e]),Wn=A.useCallback(async({silent:ie=!1}={})=>{if(s||y){ae([]),it("Wartungsmodus aktiv – Duplikat-Verwaltung ist vorübergehend deaktiviert."),je(!1),ut(!1);return}if(wn.current)return wn.current;const Se=(async()=>{var Ee,Ne,Ue;ie?ut(!0):je(!0),it("");try{const xt=(await wt.get("/api/maintenance/duplicates")).data,$t=Array.isArray(xt)?xt:(xt==null?void 0:xt.items)??[];ae($t),Br(new Date)}catch(Tt){if(((Ee=Tt.response)==null?void 0:Ee.status)===423){const xt="Wartungsmodus aktiv – Duplikat-Verwaltung ist vorübergehend deaktiviert.";ae([]),it(xt),e({variant:"warning",title:"Duplikat-Verwaltung gesperrt",description:xt})}else{const xt=((Ue=(Ne=Tt.response)==null?void 0:Ne.data)==null?void 0:Ue.error)||Tt.message||"Fehler beim Laden der Wartungsdaten";it(xt),e({variant:"error",title:"Duplikate konnten nicht geladen werden",description:xt})}}finally{ie?ut(!1):je(!1),wn.current=null}})();return wn.current=Se,Se},[s,y,e]);A.useEffect(()=>{Ur({silent:!0})},[Ur]),A.useEffect(()=>{Nr()},[Nr]),A.useEffect(()=>{oo()},[oo]),A.useEffect(()=>{Wn()},[Wn]);const _n=A.useMemo(()=>{var ie;return((ie=ee==null?void 0:ee.servers)==null?void 0:ie.items)??[]},[ee]),Hr=A.useMemo(()=>{var ie;return((ie=ee==null?void 0:ee.endpoints)==null?void 0:ie.items)??[]},[ee]),va=A.useMemo(()=>{var Se;const ie=((Se=ee==null?void 0:ee.apiKeys)==null?void 0:Se.items)??[];return new Map(ie.map(Ee=>[Ee.serverId,Ee]))},[ee]),ya=A.useMemo(()=>!!(ee!=null&&ee.setupComplete),[ee]),Un=A.useMemo(()=>{const ie=Array.isArray(Xt)?Xt.length:0,Se=Array.isArray(Xt)?Xt.reduce((Ee,Ne)=>{var Ue;return Ee+(((Ue=Ne==null?void 0:Ne.duplicates)==null?void 0:Ue.length)||0)},0):0;return{groups:ie,duplicateCount:Se}},[Xt]),$r=A.useCallback((ie,Se)=>{B(Ee=>({...Ee,[ie]:Se})),ie==="password"&&K(!1)},[]),Hn=A.useMemo(()=>{const ie={host:U.host.trim(),port:Number.parseInt(U.port,10)||22,username:U.username.trim(),extraSshArgs:(U.extraSshArgs||"").split(/\r?\n/).map(Ee=>Ee.trim()).filter(Boolean)},Se=U.password??"";return Se?ie.password=Se:H||(ie.password=""),ie},[U,H]),li=A.useCallback(async()=>{var ie,Se;if(n)try{N(!0),await g(k),e({variant:"success",title:"Skript gespeichert",description:"Das benutzerdefinierte Portainer-Update-Skript wurde aktualisiert."})}catch(Ee){const Ne=((Se=(ie=Ee.response)==null?void 0:ie.data)==null?void 0:Se.error)||Ee.message||"Skript konnte nicht gespeichert werden";e({variant:"error",title:"Speichern fehlgeschlagen",description:Ne})}finally{N(!1)}},[g,k,n,e]),ao=A.useCallback(async()=>{var ie,Se;try{N(!0),await m(),e({variant:"info",title:"Standardskript wiederhergestellt",description:"Es wird wieder das Standard-Update-Skript verwendet."})}catch(Ee){const Ne=((Se=(ie=Ee.response)==null?void 0:ie.data)==null?void 0:Se.error)||Ee.message||"Standardskript konnte nicht wiederhergestellt werden";e({variant:"error",title:"Zurücksetzen fehlgeschlagen",desc
Line truncated
|
||||||
|
Dies setzt die Superuser-Einrichtung zurück.`)))try{qe(!0);const Ne=await c(),Ue=Number((Ne==null?void 0:Ne.usersRemoved)??0);e({variant:"success",title:"Superuser gelöscht",description:Ue>1?`${Ue} Konten aus der Superuser-Gruppe wurden entfernt.`:"Superuser-Konto wurde entfernt."}),await Ur({silent:!0})}catch(Ne){if(((ie=Ne.response)==null?void 0:ie.status)===404)await Ur({silent:!0}),e({variant:"info",title:"Kein Superuser vorhanden",description:"Es ist kein Superuser mehr vorhanden."});else{const Tt=((Ee=(Se=Ne.response)==null?void 0:Se.data)==null?void 0:Ee.error)||Ne.message||"Superuser konnte nicht gelöscht werden";e({variant:"error",title:"Löschen fehlgeschlagen",description:Tt})}}finally{qe(!1)}},[Je,xe,$e,c,Ur,e]),Tl=A.useCallback(async ie=>{var Ue,Tt;if(Kt===ie)return;const Se=Hr.find(xt=>xt.id===ie);if(!Se)return;const Ee=Se.name||`Endpoint ${Se.external_id}`,Ne=`Endpoint "${Ee}" wirklich löschen?${Se.is_default?`
|
||||||
|
Dieser Endpoint ist aktuell als Standard markiert.`:""}`;if(!(typeof window<"u"&&!window.confirm(Ne))){At(ie);try{await b(ie),e({variant:"success",title:"Endpoint gelöscht",description:`Endpoint "${Ee}" wurde entfernt.`}),await Nr({silent:!0})}catch(xt){const $t=((Tt=(Ue=xt.response)==null?void 0:Ue.data)==null?void 0:Tt.error)||xt.message||"Endpoint konnte nicht gelöscht werden.";e({variant:"error",title:"Endpoint löschen fehlgeschlagen",description:$t})}finally{At(null)}}},[b,Kt,Nr,Hr,e]),xa=A.useCallback(async ie=>{var Tt,xt;if(Xe===ie)return;const Se=_n.find($t=>$t.id===ie);if(!Se)return;const Ee=Hr.filter($t=>$t.server_id===ie),Ne=Se.name||Se.url||`Server ${ie}`,Ue=[`Server "${Ne}" wirklich löschen?`,Ee.length?`Dabei werden ${Ee.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(Ue))){Gt(ie);try{await d(ie),e({variant:"success",title:"Server gelöscht",description:`Server "${Ne}" wurde entfernt.`}),await Nr({silent:!0})}catch($t){const gr=((xt=(Tt=$t.response)==null?void 0:Tt.data)==null?void 0:xt.error)||$t.message||"Server konnte nicht gelöscht werden.";e({variant:"error",title:"Server löschen fehlgeschlagen",description:gr})}finally{Gt(null)}}},[d,Nr,Xe,Hr,_n,e]),io=A.useCallback((ie,Se)=>{st(Ee=>({...Ee,[ie]:Se}))},[]),Ot=A.useCallback(async ie=>{var Ee,Ne;const Se=(He[ie]??"").trim();if(!Se){e({variant:"warning",title:"API-Key fehlt",description:"Bitte gib einen API-Key ein."});return}if(ct!==ie){Ut(ie);try{await l(ie,Se),e({variant:"success",title:"API-Key aktualisiert",description:"Der API-Key wurde gespeichert."}),st(Ue=>({...Ue,[ie]:""})),await Nr({silent:!0})}catch(Ue){const Tt=((Ne=(Ee=Ue.response)==null?void 0:Ee.data)==null?void 0:Ne.error)||Ue.message||"API-Key konnte nicht aktualisiert werden.";e({variant:"error",title:"Aktualisierung fehlgeschlagen",description:Tt})}finally{Ut(null)}}},[He,ct,Nr,e,l]),Ht=A.useCallback(async ie=>{var Se,Ee;if(!(a||G)&&ie!==s){if(y){e({variant:"warning",title:"Aktion nicht möglich",description:"Während eines laufenden Updates kann der Wartungsmodus nicht geändert werden."});return}X(!0);try{const Ne=ie&&!!v,Ue={active:ie};Ne&&h?Ue.message=h:Ue.message=null,await u(Ue),e({variant:ie?"info":"success",title:ie?"Wartungsmodus aktiviert":"Wartungsmodus deaktiviert",description:ie?"Benutzer sehen jetzt die Wartungsseite.":"StackPulse steht wieder zur Verfügung."})}catch(Ne){const Ue=((Ee=(Se=Ne.response)==null?void 0:Se.data)==null?void 0:Ee.error)||Ne.message||"Wartungsmodus konnte nicht geändert werden";e({variant:"error",title:"Änderung fehlgeschlagen",description:Ue})}finally{X(!1)}}},[a,G,s,y,u,h,v,e]),kt=A.useCallback(async()=>{var Se,Ee;if(s||y){e({variant:"warning",title:"Update nicht möglich",description:"Während eines aktiven Wartungsmodus kann kein weiteres Update gestartet werden."});return}const ie=(mt==null?void 0:mt.latestVersion)??(mt==null?void 0:mt.currentVersion)??"unbekannt";if(!(typeof window<"u"&&!window.confirm(`Portainer-Update starten?
|
||||||
|
Zielversion: ${ie}.
|
||||||
|
Während des Updates befindet sich StackPulse im Wartungsmodus.
|
||||||
|
Fortfahren?`)))try{W(""),D(!0),await p(),e({variant:"info",title:"Update gestartet",description:"Das Portainer-Update wurde gestartet."})}catch(Ne){const Ue=((Ee=(Se=Ne.response)==null?void 0:Se.data)==null?void 0:Ee.error)||Ne.message||"Portainer-Update konnte nicht gestartet werden";W(Ue),e({variant:"error",title:"Update fehlgeschlagen",description:Ue})}finally{D(!1)}},[s,y,mt,p,e]),nn=A.useCallback(async ie=>{var Ue,Tt,xt,$t;if(!ie)return;if(s||y){e({variant:"warning",title:"Aktion nicht möglich",description:"Während des Wartungsmodus sind Bereinigungen deaktiviert."});return}const Se=(Ue=ie.canonical)==null?void 0:Ue.Id;if(!Se)return;const Ee=(ie.duplicates||[]).map(gr=>gr.Id).filter(Boolean);if(!Ee.length)return;const Ne=((Tt=ie.canonical)==null?void 0:Tt.Name)||ie.name||`Stack ${Se}`;if(!(typeof window<"u"&&!window.confirm(`Bereinigung für "${Ne}" starten?
|
||||||
|
Es werden ${Ee.length} Duplikate entfernt: ${Ee.join(", ")}`))){er(String(Se));try{const lo=(await wt.post("/api/maintenance/duplicates/cleanup",{canonicalId:Se,duplicateIds:Ee})).data??{};if(lo.success===!1)throw new Error(lo.error||"Bereinigung fehlgeschlagen");const Ol=Array.isArray(lo.results)?lo.results.filter(Ta=>Ta.status==="deleted").map(Ta=>Ta.id):Ee;e({variant:"success",title:"Bereinigung abgeschlossen",description:`${Ne} – entfernte IDs: ${Ol.join(", ")}`}),await Wn({silent:!0})}catch(gr){const lo=(($t=(xt=gr.response)==null?void 0:xt.data)==null?void 0:$t.error)||gr.message||"Bereinigung fehlgeschlagen";e({variant:"error",title:"Bereinigung fehlgeschlagen",description:lo})}finally{er(null)}}},[Wn,s,e,y]);t!=null&&t.activatedAt&&ka(t.activatedAt),t!=null&&t.updatedAt&&ka(t.updatedAt);const Cr=s||y,on=(r==null?void 0:r.status)??(y?"running":"idle"),_a=Sz[on]??on,si=Gl[on]??Gl.idle,ui=r!=null&&r.stage?Oz[r.stage]??r.stage:"–",ci=(r==null?void 0:r.logs)??[],Of=(r==null?void 0:r.targetVersion)??"-",Pf=(r==null?void 0:r.resultVersion)??"-",kf=r!=null&&r.startedAt?ka(r.startedAt):"-",Ef=r!=null&&r.finishedAt?ka(r.finishedAt):"-",fu=(r==null?void 0:r.message)??(y?"Update läuft…":""),pu=y||E||a;return R.jsxs("div",{className:"mt-12 mb-8 flex flex-col gap-12",children:[(s||y)&&R.jsx("div",{className:"rounded-lg border border-cyan-500/60 bg-cyan-900/30 px-4 py-3 text-sm text-bluegray-100",children:R.jsxs("div",{className:"flex flex-col gap-1",children:[R.jsxs("span",{children:["Wartungsmodus aktiv",h?` – ${h}`:y?" – Portainer-Update läuft":"","."]}),y&&R.jsxs("span",{className:"text-xs text-indigo-900",children:["Phase: ",ui]})]})}),R.jsxs(le.Card,{children:[R.jsx(le.CardHeader,{variant:"gradient",color:"gray",className:"mb-5 p-4",children:R.jsx(le.Typography,{variant:"h6",color:"white",className:"flex items-center justify-between",children:R.jsx("span",{children:"Wartungsmodus"})})}),R.jsxs(le.CardBody,{className:"flex flex-col gap-4 p-4",children:[R.jsxs("div",{className:"flex items-start justify-between gap-4",children:[R.jsx("div",{className:"space-y-1",children:R.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."})}),R.jsx(le.Switch,{checked:s,disabled:G||a||y,onChange:ie=>Ht(ie.target.checked),ripple:!1,color:"amber"})]}),G&&R.jsx("p",{className:"text-xs text-stormGrey-500",children:"Wartungsmodus wird aktualisiert…"})]})]}),R.jsxs(le.Card,{children:[R.jsx(le.CardHeader,{variant:"gradient",color:"gray",className:"mb-5 p-4",children:R.jsx(le.Typography,{variant:"h6",color:"white",className:"flex items-center justify-between",children:R.jsx("span",{children:"Server & Endpoints"})})}),R.jsxs(le.CardBody,{className:"flex flex-col gap-4 p-4",children:[Te&&R.jsx("p",{className:"text-sm text-stormGrey-500",children:"Daten werden geladen…"}),!Te&&ye&&R.jsx(Alert,{color:"red",className:"border border-red-200 bg-red-50 text-red-700",children:ye}),!Te&&!ye&&R.jsxs("div",{className:"flex flex-col gap-6",children:[R.jsxs("div",{className:"flex flex-col gap-3",children:[R.jsxs("div",{className:"flex items-center justify-between",children:[R.jsx(le.Typography,{variant:"small",color:"blue-gray",className:"font-semibold uppercase",children:"Server"}),R.jsxs(le.Typography,{variant:"small",color:"blue-gray",children:[_n.length," vorhanden"]})]}),_n.length===0?R.jsx("p",{className:"text-sm text-stormGrey-500",children:"Es sind derzeit keine Server hinterlegt."}):R.jsx("div",{className:"flex flex-col gap-3",children:_n.map(ie=>{const Se=va.get(ie.id)||null,Ee=!!(Se!=null&&Se.hasKey),Ne=Se!=null&&Se.updatedAt?new Date(Se.updatedAt).toLocaleString("de-DE",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"}):null;return R.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:[R.jsxs("div",{className:"flex flex-col gap-1",children:[R.jsx("p",{className:"text-sm font-medium text-stormGrey-900",children:ie.name||"Ohne Namen"}),R.jsx("p",{className:"text-xs text-stormGrey-500 break-all",children:ie.url}),R.jsxs("p",{className:"text-xs text-stormGrey-500",children:["API-Key: ",Ee?"vorhanden":"nicht hinterlegt",Ne?` (aktualisiert am ${Ne})`:""]})]}),R.jsxs("div",{className:"flex w-full flex-col gap-2 md:max-w-xs",children:[R.jsx(le.Input,{type:"password",label:"Neuer API-Key",value:He[ie.id]??"",onChange:Ue=>io(ie.id,Ue.target.value),disabled:Te||ct===ie.id}),R.jsxs("div",{className:"flex items-center gap-2 justify-end",children:[R.jsx(le.Button,{variant:"text",color:"red",size:"sm",onClick:()=>xa(ie.id),disabled:Te||Xe===ie.id||Kt!==null||ct===ie.id,children:Xe===ie.id?"Lösche…":"Löschen"}),R.jsx(le.Button,{color:"blue",size:"sm",onClick:()=>Ot(ie.id),disabled:Te||ct===ie.id,children:ct===ie.id?"Speichere…":"API-Key speichern"})]})]})]},ie.id)})})]}),R.jsxs("div",{className:"flex f
Line truncated
|
||||||
@@ -32,8 +32,8 @@
|
|||||||
<!-- Nepcha Analytics (nepcha.com) -->
|
<!-- Nepcha Analytics (nepcha.com) -->
|
||||||
<!-- Nepcha is a easy-to-use web analytics. No cookies and fully compliant with GDPR, CCPA and PECR. -->
|
<!-- Nepcha is a easy-to-use web analytics. No cookies and fully compliant with GDPR, CCPA and PECR. -->
|
||||||
<script defer data-site="YOUR_DOMAIN_HERE" src="https://api.nepcha.com/js/nepcha-analytics.js"></script>
|
<script defer data-site="YOUR_DOMAIN_HERE" src="https://api.nepcha.com/js/nepcha-analytics.js"></script>
|
||||||
<script type="module" crossorigin src="/assets/index-61a55789.js"></script>
|
<script type="module" crossorigin src="/assets/index-999a8a11.js"></script>
|
||||||
<link rel="stylesheet" href="/assets/index-8ee0592b.css">
|
<link rel="stylesheet" href="/assets/index-4350b70a.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -0,0 +1,649 @@
|
|||||||
|
import crypto from 'crypto';
|
||||||
|
import { db } from '../db/index.js';
|
||||||
|
import { hasSuperuser } from '../auth/superuser.js';
|
||||||
|
|
||||||
|
const selectAllServers = db.prepare('SELECT * FROM servers ORDER BY id ASC');
|
||||||
|
const selectServerById = db.prepare('SELECT * FROM servers WHERE id = ?');
|
||||||
|
const selectServerByUrl = db.prepare('SELECT * FROM servers WHERE url = ?');
|
||||||
|
const insertServer = db.prepare(`
|
||||||
|
INSERT INTO servers (name, url)
|
||||||
|
VALUES (?, ?)
|
||||||
|
`);
|
||||||
|
const updateServer = db.prepare(`
|
||||||
|
UPDATE servers
|
||||||
|
SET name = ?, url = ?, updated_at = CURRENT_TIMESTAMP
|
||||||
|
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 = ?');
|
||||||
|
const countApiKeysStmt = db.prepare('SELECT COUNT(*) as count FROM server_api_keys');
|
||||||
|
const selectAllApiKeys = db.prepare('SELECT server_id, created_at, updated_at FROM server_api_keys');
|
||||||
|
const upsertApiKey = db.prepare(`
|
||||||
|
INSERT INTO server_api_keys (server_id, key_cipher, key_iv, key_tag, created_at, updated_at)
|
||||||
|
VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||||
|
ON CONFLICT(server_id) DO UPDATE SET
|
||||||
|
key_cipher = excluded.key_cipher,
|
||||||
|
key_iv = excluded.key_iv,
|
||||||
|
key_tag = excluded.key_tag,
|
||||||
|
updated_at = CURRENT_TIMESTAMP
|
||||||
|
`);
|
||||||
|
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')
|
||||||
|
.digest();
|
||||||
|
|
||||||
|
function encryptApiKey(apiKey) {
|
||||||
|
if (!apiKey || typeof apiKey !== 'string') return null;
|
||||||
|
const trimmed = apiKey.trim();
|
||||||
|
if (!trimmed) return null;
|
||||||
|
const iv = crypto.randomBytes(12);
|
||||||
|
const cipher = crypto.createCipheriv('aes-256-gcm', API_KEY_SECRET, iv);
|
||||||
|
const encrypted = Buffer.concat([cipher.update(trimmed, 'utf8'), cipher.final()]);
|
||||||
|
const tag = cipher.getAuthTag();
|
||||||
|
return {
|
||||||
|
iv: iv.toString('base64'),
|
||||||
|
content: encrypted.toString('base64'),
|
||||||
|
tag: tag.toString('base64')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function decryptApiKey(row) {
|
||||||
|
if (!row) return '';
|
||||||
|
try {
|
||||||
|
const iv = Buffer.from(row.key_iv, 'base64');
|
||||||
|
const content = Buffer.from(row.key_cipher, 'base64');
|
||||||
|
const tag = Buffer.from(row.key_tag, 'base64');
|
||||||
|
const decipher = crypto.createDecipheriv('aes-256-gcm', API_KEY_SECRET, iv);
|
||||||
|
decipher.setAuthTag(tag);
|
||||||
|
const decrypted = Buffer.concat([decipher.update(content), decipher.final()]);
|
||||||
|
return decrypted.toString('utf8');
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('⚠️ [Setup] API-Key konnte nicht entschlüsselt werden:', error.message);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeUrl(url) {
|
||||||
|
if (!url || typeof url !== 'string') return null;
|
||||||
|
const trimmed = url.trim();
|
||||||
|
if (!trimmed) return null;
|
||||||
|
try {
|
||||||
|
const hasProtocol = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(trimmed);
|
||||||
|
const candidate = hasProtocol ? trimmed : `https://${trimmed}`;
|
||||||
|
const normalized = new URL(candidate);
|
||||||
|
const pathname = normalized.pathname.replace(/\/+$/, '');
|
||||||
|
normalized.pathname = pathname || '/';
|
||||||
|
normalized.hash = '';
|
||||||
|
return normalized.toString().replace(/\/$/, '');
|
||||||
|
} catch (err) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deriveServerName(url) {
|
||||||
|
try {
|
||||||
|
const parsed = new URL(url);
|
||||||
|
return parsed.hostname || url;
|
||||||
|
} catch (err) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureServer({ name, url }) {
|
||||||
|
const normalizedUrl = normalizeUrl(url);
|
||||||
|
if (!normalizedUrl) {
|
||||||
|
const error = new Error('SERVER_URL_REQUIRED');
|
||||||
|
error.code = 'SERVER_URL_REQUIRED';
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
const normalizedName = (name || deriveServerName(normalizedUrl)).trim();
|
||||||
|
if (!normalizedName) {
|
||||||
|
const error = new Error('SERVER_NAME_REQUIRED');
|
||||||
|
error.code = 'SERVER_NAME_REQUIRED';
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const existing = selectServerByUrl.get(normalizedUrl);
|
||||||
|
if (existing) {
|
||||||
|
if (existing.name !== normalizedName) {
|
||||||
|
updateServer.run(normalizedName, normalizedUrl, existing.id);
|
||||||
|
const updated = selectServerById.get(existing.id);
|
||||||
|
console.log(`ℹ️ [Setup] Server aktualisiert: ${updated.name} (${updated.id})`);
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = insertServer.run(normalizedName, normalizedUrl);
|
||||||
|
const created = selectServerById.get(result.lastInsertRowid);
|
||||||
|
console.log(`✅ [Setup] Server angelegt: ${created.name} (${created.id})`);
|
||||||
|
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)) {
|
||||||
|
const error = new Error('SERVER_ID_INVALID');
|
||||||
|
error.code = 'SERVER_ID_INVALID';
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = selectServerById.get(id);
|
||||||
|
if (!server) {
|
||||||
|
const error = new Error('SERVER_NOT_FOUND');
|
||||||
|
error.code = 'SERVER_NOT_FOUND';
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedKey = typeof apiKey === 'string' ? apiKey.trim() : '';
|
||||||
|
if (!normalizedKey) {
|
||||||
|
const error = new Error('API_KEY_REQUIRED');
|
||||||
|
error.code = 'API_KEY_REQUIRED';
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const encrypted = encryptApiKey(normalizedKey);
|
||||||
|
if (!encrypted) {
|
||||||
|
const error = new Error('API_KEY_ENCRYPT_FAILED');
|
||||||
|
error.code = 'API_KEY_ENCRYPT_FAILED';
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const existing = selectApiKeyByServerId.get(id);
|
||||||
|
upsertApiKey.run(id, encrypted.content, encrypted.iv, encrypted.tag);
|
||||||
|
console.log(`${existing ? 'ℹ️' : '🔐'} [Setup] API-Key ${existing ? 'aktualisiert' : 'angelegt'}: Server ${server.name} (${server.id})`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
serverId: server.id,
|
||||||
|
updatedAt: new Date().toISOString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
const key = decryptApiKey(row);
|
||||||
|
if (key) return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
const envKey = process.env.PORTAINER_API_KEY ? process.env.PORTAINER_API_KEY.trim() : '';
|
||||||
|
return envKey || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (normalizedUrl) {
|
||||||
|
return normalizedUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const envUrl = process.env.PORTAINER_URL ? process.env.PORTAINER_URL.trim() : '';
|
||||||
|
const normalizedEnvUrl = envUrl ? normalizeUrl(envUrl) : '';
|
||||||
|
return normalizedEnvUrl || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasServer() {
|
||||||
|
const { count } = countServersStmt.get();
|
||||||
|
return count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeServer(serverId) {
|
||||||
|
const id = Number(serverId);
|
||||||
|
if (!Number.isFinite(id)) {
|
||||||
|
const error = new Error('SERVER_ID_INVALID');
|
||||||
|
error.code = 'SERVER_ID_INVALID';
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = selectServerById.get(id);
|
||||||
|
if (!server) {
|
||||||
|
const error = new Error('SERVER_NOT_FOUND');
|
||||||
|
error.code = 'SERVER_NOT_FOUND';
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const relatedEndpoints = selectEndpointsByServerId.all(id);
|
||||||
|
const hadDefaultEndpoint = relatedEndpoints.some((endpoint) => endpoint.is_default);
|
||||||
|
const existingApiKey = selectApiKeyByServerId.get(id);
|
||||||
|
|
||||||
|
deleteServerStmt.run(id);
|
||||||
|
|
||||||
|
if (existingApiKey) {
|
||||||
|
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}`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
server,
|
||||||
|
removed: true,
|
||||||
|
endpointsRemoved: relatedEndpoints.length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
server = ensureServer({ name: envServerName, url: envServerUrlRaw });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('⚠️ [Setup] Konnte Server aus Umgebungsvariablen nicht anlegen:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
if (targetServer) {
|
||||||
|
try {
|
||||||
|
setServerApiKey({ serverId: targetServer.id, apiKey: envApiKeyRaw.trim() });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('⚠️ [Setup] Konnte API-Key aus Umgebungsvariablen nicht speichern:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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]));
|
||||||
|
|
||||||
|
const rawEnvServerName = typeof process.env.PORTAINER_SERVER_NAME === 'string' ? process.env.PORTAINER_SERVER_NAME : '';
|
||||||
|
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);
|
||||||
|
const envSuperuserUsernameRaw = typeof process.env.SUPERUSER_USERNAME === 'string' ? process.env.SUPERUSER_USERNAME : '';
|
||||||
|
const envSuperuserEmailRaw = typeof process.env.SUPERUSER_EMAIL === 'string' ? process.env.SUPERUSER_EMAIL : '';
|
||||||
|
const envSuperuserPasswordRaw = typeof process.env.SUPERUSER_PASSWORD === 'string' ? process.env.SUPERUSER_PASSWORD : '';
|
||||||
|
const envSuperuserUsername = envSuperuserUsernameRaw.trim();
|
||||||
|
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;
|
||||||
|
return {
|
||||||
|
serverId: server.id,
|
||||||
|
serverName: server.name,
|
||||||
|
hasKey: Boolean(keyMeta),
|
||||||
|
updatedAt: keyMeta?.updated_at ?? null
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const apiKeyCount = apiKeyItems.filter((item) => item.hasKey).length;
|
||||||
|
const apiKeyRequired = servers.length > 0 && apiKeyCount === 0;
|
||||||
|
|
||||||
|
const superuserExists = hasSuperuser();
|
||||||
|
const setupComplete = superuserExists && !serverRequired && !endpointRequired && !apiKeyRequired;
|
||||||
|
|
||||||
|
return {
|
||||||
|
superuser: {
|
||||||
|
exists: superuserExists,
|
||||||
|
envProvided: Boolean(envSuperuserUsername || envSuperuserEmail || envSuperuserPasswordRaw),
|
||||||
|
env: {
|
||||||
|
username: envSuperuserUsernameRaw,
|
||||||
|
email: envSuperuserEmailRaw,
|
||||||
|
password: envSuperuserPasswordRaw
|
||||||
|
}
|
||||||
|
},
|
||||||
|
servers: {
|
||||||
|
count: servers.length,
|
||||||
|
items: servers,
|
||||||
|
requireInput: serverRequired,
|
||||||
|
envProvided: Boolean(envServerUrl)
|
||||||
|
},
|
||||||
|
endpoints: {
|
||||||
|
count: endpoints.length,
|
||||||
|
items: endpoints,
|
||||||
|
requireInput: endpointRequired,
|
||||||
|
envProvided: Boolean(envEndpointId),
|
||||||
|
default: defaultEndpoint
|
||||||
|
},
|
||||||
|
apiKeys: {
|
||||||
|
count: apiKeyCount,
|
||||||
|
items: apiKeyItems,
|
||||||
|
requireInput: apiKeyRequired,
|
||||||
|
envProvided: envApiKeyProvided,
|
||||||
|
envValue: rawEnvApiKey
|
||||||
|
},
|
||||||
|
requirements: {
|
||||||
|
superuser: !superuserExists,
|
||||||
|
server: serverRequired,
|
||||||
|
endpoint: endpointRequired,
|
||||||
|
apiKey: apiKeyRequired
|
||||||
|
},
|
||||||
|
setupComplete,
|
||||||
|
envDefaults: {
|
||||||
|
serverName: envServerName || (envServerUrl ? deriveServerName(envServerUrl) : ''),
|
||||||
|
serverNameFromEnv: rawEnvServerName,
|
||||||
|
serverUrl: envServerUrl,
|
||||||
|
endpointName: envEndpointName || (envEndpointId ? `Endpoint ${envEndpointId}` : ''),
|
||||||
|
endpointNameFromEnv: rawEnvEndpointName,
|
||||||
|
endpointExternalId: envEndpointId,
|
||||||
|
apiKeyProvided: envApiKeyProvided,
|
||||||
|
apiKeyValue: rawEnvApiKey,
|
||||||
|
superuserUsername: envSuperuserUsernameRaw,
|
||||||
|
superuserEmail: envSuperuserEmailRaw,
|
||||||
|
superuserPassword: envSuperuserPasswordRaw
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const createEndpointWithDefaultTransaction = db.transaction(({ serverInput, endpointInput }) => {
|
||||||
|
let server = 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) {
|
||||||
|
server = ensureServer(serverInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server) {
|
||||||
|
server = selectFirstServerStmt.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server) {
|
||||||
|
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()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
ensureDefaultsFromEnv,
|
||||||
|
ensureServer,
|
||||||
|
ensureEndpoint,
|
||||||
|
setServerApiKey,
|
||||||
|
setDefaultEndpoint,
|
||||||
|
getDefaultEndpoint,
|
||||||
|
getActiveEndpointExternalId,
|
||||||
|
getActiveApiKey,
|
||||||
|
getActiveServerUrl,
|
||||||
|
hasServer,
|
||||||
|
hasEndpoint,
|
||||||
|
hasApiKey,
|
||||||
|
hasCompleteSetup,
|
||||||
|
getSetupStatus,
|
||||||
|
completeSetup,
|
||||||
|
removeEndpoint,
|
||||||
|
removeServer
|
||||||
|
};
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
import { Routes, Route, Navigate } from "react-router-dom";
|
import { Routes, Route, Navigate } from "react-router-dom";
|
||||||
import { Dashboard, Auth } from "@/layouts";
|
import { Dashboard, Auth } from "@/layouts";
|
||||||
|
import Setup from "@/pages/setup/setup";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/dashboard/*" element={<Dashboard />} />
|
<Route path="/dashboard/*" element={<Dashboard />} />
|
||||||
<Route path="/auth/*" element={<Auth />} />
|
<Route path="/auth/*" element={<Auth />} />
|
||||||
|
<Route path="/setup" element={<Setup />} />
|
||||||
<Route path="*" element={<Navigate to="/dashboard/stacks" replace />} />
|
<Route path="*" element={<Navigate to="/dashboard/stacks" replace />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -133,7 +133,31 @@ export default function MaintenanceProvider({ children }) {
|
|||||||
|
|
||||||
const fetchSuperuserStatus = useCallback(async () => {
|
const fetchSuperuserStatus = useCallback(async () => {
|
||||||
const response = await axios.get("/api/auth/superuser/status");
|
const response = await axios.get("/api/auth/superuser/status");
|
||||||
return response.data ?? { exists: false, user: null };
|
const data = response.data ?? {};
|
||||||
|
return {
|
||||||
|
exists: Boolean(data.exists),
|
||||||
|
user: data.user ?? null
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const fetchSetupStatus = useCallback(async () => {
|
||||||
|
const response = await axios.get("/api/setup/status");
|
||||||
|
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 };
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const updateSetupApiKey = useCallback(async (serverId, apiKey) => {
|
||||||
|
const response = await axios.put(`/api/setup/servers/${serverId}/api-key`, { apiKey });
|
||||||
|
return response.data ?? { success: false };
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const removeSuperuserAccount = useCallback(async () => {
|
const removeSuperuserAccount = useCallback(async () => {
|
||||||
@@ -159,8 +183,12 @@ export default function MaintenanceProvider({ children }) {
|
|||||||
deleteSshConfig,
|
deleteSshConfig,
|
||||||
testSshConnection,
|
testSshConnection,
|
||||||
fetchSuperuserStatus,
|
fetchSuperuserStatus,
|
||||||
|
fetchSetupStatus,
|
||||||
|
deleteSetupEndpoint,
|
||||||
|
deleteSetupServer,
|
||||||
|
updateSetupApiKey,
|
||||||
removeSuperuserAccount
|
removeSuperuserAccount
|
||||||
}), [state, fetchConfig, refreshUpdateStatus, setMaintenanceMode, triggerUpdate, saveScript, resetScript, saveSshConfig, deleteSshConfig, testSshConnection, fetchSuperuserStatus, removeSuperuserAccount]);
|
}), [state, fetchConfig, refreshUpdateStatus, setMaintenanceMode, triggerUpdate, saveScript, resetScript, saveSshConfig, deleteSshConfig, testSshConnection, fetchSuperuserStatus, fetchSetupStatus, deleteSetupEndpoint, deleteSetupServer, updateSetupApiKey, removeSuperuserAccount]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MaintenanceContext.Provider value={value}>
|
<MaintenanceContext.Provider value={value}>
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import {
|
|||||||
} from "@heroicons/react/24/solid";
|
} from "@heroicons/react/24/solid";
|
||||||
import { Navbar, Footer } from "@/widgets/layout";
|
import { Navbar, Footer } from "@/widgets/layout";
|
||||||
import routes from "@/routes";
|
import routes from "@/routes";
|
||||||
import { RegSuperuser } from "@/pages/auth/regsuperuser";
|
|
||||||
|
|
||||||
export function Auth() {
|
export function Auth() {
|
||||||
const navbarRoutes = [
|
const navbarRoutes = [
|
||||||
{
|
{
|
||||||
@@ -36,7 +34,6 @@ export function Auth() {
|
|||||||
return (
|
return (
|
||||||
<div className="relative min-h-screen w-full">
|
<div className="relative min-h-screen w-full">
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/regsuperuser" element={<RegSuperuser />} />
|
|
||||||
{routes.map(
|
{routes.map(
|
||||||
({ layout, pages }) =>
|
({ layout, pages }) =>
|
||||||
layout === "auth" &&
|
layout === "auth" &&
|
||||||
|
|||||||
@@ -16,53 +16,112 @@ export function Dashboard() {
|
|||||||
const { sidenavType } = controller;
|
const { sidenavType } = controller;
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [superuserRequired, setSuperuserRequired] = useState(false);
|
const [setupChecked, setSetupChecked] = useState(false);
|
||||||
const [statusChecked, setStatusChecked] = useState(false);
|
const [setupIncomplete, setSetupIncomplete] = useState(true);
|
||||||
|
const [authChecked, setAuthChecked] = useState(false);
|
||||||
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||||
|
|
||||||
const checkSuperuserStatus = useCallback(async () => {
|
const checkSetupStatus = useCallback(async () => {
|
||||||
setStatusChecked(false);
|
setSetupChecked(false);
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/api/auth/superuser/status");
|
const response = await fetch("/api/setup/status", { credentials: "include" });
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error("STATUS_REQUEST_FAILED");
|
throw new Error("STATUS_REQUEST_FAILED");
|
||||||
}
|
}
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
setSuperuserRequired(!data.exists);
|
setSetupIncomplete(!data.setupComplete);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("⚠️ [Superuser] Statusprüfung fehlgeschlagen:", error);
|
console.error("⚠️ [Setup] Statusprüfung fehlgeschlagen:", error);
|
||||||
setSuperuserRequired(true);
|
setSetupIncomplete(true);
|
||||||
} finally {
|
} finally {
|
||||||
setStatusChecked(true);
|
setSetupChecked(true);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
const checkSession = useCallback(async () => {
|
||||||
checkSuperuserStatus();
|
setAuthChecked(false);
|
||||||
}, [checkSuperuserStatus]);
|
try {
|
||||||
|
const response = await fetch("/api/auth/session", { credentials: "include" });
|
||||||
|
if (response.status === 403) {
|
||||||
|
setSetupIncomplete(true);
|
||||||
|
setIsAuthenticated(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!response.ok) {
|
||||||
|
setIsAuthenticated(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
setIsAuthenticated(Boolean(data?.user));
|
||||||
|
} catch (error) {
|
||||||
|
console.error("⚠️ [Auth] Sessionprüfung fehlgeschlagen:", error);
|
||||||
|
setIsAuthenticated(false);
|
||||||
|
} finally {
|
||||||
|
setAuthChecked(true);
|
||||||
|
}
|
||||||
|
}, [setSetupIncomplete, setIsAuthenticated, setAuthChecked]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!statusChecked) return;
|
checkSetupStatus();
|
||||||
if (superuserRequired) {
|
}, [checkSetupStatus]);
|
||||||
if (location.pathname !== "/auth/regsuperuser") {
|
|
||||||
navigate("/auth/regsuperuser", { replace: true });
|
useEffect(() => {
|
||||||
|
if (!setupChecked || setupIncomplete) return;
|
||||||
|
checkSession();
|
||||||
|
}, [setupChecked, setupIncomplete, checkSession]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!setupChecked) return;
|
||||||
|
|
||||||
|
if (setupIncomplete) {
|
||||||
|
if (location.pathname !== "/setup") {
|
||||||
|
navigate("/setup", { replace: true });
|
||||||
}
|
}
|
||||||
} else if (location.pathname === "/auth/regsuperuser") {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!authChecked) return;
|
||||||
|
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
if (location.pathname !== "/auth/sign-in") {
|
||||||
|
navigate("/auth/sign-in", { replace: true });
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (location.pathname === "/setup" || location.pathname.startsWith("/auth/")) {
|
||||||
navigate("/dashboard/stacks", { replace: true });
|
navigate("/dashboard/stacks", { replace: true });
|
||||||
}
|
}
|
||||||
}, [superuserRequired, statusChecked, location.pathname, navigate]);
|
}, [setupChecked, setupIncomplete, authChecked, isAuthenticated, location.pathname, navigate]);
|
||||||
|
|
||||||
if (!statusChecked) {
|
if (!setupChecked) {
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center bg-blue-gray-50/50">
|
<div className="flex min-h-screen items-center justify-center bg-blue-gray-50/50">
|
||||||
<span className="text-blue-gray-500">Lade Systemstatus ...</span>
|
<span className="text-blue-gray-500">Pruefe Systemkonfiguration ...</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (superuserRequired) {
|
if (setupIncomplete) {
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center bg-blue-gray-50/50">
|
<div className="flex min-h-screen items-center justify-center bg-blue-gray-50/50">
|
||||||
<span className="text-blue-gray-500">Superuser-Einrichtung erforderlich ...</span>
|
<span className="text-blue-gray-500">Setup erforderlich ...</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!authChecked) {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen items-center justify-center bg-blue-gray-50/50">
|
||||||
|
<span className="text-blue-gray-500">Pruefe Anmeldung ...</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen items-center justify-center bg-blue-gray-50/50">
|
||||||
|
<span className="text-blue-gray-500">Weiterleitung zur Anmeldung ...</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
*/
|
*/
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import axios from "axios";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
import { BrowserRouter } from "react-router-dom";
|
import { BrowserRouter } from "react-router-dom";
|
||||||
@@ -20,6 +21,8 @@ import ToastProvider from "@/components/ToastProvider.jsx";
|
|||||||
import MaintenanceProvider from "@/components/MaintenanceProvider.jsx";
|
import MaintenanceProvider from "@/components/MaintenanceProvider.jsx";
|
||||||
import PageProvider from "@/components/PageProvider.jsx";
|
import PageProvider from "@/components/PageProvider.jsx";
|
||||||
|
|
||||||
|
axios.defaults.withCredentials = true;
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root")).render(
|
ReactDOM.createRoot(document.getElementById("root")).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
export * from "@/pages/auth/sign-in";
|
export * from "@/pages/auth/sign-in";
|
||||||
export * from "@/pages/auth/sign-up";
|
export * from "@/pages/auth/sign-up";
|
||||||
export * from "@/pages/auth/regsuperuser";
|
|
||||||
@@ -1,190 +0,0 @@
|
|||||||
import PropTypes from "prop-types";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardHeader,
|
|
||||||
CardBody,
|
|
||||||
Typography,
|
|
||||||
Input,
|
|
||||||
Button,
|
|
||||||
Alert,
|
|
||||||
} from "@material-tailwind/react";
|
|
||||||
|
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
|
|
||||||
export function RegSuperuser({ onCompleted }) {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const [checkingStatus, setCheckingStatus] = useState(true);
|
|
||||||
const [form, setForm] = useState({
|
|
||||||
username: "",
|
|
||||||
email: "",
|
|
||||||
password: "",
|
|
||||||
});
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [error, setError] = useState(null);
|
|
||||||
const [success, setSuccess] = useState(false);
|
|
||||||
|
|
||||||
const handleChange = (field) => (event) => {
|
|
||||||
setForm((prev) => ({ ...prev, [field]: event.target.value }));
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let isActive = true;
|
|
||||||
const controller = new AbortController();
|
|
||||||
|
|
||||||
const verifyStatus = async () => {
|
|
||||||
try {
|
|
||||||
const response = await fetch("/api/auth/superuser/status", { signal: controller.signal });
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error("STATUS_REQUEST_FAILED");
|
|
||||||
}
|
|
||||||
const data = await response.json();
|
|
||||||
if (data.exists && isActive) {
|
|
||||||
navigate("/dashboard/stacks", { replace: true });
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
if (err.name !== "AbortError") {
|
|
||||||
console.error("⚠️ [Superuser] Statusabfrage fehlgeschlagen:", err);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (isActive) {
|
|
||||||
setCheckingStatus(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
verifyStatus();
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
isActive = false;
|
|
||||||
controller.abort();
|
|
||||||
};
|
|
||||||
}, [navigate]);
|
|
||||||
|
|
||||||
const handleSubmit = async (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
if (loading) return;
|
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
setError(null);
|
|
||||||
setSuccess(false);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch("/api/auth/superuser/register", {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(form),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const payload = await response.json().catch(() => ({}));
|
|
||||||
switch (payload.error) {
|
|
||||||
case "MISSING_FIELDS":
|
|
||||||
throw new Error("Bitte alle Felder ausfüllen.");
|
|
||||||
case "USERNAME_REQUIRED":
|
|
||||||
throw new Error("Benutzername darf nicht leer sein.");
|
|
||||||
case "EMAIL_INVALID":
|
|
||||||
throw new Error("Bitte eine gültige E-Mail-Adresse angeben.");
|
|
||||||
case "PASSWORD_TOO_SHORT":
|
|
||||||
throw new Error("Passwort muss mindestens 8 Zeichen enthalten.");
|
|
||||||
case "SUPERUSER_EXISTS":
|
|
||||||
throw new Error("Superuser existiert bereits.");
|
|
||||||
default:
|
|
||||||
throw new Error("Registrierung fehlgeschlagen. Bitte erneut versuchen.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setSuccess(true);
|
|
||||||
if (typeof onCompleted === "function") {
|
|
||||||
onCompleted();
|
|
||||||
} else {
|
|
||||||
navigate("/dashboard/stacks", { replace: true });
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
setError(err.message || "Unbekannter Fehler");
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (checkingStatus) {
|
|
||||||
return (
|
|
||||||
<div className="flex min-h-screen items-center justify-center bg-blue-gray-50/50">
|
|
||||||
<span className="text-blue-gray-500">Pruefe Superuser-Status ...</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex min-h-screen w-full items-center justify-center py-10">
|
|
||||||
<Card className="w-full max-w-lg border border-blue-gray-100 shadow-sm">
|
|
||||||
<CardHeader
|
|
||||||
floated={false}
|
|
||||||
shadow={false}
|
|
||||||
className="mb-0 grid place-items-start gap-2 rounded-none bg-transparent p-6"
|
|
||||||
>
|
|
||||||
<Typography variant="h4" color="blue-gray">
|
|
||||||
Superuser anlegen
|
|
||||||
</Typography>
|
|
||||||
<Typography color="gray" className="font-normal">
|
|
||||||
Lege den ersten Benutzer deines Systems an. Dieser verfügt über uneingeschränkte Rechte
|
|
||||||
und kann später weitere Benutzer verwalten.
|
|
||||||
</Typography>
|
|
||||||
</CardHeader>
|
|
||||||
<CardBody className="pt-0">
|
|
||||||
<form className="mt-4 flex flex-col gap-6" onSubmit={handleSubmit}>
|
|
||||||
<Input
|
|
||||||
label="Benutzername"
|
|
||||||
value={form.username}
|
|
||||||
onChange={handleChange("username")}
|
|
||||||
required
|
|
||||||
disabled={loading || success}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
label="E-Mail-Adresse"
|
|
||||||
type="email"
|
|
||||||
value={form.email}
|
|
||||||
onChange={handleChange("email")}
|
|
||||||
required
|
|
||||||
disabled={loading || success}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
label="Passwort"
|
|
||||||
type="password"
|
|
||||||
value={form.password}
|
|
||||||
onChange={handleChange("password")}
|
|
||||||
required
|
|
||||||
disabled={loading || success}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{error && (
|
|
||||||
<Alert color="red" className="border border-red-200 bg-red-50 text-red-700">
|
|
||||||
{error}
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{success && (
|
|
||||||
<Alert color="green" className="border border-green-200 bg-green-50 text-green-700">
|
|
||||||
Superuser wurde erfolgreich angelegt. Du wirst gleich weitergeleitet.
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Button type="submit" color="blue" disabled={loading || success}>
|
|
||||||
{loading ? "Wird angelegt..." : "Superuser erstellen"}
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
RegSuperuser.propTypes = {
|
|
||||||
onCompleted: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
RegSuperuser.defaultProps = {
|
|
||||||
onCompleted: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RegSuperuser;
|
|
||||||
@@ -1,29 +1,165 @@
|
|||||||
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Card,
|
|
||||||
Input,
|
Input,
|
||||||
Checkbox,
|
|
||||||
Button,
|
Button,
|
||||||
Typography,
|
Typography,
|
||||||
|
Alert,
|
||||||
} from "@material-tailwind/react";
|
} from "@material-tailwind/react";
|
||||||
import { Link } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
|
||||||
export function SignIn() {
|
export function SignIn() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [identifier, setIdentifier] = useState("");
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
const [statusChecked, setStatusChecked] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let isActive = true;
|
||||||
|
const controller = new AbortController();
|
||||||
|
|
||||||
|
const initialize = async () => {
|
||||||
|
try {
|
||||||
|
const setupResponse = await fetch("/api/setup/status", {
|
||||||
|
credentials: "include",
|
||||||
|
signal: controller.signal,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (setupResponse.ok) {
|
||||||
|
const setupData = await setupResponse.json();
|
||||||
|
if (!setupData.setupComplete && isActive) {
|
||||||
|
navigate("/setup", { replace: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sessionResponse = await fetch("/api/auth/session", {
|
||||||
|
credentials: "include",
|
||||||
|
signal: controller.signal,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (sessionResponse.ok) {
|
||||||
|
const sessionData = await sessionResponse.json();
|
||||||
|
if (sessionData?.user && isActive) {
|
||||||
|
navigate("/dashboard/stacks", { replace: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (err.name !== "AbortError") {
|
||||||
|
console.error("⚠️ [Auth] Initiale Prüfung fehlgeschlagen:", err);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (isActive) {
|
||||||
|
setStatusChecked(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
isActive = false;
|
||||||
|
controller.abort();
|
||||||
|
};
|
||||||
|
}, [navigate]);
|
||||||
|
|
||||||
|
const handleSubmit = useCallback(
|
||||||
|
async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
if (loading) return;
|
||||||
|
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
const trimmedIdentifier = identifier.trim();
|
||||||
|
const trimmedPassword = password.trim();
|
||||||
|
|
||||||
|
if (!trimmedIdentifier || !trimmedPassword) {
|
||||||
|
setError("Bitte fülle beide Felder aus.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/auth/login", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
credentials: "include",
|
||||||
|
body: JSON.stringify({ identifier: trimmedIdentifier, password: trimmedPassword }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.status === 403) {
|
||||||
|
navigate("/setup", { replace: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const payload = await response.json().catch(() => ({}));
|
||||||
|
if (response.status === 401 || payload.error === "INVALID_CREDENTIALS") {
|
||||||
|
setError("Ungültige Zugangsdaten.");
|
||||||
|
} else if (payload.error === "MISSING_CREDENTIALS") {
|
||||||
|
setError("Bitte fülle beide Felder aus.");
|
||||||
|
} else if (payload.error === "SETUP_REQUIRED") {
|
||||||
|
navigate("/setup", { replace: true });
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
setError("Anmeldung fehlgeschlagen. Bitte versuche es erneut.");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await response.json().catch(() => ({}));
|
||||||
|
navigate("/dashboard/stacks", { replace: true });
|
||||||
|
} catch (err) {
|
||||||
|
console.error("⚠️ [Auth] Anmeldung fehlgeschlagen:", err);
|
||||||
|
setError("Netzwerkfehler – bitte erneut versuchen.");
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[identifier, password, loading, navigate]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!statusChecked) {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen items-center justify-center bg-blue-gray-50/50">
|
||||||
|
<span className="text-blue-gray-500">Pruefe Anmeldestatus ...</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="m-8 flex gap-4">
|
<section className="m-8 flex gap-4">
|
||||||
<div className="w-full lg:w-3/5 mt-24">
|
<div className="w-full lg:w-3/5 mt-24">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<Typography variant="h2" className="font-bold mb-4">Anmelden</Typography>
|
<Typography variant="h2" className="font-bold mb-4">
|
||||||
<Typography variant="paragraph" color="blue-gray" className="text-lg font-normal">Geben Sie Ihre eMail oder Ihren Benutzernamen und Ihr Passwort ein, um sich anzumelden.</Typography>
|
Anmelden
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="paragraph"
|
||||||
|
color="blue-gray"
|
||||||
|
className="text-lg font-normal"
|
||||||
|
>
|
||||||
|
Gib deine E-Mail oder deinen Benutzernamen sowie dein Passwort ein, um dich anzumelden.
|
||||||
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
<form className="mt-8 mb-2 mx-auto w-80 max-w-screen-lg lg:w-1/2">
|
<form
|
||||||
|
className="mt-8 mb-2 mx-auto w-80 max-w-screen-lg lg:w-1/2"
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
<div className="mb-1 flex flex-col gap-6">
|
<div className="mb-1 flex flex-col gap-6">
|
||||||
<Typography variant="small" color="blue-gray" className="-mb-3 font-medium">
|
<Typography variant="small" color="blue-gray" className="-mb-3 font-medium">
|
||||||
eMail oder Benutzername
|
E-Mail oder Benutzername
|
||||||
</Typography>
|
</Typography>
|
||||||
<Input
|
<Input
|
||||||
size="lg"
|
size="lg"
|
||||||
placeholder="eMail oder Benutzername"
|
placeholder="E-Mail oder Benutzername"
|
||||||
|
value={identifier}
|
||||||
|
onChange={(event) => setIdentifier(event.target.value)}
|
||||||
|
required
|
||||||
|
autoFocus
|
||||||
|
disabled={loading}
|
||||||
className=" !border-t-blue-gray-200 focus:!border-t-gray-900"
|
className=" !border-t-blue-gray-200 focus:!border-t-gray-900"
|
||||||
labelProps={{
|
labelProps={{
|
||||||
className: "before:content-none after:content-none",
|
className: "before:content-none after:content-none",
|
||||||
@@ -33,39 +169,34 @@ export function SignIn() {
|
|||||||
Passwort
|
Passwort
|
||||||
</Typography>
|
</Typography>
|
||||||
<Input
|
<Input
|
||||||
type="passwort"
|
type="password"
|
||||||
size="lg"
|
size="lg"
|
||||||
placeholder="********"
|
placeholder="********"
|
||||||
|
value={password}
|
||||||
|
onChange={(event) => setPassword(event.target.value)}
|
||||||
|
required
|
||||||
|
disabled={loading}
|
||||||
className=" !border-t-blue-gray-200 focus:!border-t-gray-900"
|
className=" !border-t-blue-gray-200 focus:!border-t-gray-900"
|
||||||
labelProps={{
|
labelProps={{
|
||||||
className: "before:content-none after:content-none",
|
className: "before:content-none after:content-none",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button className="mt-6" fullWidth>
|
|
||||||
Anmelden
|
|
||||||
</Button>
|
|
||||||
<div className="flex items-center justify-between gap-2 mt-6">
|
|
||||||
<Typography variant="small" className="font-medium text-gray-900">
|
|
||||||
<a href="#">
|
|
||||||
Forgot Password
|
|
||||||
</a>
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
{/* <Typography variant="paragraph" className="text-center text-blue-gray-500 font-medium mt-4">
|
|
||||||
Not registered?
|
|
||||||
<Link to="/auth/sign-up" className="text-gray-900 ml-1">Create account</Link>
|
|
||||||
</Typography> */}
|
|
||||||
</form>
|
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<Alert color="red" className="mt-2 border border-red-200 bg-red-50 text-red-700">
|
||||||
|
{error}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Button type="submit" className="mt-6" fullWidth disabled={loading}>
|
||||||
|
{loading ? "Anmeldung läuft ..." : "Anmelden"}
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-2/5 h-full hidden lg:block">
|
<div className="w-2/5 h-full hidden lg:block">
|
||||||
<img
|
<img src="/img/pattern.png" className="h-full w-full object-cover rounded-3xl" />
|
||||||
src="/img/pattern.png"
|
|
||||||
className="h-full w-full object-cover rounded-3xl"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
export * from "@/pages/dashboard/stacks";
|
export * from "@/pages/dashboard/stacks";
|
||||||
export * from "@/pages/dashboard/maintenance";
|
export * from "@/pages/dashboard/maintenance";
|
||||||
export * from "@/pages/dashboard/logs";
|
export * from "@/pages/dashboard/logs";
|
||||||
|
export * from "@/pages/dashboard/users";
|
||||||
|
export * from "@/pages/dashboard/usergroups";
|
||||||
@@ -9,7 +9,8 @@ import {
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
CardBody,
|
CardBody,
|
||||||
Button,
|
Button,
|
||||||
Switch
|
Switch,
|
||||||
|
Input
|
||||||
} from "@material-tailwind/react";
|
} from "@material-tailwind/react";
|
||||||
|
|
||||||
const UPDATE_STATUS_LABELS = {
|
const UPDATE_STATUS_LABELS = {
|
||||||
@@ -125,6 +126,10 @@ export function Maintenance() {
|
|||||||
deleteSshConfig,
|
deleteSshConfig,
|
||||||
testSshConnection,
|
testSshConnection,
|
||||||
fetchSuperuserStatus,
|
fetchSuperuserStatus,
|
||||||
|
fetchSetupStatus,
|
||||||
|
deleteSetupEndpoint,
|
||||||
|
deleteSetupServer,
|
||||||
|
updateSetupApiKey,
|
||||||
removeSuperuserAccount
|
removeSuperuserAccount
|
||||||
} = useMaintenance();
|
} = useMaintenance();
|
||||||
|
|
||||||
@@ -189,6 +194,13 @@ export function Maintenance() {
|
|||||||
const [superuserSummary, setSuperuserSummary] = useState(null);
|
const [superuserSummary, setSuperuserSummary] = useState(null);
|
||||||
const [superuserStatusError, setSuperuserStatusError] = useState("");
|
const [superuserStatusError, setSuperuserStatusError] = useState("");
|
||||||
const [superuserDeleteLoading, setSuperuserDeleteLoading] = useState(false);
|
const [superuserDeleteLoading, setSuperuserDeleteLoading] = useState(false);
|
||||||
|
const [setupResources, setSetupResources] = useState(null);
|
||||||
|
const [setupResourcesLoading, setSetupResourcesLoading] = useState(true);
|
||||||
|
const [setupResourcesError, setSetupResourcesError] = useState("");
|
||||||
|
const [apiKeyDrafts, setApiKeyDrafts] = useState({});
|
||||||
|
const [apiKeyUpdatingId, setApiKeyUpdatingId] = useState(null);
|
||||||
|
const [serverDeleteId, setServerDeleteId] = useState(null);
|
||||||
|
const [endpointDeleteId, setEndpointDeleteId] = useState(null);
|
||||||
|
|
||||||
const [duplicates, setDuplicates] = useState([]);
|
const [duplicates, setDuplicates] = useState([]);
|
||||||
const [duplicatesLoading, setDuplicatesLoading] = useState(true);
|
const [duplicatesLoading, setDuplicatesLoading] = useState(true);
|
||||||
@@ -226,6 +238,30 @@ export function Maintenance() {
|
|||||||
}
|
}
|
||||||
}, [fetchSuperuserStatus, showToast]);
|
}, [fetchSuperuserStatus, showToast]);
|
||||||
|
|
||||||
|
const loadSetupResources = useCallback(async ({ silent = false } = {}) => {
|
||||||
|
if (!silent) {
|
||||||
|
setSetupResourcesLoading(true);
|
||||||
|
}
|
||||||
|
setSetupResourcesError("");
|
||||||
|
try {
|
||||||
|
const data = await fetchSetupStatus();
|
||||||
|
setSetupResources(data);
|
||||||
|
setApiKeyDrafts({});
|
||||||
|
} catch (err) {
|
||||||
|
const message = err.response?.data?.error || err.message || "Setup-Daten konnten nicht geladen werden";
|
||||||
|
setSetupResourcesError(message);
|
||||||
|
if (!silent) {
|
||||||
|
showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: "Setup-Daten",
|
||||||
|
description: message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setSetupResourcesLoading(false);
|
||||||
|
}
|
||||||
|
}, [fetchSetupStatus, showToast]);
|
||||||
|
|
||||||
const fetchPortainerStatus = useCallback(async ({ silent = false } = {}) => {
|
const fetchPortainerStatus = useCallback(async ({ silent = false } = {}) => {
|
||||||
if (portainerRequestRef.current) {
|
if (portainerRequestRef.current) {
|
||||||
return portainerRequestRef.current;
|
return portainerRequestRef.current;
|
||||||
@@ -318,6 +354,10 @@ export function Maintenance() {
|
|||||||
loadSuperuserStatus({ silent: true });
|
loadSuperuserStatus({ silent: true });
|
||||||
}, [loadSuperuserStatus]);
|
}, [loadSuperuserStatus]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadSetupResources();
|
||||||
|
}, [loadSetupResources]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchPortainerStatus();
|
fetchPortainerStatus();
|
||||||
}, [fetchPortainerStatus]);
|
}, [fetchPortainerStatus]);
|
||||||
@@ -326,6 +366,14 @@ export function Maintenance() {
|
|||||||
fetchDuplicates();
|
fetchDuplicates();
|
||||||
}, [fetchDuplicates]);
|
}, [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 totals = useMemo(() => {
|
const totals = useMemo(() => {
|
||||||
const groups = Array.isArray(duplicates) ? duplicates.length : 0;
|
const groups = Array.isArray(duplicates) ? duplicates.length : 0;
|
||||||
const duplicateCount = Array.isArray(duplicates)
|
const duplicateCount = Array.isArray(duplicates)
|
||||||
@@ -499,6 +547,135 @@ export function Maintenance() {
|
|||||||
setSuperuserDeleteLoading(false);
|
setSuperuserDeleteLoading(false);
|
||||||
}
|
}
|
||||||
}, [superuserDeleteLoading, superuserStatusLoading, superuserExists, removeSuperuserAccount, loadSuperuserStatus, showToast]);
|
}, [superuserDeleteLoading, superuserStatusLoading, superuserExists, removeSuperuserAccount, loadSuperuserStatus, showToast]);
|
||||||
|
|
||||||
|
const handleDeleteEndpoint = useCallback(async (endpointId) => {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}, [deleteSetupEndpoint, endpointDeleteId, loadSetupResources, setupEndpoints, showToast]);
|
||||||
|
|
||||||
|
const handleDeleteServer = useCallback(async (serverId) => {
|
||||||
|
if (serverDeleteId === serverId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const target = setupServers.find((entry) => entry.id === serverId);
|
||||||
|
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."
|
||||||
|
].filter(Boolean).join("\n");
|
||||||
|
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
const confirmed = window.confirm(confirmMessage);
|
||||||
|
if (!confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setServerDeleteId(serverId);
|
||||||
|
try {
|
||||||
|
await deleteSetupServer(serverId);
|
||||||
|
showToast({
|
||||||
|
variant: "success",
|
||||||
|
title: "Server gelöscht",
|
||||||
|
description: `Server "${label}" wurde entfernt.`
|
||||||
|
});
|
||||||
|
await loadSetupResources({ silent: true });
|
||||||
|
} catch (err) {
|
||||||
|
const message = err.response?.data?.error || err.message || "Server konnte nicht gelöscht werden.";
|
||||||
|
showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: "Server löschen fehlgeschlagen",
|
||||||
|
description: message
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setServerDeleteId(null);
|
||||||
|
}
|
||||||
|
}, [deleteSetupServer, loadSetupResources, serverDeleteId, setupEndpoints, setupServers, showToast]);
|
||||||
|
|
||||||
|
const handleApiKeyDraftChange = useCallback((serverId, value) => {
|
||||||
|
setApiKeyDrafts((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[serverId]: value
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleApiKeyUpdate = useCallback(async (serverId) => {
|
||||||
|
const draft = (apiKeyDrafts[serverId] ?? "").trim();
|
||||||
|
if (!draft) {
|
||||||
|
showToast({
|
||||||
|
variant: "warning",
|
||||||
|
title: "API-Key fehlt",
|
||||||
|
description: "Bitte gib einen API-Key ein."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (apiKeyUpdatingId === serverId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setApiKeyUpdatingId(serverId);
|
||||||
|
try {
|
||||||
|
await updateSetupApiKey(serverId, draft);
|
||||||
|
showToast({
|
||||||
|
variant: "success",
|
||||||
|
title: "API-Key aktualisiert",
|
||||||
|
description: "Der API-Key wurde gespeichert."
|
||||||
|
});
|
||||||
|
setApiKeyDrafts((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[serverId]: ""
|
||||||
|
}));
|
||||||
|
await loadSetupResources({ silent: true });
|
||||||
|
} catch (err) {
|
||||||
|
const message = err.response?.data?.error || err.message || "API-Key konnte nicht aktualisiert werden.";
|
||||||
|
showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: "Aktualisierung fehlgeschlagen",
|
||||||
|
description: message
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setApiKeyUpdatingId(null);
|
||||||
|
}
|
||||||
|
}, [apiKeyDrafts, apiKeyUpdatingId, loadSetupResources, showToast, updateSetupApiKey]);
|
||||||
|
|
||||||
const handleMaintenanceToggle = useCallback(async (nextActive) => {
|
const handleMaintenanceToggle = useCallback(async (nextActive) => {
|
||||||
if (maintenanceLoading || maintenanceToggleLoading) return;
|
if (maintenanceLoading || maintenanceToggleLoading) return;
|
||||||
if (nextActive === maintenanceActive) return;
|
if (nextActive === maintenanceActive) return;
|
||||||
@@ -706,6 +883,181 @@ export function Maintenance() {
|
|||||||
{maintenanceToggleLoading && (
|
{maintenanceToggleLoading && (
|
||||||
<p className="text-xs text-stormGrey-500">Wartungsmodus wird aktualisiert…</p>
|
<p className="text-xs text-stormGrey-500">Wartungsmodus wird aktualisiert…</p>
|
||||||
)}
|
)}
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader variant="gradient" color="gray" className="mb-5 p-4">
|
||||||
|
<Typography
|
||||||
|
variant="h6"
|
||||||
|
color="white"
|
||||||
|
className="flex items-center justify-between"
|
||||||
|
>
|
||||||
|
<span>Server & Endpoints</span>
|
||||||
|
|
||||||
|
</Typography>
|
||||||
|
</CardHeader>
|
||||||
|
<CardBody className="flex flex-col gap-4 p-4">
|
||||||
|
{setupResourcesLoading && (
|
||||||
|
<p className="text-sm text-stormGrey-500">Daten werden geladen…</p>
|
||||||
|
)}
|
||||||
|
{!setupResourcesLoading && setupResourcesError && (
|
||||||
|
<Alert color="red" className="border border-red-200 bg-red-50 text-red-700">
|
||||||
|
{setupResourcesError}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
{!setupResourcesLoading && !setupResourcesError && (
|
||||||
|
<div className="flex flex-col gap-6">
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<Typography variant="small" color="blue-gray" className="font-semibold uppercase">
|
||||||
|
Server
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="small" color="blue-gray">
|
||||||
|
{setupServers.length} vorhanden
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
{setupServers.length === 0 ? (
|
||||||
|
<p className="text-sm text-stormGrey-500">
|
||||||
|
Es sind derzeit keine Server hinterlegt.
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
{setupServers.map((server) => {
|
||||||
|
const apiKeyInfo = apiKeyInfoMap.get(server.id) || null;
|
||||||
|
const hasKey = Boolean(apiKeyInfo?.hasKey);
|
||||||
|
const apiKeyUpdatedLabel = apiKeyInfo?.updatedAt
|
||||||
|
? new Date(apiKeyInfo.updatedAt).toLocaleString("de-DE", {
|
||||||
|
year: "numeric",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit"
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={server.id}
|
||||||
|
className="flex flex-col gap-3 rounded-md border border-blue-gray-100 p-3 md:flex-row md:items-start md:justify-between"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<p className="text-sm font-medium text-stormGrey-900">
|
||||||
|
{server.name || "Ohne Namen"}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-stormGrey-500 break-all">
|
||||||
|
{server.url}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-stormGrey-500">
|
||||||
|
API-Key: {hasKey ? "vorhanden" : "nicht hinterlegt"}{apiKeyUpdatedLabel ? ` (aktualisiert am ${apiKeyUpdatedLabel})` : ""}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full flex-col gap-2 md:max-w-xs">
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
label="Neuer API-Key"
|
||||||
|
value={apiKeyDrafts[server.id] ?? ""}
|
||||||
|
onChange={(event) => handleApiKeyDraftChange(server.id, event.target.value)}
|
||||||
|
disabled={setupResourcesLoading || apiKeyUpdatingId === server.id}
|
||||||
|
/>
|
||||||
|
<div className="flex items-center gap-2 justify-end">
|
||||||
|
<Button
|
||||||
|
variant="text"
|
||||||
|
color="red"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => handleDeleteServer(server.id)}
|
||||||
|
disabled={setupResourcesLoading || serverDeleteId === server.id || endpointDeleteId !== null || apiKeyUpdatingId === server.id}
|
||||||
|
>
|
||||||
|
{serverDeleteId === server.id ? "Lösche…" : "Löschen"}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="blue"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => handleApiKeyUpdate(server.id)}
|
||||||
|
disabled={setupResourcesLoading || apiKeyUpdatingId === server.id}
|
||||||
|
>
|
||||||
|
{apiKeyUpdatingId === server.id ? "Speichere…" : "API-Key speichern"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<Typography variant="small" color="blue-gray" className="font-semibold uppercase">
|
||||||
|
Endpoints
|
||||||
|
</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>
|
||||||
|
) : (
|
||||||
|
<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={setupResourcesLoading || endpointDeleteId === endpoint.id || serverDeleteId !== null}
|
||||||
|
>
|
||||||
|
{endpointDeleteId === endpoint.id ? "Lösche…" : "Löschen"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</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.
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex items-center justify-end">
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
color="gray"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => loadSetupResources()}
|
||||||
|
disabled={setupResourcesLoading}
|
||||||
|
>
|
||||||
|
Aktualisieren
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import axios from "axios";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
CardBody,
|
||||||
|
Typography,
|
||||||
|
Avatar,
|
||||||
|
Chip,
|
||||||
|
Tooltip,
|
||||||
|
Progress,
|
||||||
|
Collapse,
|
||||||
|
Button,
|
||||||
|
ButtonGroup,
|
||||||
|
Select,
|
||||||
|
Option,
|
||||||
|
Input,
|
||||||
|
useSelect
|
||||||
|
} from "@material-tailwind/react";
|
||||||
|
import { PaginationControls, usePage } from "@/components/PageProvider.jsx";
|
||||||
|
import { useMaintenance } from "@/components/MaintenanceProvider.jsx";
|
||||||
|
import { useToast } from "@/components/ToastProvider.jsx";
|
||||||
|
|
||||||
|
export function Usergroups() {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Usergroups;
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import axios from "axios";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
CardBody,
|
||||||
|
Typography,
|
||||||
|
Avatar,
|
||||||
|
Chip,
|
||||||
|
Tooltip,
|
||||||
|
Progress,
|
||||||
|
Collapse,
|
||||||
|
Button,
|
||||||
|
ButtonGroup,
|
||||||
|
Select,
|
||||||
|
Option,
|
||||||
|
Input,
|
||||||
|
useSelect
|
||||||
|
} from "@material-tailwind/react";
|
||||||
|
import { PaginationControls, usePage } from "@/components/PageProvider.jsx";
|
||||||
|
import { useMaintenance } from "@/components/MaintenanceProvider.jsx";
|
||||||
|
import { useToast } from "@/components/ToastProvider.jsx";
|
||||||
|
|
||||||
|
export function Users() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Users;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Setup as default, Setup } from "./setup";
|
||||||
@@ -0,0 +1,471 @@
|
|||||||
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
CardBody,
|
||||||
|
Typography,
|
||||||
|
Input,
|
||||||
|
Button,
|
||||||
|
Alert,
|
||||||
|
} from "@material-tailwind/react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
const initialFormState = {
|
||||||
|
superuser: {
|
||||||
|
username: "",
|
||||||
|
email: "",
|
||||||
|
password: "",
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
name: "",
|
||||||
|
url: "",
|
||||||
|
},
|
||||||
|
endpoint: {
|
||||||
|
name: "",
|
||||||
|
externalId: "",
|
||||||
|
serverId: "",
|
||||||
|
},
|
||||||
|
apiKey: ""
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Setup() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [status, setStatus] = useState(null);
|
||||||
|
const [loadingStatus, setLoadingStatus] = useState(true);
|
||||||
|
const [fetchError, setFetchError] = useState(null);
|
||||||
|
const [form, setForm] = useState(initialFormState);
|
||||||
|
const [submitting, setSubmitting] = useState(false);
|
||||||
|
const [submitError, setSubmitError] = useState(null);
|
||||||
|
const [submitSuccess, setSubmitSuccess] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let isActive = true;
|
||||||
|
const controller = new AbortController();
|
||||||
|
|
||||||
|
const loadStatus = async () => {
|
||||||
|
setLoadingStatus(true);
|
||||||
|
setFetchError(null);
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/setup/status", {
|
||||||
|
signal: controller.signal,
|
||||||
|
credentials: "include"
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("STATUS_REQUEST_FAILED");
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (!isActive) return;
|
||||||
|
|
||||||
|
setStatus(data);
|
||||||
|
|
||||||
|
if (data.setupComplete) {
|
||||||
|
navigate("/auth/sign-in", { replace: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setForm((prev) => ({
|
||||||
|
superuser: {
|
||||||
|
username: data.envDefaults?.superuserUsername?.length ? data.envDefaults.superuserUsername : prev.superuser.username,
|
||||||
|
email: data.envDefaults?.superuserEmail?.length ? data.envDefaults.superuserEmail : prev.superuser.email,
|
||||||
|
password: data.envDefaults?.superuserPassword?.length ? data.envDefaults.superuserPassword : prev.superuser.password,
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
name: data.envDefaults?.serverName || prev.server.name,
|
||||||
|
url: data.envDefaults?.serverUrl?.length ? data.envDefaults.serverUrl : prev.server.url,
|
||||||
|
},
|
||||||
|
endpoint: {
|
||||||
|
name: data.envDefaults?.endpointName || prev.endpoint.name,
|
||||||
|
externalId: data.envDefaults?.endpointExternalId?.length ? data.envDefaults.endpointExternalId : prev.endpoint.externalId,
|
||||||
|
serverId: prev.endpoint.serverId || ""
|
||||||
|
},
|
||||||
|
apiKey: data.envDefaults?.apiKeyValue?.length ? data.envDefaults.apiKeyValue : prev.apiKey
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
if (error.name === "AbortError") return;
|
||||||
|
console.error("⚠️ [Setup] Status konnte nicht geladen werden:", error);
|
||||||
|
if (isActive) {
|
||||||
|
setFetchError("Setup-Status konnte nicht geladen werden. Bitte Seite aktualisieren.");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (isActive) {
|
||||||
|
setLoadingStatus(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadStatus();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
isActive = false;
|
||||||
|
controller.abort();
|
||||||
|
};
|
||||||
|
}, [navigate]);
|
||||||
|
|
||||||
|
const envDefaults = status?.envDefaults || {};
|
||||||
|
const envSuperuserUsername = envDefaults.superuserUsername ?? "";
|
||||||
|
const envSuperuserEmail = envDefaults.superuserEmail ?? "";
|
||||||
|
const envSuperuserPassword = envDefaults.superuserPassword ?? "";
|
||||||
|
const envServerUrl = envDefaults.serverUrl ?? "";
|
||||||
|
const envEndpointExternalId = envDefaults.endpointExternalId ?? "";
|
||||||
|
const envApiKeyValue = envDefaults.apiKeyValue ?? "";
|
||||||
|
|
||||||
|
const requireSuperuser = Boolean(status?.requirements?.superuser);
|
||||||
|
const requireServer = Boolean(status?.requirements?.server);
|
||||||
|
const requireEndpoint = Boolean(status?.requirements?.endpoint);
|
||||||
|
const requireApiKey = Boolean(status?.requirements?.apiKey);
|
||||||
|
const hasAnyApiKey = Boolean(status?.apiKeys?.count);
|
||||||
|
|
||||||
|
const serverEnvProvided = Boolean(envServerUrl || status?.servers?.envProvided);
|
||||||
|
const endpointEnvProvided = Boolean(envEndpointExternalId || status?.endpoints?.envProvided);
|
||||||
|
const apiKeyEnvProvided = Boolean(envApiKeyValue || status?.apiKeys?.envProvided);
|
||||||
|
|
||||||
|
const showServerSection = requireServer || serverEnvProvided;
|
||||||
|
const showEndpointSection = requireEndpoint || endpointEnvProvided;
|
||||||
|
const showApiKeyField = requireApiKey || !hasAnyApiKey || apiKeyEnvProvided;
|
||||||
|
|
||||||
|
const superuserUsernameReadOnly = Boolean(envSuperuserUsername);
|
||||||
|
const superuserEmailReadOnly = Boolean(envSuperuserEmail);
|
||||||
|
const superuserPasswordReadOnly = Boolean(envSuperuserPassword);
|
||||||
|
const serverNameReadOnly = Boolean(envDefaults.serverNameFromEnv);
|
||||||
|
const serverUrlReadOnly = Boolean(envServerUrl);
|
||||||
|
const endpointNameReadOnly = Boolean(envDefaults.endpointNameFromEnv);
|
||||||
|
const endpointExternalIdReadOnly = Boolean(envEndpointExternalId);
|
||||||
|
const apiKeyReadOnly = Boolean(envApiKeyValue);
|
||||||
|
|
||||||
|
const handleInputChange = useCallback((section, field, { locked = false } = {}) => (event) => {
|
||||||
|
if (locked) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const value = event.target.value;
|
||||||
|
setForm((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[section]: {
|
||||||
|
...prev[section],
|
||||||
|
[field]: value
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const validationErrors = useMemo(() => {
|
||||||
|
if (!status) return [];
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
if (requireSuperuser) {
|
||||||
|
const { username, email, password } = form.superuser;
|
||||||
|
if (!username.trim()) errors.push("Benutzername ist erforderlich.");
|
||||||
|
if (!email.trim()) errors.push("E-Mail-Adresse ist erforderlich.");
|
||||||
|
if (!password.trim()) errors.push("Passwort ist erforderlich.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requireServer && !form.server.url.trim()) {
|
||||||
|
errors.push("Server-URL ist erforderlich.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requireEndpoint && !form.endpoint.externalId.trim()) {
|
||||||
|
errors.push("Endpoint-ID ist erforderlich.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showApiKeyField && requireApiKey && !form.apiKey.trim()) {
|
||||||
|
errors.push("API-Key ist erforderlich.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}, [form, requireSuperuser, requireServer, requireEndpoint, showApiKeyField, requireApiKey, status]);
|
||||||
|
|
||||||
|
const handleSubmit = useCallback(async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
if (submitting || !status) return;
|
||||||
|
|
||||||
|
setSubmitError(null);
|
||||||
|
setSubmitSuccess(false);
|
||||||
|
|
||||||
|
if (validationErrors.length) {
|
||||||
|
setSubmitError(validationErrors.join(" "));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = {};
|
||||||
|
|
||||||
|
if (requireSuperuser) {
|
||||||
|
payload.superuser = {
|
||||||
|
username: form.superuser.username.trim(),
|
||||||
|
email: form.superuser.email.trim(),
|
||||||
|
password: form.superuser.password
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showServerSection) {
|
||||||
|
const serverName = form.server.name.trim();
|
||||||
|
const serverUrl = form.server.url.trim();
|
||||||
|
if (requireServer || (!serverNameReadOnly && serverName) || (!serverUrlReadOnly && serverUrl)) {
|
||||||
|
payload.server = {
|
||||||
|
name: serverName,
|
||||||
|
url: serverUrl
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showEndpointSection) {
|
||||||
|
const endpointName = form.endpoint.name.trim();
|
||||||
|
const endpointExternalId = form.endpoint.externalId.trim();
|
||||||
|
const endpointServerId = form.endpoint.serverId ? Number(form.endpoint.serverId) : undefined;
|
||||||
|
if (requireEndpoint || (!endpointNameReadOnly && endpointName) || (!endpointExternalIdReadOnly && endpointExternalId) || endpointServerId) {
|
||||||
|
payload.endpoint = {
|
||||||
|
name: endpointName || null,
|
||||||
|
externalId: endpointExternalId || null,
|
||||||
|
serverId: endpointServerId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiKeyValue = form.apiKey.trim();
|
||||||
|
if (showApiKeyField || apiKeyValue) {
|
||||||
|
payload.apiKey = { value: apiKeyValue };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setSubmitting(true);
|
||||||
|
const response = await fetch("/api/setup/complete", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
credentials: "include",
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const payload = await response.json().catch(() => ({}));
|
||||||
|
switch (payload.error) {
|
||||||
|
case "SERVER_DETAILS_REQUIRED":
|
||||||
|
throw new Error("Bitte gib eine gültige Server-URL an.");
|
||||||
|
case "ENDPOINT_DETAILS_REQUIRED":
|
||||||
|
throw new Error("Bitte gib eine gültige Endpoint-ID an.");
|
||||||
|
case "API_KEY_REQUIRED":
|
||||||
|
throw new Error("Bitte gib einen gültigen API-Key an.");
|
||||||
|
case "USERNAME_REQUIRED":
|
||||||
|
throw new Error("Benutzername wird benötigt.");
|
||||||
|
case "EMAIL_INVALID":
|
||||||
|
throw new Error("Bitte gib eine gültige E-Mail-Adresse an.");
|
||||||
|
case "PASSWORD_TOO_SHORT":
|
||||||
|
throw new Error("Passwort muss mindestens 8 Zeichen enthalten.");
|
||||||
|
case "INVALID_PASSWORD":
|
||||||
|
throw new Error("Das Passwort ist ungültig.");
|
||||||
|
case "API_KEY_ENCRYPT_FAILED":
|
||||||
|
throw new Error("API-Key konnte nicht verschlüsselt werden. Bitte erneut versuchen.");
|
||||||
|
case "SUPERUSER_EXISTS":
|
||||||
|
throw new Error("Der Superuser wurde bereits angelegt.");
|
||||||
|
default:
|
||||||
|
throw new Error("Setup konnte nicht abgeschlossen werden. Bitte erneut versuchen.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json().catch(() => ({}));
|
||||||
|
setStatus(result.status);
|
||||||
|
setSubmitSuccess(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
navigate("/auth/sign-in", { replace: true });
|
||||||
|
}, 1500);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("⚠️ [Setup] Abschluss fehlgeschlagen:", error);
|
||||||
|
setSubmitError(error.message || "Setup fehlgeschlagen.");
|
||||||
|
} finally {
|
||||||
|
setSubmitting(false);
|
||||||
|
}
|
||||||
|
}, [form, navigate, requireSuperuser, requireServer, requireEndpoint, showServerSection, showEndpointSection, showApiKeyField, status, submitting, validationErrors, serverNameReadOnly, serverUrlReadOnly, endpointNameReadOnly, endpointExternalIdReadOnly]);
|
||||||
|
|
||||||
|
if (loadingStatus) {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen items-center justify-center bg-blue-gray-50/50">
|
||||||
|
<span className="text-blue-gray-500">Lade Setup-Status ...</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fetchError) {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen items-center justify-center bg-blue-gray-50/50">
|
||||||
|
<Alert color="red" className="w-full max-w-lg border border-red-200 bg-red-50 text-red-700">
|
||||||
|
{fetchError}
|
||||||
|
</Alert>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!status) {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen items-center justify-center bg-blue-gray-50/50">
|
||||||
|
<span className="text-blue-gray-500">Keine Setup-Informationen verfügbar.</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const superuserSection = requireSuperuser ? (
|
||||||
|
<>
|
||||||
|
<Typography variant="h6" color="blue-gray" className="font-semibold">
|
||||||
|
Superuser
|
||||||
|
</Typography>
|
||||||
|
<div className="grid grid-cols-1 gap-4">
|
||||||
|
<Input
|
||||||
|
label="Benutzername"
|
||||||
|
required
|
||||||
|
value={form.superuser.username}
|
||||||
|
onChange={handleInputChange("superuser", "username", { locked: superuserUsernameReadOnly })}
|
||||||
|
disabled={submitting}
|
||||||
|
readOnly={superuserUsernameReadOnly}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
label="E-Mail-Adresse"
|
||||||
|
required
|
||||||
|
type="email"
|
||||||
|
value={form.superuser.email}
|
||||||
|
onChange={handleInputChange("superuser", "email", { locked: superuserEmailReadOnly })}
|
||||||
|
disabled={submitting}
|
||||||
|
readOnly={superuserEmailReadOnly}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
label="Passwort"
|
||||||
|
required
|
||||||
|
type="password"
|
||||||
|
value={form.superuser.password}
|
||||||
|
onChange={handleInputChange("superuser", "password", { locked: superuserPasswordReadOnly })}
|
||||||
|
disabled={submitting}
|
||||||
|
readOnly={superuserPasswordReadOnly}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Alert color="green" className="border border-green-200 bg-green-50 text-green-700">
|
||||||
|
Superuser ist bereits vorhanden.
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
|
||||||
|
const serverSection = showServerSection ? (
|
||||||
|
<>
|
||||||
|
<Typography variant="h6" color="blue-gray" className="mt-6 font-semibold">
|
||||||
|
Server
|
||||||
|
</Typography>
|
||||||
|
<div className="grid grid-cols-1 gap-4">
|
||||||
|
<Input
|
||||||
|
label="Server-Name"
|
||||||
|
value={form.server.name}
|
||||||
|
onChange={handleInputChange("server", "name", { locked: serverNameReadOnly })}
|
||||||
|
readOnly={serverNameReadOnly}
|
||||||
|
disabled={submitting}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
label="Server-URL"
|
||||||
|
required={requireServer}
|
||||||
|
value={form.server.url}
|
||||||
|
onChange={handleInputChange("server", "url", { locked: serverUrlReadOnly })}
|
||||||
|
readOnly={serverUrlReadOnly}
|
||||||
|
disabled={submitting}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
const endpointSection = showEndpointSection ? (
|
||||||
|
<>
|
||||||
|
<Typography variant="h6" color="blue-gray" className="mt-6 font-semibold">
|
||||||
|
Endpoint
|
||||||
|
</Typography>
|
||||||
|
<div className="grid grid-cols-1 gap-4">
|
||||||
|
<Input
|
||||||
|
label="Endpoint-Name"
|
||||||
|
value={form.endpoint.name}
|
||||||
|
onChange={handleInputChange("endpoint", "name", { locked: endpointNameReadOnly })}
|
||||||
|
readOnly={endpointNameReadOnly}
|
||||||
|
disabled={submitting}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
label="Endpoint-ID"
|
||||||
|
required={requireEndpoint}
|
||||||
|
value={form.endpoint.externalId}
|
||||||
|
onChange={handleInputChange("endpoint", "externalId", { locked: endpointExternalIdReadOnly })}
|
||||||
|
readOnly={endpointExternalIdReadOnly}
|
||||||
|
disabled={submitting}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
const apiKeySection = showApiKeyField ? (
|
||||||
|
<>
|
||||||
|
<Typography variant="h6" color="blue-gray" className="mt-6 font-semibold">
|
||||||
|
API-Key
|
||||||
|
</Typography>
|
||||||
|
<div className="grid grid-cols-1 gap-4">
|
||||||
|
<Input
|
||||||
|
label="API-Key"
|
||||||
|
required={requireApiKey}
|
||||||
|
type="password"
|
||||||
|
value={form.apiKey}
|
||||||
|
onChange={(event) => {
|
||||||
|
if (apiKeyReadOnly) return;
|
||||||
|
setForm((prev) => ({ ...prev, apiKey: event.target.value }));
|
||||||
|
}}
|
||||||
|
readOnly={apiKeyReadOnly}
|
||||||
|
disabled={submitting}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
const showEnvHint = Boolean(
|
||||||
|
status?.superuser?.envProvided ||
|
||||||
|
serverEnvProvided ||
|
||||||
|
endpointEnvProvided ||
|
||||||
|
apiKeyEnvProvided
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen w-full items-center justify-center py-10">
|
||||||
|
<Card className="w-full max-w-lg border border-blue-gray-100 shadow-sm">
|
||||||
|
<CardHeader
|
||||||
|
floated={false}
|
||||||
|
shadow={false}
|
||||||
|
className="mb-0 grid place-items-start gap-2 rounded-none bg-transparent p-6"
|
||||||
|
>
|
||||||
|
<Typography variant="h4" color="blue-gray">
|
||||||
|
System Setup
|
||||||
|
</Typography>
|
||||||
|
<Typography color="gray" className="font-normal">
|
||||||
|
Lege einen Superuser sowie mindestens einen Server mit Endpoint und API-Key fest, um StackPulse zu starten.
|
||||||
|
</Typography>
|
||||||
|
</CardHeader>
|
||||||
|
<CardBody className="pt-0">
|
||||||
|
<form className="mt-4 flex flex-col gap-6" onSubmit={handleSubmit}>
|
||||||
|
{showEnvHint && (
|
||||||
|
<Alert color="blue" className="border border-blue-200 bg-blue-50 text-blue-700">
|
||||||
|
Teile der Konfiguration stammen bereits aus den Umgebungsvariablen.
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{superuserSection}
|
||||||
|
{serverSection}
|
||||||
|
{endpointSection}
|
||||||
|
{apiKeySection}
|
||||||
|
|
||||||
|
{submitError && (
|
||||||
|
<Alert color="red" className="border border-red-200 bg-red-50 text-red-700">
|
||||||
|
{submitError}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{submitSuccess && (
|
||||||
|
<Alert color="green" className="border border-green-200 bg-green-50 text-green-700">
|
||||||
|
Setup abgeschlossen. Du wirst zum Login weitergeleitet ...
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Button type="submit" color="blue" disabled={submitting || submitSuccess}>
|
||||||
|
{submitting ? "Setup läuft ..." : "Setup abschließen"}
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Setup;
|
||||||
+16
-2
@@ -3,9 +3,11 @@ import {
|
|||||||
WrenchScrewdriverIcon,
|
WrenchScrewdriverIcon,
|
||||||
ListBulletIcon,
|
ListBulletIcon,
|
||||||
ServerStackIcon,
|
ServerStackIcon,
|
||||||
RectangleStackIcon
|
RectangleStackIcon,
|
||||||
|
UserIcon,
|
||||||
|
UserGroupIcon
|
||||||
} from "@heroicons/react/24/solid";
|
} from "@heroicons/react/24/solid";
|
||||||
import { Stacks, Maintenance, Logs } from "@/pages/dashboard";
|
import { Stacks, Maintenance, Logs, Users, Usergroups } from "@/pages/dashboard";
|
||||||
import { SignIn, SignUp } from "@/pages/auth";
|
import { SignIn, SignUp } from "@/pages/auth";
|
||||||
|
|
||||||
const icon = {
|
const icon = {
|
||||||
@@ -34,6 +36,18 @@ export const routes = [
|
|||||||
path: "/logs",
|
path: "/logs",
|
||||||
element: <Logs />,
|
element: <Logs />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: <UserIcon {...icon} />,
|
||||||
|
name: "benutzer",
|
||||||
|
path: "/users",
|
||||||
|
element: <Users />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <UserGroupIcon {...icon} />,
|
||||||
|
name: "rechtegruppen",
|
||||||
|
path: "/usergroups",
|
||||||
|
element: <Usergroups />,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in new issue
Block a user