fix: skip the setup wizard in demo mode without a mail server

This commit is contained in:
Linus Rath
2026-10-31 18:00:13 +01:00
parent e15c335bec
commit 100f6db309
2 changed files with 58 additions and 2 deletions
+51
View File
@@ -0,0 +1,51 @@
// @vitest-environment node
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { configManager } from '@/lib/admin/config-manager';
import { detectSetupState } from '@/lib/setup/state';
let dir: string;
async function withConfig(config: Record<string, unknown>) {
await writeFile(path.join(dir, 'config.json'), JSON.stringify(config), 'utf-8');
await configManager.reload();
}
beforeEach(async () => {
dir = await mkdtemp(path.join(tmpdir(), 'bw-setup-state-'));
vi.stubEnv('ADMIN_CONFIG_DIR', dir);
vi.stubEnv('JMAP_SERVER_URL', '');
vi.stubEnv('DEMO_MODE', '');
});
afterEach(async () => {
vi.unstubAllEnvs();
await rm(dir, { recursive: true, force: true });
});
describe('detectSetupState', () => {
it('sends a fresh install without a server to the wizard', async () => {
await withConfig({});
expect(detectSetupState()).toBe('bootstrap');
});
it('needs no wizard with DEMO_MODE=true and no JMAP_SERVER_URL', async () => {
vi.stubEnv('DEMO_MODE', 'true');
await withConfig({});
expect(detectSetupState()).toBe('env-managed');
});
it('lets an admin override of demoMode win over DEMO_MODE', async () => {
vi.stubEnv('DEMO_MODE', 'true');
await withConfig({ demoMode: false });
expect(detectSetupState()).toBe('bootstrap');
});
it('still reports a finished wizard as configured', async () => {
vi.stubEnv('DEMO_MODE', 'true');
await withConfig({ setupComplete: true });
expect(detectSetupState()).toBe('configured');
});
});
+7 -2
View File
@@ -10,8 +10,9 @@ import { isConfigReadOnly } from '@/lib/admin/paths';
* configured - setup wizard finished (admin override config.json carries
* setupComplete=true). Normal app; /setup returns 404.
* env-managed - JMAP_SERVER_URL is set in the environment, so the
* operator is configuring via .env (legacy / CI path). The
* wizard stays disabled.
* operator is configuring via .env (legacy / CI path), or
* demo mode is on (DEMO_MODE=true), which needs no mail
* server at all. The wizard stays disabled.
*/
export type SetupState = 'bootstrap' | 'configured' | 'env-managed';
@@ -25,6 +26,10 @@ export function detectSetupState(): SetupState {
if (process.env.JMAP_SERVER_URL && process.env.JMAP_SERVER_URL.trim() !== '') {
return 'env-managed';
}
// Demo mode signs everyone into built-in sample data; there is nothing for
// the wizard to set up. Read through the config manager so an admin
// override of demoMode wins over DEMO_MODE, as everywhere else.
if (configManager.get<boolean>('demoMode', false) === true) return 'env-managed';
// Read-only config dir + no setupComplete flag means the volume was
// mounted :ro before the wizard ran. Fall through to bootstrap so the
// failure (write attempt during wizard) surfaces with a clear error