SSH Dbs
This commit is contained in:
@@ -35,3 +35,6 @@ dist-ssr
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
/site/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
@@ -185,6 +185,32 @@ CREATE TABLE server_api_keys (
|
|||||||
FOREIGN KEY (server_id) REFERENCES servers(id) ON DELETE CASCADE
|
FOREIGN KEY (server_id) REFERENCES servers(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE server_ssh_configs (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
server_id INTEGER NOT NULL UNIQUE,
|
||||||
|
host TEXT NOT NULL,
|
||||||
|
port INTEGER NOT NULL DEFAULT 22,
|
||||||
|
username TEXT NOT NULL,
|
||||||
|
password_cipher TEXT,
|
||||||
|
password_iv TEXT,
|
||||||
|
password_tag TEXT,
|
||||||
|
extra_args TEXT,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (server_id) REFERENCES servers(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX idx_server_ssh_configs_server ON server_ssh_configs (server_id);
|
||||||
|
|
||||||
|
CREATE TABLE server_update_scripts (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
server_id INTEGER NOT NULL UNIQUE,
|
||||||
|
custom_script TEXT,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (server_id) REFERENCES servers(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX idx_server_update_scripts_server ON server_update_scripts (server_id);
|
||||||
|
|
||||||
CREATE TABLE user_settings (
|
CREATE TABLE user_settings (
|
||||||
user_id INTEGER NOT NULL,
|
user_id INTEGER NOT NULL,
|
||||||
setting_key TEXT NOT NULL,
|
setting_key TEXT NOT NULL,
|
||||||
|
|||||||
+288
-68
@@ -633,8 +633,39 @@ const applySelfStackStatus = (status) => {
|
|||||||
|
|
||||||
const buildSetupStatusResponse = () => applySelfStackStatus(getSetupStatus());
|
const buildSetupStatusResponse = () => applySelfStackStatus(getSetupStatus());
|
||||||
|
|
||||||
const PORTAINER_SCRIPT_SETTING_KEY = 'portainer_update_script';
|
const LEGACY_PORTAINER_SCRIPT_SETTING_KEY = 'portainer_update_script';
|
||||||
const PORTAINER_SSH_CONFIG_KEY = 'portainer_ssh_config';
|
const LEGACY_PORTAINER_SSH_CONFIG_KEY = 'portainer_ssh_config';
|
||||||
|
|
||||||
|
const selectPrimaryServerIdStmt = db.prepare('SELECT id FROM servers ORDER BY id ASC LIMIT 1');
|
||||||
|
const selectServerSshConfigByServerStmt = db.prepare('SELECT * FROM server_ssh_configs WHERE server_id = ?');
|
||||||
|
const selectAnyServerSshConfigStmt = db.prepare('SELECT * FROM server_ssh_configs ORDER BY server_id ASC LIMIT 1');
|
||||||
|
const upsertServerSshConfigStmt = db.prepare(`
|
||||||
|
INSERT INTO server_ssh_configs (server_id, host, port, username, password_cipher, password_iv, password_tag, extra_args, created_at, updated_at)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||||
|
ON CONFLICT(server_id) DO UPDATE SET
|
||||||
|
host = excluded.host,
|
||||||
|
port = excluded.port,
|
||||||
|
username = excluded.username,
|
||||||
|
password_cipher = excluded.password_cipher,
|
||||||
|
password_iv = excluded.password_iv,
|
||||||
|
password_tag = excluded.password_tag,
|
||||||
|
extra_args = excluded.extra_args,
|
||||||
|
updated_at = CURRENT_TIMESTAMP
|
||||||
|
`);
|
||||||
|
const deleteServerSshConfigStmt = db.prepare('DELETE FROM server_ssh_configs WHERE server_id = ?');
|
||||||
|
const deleteAllServerSshConfigsStmt = db.prepare('DELETE FROM server_ssh_configs');
|
||||||
|
|
||||||
|
const selectServerUpdateScriptStmt = db.prepare('SELECT * FROM server_update_scripts WHERE server_id = ?');
|
||||||
|
const selectAnyServerUpdateScriptStmt = db.prepare('SELECT * FROM server_update_scripts ORDER BY server_id ASC LIMIT 1');
|
||||||
|
const upsertServerUpdateScriptStmt = db.prepare(`
|
||||||
|
INSERT INTO server_update_scripts (server_id, custom_script, created_at, updated_at)
|
||||||
|
VALUES (?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||||
|
ON CONFLICT(server_id) DO UPDATE SET
|
||||||
|
custom_script = excluded.custom_script,
|
||||||
|
updated_at = CURRENT_TIMESTAMP
|
||||||
|
`);
|
||||||
|
const deleteServerUpdateScriptStmt = db.prepare('DELETE FROM server_update_scripts WHERE server_id = ?');
|
||||||
|
const deleteAllServerUpdateScriptsStmt = db.prepare('DELETE FROM server_update_scripts');
|
||||||
|
|
||||||
const DEFAULT_PORTAINER_UPDATE_SCRIPT = [
|
const DEFAULT_PORTAINER_UPDATE_SCRIPT = [
|
||||||
'docker stop portainer',
|
'docker stop portainer',
|
||||||
@@ -682,26 +713,88 @@ const getPortainerUpdateStatus = () => ({
|
|||||||
...portainerUpdateState
|
...portainerUpdateState
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const normalizeScriptText = (script) => String(script ?? '').replace(/\r\n/g, '\n');
|
||||||
|
|
||||||
|
let legacyUpdateScriptMigrated = false;
|
||||||
|
|
||||||
|
const migrateLegacyUpdateScript = (serverIdHint = null) => {
|
||||||
|
if (legacyUpdateScriptMigrated) return;
|
||||||
|
|
||||||
|
const stored = getSetting(LEGACY_PORTAINER_SCRIPT_SETTING_KEY);
|
||||||
|
if (!stored) {
|
||||||
|
legacyUpdateScriptMigrated = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetServerId = serverIdHint ?? resolvePrimaryServerId();
|
||||||
|
if (!targetServerId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const raw = typeof stored.value === 'string' ? stored.value : '';
|
||||||
|
const normalized = normalizeScriptText(raw);
|
||||||
|
if (!normalized.trim()) {
|
||||||
|
deleteSetting(LEGACY_PORTAINER_SCRIPT_SETTING_KEY);
|
||||||
|
legacyUpdateScriptMigrated = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
upsertServerUpdateScriptStmt.run(targetServerId, normalized);
|
||||||
|
deleteSetting(LEGACY_PORTAINER_SCRIPT_SETTING_KEY);
|
||||||
|
legacyUpdateScriptMigrated = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveStoredUpdateScriptRow = () => {
|
||||||
|
const primaryServerId = resolvePrimaryServerId();
|
||||||
|
migrateLegacyUpdateScript(primaryServerId);
|
||||||
|
|
||||||
|
if (primaryServerId) {
|
||||||
|
const row = selectServerUpdateScriptStmt.get(primaryServerId);
|
||||||
|
if (row) return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectAnyServerUpdateScriptStmt.get() ?? null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveTargetServerIdForUpdateScript = () => {
|
||||||
|
const primaryId = resolvePrimaryServerId();
|
||||||
|
if (primaryId) return primaryId;
|
||||||
|
const fallback = selectAnyServerUpdateScriptStmt.get();
|
||||||
|
return fallback?.server_id ?? null;
|
||||||
|
};
|
||||||
|
|
||||||
const getCustomPortainerScript = () => {
|
const getCustomPortainerScript = () => {
|
||||||
const row = getSetting(PORTAINER_SCRIPT_SETTING_KEY);
|
const row = resolveStoredUpdateScriptRow();
|
||||||
if (!row) return null;
|
if (!row) return null;
|
||||||
const raw = typeof row.value === 'string' ? row.value : '';
|
const normalized = normalizeScriptText(row.custom_script ?? '');
|
||||||
if (!raw.trim()) return null;
|
if (!normalized.trim()) return null;
|
||||||
return {
|
return {
|
||||||
script: normalizeScriptText(raw),
|
script: normalized,
|
||||||
updatedAt: row.updated_at || null
|
updatedAt: row.updated_at || null
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const normalizeScriptText = (script) => String(script ?? '').replace(/\r\n/g, '\n');
|
|
||||||
|
|
||||||
const saveCustomPortainerScript = (script) => {
|
const saveCustomPortainerScript = (script) => {
|
||||||
const normalized = normalizeScriptText(script);
|
const normalized = normalizeScriptText(script);
|
||||||
if (!normalized.trim()) {
|
const trimmed = normalized.trim();
|
||||||
deleteSetting(PORTAINER_SCRIPT_SETTING_KEY);
|
const serverId = resolveTargetServerIdForUpdateScript();
|
||||||
|
|
||||||
|
if (!trimmed) {
|
||||||
|
if (serverId) {
|
||||||
|
deleteServerUpdateScriptStmt.run(serverId);
|
||||||
|
} else {
|
||||||
|
deleteAllServerUpdateScriptsStmt.run();
|
||||||
|
}
|
||||||
|
deleteSetting(LEGACY_PORTAINER_SCRIPT_SETTING_KEY);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
setSetting(PORTAINER_SCRIPT_SETTING_KEY, normalized);
|
|
||||||
|
if (!serverId) {
|
||||||
|
throw new Error('SERVER_NOT_CONFIGURED');
|
||||||
|
}
|
||||||
|
|
||||||
|
upsertServerUpdateScriptStmt.run(serverId, normalized);
|
||||||
|
deleteSetting(LEGACY_PORTAINER_SCRIPT_SETTING_KEY);
|
||||||
return normalized;
|
return normalized;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -763,6 +856,24 @@ const DEFAULT_PORTAINER_SSH_CONFIG = {
|
|||||||
extraSshArgs: []
|
extraSshArgs: []
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deserializeSshExtraArgs = (value) => {
|
||||||
|
if (!value) return [];
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(value);
|
||||||
|
if (Array.isArray(parsed)) {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
if (typeof parsed === 'string') {
|
||||||
|
return parsed.split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const serializeSshExtraArgs = (value) => JSON.stringify(normalizeExtraArgs(value));
|
||||||
|
|
||||||
const normalizeString = (value, fallback = '') => {
|
const normalizeString = (value, fallback = '') => {
|
||||||
if (value === undefined || value === null) return fallback;
|
if (value === undefined || value === null) return fallback;
|
||||||
return String(value).trim();
|
return String(value).trim();
|
||||||
@@ -809,37 +920,119 @@ const normalizePassword = (value, fallback = DEFAULT_PORTAINER_SSH_CONFIG.passwo
|
|||||||
return String(value);
|
return String(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPortainerSshConfig = () => {
|
const resolvePrimaryServerId = () => {
|
||||||
const stored = getSetting(PORTAINER_SSH_CONFIG_KEY);
|
const row = selectPrimaryServerIdStmt.get();
|
||||||
if (!stored) {
|
return row?.id ?? null;
|
||||||
return { ...DEFAULT_PORTAINER_SSH_CONFIG };
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const parsed = stored.value ? JSON.parse(stored.value) : {};
|
|
||||||
const decryptedPassword = parsed.passwordEncrypted ? decryptSensitive(parsed.passwordEncrypted) : null;
|
|
||||||
const fallbackPassword = parsed.password !== undefined ? parsed.password : null;
|
|
||||||
return {
|
|
||||||
host: normalizeString(parsed.host),
|
|
||||||
port: normalizePort(parsed.port),
|
|
||||||
username: normalizeString(parsed.username),
|
|
||||||
password: normalizePassword(decryptedPassword ?? fallbackPassword),
|
|
||||||
extraSshArgs: normalizeExtraArgs(parsed.extraSshArgs)
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
console.warn('⚠️ [Maintenance] Konnte Portainer SSH Konfiguration nicht parsen:', err.message);
|
|
||||||
return { ...DEFAULT_PORTAINER_SSH_CONFIG };
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const persistPortainerSshConfig = (config) => {
|
let legacySshConfigMigrated = false;
|
||||||
const payload = {
|
|
||||||
host: config.host,
|
const migrateLegacySshConfig = (serverIdHint = null) => {
|
||||||
port: config.port,
|
if (legacySshConfigMigrated) return;
|
||||||
username: config.username,
|
|
||||||
extraSshArgs: config.extraSshArgs,
|
const stored = getSetting(LEGACY_PORTAINER_SSH_CONFIG_KEY);
|
||||||
passwordEncrypted: config.password ? encryptSensitive(config.password) : null
|
if (!stored) {
|
||||||
|
legacySshConfigMigrated = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetServerId = serverIdHint ?? resolvePrimaryServerId();
|
||||||
|
if (!targetServerId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let parsed = {};
|
||||||
|
try {
|
||||||
|
parsed = stored.value ? JSON.parse(stored.value) : {};
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('⚠️ [Maintenance] Konnte Legacy SSH Konfiguration nicht parsen:', err.message);
|
||||||
|
legacySshConfigMigrated = true;
|
||||||
|
deleteSetting(LEGACY_PORTAINER_SSH_CONFIG_KEY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const decryptedPassword = parsed?.passwordEncrypted
|
||||||
|
? decryptSensitive(parsed.passwordEncrypted)
|
||||||
|
: parsed?.password ?? null;
|
||||||
|
|
||||||
|
const normalizedPayload = {
|
||||||
|
host: normalizeString(parsed?.host),
|
||||||
|
port: normalizePort(parsed?.port),
|
||||||
|
username: normalizeString(parsed?.username),
|
||||||
|
password: normalizePassword(decryptedPassword),
|
||||||
|
extraSshArgs: normalizeExtraArgs(parsed?.extraSshArgs)
|
||||||
|
};
|
||||||
|
|
||||||
|
const encryptedPassword = normalizedPayload.password ? encryptSensitive(normalizedPayload.password) : null;
|
||||||
|
|
||||||
|
upsertServerSshConfigStmt.run(
|
||||||
|
targetServerId,
|
||||||
|
normalizedPayload.host,
|
||||||
|
normalizedPayload.port,
|
||||||
|
normalizedPayload.username,
|
||||||
|
encryptedPassword?.content ?? null,
|
||||||
|
encryptedPassword?.iv ?? null,
|
||||||
|
encryptedPassword?.tag ?? null,
|
||||||
|
serializeSshExtraArgs(normalizedPayload.extraSshArgs)
|
||||||
|
);
|
||||||
|
|
||||||
|
deleteSetting(LEGACY_PORTAINER_SSH_CONFIG_KEY);
|
||||||
|
legacySshConfigMigrated = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveStoredSshConfigRow = () => {
|
||||||
|
const primaryServerId = resolvePrimaryServerId();
|
||||||
|
migrateLegacySshConfig(primaryServerId);
|
||||||
|
|
||||||
|
if (primaryServerId) {
|
||||||
|
const row = selectServerSshConfigByServerStmt.get(primaryServerId);
|
||||||
|
if (row) return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectAnyServerSshConfigStmt.get() ?? null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const persistPortainerSshConfig = (serverId, config) => {
|
||||||
|
if (!serverId) {
|
||||||
|
throw new Error('SSH_SERVER_NOT_AVAILABLE');
|
||||||
|
}
|
||||||
|
|
||||||
|
const encryptedPassword = config.password ? encryptSensitive(config.password) : null;
|
||||||
|
upsertServerSshConfigStmt.run(
|
||||||
|
serverId,
|
||||||
|
normalizeString(config.host),
|
||||||
|
normalizePort(config.port),
|
||||||
|
normalizeString(config.username),
|
||||||
|
encryptedPassword?.content ?? null,
|
||||||
|
encryptedPassword?.iv ?? null,
|
||||||
|
encryptedPassword?.tag ?? null,
|
||||||
|
serializeSshExtraArgs(config.extraSshArgs)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPortainerSshConfig = () => {
|
||||||
|
const row = resolveStoredSshConfigRow();
|
||||||
|
if (!row) {
|
||||||
|
return { ...DEFAULT_PORTAINER_SSH_CONFIG };
|
||||||
|
}
|
||||||
|
|
||||||
|
const passwordPayload = row.password_cipher
|
||||||
|
? {
|
||||||
|
content: row.password_cipher,
|
||||||
|
iv: row.password_iv,
|
||||||
|
tag: row.password_tag
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const decryptedPassword = passwordPayload ? decryptSensitive(passwordPayload) : '';
|
||||||
|
|
||||||
|
return {
|
||||||
|
host: normalizeString(row.host),
|
||||||
|
port: normalizePort(row.port),
|
||||||
|
username: normalizeString(row.username),
|
||||||
|
password: normalizePassword(decryptedPassword),
|
||||||
|
extraSshArgs: normalizeExtraArgs(deserializeSshExtraArgs(row.extra_args))
|
||||||
};
|
};
|
||||||
setSetting(PORTAINER_SSH_CONFIG_KEY, JSON.stringify(payload));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const mergeSshConfig = (base, overrides = {}) => ({
|
const mergeSshConfig = (base, overrides = {}) => ({
|
||||||
@@ -850,15 +1043,32 @@ const mergeSshConfig = (base, overrides = {}) => ({
|
|||||||
extraSshArgs: normalizeExtraArgs(overrides.extraSshArgs, base.extraSshArgs)
|
extraSshArgs: normalizeExtraArgs(overrides.extraSshArgs, base.extraSshArgs)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const resolveTargetServerIdForSshConfig = () => {
|
||||||
|
const primaryId = resolvePrimaryServerId();
|
||||||
|
if (primaryId) return primaryId;
|
||||||
|
const fallback = selectAnyServerSshConfigStmt.get();
|
||||||
|
return fallback?.server_id ?? null;
|
||||||
|
};
|
||||||
|
|
||||||
const savePortainerSshConfig = (payload = {}) => {
|
const savePortainerSshConfig = (payload = {}) => {
|
||||||
const current = getPortainerSshConfig();
|
const current = getPortainerSshConfig();
|
||||||
const next = mergeSshConfig(current, payload);
|
const next = mergeSshConfig(current, payload);
|
||||||
persistPortainerSshConfig(next);
|
const serverId = resolveTargetServerIdForSshConfig();
|
||||||
|
if (!serverId) {
|
||||||
|
throw new Error('SERVER_NOT_CONFIGURED');
|
||||||
|
}
|
||||||
|
persistPortainerSshConfig(serverId, next);
|
||||||
return next;
|
return next;
|
||||||
};
|
};
|
||||||
|
|
||||||
const deletePortainerSshConfig = () => {
|
const deletePortainerSshConfig = () => {
|
||||||
deleteSetting(PORTAINER_SSH_CONFIG_KEY);
|
const serverId = resolveTargetServerIdForSshConfig();
|
||||||
|
if (serverId) {
|
||||||
|
deleteServerSshConfigStmt.run(serverId);
|
||||||
|
} else {
|
||||||
|
deleteAllServerSshConfigsStmt.run();
|
||||||
|
}
|
||||||
|
deleteSetting(LEGACY_PORTAINER_SSH_CONFIG_KEY);
|
||||||
return { ...DEFAULT_PORTAINER_SSH_CONFIG };
|
return { ...DEFAULT_PORTAINER_SSH_CONFIG };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2937,22 +3147,27 @@ app.put('/api/maintenance/update-script', requirePermission('maintenance-ssh-upd
|
|||||||
return res.status(400).json({ error: 'Feld "script" (string) wird benötigt.' });
|
return res.status(400).json({ error: 'Feld "script" (string) wird benötigt.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
saveCustomPortainerScript(incoming);
|
try {
|
||||||
|
saveCustomPortainerScript(incoming);
|
||||||
|
|
||||||
const custom = getCustomPortainerScript();
|
const custom = getCustomPortainerScript();
|
||||||
const effective = getEffectivePortainerScript();
|
const effective = getEffectivePortainerScript();
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
script: {
|
script: {
|
||||||
default: DEFAULT_PORTAINER_UPDATE_SCRIPT,
|
default: DEFAULT_PORTAINER_UPDATE_SCRIPT,
|
||||||
custom: custom?.script ?? null,
|
custom: custom?.script ?? null,
|
||||||
customUpdatedAt: custom?.updatedAt ?? null,
|
customUpdatedAt: custom?.updatedAt ?? null,
|
||||||
effective: effective.script,
|
effective: effective.script,
|
||||||
source: effective.source,
|
source: effective.source,
|
||||||
updatedAt: effective.updatedAt
|
updatedAt: effective.updatedAt
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('❌ [Maintenance] Fehler beim Speichern des Update-Skripts:', err.message);
|
||||||
|
res.status(400).json({ error: err.message || 'Ungültiges Update-Skript' });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.delete('/api/maintenance/update-script', requirePermission('maintenance-ssh-update', 'full'), (req, res) => {
|
app.delete('/api/maintenance/update-script', requirePermission('maintenance-ssh-update', 'full'), (req, res) => {
|
||||||
@@ -2963,20 +3178,25 @@ app.delete('/api/maintenance/update-script', requirePermission('maintenance-ssh-
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
saveCustomPortainerScript('');
|
try {
|
||||||
const effective = getEffectivePortainerScript();
|
saveCustomPortainerScript('');
|
||||||
|
const effective = getEffectivePortainerScript();
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
script: {
|
script: {
|
||||||
default: DEFAULT_PORTAINER_UPDATE_SCRIPT,
|
default: DEFAULT_PORTAINER_UPDATE_SCRIPT,
|
||||||
custom: null,
|
custom: null,
|
||||||
customUpdatedAt: null,
|
customUpdatedAt: null,
|
||||||
effective: effective.script,
|
effective: effective.script,
|
||||||
source: effective.source,
|
source: effective.source,
|
||||||
updatedAt: effective.updatedAt
|
updatedAt: effective.updatedAt
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('❌ [Maintenance] Fehler beim Zurücksetzen des Update-Skripts:', err.message);
|
||||||
|
res.status(400).json({ error: err.message || 'Update-Skript konnte nicht entfernt werden' });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/api/maintenance/update-status', requirePermission('maintenance-update', 'read'), (req, res) => {
|
app.get('/api/maintenance/update-status', requirePermission('maintenance-update', 'read'), (req, res) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user