Skip to content

Conversation

@ztygod
Copy link
Contributor

@ztygod ztygod commented Feb 9, 2026

背景 / 历史问题

  • 历史 migration m20251203_013745_add_dynamic_sidebar.rs 中插入了硬编码的默认菜单数据。
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        // ......

        let backend = manager.get_database_backend();

        match backend {
            DatabaseBackend::Postgres => {
                let sql = r#"
                    INSERT INTO dynamic_sidebar (public_id, label, href, visible, order_index) VALUES
                    ('home', 'Home', '/posts', true, 0),
                    ('inbox', 'Inbox', '/inbox', true, 1),
                    ('chat', 'Chat', '/chat', true, 2),
                    ('notes', 'Docs', '/notes', true, 3),
                    ('calls', 'Calls', '/calls', true, 4),
                    ('drafts', 'Drafts', '/drafts', true, 5),
                    ('code', 'Code', '/code', true, 6),
                    ('tags', 'Tags', '/code/tags', true, 7),
                    ('cl', 'Change List', '/cl', true, 8),
                    ('mq', 'Merge Queue', '/queue/main', true, 9),
                    ('issue', 'Issue', '/issue', true, 10),
                    ('rust', 'Rust', '/rust', true, 11);
                "#;

                manager.get_connection().execute_unprepared(sql).await?;
            }
            DatabaseBackend::Sqlite | DatabaseBackend::MySql => {}
        }

        Ok(())
    }
  • 这样导致:

    • 新环境无法灵活修改默认菜单;
    • 表始终非空,无法通过表为空来判断新环境;
    • 默认菜单逻辑散落在 migration,维护成本高。
  • 我们希望遵循基本的约定:

    1. Migration 只做 表 结构明确的数据升级
    2. 默认菜单的声明只存在于 config
    3. init 逻辑只在运行时检查表为空时执行

设计思路

  • 移除历史 migration 的默认数据插入

    • 保留表结构创建;
    • 针对历史负债,可以单独写 migration 清空表(让表变为空)。
  • 动态初始化默认菜单

    • DynamicSidebarStorage 中增加 init_default_sidebars 方法;
    • 方法从 config/config.toml 读取 sidebar.default_items
# Default sidebar menu items.
# - Visible = false means the menu is hidden by default.
# - Order_index controls the display order in the UI.
# - Changes here do not affect old environments; only new or empty DBs will use these defaults.
[sidebar]
default_items = [
    { public_id = "home", label = "Home", href = "/posts", visible = true, order_index = 0 },
    { public_id = "inbox", label = "Inbox", href = "/inbox", visible = true, order_index = 1 },
    { public_id = "chat", label = "Chat", href = "/chat", visible = true, order_index = 2 },
    { public_id = "notes", label = "Docs", href = "/notes", visible = true, order_index = 3 },
    { public_id = "calls", label = "Calls", href = "/calls", visible = false, order_index = 4 },
    { public_id = "drafts", label = "Drafts", href = "/drafts", visible = true, order_index = 5 },
    { public_id = "code", label = "Code", href = "/code", visible = true, order_index = 6 },
    { public_id = "tags", label = "Tags", href = "/code/tags", visible = true, order_index = 7 },
    { public_id = "cl", label = "Change List", href = "/cl", visible = true, order_index = 8 },
    { public_id = "mq", label = "Merge Queue", href = "/queue/main", visible = true, order_index = 9 },
    { public_id = "issue", label = "Issue", href = "/issue", visible = true, order_index = 10 },
    { public_id = "rust", label = "Rust", href = "/rust", visible = false, order_index = 11 },
    { public_id = "oc", label = "Orion Client", href = "/oc", visible = true, order_index = 13 },
]
  • 如果表为空,插入默认菜单,保证幂等性;

  • Storage 初始化阶段调用该方法,确保新环境启动时自动 bootstrap。

  • 默认菜单更新

    • 现在默认菜单配置集中在 config/config.toml,修改时只需要改配置,不再改 migration。

默认 sidebar 改动

  • 隐藏了历史 migration 中的 CallsRust 默认项,即字段visible = false
  • 新增 Orion Client(public_id=oc);

@benjamin-747 benjamin-747 added this pull request to the merge queue Feb 9, 2026
Merged via the queue into web3infra-foundation:main with commit 71a8bd6 Feb 9, 2026
15 checks passed
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR moves DynamicSidebar’s default menu initialization out of a historical migration and into runtime bootstrap driven by [sidebar] in config.toml, aiming to make defaults configurable and keep migrations schema-focused.

Changes:

  • Initialize default dynamic_sidebar rows at startup from config.sidebar.default_items when the table is empty.
  • Add a new migration intended to remove historically inserted default sidebar rows.
  • Extend common::config::Config with a sidebar section and add sidebar defaults to the repo’s TOML configs.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
jupiter/src/storage/mod.rs Calls sidebar default initialization during Storage::new() startup.
jupiter/src/storage/dynamic_sidebar_storage.rs Adds init_default_sidebars() that seeds DB rows from SidebarConfig when empty.
jupiter/src/migration/mod.rs Registers the new cleanup migration.
jupiter/src/migration/m20260209_064016_remove_default_dynamic_sidebar.rs New migration that deletes dynamic_sidebar contents (currently unconditional for Postgres).
config/config.toml Adds [sidebar] default_items defaults.
config/config-workflow.toml Adds [sidebar] default_items defaults for workflow config.
config/config-test.toml Adds [sidebar] default_items defaults for test config.
common/src/config/mod.rs Introduces SidebarConfig / SidebarItem and adds Config.sidebar.

