Release v0.5.2

- Add admin content consistency diagnostics
- Add admin-migrate CLI
- Harden RBAC for API endpoints
- Normalize multi-tenant storage paths
- Improve backup and restore compatibility
- Fix administrator routing consistency
- Improve doctor and stats commands
This commit is contained in:
thakares committed 2026-06-29 16:11:43 +05:30
1 parent a32c0fd7ca
commit 2761863c14
14 files changed
+437 -75

No files matched your search

+3 -7
View File
@@ -50,7 +50,7 @@ impl Db {
// 1. If legacy admin.db exists at root, move admin/system DBs to config.data_dir/admin/
if legacy_admin_db.exists() {
info!("Legacy admin.db found at root. Moving administrative databases to admin/ subfolder...");
tracing::warn!("LEGACY DETECTED: admin.db found at root. Moving administrative databases to multi-tenant admin/ subfolder...");
let files = vec![
"admin.db",
"admin.db-wal",
@@ -219,7 +219,7 @@ impl Db {
fs::create_dir_all(&legacy_user_dir)?;
if legacy_content_db.exists() || legacy_analytics_db.exists() {
info!("Legacy content/analytics databases found at root. Moving to user ID 1 directory...");
tracing::warn!("LEGACY DETECTED: content/analytics databases found at root. Moving to multi-tenant user ID 1 directory...");
let content_files = vec!["content.db", "content.db-wal", "content.db-shm"];
for f in content_files {
let src = config.data_dir.join(f);
@@ -461,11 +461,7 @@ impl Db {
for user_id in user_ids {
let user_dir = config.data_dir.join("users").join(user_id.to_string());
let content_path = if user_id == 1 {
config.data_dir.join("content.db") // legacy admin content db path
} else {
user_dir.join("content.db")
};
let content_path = user_dir.join("content.db");
if content_path.exists() {
let content_conn = Connection::open(&content_path)?;
+73 -6
View File
@@ -808,12 +808,7 @@ pub fn verify_global_slug_registry_integrity(
|| (status == "reserving" && !target_id.is_empty())
{
let content_db_path = if owner_user_id == 1 {
let p1 = data_dir.join("users").join("1").join("content.db");
if p1.exists() {
p1
} else {
data_dir.join("content.db")
}
data_dir.join("users").join("1").join("content.db")
} else {
data_dir
.join("users")
@@ -862,6 +857,78 @@ pub fn verify_global_slug_registry_integrity(
}
}
// 3. Admin Content Reverse Consistency Check (Legacy DB)
let admin_content_db_path = data_dir.join("users").join("1").join("content.db");
if admin_content_db_path.exists() {
if let Ok(admin_content_conn) = Connection::open(&admin_content_db_path) {
// Check URLs
if let Ok(mut stmt) = admin_content_conn.prepare("SELECT code, id FROM urls;") {
if let Ok(mut rows) = stmt.query([]) {
while let Ok(Some(row)) = rows.next() {
let code: String = row.get(0).unwrap_or_default();
let id: String = row.get(1).unwrap_or_default();
let exists: bool = system_conn.query_row(
"SELECT EXISTS(SELECT 1 FROM global_slugs WHERE slug = ?1 AND owner_user_id = 1 AND target_id = ?2);",
rusqlite::params![code, id],
|r| r.get(0)
).unwrap_or(false);
if !exists {
warnings.push(format!("Orphaned admin URL detected in legacy content DB: code='{}', id='{}' is missing from global_slugs", code, id));
}
}
}
}
// Check Landing Pages
if let Ok(mut stmt) = admin_content_conn.prepare("SELECT code, id FROM landing_pages;")
{
if let Ok(mut rows) = stmt.query([]) {
while let Ok(Some(row)) = rows.next() {
let code: String = row.get(0).unwrap_or_default();
let id: String = row.get(1).unwrap_or_default();
let exists: bool = system_conn.query_row(
"SELECT EXISTS(SELECT 1 FROM global_slugs WHERE slug = ?1 AND owner_user_id = 1 AND target_id = ?2);",
rusqlite::params![code, id],
|r| r.get(0)
).unwrap_or(false);
if !exists {
warnings.push(format!("Orphaned admin Landing Page detected in legacy content DB: code='{}', id='{}' is missing from global_slugs", code, id));
}
}
}
}
}
}
// 4. Check for admin content in non-legacy tenant DBs
if let Ok(mut stmt) = users_conn
.prepare("SELECT id, username FROM users WHERE account_type = 'admin' AND id != 1;")
{
if let Ok(mut rows) = stmt.query([]) {
while let Ok(Some(row)) = rows.next() {
let id: i64 = row.get(0).unwrap_or(0);
let username: String = row.get(1).unwrap_or_default();
let tenant_db_path = data_dir
.join("users")
.join(id.to_string())
.join("content.db");
if tenant_db_path.exists() {
if let Ok(conn) = Connection::open(&tenant_db_path) {
let url_count: i64 = conn
.query_row("SELECT COUNT(*) FROM urls;", [], |r| r.get(0))
.unwrap_or(0);
let page_count: i64 = conn
.query_row("SELECT COUNT(*) FROM landing_pages;", [], |r| r.get(0))
.unwrap_or(0);
if url_count > 0 || page_count > 0 {
warnings.push(format!("Admin user '{}' (ID {}) has content in isolated tenant DB ({} URLs, {} pages). Admin content should be in legacy DB 1.", username, id, url_count, page_count));
}
}
}
}
}
}
Ok((errors, warnings))
}