Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
ba24991
feat: add wizard_step table and model for setup wizard
mohammadsherif0 Feb 23, 2026
9d5e6cf
feat: add wizard columns to setting table (showInWizard, wizardOrder,…
mohammadsherif0 Feb 23, 2026
3130b7a
feat: add wizard steps and wizard completion settings
mohammadsherif0 Feb 23, 2026
a379667
feat: assign wizard settings to steps (general, mail, registration, m…
mohammadsherif0 Feb 23, 2026
bcaaad3
refactor: remove default admin to require setup wizard
mohammadsherif0 Feb 23, 2026
59a6fc5
feat: add getWizardSettingsByStep and setup config route
mohammadsherif0 Feb 23, 2026
b3eb522
feat: add setup wizard support to auth routes (check, setup-admin)
mohammadsherif0 Feb 23, 2026
e427b62
feat: add Collapsible form component for sections
mohammadsherif0 Feb 23, 2026
0736ee8
feat: add SetupWizard with 6 steps, collapsibles, and JSON import modal
mohammadsherif0 Feb 23, 2026
0cf693d
feat: add router guard to redirect when wizard not completed
mohammadsherif0 Feb 23, 2026
e686bc1
feat : add app_state migration for wizard state storage
mohammadsherif0 Feb 23, 2026
69459e6
feat : add AppState model with get and set methods
mohammadsherif0 Feb 23, 2026
17dcdbe
refactor : use AppState for wizardCompleted in auth check
mohammadsherif0 Feb 23, 2026
8a5d985
feat : add PATCH /setup/state endpoint for wizard state
mohammadsherif0 Feb 23, 2026
376f870
refactor : save wizard state via PATCH /setup/state in SetupWizard
mohammadsherif0 Feb 23, 2026
692a370
feat: add displayName and displayGroup columns to settings
mohammadsherif0 Feb 26, 2026
21653b8
feat: add app.register.enabled to wizard registration step
mohammadsherif0 Feb 26, 2026
c6f48be
refactor: use displayGroup for settings page layout
mohammadsherif0 Feb 26, 2026
5ad0ea0
refactor: use displayName from backend in setup wizard
mohammadsherif0 Feb 26, 2026
91a7311
feat: add displaySubsection to settings
mohammadsherif0 Mar 1, 2026
ba11464
refactor: use displayGroup and displaySubsection for Settings layout
mohammadsherif0 Mar 1, 2026
9bc2213
refactor: use displaySubsection in SetupWizard
mohammadsherif0 Mar 1, 2026
f861e64
refactor: simplify wizard steps and settings mapping
mohammadsherif0 Mar 26, 2026
9fbd2d8
refactor: simplify setup wizard and create admin on finish
mohammadsherif0 Mar 26, 2026
18c22c8
refactor: show terms of service in registration settings
mohammadsherif0 Mar 26, 2026
e3c2e33
feat: add optional moodle keys to wizard settings migration
mohammadsherif0 Mar 29, 2026
595252f
docs: clarify moodle wizard note in setup route
mohammadsherif0 Mar 29, 2026
512420e
feat: show setting help beside label in setting item
mohammadsherif0 Mar 29, 2026
7536e2a
feat: add moodle block, import flow, and terms modal to setup wizard
mohammadsherif0 Mar 29, 2026
1764831
feat : reinitialize mail transport after mail settings save
mohammadsherif0 Apr 7, 2026
4b0d8e8
feat : add admin mail send test socket and shared mail test helpers
mohammadsherif0 Apr 7, 2026
59bed69
feat : add setup config and test-mail endpoints
mohammadsherif0 Apr 7, 2026
8be8026
feat : update setup wizard general step with download template and te…
mohammadsherif0 Apr 7, 2026
e1eabbb
feat : add mail test area via settings section footer slot and switch…
mohammadsherif0 Apr 7, 2026
26e2199
fix : sync editor modal inner state from model value using immediate …
mohammadsherif0 Apr 7, 2026
dc87a7f
refactor : use basicbutton and btn-group for import settings modal
mohammadsherif0 Apr 19, 2026
a7d5fa6
refactor : extract initial admin creation into shared helper
mohammadsherif0 Apr 19, 2026
527b691
feat : add dev admin setup to skip wizard on boot
mohammadsherif0 Apr 19, 2026
b8b26c4
chore : add make dev-no-wizard
mohammadsherif0 Apr 19, 2026
e780806
refactor : remove unused rerun wizard parameter
mohammadsherif0 Apr 19, 2026
d5a5c2a
fix : redirect /wizard to home when setup is complete
mohammadsherif0 Apr 19, 2026
c6db20b
Merge branch 'dev' into feat-99-setup-wizard
mohammadsherif0 Apr 19, 2026
dcc0f7a
feat: add migration for new settings display metadata
mohammadsherif0 Apr 19, 2026
4d37883
feat: extend dashboard settings subsection ordering
mohammadsherif0 Apr 19, 2026
e692b5b
docs: revise settingitem row description
mohammadsherif0 Apr 19, 2026
6197fd1
fix: return json payload from auth check endpoint
mohammadsherif0 Apr 19, 2026
04a3575
refactor: make dev skip setup wizard by default
mohammadsherif0 Apr 20, 2026
7ce292c
fix: validate wizard JSON import keys and show import result toasts
mohammadsherif0 Apr 20, 2026
9f68bdc
fix: remove duplicate modal close trigger from editor footer button
mohammadsherif0 Apr 20, 2026
6e964f6
docs : add setup wizard and settings documentation
mohammadsherif0 Apr 20, 2026
b19e565
Merge branch 'dev' into feat-99-setup-wizard
mohammadsherif0 Apr 20, 2026
7a65657
refactor: rename and align displayname metadata migrations
mohammadsherif0 Apr 26, 2026
bf27c68
refactor: use wizard_step fk for setting wizard mapping
mohammadsherif0 Apr 26, 2026
4ad98a5
refactor: store setup wizard state in setting keys
mohammadsherif0 Apr 26, 2026
24e4ebb
fix: exclude internal setup keys from settings import export
mohammadsherif0 Apr 26, 2026
659a373
docs : reorder frontend major views for setup flow
mohammadsherif0 Apr 29, 2026
dff971e
refactor : unify router guards and reduce auth check duplication
mohammadsherif0 Apr 29, 2026
6466d14
refactor : align mail test JSDoc and settings retrieval
mohammadsherif0 Apr 29, 2026
ec769d6
refactor : simplify mail server init flow naming
mohammadsherif0 Apr 29, 2026
1c99b6f
chore : configure dev skip wizard behavior for demo environments
mohammadsherif0 Apr 29, 2026
6a45dfc
fix : handle setup wizard description warning
mohammadsherif0 Apr 29, 2026
af2fc15
refactor : reassign setup configurations by owner instead of hardcode…
mohammadsherif0 Apr 30, 2026
d3f1343
refactor : share setting save logic for setup and socket flows
mohammadsherif0 Apr 30, 2026
3708f8e
feat : complete setup wizard through backend route and standardize wi…
mohammadsherif0 Apr 30, 2026
4428f2f
refactor : reduce route auth checks
mohammadsherif0 Apr 30, 2026
6abf368
refactor : split setup wizard into focused components and simplify st…
mohammadsherif0 Apr 30, 2026
def6a81
refactor : update setup wizard import guidance
mohammadsherif0 Apr 30, 2026
b7d4172
refactor : update basic setting display metadata
mohammadsherif0 Apr 30, 2026
a6efd12
docs : update setup wizard documentation and component structure notes
mohammadsherif0 Apr 30, 2026
a3daf8c
Merge branch 'dev' into feat-99-setup-wizard
mohammadsherif0 May 1, 2026
e43de5d
feat : add wizard_step_type table and link wizard_step to types
mohammadsherif0 May 6, 2026
360ee14
fix : validate setup-admin password
mohammadsherif0 May 6, 2026
a1e4daa
feat : add wizard step types and typeId to setup
mohammadsherif0 May 6, 2026
72b5f52
refactor : split settings setup wizard step into separate general, ma…
mohammadsherif0 May 6, 2026
a68a31c
docs : update setup wizard frontend documentation
mohammadsherif0 May 6, 2026
6d4c0a6
Merge branch 'dev' into feat-99-setup-wizard
mohammadsherif0 May 6, 2026
e37fc00
refactor : use BasicButton for settings mail test
mohammadsherif0 May 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.dev
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ ADMIN_PWD=admincare
GUEST_EMAIL=guest@guest.com
GUEST_PWD=guestguest

# Skip first-time setup wizard: create admin from ADMIN_EMAIL / ADMIN_PWD and mark wizard complete (demo / dev pipelines).
DEV_SKIP_WIZARD=true

# nlp server
SERVICE_NLP_ENABLED=true
SERVICE_NLP_URL=http://care.ukp.informatik.tu-darmstadt.de:4853
Expand Down
3 changes: 3 additions & 0 deletions .env.main
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ ADMIN_PWD=admincare
GUEST_EMAIL=guest@guest.com
GUEST_PWD=guestguest

# Skip first-time setup wizard: create admin from ADMIN_EMAIL / ADMIN_PWD and mark wizard complete (demo / dev pipelines).
DEV_SKIP_WIZARD=true

# nlp server
SERVICE_NLP_ENABLED=true
SERVICE_NLP_URL=http://care.ukp.informatik.tu-darmstadt.de:4852
Expand Down
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ default: help
.PHONY: help
help:
@echo "make help Show this help message"
@echo "make dev Run in development mode (only unix)"
@echo "make dev Run in development mode, skipping the first-time setup wizard (only unix)"
@echo "make dev-wizard Run dev mode and go through the first-time setup wizard"
@echo "make doc Build the documentation"
@echo "make dev-build Build the frontend (make dev-build-frontend) and run the backend in development mode"
@echo "make dev-backend Run backend in development mode"
Expand Down Expand Up @@ -79,6 +80,10 @@ init: modules db

.PHONY: dev
dev: frontend/node_modules/.uptodate backend/node_modules/.uptodate
cd frontend && npm run frontend-dev & cd backend && DEV_SKIP_WIZARD=true npm run start

.PHONY: dev-wizard
dev-wizard: frontend/node_modules/.uptodate backend/node_modules/.uptodate
cd frontend && npm run frontend-dev & cd backend && npm run start

.PHONY: dev-frontend
Expand Down
56 changes: 56 additions & 0 deletions backend/db/migrations/20260119185828-create-wizard_step.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use strict';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('wizard_step', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
key: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
},
order: {
type: Sequelize.INTEGER,
allowNull: false,
},
title: {
type: Sequelize.STRING,
allowNull: false,
},
description: {
type: Sequelize.STRING,
allowNull: true,
},
type: {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use integer instead of strings in that table? I guess the types are predefined somehow?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept the type as STRING because these are step keys used directly in the setup flow. I thought using integers here would only add backend/frontend mapping. Is there a specific benefit from integer types in this case?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reducing the size of the database and fasten up processing, it also aligns with https://en.wikipedia.org/wiki/Database_normalization in not writing same information multiple times in the database. Actually, correct would be to have another table wizard_step_type and a FK here

type: Sequelize.STRING,
allowNull: false,
},
deleted: {
type: Sequelize.BOOLEAN,
defaultValue: false,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
deletedAt: {
allowNull: true,
type: Sequelize.DATE,
},
});
},