Comment on lines +48 to +50
async fn down(&self, _: &SchemaManager) -> Result<(), DbErr> {
Ok(())
}
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

down() is a no-op for a destructive data migration. In this repo, migrations generally provide a meaningful rollback; if rollback isn’t possible, it’s better to either re-insert the previous default rows (if acceptable) or return an explicit error indicating the migration is irreversible, rather than silently succeeding.

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +35
pub async fn init_default_sidebars(&self, config: &SidebarConfig) -> Result<(), MegaError> {
let count = dynamic_sidebar::Entity::find()
.count(self.get_connection())
.await?;
if count > 0 {
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

init_default_sidebars uses a separate COUNT(*) check followed by individual inserts without a transaction/lock. Under concurrent startups (multiple instances), two processes can both observe count=0 and then race inserting, leading to unique constraint violations on public_id (or partial inserts if a later insert fails). Prefer an atomic "insert defaults if table empty" approach (single SQL statement / transaction with proper locking) or conflict-tolerant inserts so startup remains idempotent.

Copilot uses AI. Check for mistakes.
Comment on lines +914 to +920
SidebarItem {
public_id: "docs".to_string(),
label: "Docs".to_string(),
href: "/notes".to_string(),
visible: true,
order_index: 2,
},
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

SidebarConfig::default() seeds an item with public_id = "docs", but the historical migration and the new TOML defaults use public_id = "notes" for the Docs entry. If the [sidebar] config is omitted (or in tests using Config::mock()), this will initialize a different public_id and can break any code/UI that relies on stable IDs. Align the Rust defaults with the TOML/migration IDs.

Copilot uses AI. Check for mistakes.
# Default sidebar menu items.
# - Visible = false means the menu is hidden by default.
# - Order_index controls the display order in the UI.
# - Changes here do not affect old environments; only new or empty DBs will use these defaults.
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

The comment says changes here don’t affect old environments, but this PR also adds a migration that deletes all rows from dynamic_sidebar on Postgres, which will make existing DBs empty and therefore re-seeded from these defaults at runtime. Either adjust the migration to avoid wiping existing data, or update this comment to reflect the actual behavior.

Suggested change
# - Changes here do not affect old environments; only new or empty DBs will use these defaults.
# - These defaults are applied to new or empty DBs, and to existing DBs after
# migrations that clear the dynamic_sidebar table. Changing them may affect
# old environments once such a migration has been applied.

Copilot uses AI. Check for mistakes.
# Default sidebar menu items.
# - Visible = false means the menu is hidden by default.
# - Order_index controls the display order in the UI.
# - Changes here do not affect old environments; only new or empty DBs will use these defaults.
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

The comment says changes here don’t affect old environments, but this PR also adds a migration that deletes all rows from dynamic_sidebar on Postgres, which will make existing DBs empty and therefore re-seeded from these defaults at runtime. Either adjust the migration to avoid wiping existing data, or update this comment to reflect the actual behavior.

Suggested change
# - Changes here do not affect old environments; only new or empty DBs will use these defaults.
# - Changes here affect environments whenever the dynamic_sidebar table is empty
# (for example, on new installs or after migrations that clear sidebar data).

Copilot uses AI. Check for mistakes.
# Default sidebar menu items.
# - Visible = false means the menu is hidden by default.
# - Order_index controls the display order in the UI.
# - Changes here do not affect old environments; only new or empty DBs will use these defaults.
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

The comment says changes here don’t affect old environments, but this PR also adds a migration that deletes all rows from dynamic_sidebar on Postgres, which will make existing DBs empty and therefore re-seeded from these defaults at runtime. Either adjust the migration to avoid wiping existing data, or update this comment to reflect the actual behavior.

Suggested change
# - Changes here do not affect old environments; only new or empty DBs will use these defaults.
# - Note: a Postgres migration clears the `dynamic_sidebar` table on upgrade,
# so existing databases may also be reseeded from these defaults.

Copilot uses AI. Check for mistakes.
Comment on lines +38 to +41
DatabaseBackend::Postgres => {
let sql = r#"DELETE FROM dynamic_sidebar;"#;
manager.get_connection().execute_unprepared(sql).await?;
}
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

This migration unconditionally deletes all rows from dynamic_sidebar on Postgres. Since the table supports user-driven updates (see DynamicSidebarStorage CRUD/sync), applying this in existing environments will wipe any customized sidebar configuration. Consider making the cleanup conditional (e.g., only delete if rows exactly match the legacy hard-coded defaults), or delete only the legacy public_ids, to avoid data loss.

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +15
//! 1. **Schema vs. Data Separation**
//! - Migrations should only define or modify database **schema** (tables, columns, indexes).
//! - They should **not** insert default or seed data.
//! - Default menu data is now managed at **runtime** via a bootstrap method (`DynamicSidebarStorage::bootstrap_sidebar`) that reads from configuration (e.g., `sidebar.default.toml`).
//!
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

The module-level docs mention DynamicSidebarStorage::bootstrap_sidebar and sidebar.default.toml, but the actual runtime initializer added in this PR is init_default_sidebars and it reads from the main config [sidebar] section. Please update these references to match the implementation to avoid misleading future maintainers.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants