-
Notifications
You must be signed in to change notification settings - Fork 1
Feat 99 setup wizard #173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mohammadsherif0
wants to merge
77
commits into
dev
Choose a base branch
from
feat-99-setup-wizard
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Feat 99 setup wizard #173
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 9d5e6cf
feat: add wizard columns to setting table (showInWizard, wizardOrder,…
mohammadsherif0 3130b7a
feat: add wizard steps and wizard completion settings
mohammadsherif0 a379667
feat: assign wizard settings to steps (general, mail, registration, m…
mohammadsherif0 bcaaad3
refactor: remove default admin to require setup wizard
mohammadsherif0 59a6fc5
feat: add getWizardSettingsByStep and setup config route
mohammadsherif0 b3eb522
feat: add setup wizard support to auth routes (check, setup-admin)
mohammadsherif0 e427b62
feat: add Collapsible form component for sections
mohammadsherif0 0736ee8
feat: add SetupWizard with 6 steps, collapsibles, and JSON import modal
mohammadsherif0 0cf693d
feat: add router guard to redirect when wizard not completed
mohammadsherif0 e686bc1
feat : add app_state migration for wizard state storage
mohammadsherif0 69459e6
feat : add AppState model with get and set methods
mohammadsherif0 17dcdbe
refactor : use AppState for wizardCompleted in auth check
mohammadsherif0 8a5d985
feat : add PATCH /setup/state endpoint for wizard state
mohammadsherif0 376f870
refactor : save wizard state via PATCH /setup/state in SetupWizard
mohammadsherif0 692a370
feat: add displayName and displayGroup columns to settings
mohammadsherif0 21653b8
feat: add app.register.enabled to wizard registration step
mohammadsherif0 c6f48be
refactor: use displayGroup for settings page layout
mohammadsherif0 5ad0ea0
refactor: use displayName from backend in setup wizard
mohammadsherif0 91a7311
feat: add displaySubsection to settings
mohammadsherif0 ba11464
refactor: use displayGroup and displaySubsection for Settings layout
mohammadsherif0 9bc2213
refactor: use displaySubsection in SetupWizard
mohammadsherif0 f861e64
refactor: simplify wizard steps and settings mapping
mohammadsherif0 9fbd2d8
refactor: simplify setup wizard and create admin on finish
mohammadsherif0 18c22c8
refactor: show terms of service in registration settings
mohammadsherif0 e3c2e33
feat: add optional moodle keys to wizard settings migration
mohammadsherif0 595252f
docs: clarify moodle wizard note in setup route
mohammadsherif0 512420e
feat: show setting help beside label in setting item
mohammadsherif0 7536e2a
feat: add moodle block, import flow, and terms modal to setup wizard
mohammadsherif0 1764831
feat : reinitialize mail transport after mail settings save
mohammadsherif0 4b0d8e8
feat : add admin mail send test socket and shared mail test helpers
mohammadsherif0 59bed69
feat : add setup config and test-mail endpoints
mohammadsherif0 8be8026
feat : update setup wizard general step with download template and te…
mohammadsherif0 e1eabbb
feat : add mail test area via settings section footer slot and switch…
mohammadsherif0 26e2199
fix : sync editor modal inner state from model value using immediate …
mohammadsherif0 dc87a7f
refactor : use basicbutton and btn-group for import settings modal
mohammadsherif0 a7d5fa6
refactor : extract initial admin creation into shared helper
mohammadsherif0 527b691
feat : add dev admin setup to skip wizard on boot
mohammadsherif0 b8b26c4
chore : add make dev-no-wizard
mohammadsherif0 e780806
refactor : remove unused rerun wizard parameter
mohammadsherif0 d5a5c2a
fix : redirect /wizard to home when setup is complete
mohammadsherif0 c6db20b
Merge branch 'dev' into feat-99-setup-wizard
mohammadsherif0 dcc0f7a
feat: add migration for new settings display metadata
mohammadsherif0 4d37883
feat: extend dashboard settings subsection ordering
mohammadsherif0 e692b5b
docs: revise settingitem row description
mohammadsherif0 6197fd1
fix: return json payload from auth check endpoint
mohammadsherif0 04a3575
refactor: make dev skip setup wizard by default
mohammadsherif0 7ce292c
fix: validate wizard JSON import keys and show import result toasts
mohammadsherif0 9f68bdc
fix: remove duplicate modal close trigger from editor footer button
mohammadsherif0 6e964f6
docs : add setup wizard and settings documentation
mohammadsherif0 b19e565
Merge branch 'dev' into feat-99-setup-wizard
mohammadsherif0 7a65657
refactor: rename and align displayname metadata migrations
mohammadsherif0 bf27c68
refactor: use wizard_step fk for setting wizard mapping
mohammadsherif0 4ad98a5
refactor: store setup wizard state in setting keys
mohammadsherif0 24e4ebb
fix: exclude internal setup keys from settings import export
mohammadsherif0 659a373
docs : reorder frontend major views for setup flow
mohammadsherif0 dff971e
refactor : unify router guards and reduce auth check duplication
mohammadsherif0 6466d14
refactor : align mail test JSDoc and settings retrieval
mohammadsherif0 ec769d6
refactor : simplify mail server init flow naming
mohammadsherif0 1c99b6f
chore : configure dev skip wizard behavior for demo environments
mohammadsherif0 6a45dfc
fix : handle setup wizard description warning
mohammadsherif0 af2fc15
refactor : reassign setup configurations by owner instead of hardcode…
mohammadsherif0 d3f1343
refactor : share setting save logic for setup and socket flows
mohammadsherif0 3708f8e
feat : complete setup wizard through backend route and standardize wi…
mohammadsherif0 4428f2f
refactor : reduce route auth checks
mohammadsherif0 6abf368
refactor : split setup wizard into focused components and simplify st…
mohammadsherif0 def6a81
refactor : update setup wizard import guidance
mohammadsherif0 b7d4172
refactor : update basic setting display metadata
mohammadsherif0 a6efd12
docs : update setup wizard documentation and component structure notes
mohammadsherif0 a3daf8c
Merge branch 'dev' into feat-99-setup-wizard
mohammadsherif0 e43de5d
feat : add wizard_step_type table and link wizard_step to types
mohammadsherif0 360ee14
fix : validate setup-admin password
mohammadsherif0 a1e4daa
feat : add wizard step types and typeId to setup
mohammadsherif0 72b5f52
refactor : split settings setup wizard step into separate general, ma…
mohammadsherif0 a68a31c
docs : update setup wizard frontend documentation
mohammadsherif0 6d4c0a6
Merge branch 'dev' into feat-99-setup-wizard
mohammadsherif0 e37fc00
refactor : use BasicButton for settings mail test
mohammadsherif0 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
56 changes: 56 additions & 0 deletions
56
backend/db/migrations/20260119185828-create-wizard_step.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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: { | ||
| 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
51
backend/db/migrations/20260119185850-extend-setting-wizard.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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
22
backend/db/migrations/20260119192230-basic-wizard_steps.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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
80
backend/db/migrations/20260119192356-transform-user-admin.js
|
dennis-zyska marked this conversation as resolved.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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
81
backend/db/migrations/20260119194252-transform-setting-wizard.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 }, | ||
| { 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
42
backend/db/migrations/20260223130010-basic-setting-app_setup.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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'], | ||
| }, {}); | ||
| }, | ||
| }; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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