async down(queryInterface, Sequelize) {
await queryInterface.dropTable('wizard_step');
},
};
51 changes: 51 additions & 0 deletions backend/db/migrations/20260119185850-extend-setting-wizard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
'use strict';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.addColumn('setting', 'showInWizard', {
type: Sequelize.BOOLEAN,
defaultValue: false,
});
await queryInterface.addColumn('setting', 'wizardOrder', {
type: Sequelize.INTEGER,
allowNull: true,
});
await queryInterface.addColumn('setting', 'requiredInWizard', {
type: Sequelize.BOOLEAN,
defaultValue: false,
});
await queryInterface.addColumn('setting', 'wizardStepId', {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'wizard_step',
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
});
await queryInterface.addColumn('setting', 'displayName', {
type: Sequelize.STRING(256),
allowNull: true,
});
await queryInterface.addColumn('setting', 'displayGroup', {
type: Sequelize.STRING(128),
allowNull: true,
});
await queryInterface.addColumn('setting', 'displaySubsection', {
type: Sequelize.STRING(128),
allowNull: true,
});
},

async down(queryInterface, Sequelize) {
await queryInterface.removeColumn('setting', 'displaySubsection');
await queryInterface.removeColumn('setting', 'displayGroup');
await queryInterface.removeColumn('setting', 'displayName');
await queryInterface.removeColumn('setting', 'showInWizard');
await queryInterface.removeColumn('setting', 'wizardOrder');
await queryInterface.removeColumn('setting', 'requiredInWizard');
await queryInterface.removeColumn('setting', 'wizardStepId');
},
};
22 changes: 22 additions & 0 deletions backend/db/migrations/20260119192230-basic-wizard_steps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
const now = new Date();
await queryInterface.bulkInsert('wizard_step', [
{ key: 'admin', order: 1, title: 'Admin Account', description: 'Create the administrator account', type: 'admin', deleted: false, createdAt: now, updatedAt: now, deletedAt: null },
{ key: 'general', order: 2, title: 'General Settings', description: 'Copyright, consent, guest login, external links', type: 'general', deleted: false, createdAt: now, updatedAt: now, deletedAt: null },
{ key: 'mail', order: 3, title: 'Mail Configuration', description: 'Enable email service and configure SMTP/sendmail', type: 'mail', deleted: false, createdAt: now, updatedAt: now, deletedAt: null },
{ key: 'registration', order: 4, title: 'User Registration', description: 'What is required at signup', type: 'registration', deleted: false, createdAt: now, updatedAt: now, deletedAt: null },
{ key: 'moodle', order: 5, title: 'Moodle Settings', description: 'Optional Moodle API settings', type: 'moodle', deleted: false, createdAt: now, updatedAt: now, deletedAt: null },
{ key: 'summary', order: 6, title: 'Summary', description: 'Review your choices before finishing', type: 'summary', deleted: false, createdAt: now, updatedAt: now, deletedAt: null },
], {});
},

