mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-26 11:49:16 +00:00
c6d1631afe
* feat(recovery): add safe-mode recovery surface and emergency CLI Add a read-only Recovery tab under Settings (admin-only) backed by a new GET /api/diagnostics endpoint reporting app version, database integrity, encryption-key status, Docker reachability, account and SSO counts, and non-secret configuration. The endpoint loads without Docker or live metrics so it stays available when the dashboard does not, requires a genuine admin session, and builds its config block from a non-secret allowlist so no credentials are ever exposed. Expand the emergency command-line toolkit beyond the two-factor reset with seven host-level commands: reset-password, create-emergency-admin, clear-sessions, disable-sso, diagnostics, validate-db, and backup-data. Each prints its result, exits with a meaningful status code, and writes an audit entry where it changes state. Document the toolkit in a new operator guide and link it from the recovery and two-factor pages. * feat(recovery): download the emergency command reference as a text file The recovery commands are needed exactly when the dashboard is unreachable, so reading them only in-app is a chicken-and-egg problem. Add a Download button to the command-line section that saves the full `docker compose exec sencho ...` reference as a text file, letting operators keep it on hand before they need it. Reuses a shared download helper with the existing diagnostics export. * fix(recovery): harden diagnostics, backup, and emergency-admin against edge cases Address findings from an independent review of the recovery toolkit: - DiagnosticsService now degrades instead of throwing when a queried table is missing or corrupt: each read falls back and is folded into database.ok, so a broken database reports "problem detected" rather than failing the whole endpoint or showing a misleading healthy state with zeroed counts. - backup-data refuses a destination that resolves to the live database, which would otherwise report success while producing no separate copy. - create-emergency-admin now applies the same username rule as the user- management route, extracted to a shared helper so both stay in sync. Adds tests for a missing read table, a malformed emergency-admin username, and the backup same-target rejection.
51 lines
2.0 KiB
TypeScript
51 lines
2.0 KiB
TypeScript
/**
|
|
* Emergency CLI: disable a broken SSO/OIDC/LDAP provider so local password
|
|
* sign-in is reachable again. Used when a misconfigured identity provider
|
|
* blocks the login screen.
|
|
*
|
|
* Run via:
|
|
* docker compose exec sencho node dist/cli/disableSso.js [provider]
|
|
*
|
|
* With no argument it disables every enabled provider. The stored configuration
|
|
* is preserved (only the enabled flag is cleared) so it can be fixed and
|
|
* re-enabled from the UI. Written to the audit log with actor `cli`.
|
|
*/
|
|
import { DatabaseService } from '../services/DatabaseService';
|
|
import { auditCli, exitWith, type CliResult } from './_shared';
|
|
|
|
export function disableSso(provider?: string): CliResult {
|
|
const db = DatabaseService.getInstance();
|
|
|
|
if (provider) {
|
|
const config = db.getSSOConfig(provider);
|
|
if (!config) {
|
|
return { ok: false, message: `No SSO config found for provider: ${provider}` };
|
|
}
|
|
if (config.enabled !== 1) {
|
|
return { ok: true, message: `SSO provider ${provider} is already disabled.` };
|
|
}
|
|
db.upsertSSOConfig(provider, false, config.config_json);
|
|
auditCli(db, `/cli/disable-sso/${provider}`, `CLI disabled SSO provider ${provider}`);
|
|
return { ok: true, message: `Disabled SSO provider ${provider}. Its configuration was preserved.` };
|
|
}
|
|
|
|
const enabled = db.getEnabledSSOConfigs();
|
|
if (enabled.length === 0) {
|
|
return { ok: true, message: 'No SSO providers are currently enabled.' };
|
|
}
|
|
for (const config of enabled) {
|
|
db.upsertSSOConfig(config.provider, false, config.config_json);
|
|
}
|
|
const names = enabled.map(c => c.provider).join(', ');
|
|
auditCli(db, '/cli/disable-sso', `CLI disabled all SSO providers (${enabled.length})`);
|
|
return { ok: true, message: `Disabled ${enabled.length} SSO provider(s): ${names}. Configurations were preserved.` };
|
|
}
|
|
|
|
function main(): void {
|
|
exitWith(disableSso(process.argv[2]));
|
|
}
|
|
|
|
if (require.main === module) {
|
|
main();
|
|
}
|