feat(opsserver-admin): add persisted admin bootstrap flow with optional idp.global authentication

This commit is contained in:
2026-05-14 00:30:09 +00:00
parent 47a1f5d7db
commit 70fcd46d52
14 changed files with 733 additions and 40 deletions
+100
View File
@@ -66,6 +66,9 @@ export class OpsDashboard extends DeesElement {
isLoggedIn: false,
};
private bootstrapStepper?: any;
private bootstrapCheckPromise?: Promise<void>;
@state() accessor uiState: appstate.IUiState = {
activeView: 'overview',
activeSubview: null,
@@ -336,6 +339,7 @@ export class OpsDashboard extends DeesElement {
await (simpleLogin as any).switchToSlottedContent();
await appstate.statsStatePart.dispatchAction(appstate.fetchAllStatsAction, null);
await appstate.configStatePart.dispatchAction(appstate.fetchConfigurationAction, null);
await this.ensureAdminBootstrap();
} else {
// Server rejected the JWT — clear state, show login
await appstate.loginStatePart.dispatchAction(appstate.logoutAction, null);
@@ -370,10 +374,106 @@ export class OpsDashboard extends DeesElement {
await simpleLogin!.switchToSlottedContent();
await appstate.statsStatePart.dispatchAction(appstate.fetchAllStatsAction, null);
await appstate.configStatePart.dispatchAction(appstate.fetchConfigurationAction, null);
await this.ensureAdminBootstrap();
} else {
form!.setStatus('error', 'Login failed!');
await domtools.convenience.smartdelay.delayFor(2000);
form!.reset();
}
}
private async ensureAdminBootstrap(): Promise<void> {
if (!this.loginState.identity || this.bootstrapStepper?.isConnected) {
return;
}
if (this.bootstrapCheckPromise) {
return this.bootstrapCheckPromise;
}
this.bootstrapCheckPromise = (async () => {
try {
const status = await appstate.getAdminBootstrapStatus();
if (status.needsBootstrap) {
await this.showAdminBootstrapStepper(status);
}
} catch (error) {
console.error('Admin bootstrap status check failed:', error);
} finally {
this.bootstrapCheckPromise = undefined;
}
})();
return this.bootstrapCheckPromise;
}
private async showAdminBootstrapStepper(statusArg: appstate.IAdminBootstrapStatus): Promise<void> {
const { DeesStepper } = await import('@design.estate/dees-catalog');
this.bootstrapStepper = await DeesStepper.createAndShow({
cancelable: false,
steps: [
{
title: 'Create Persisted Admin',
content: html`
<div style="display: grid; gap: 16px; color: var(--dees-color-text-secondary); font-size: 14px; line-height: 1.5;">
<p style="margin: 0;">
This router is currently using the temporary bootstrap admin. Create the first persisted admin account to continue.
</p>
<dees-form>
<dees-input-text .key=${'email'} .label=${'Admin email'} .required=${true}></dees-input-text>
<dees-input-text .key=${'name'} .label=${'Display name'}></dees-input-text>
<dees-input-text .key=${'password'} .label=${'Password'} .required=${true} .isPasswordBool=${true}></dees-input-text>
<dees-input-text .key=${'passwordConfirm'} .label=${'Confirm password'} .required=${true} .isPasswordBool=${true}></dees-input-text>
<dees-input-checkbox
.key=${'enableIdpGlobalAuth'}
.label=${'Allow idp.global login for this email'}
.description=${statusArg.idpGlobalConfigured
? 'The local account remains authoritative; idp.global only verifies identity.'
: 'Requires DCROUTER_IDP_GLOBAL_URL before idp.global logins can work.'}
></dees-input-checkbox>
</dees-form>
</div>
`,
menuOptions: [
{
name: 'Create admin',
action: async (stepperArg: any) => {
const form = stepperArg.shadowRoot?.querySelector('.selected dees-form') as any;
if (!form) return;
const formData = await form.collectFormData();
const email = String(formData.email || '').trim();
const name = String(formData.name || '').trim();
const password = String(formData.password || '');
const passwordConfirm = String(formData.passwordConfirm || '');
if (!email || !password) {
form.setStatus?.('error', 'Email and password are required.');
return;
}
if (password !== passwordConfirm) {
form.setStatus?.('error', 'Passwords do not match.');
return;
}
try {
form.setStatus?.('pending', 'Creating persisted admin...');
await appstate.createInitialAdminUser({
email,
name,
password,
enableIdpGlobalAuth: Boolean(formData.enableIdpGlobalAuth),
});
form.setStatus?.('success', 'Persisted admin created.');
await stepperArg.destroy();
this.bootstrapStepper = undefined;
await appstate.usersStatePart.dispatchAction(appstate.fetchUsersAction, null);
} catch (error) {
form.setStatus?.('error', error instanceof Error ? error.message : 'Failed to create admin.');
}
},
},
],
},
],
});
}
}