async down(queryInterface, Sequelize) {
await queryInterface.bulkDelete('wizard_step', {
key: ['admin', 'general', 'mail', 'registration', 'moodle', 'summary'],
}, {});
},
};
80 changes: 80 additions & 0 deletions backend/db/migrations/20260119192356-transform-user-admin.js
Comment thread
dennis-zyska marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
'use strict';

const { genSalt, genPwdHash } = require('../../utils/auth');

/**
* Removes the default admin user created by basic-users so the first admin
* must be created via the setup wizard. Runs after basic-configuration; all
* configurations owned by the default admin are first reassigned to Bot (userId 2)
* to satisfy the configuration.userId FK, then the admin is deleted.
* POST /auth/setup-admin reassigns those configurations from Bot to the new admin.
*
* @type {import('sequelize-cli').Migration}
*/
module.exports = {
async up(queryInterface, Sequelize) {
const rows = await queryInterface.sequelize.query(
'SELECT id FROM "user" WHERE "userName" = \'admin\' AND "deleted" = false',
{ type: queryInterface.sequelize.QueryTypes.SELECT }
);
const admin = rows && rows[0];
if (!admin) {
return;
}
const adminId = admin.id;
const BOT_USER_ID = 2;

await queryInterface.sequelize.query(
`UPDATE configuration SET "userId" = :botId, "updatedAt" = :now WHERE "userId" = :adminId`,
{
replacements: { botId: BOT_USER_ID, adminId, now: new Date() },
type: Sequelize.QueryTypes.UPDATE,
}
);

await queryInterface.bulkDelete('user_role_matching', { userId: adminId }, {});
await queryInterface.bulkDelete('user', { userName: 'admin' }, {});
},

async down(queryInterface, Sequelize) {
const salt = genSalt();
const passwordHash = await genPwdHash(process.env.ADMIN_PWD || 'admin', salt);
const email = process.env.ADMIN_EMAIL || 'admin@localhost';
const now = new Date();

await queryInterface.bulkInsert('user', [{
firstName: 'admin',
lastName: 'user',
userName: 'admin',
email: email,
passwordHash: passwordHash,
salt: salt,
acceptStats: true,
acceptTerms: true,
deleted: false,
createdAt: now,
updatedAt: now,
}], {});

const userRows = await queryInterface.sequelize.query(
'SELECT id FROM "user" WHERE "userName" = \'admin\'',
{ type: queryInterface.sequelize.QueryTypes.SELECT }
);
const roleRows = await queryInterface.sequelize.query(
'SELECT id FROM "user_role" WHERE name = \'admin\'',
{ type: queryInterface.sequelize.QueryTypes.SELECT }
);
const inserted = userRows && userRows[0];
const adminRole = roleRows && roleRows[0];
if (inserted && adminRole) {
await queryInterface.bulkInsert('user_role_matching', [{
userId: inserted.id,
userRoleId: adminRole.id,
deleted: false,
createdAt: now,
updatedAt: now,
deletedAt: null,
}], {});
}
},
};
81 changes: 81 additions & 0 deletions backend/db/migrations/20260119194252-transform-setting-wizard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
'use strict';

