Files
zesdex/crates/zesdex-backend/src/bin/migrate.rs
T
asepharyana 9a67137954 refactor: massive codebase restructuring — naming, splitting, DRY
Crate renames:
  - zesdex-entities::seaorm → domain (misleading name, no SeaORM used)
  - zesdex-dto → merged into zesdex-entities (100% re-exports)
  - zesdex-libs → zesdex-infra (vague name)

Module renames:
  - app/harness → guard (misleading: safety gatekeeper, not test harness)
  - runtime/commands → action_dispatch (name clashed with controller/command)
  - resources → prompts (embedded prompt text, not general resources)
  - tool/seqthink → sequential_think (unreadable abbreviation)
  - msglog/query → insert (module only inserts, never queries)

Dead code removal:
  - app/mode/help.rs (orphaned — not declared in mod.rs)
  - app/mode/loading.rs (orphaned — not declared in mod.rs)

File splitting (71 new files, avg ~115 lines/file):
  - app/runtime/actions/: 1→8 files (was 2030 lines)
  - view/overlays/: 1→16 files (was 1167 lines)
  - tool/lsp/: 1→8 per-tool files (was 909 lines)
  - main.rs: 1→5 files (session, daemon, attach, event_loop)
  - workflow/engine + hive_mind: 2→10 files
  - subagent/engine + auto: 2→9 files
  - lsp/provisioner: 1→5 files
  - review/: 1→6 files
  - guard/: 1→2 files (extracted patterns)
  - state/misc: 1→3 files (input, scroll)
  - mcp/: 1→3 files (transport, adapter)
  - stream/json_repair extracted from turn.rs

DRY:
  - Pattern constants (STUB_PATTERNS etc) in guard/patterns shared with subagent
  - 3 near-identical background spawners → 1 generic + thin wrappers
  - Shared spawn_subagent_with_drain() extracted
  - Shared create_session() in main
  - write_osc52 deduplicated

Bug fixes:
  - archive_message(): sess.db → db (wrong variable name)
  - execute_one_tool(): wrong parameter name
  - check_credential_read() function was missing (restored from test expectations)
2026-07-17 09:08:41 +07:00

112 lines
3.5 KiB
Rust

//! Database migration: creates/upgrades SQLite schemas for all sessions.
use std::path::Path;
fn main() -> anyhow::Result<()> {
let store = zesdex_entities::domain::common::store::Store::new();
// Find all session directories
let sessions_dir = store.base_dir.join("sessions");
if !sessions_dir.exists() {
eprintln!("No sessions directory found, nothing to migrate");
return Ok(());
}
let mut migrated = 0u32;
let mut failed = 0u32;
for entry in std::fs::read_dir(&sessions_dir)? {
let entry = entry?;
let path = entry.path();
if !path.is_dir() {
continue;
}
match migrate_session_msglog(&path) {
Ok(_) => {
migrated += 1;
eprintln!("Migrated session: {:?}", path.file_name());
}
Err(e) => {
failed += 1;
eprintln!("Failed to migrate session {:?}: {e}", path.file_name());
}
}
}
eprintln!("Migration complete: {migrated} succeeded, {failed} failed");
if failed > 0 {
anyhow::bail!("{failed} session(s) failed to migrate");
}
Ok(())
}
/// Open a session's `messages.sqlite` and initialize its schema.
fn migrate_session_msglog(session_dir: &Path) -> anyhow::Result<()> {
let msglog_path = session_dir.join("messages.sqlite");
if let Some(parent) = msglog_path.parent() {
std::fs::create_dir_all(parent)?;
}
let conn = rusqlite::Connection::open(&msglog_path)?;
conn.execute_batch("PRAGMA journal_mode = WAL;")?;
conn.execute_batch("PRAGMA busy_timeout = 5000;")?;
// Initialize schema
conn.execute_batch("PRAGMA foreign_keys = ON;")?;
conn.execute_batch(
"
CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
role TEXT NOT NULL,
content TEXT,
tool_call_id TEXT,
tool_name TEXT,
tool_arguments TEXT,
created_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS archives (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL UNIQUE,
title TEXT,
model TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
message_count INTEGER DEFAULT 0,
token_count INTEGER DEFAULT 0,
summary TEXT
);
CREATE INDEX IF NOT EXISTS idx_messages_session_id ON messages(session_id);
CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at);
CREATE INDEX IF NOT EXISTS idx_archives_created_at ON archives(created_at);
CREATE TABLE IF NOT EXISTS blobs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
blob_key TEXT NOT NULL,
data BLOB NOT NULL,
mime_type TEXT,
created_at INTEGER NOT NULL,
UNIQUE(session_id, blob_key)
);
",
)?;
// Check and upgrade schema version
let version: i32 = conn
.pragma_query_value(None, "user_version", |row| row.get(0))
.unwrap_or(0);
if version < 1 {
conn.pragma_update(None, "user_version", 1)?;
}
if version < 2 {
conn.execute_batch(
"CREATE INDEX IF NOT EXISTS idx_messages_session_role ON messages(session_id, role);",
)?;
conn.pragma_update(None, "user_version", 2)?;
}
Ok(())
}