/**
* Wizard settings per step requiredInWizard: true only where the setting must be filled.
*/
const WIZARD_SETTINGS = [
// general
{ key: 'app.config.copyright', wizardStep: 'general', wizardOrder: 1, requiredInWizard: true },
{ key: 'app.config.consent.enabled', wizardStep: 'general', wizardOrder: 2, requiredInWizard: false },
{ key: 'app.login.guest', wizardStep: 'general', wizardOrder: 3, requiredInWizard: false },
{ key: 'app.login.forgotPassword', wizardStep: 'general', wizardOrder: 4, requiredInWizard: false },
{ key: 'app.landing.showDocs', wizardStep: 'general', wizardOrder: 6, requiredInWizard: false },
{ key: 'app.landing.linkDocs', wizardStep: 'general', wizardOrder: 7, requiredInWizard: true },
{ key: 'app.landing.showProject', wizardStep: 'general', wizardOrder: 8, requiredInWizard: false },
{ key: 'app.landing.linkProject', wizardStep: 'general', wizardOrder: 9, requiredInWizard: false },
{ key: 'app.landing.showFeedback', wizardStep: 'general', wizardOrder: 10, requiredInWizard: false },
{ key: 'app.landing.linkFeedback', wizardStep: 'general', wizardOrder: 11, requiredInWizard: false },
// mail
{ key: 'system.mailService.enabled', wizardStep: 'mail', wizardOrder: 12, requiredInWizard: false },
{ key: 'system.mailService.sendMail.enabled', wizardStep: 'mail', wizardOrder: 13, requiredInWizard: false },
{ key: 'system.mailService.sendMail.path', wizardStep: 'mail', wizardOrder: 14, requiredInWizard: false },
{ key: 'system.mailService.senderAddress', wizardStep: 'mail', wizardOrder: 15, requiredInWizard: false },
{ key: 'system.mailService.smtp.enabled', wizardStep: 'mail', wizardOrder: 16, requiredInWizard: false },
{ key: 'system.mailService.smtp.host', wizardStep: 'mail', wizardOrder: 17, requiredInWizard: false },
{ key: 'system.mailService.smtp.port', wizardStep: 'mail', wizardOrder: 18, requiredInWizard: false },
{ key: 'system.mailService.smtp.secure', wizardStep: 'mail', wizardOrder: 19, requiredInWizard: false },
{ key: 'system.mailService.smtp.auth.enabled', wizardStep: 'mail', wizardOrder: 20, requiredInWizard: false },
{ key: 'system.mailService.smtp.auth.user', wizardStep: 'mail', wizardOrder: 21, requiredInWizard: false },
{ key: 'system.mailService.smtp.auth.pass', wizardStep: 'mail', wizardOrder: 22, requiredInWizard: false },
{ key: 'system.baseUrl', wizardStep: 'mail', wizardOrder: 23, requiredInWizard: false },
{ key: 'app.register.emailVerification', wizardStep: 'mail', wizardOrder: 24, requiredInWizard: false },
// app.login.forgotPassword already in general; in mail step it's a toggle in UI, same key
// registration
{ key: 'app.register.enabled', wizardStep: 'registration', wizardOrder: 25, requiredInWizard: false },
{ key: 'app.register.requestName', wizardStep: 'registration', wizardOrder: 26, requiredInWizard: false },
{ key: 'app.register.requestStats', wizardStep: 'registration', wizardOrder: 27, requiredInWizard: false },
{ key: 'app.register.requestData', wizardStep: 'registration', wizardOrder: 28, requiredInWizard: false },
{ key: 'app.register.acceptStats.default', wizardStep: 'registration', wizardOrder: 29, requiredInWizard: false },
{ key: 'app.register.acceptDataSharing.default', wizardStep: 'registration', wizardOrder: 30, requiredInWizard: false },
{ key: 'app.register.terms', wizardStep: 'general', wizardOrder: 5, requiredInWizard: false },
// Optional Moodle (wizardStep moodle keeps Settings > Moodle grouping; shown on General screen in SetupWizard)
{ key: 'rpc.moodleAPI.apiUrl', wizardStep: 'moodle', wizardOrder: 31, requiredInWizard: false },
{ key: 'rpc.moodleAPI.apiKey', wizardStep: 'moodle', wizardOrder: 32, requiredInWizard: false },
{ key: 'rpc.moodleAPI.courseID', wizardStep: 'moodle', wizardOrder: 33, requiredInWizard: false },
{ key: 'rpc.moodleAPI.showInput.apiUrl', wizardStep: 'moodle', wizardOrder: 34, requiredInWizard: false },
{ key: 'rpc.moodleAPI.showInput.apiKey', wizardStep: 'moodle', wizardOrder: 35, requiredInWizard: false },
{ key: 'rpc.moodleAPI.showInput.courseID', wizardStep: 'moodle', wizardOrder: 36, requiredInWizard: false },
];

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
for (const { key, wizardStep, wizardOrder, requiredInWizard } of WIZARD_SETTINGS) {
await queryInterface.sequelize.query(
`UPDATE setting
SET "showInWizard" = true,
"wizardStepId" = (SELECT id FROM wizard_step WHERE key = :wizardStep),
"wizardOrder" = :wizardOrder,
"requiredInWizard" = :requiredInWizard,
"updatedAt" = :now
WHERE key = :key`,
{
replacements: { key, wizardStep, wizardOrder, requiredInWizard, now: new Date() },
type: Sequelize.QueryTypes.UPDATE,
}
);
}
},

async down(queryInterface, Sequelize) {
for (const { key } of WIZARD_SETTINGS) {
await queryInterface.sequelize.query(
`UPDATE setting SET "showInWizard" = false, "wizardStepId" = NULL, "wizardOrder" = NULL, "requiredInWizard" = false, "updatedAt" = :now WHERE key = :key`,
{
replacements: { key, now: new Date() },
type: Sequelize.QueryTypes.UPDATE,
}
);
}
},
};
42 changes: 42 additions & 0 deletions backend/db/migrations/20260223130010-basic-setting-app_setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use strict';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
const now = new Date();
await queryInterface.bulkInsert('setting', [
{
key: 'app.setup.wizardCompleted',
value: 'false',
type: 'boolean',
description: 'Internal setup wizard completion state.',
onlyAdmin: true,
showInWizard: false,
requiredInWizard: false,
deleted: false,
createdAt: now,
updatedAt: now,
deletedAt: null,
},
{
key: 'app.setup.wizardCurrentStep',
value: '0',
type: 'integer',
description: 'Internal setup wizard current step.',
onlyAdmin: true,
showInWizard: false,
requiredInWizard: false,
deleted: false,
createdAt: now,
updatedAt: now,
deletedAt: null,
},
], {});
},

async down(queryInterface, Sequelize) {
await queryInterface.bulkDelete('setting', {
key: ['app.setup.wizardCompleted', 'app.setup.wizardCurrentStep'],
}, {});
},
};
Loading
Loading