refactor: complete v0.8 core architecture

This commit is contained in:
thakares committed 2026-08-21 13:56:36 +05:30
1 parent f138a645f8
commit d7e0ac7679
98 files changed
+8413 -3044

No files matched your search

+232 -152
View File
@@ -1,23 +1,27 @@
use crate::db::topology::Topology;
use crate::identity::TenantId;
use chrono::{DateTime, Utc};
use rusqlite::Connection;
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RegistryIssueType {
DuplicateSlug,
InvalidTargetType,
InvalidStatus,
MissingOwner,
MissingDatabase,
MissingTenant,
MissingTarget,
CorruptDatabase,
AccessFailure,
TrueOrphan,
Conflict,
StaleReservation,
TenantAdminHasIsolatedContent,
InvalidStatus,
InvalidTargetType,
}
#[derive(Debug, Clone)]
pub struct RegistryIssue {
pub slug: String,
pub target_type: String,
pub owner_user_id: i64,
pub owner_tenant_id: String,
pub database_path: PathBuf,
pub target_id: String,
pub issue_type: RegistryIssueType,
@@ -27,137 +31,237 @@ pub struct RegistryIssue {
pub struct RegistryValidator;
impl RegistryValidator {
/// Scans the global_slugs registry and returns a list of detected issues.
/// Scans the v0.8 slug registries (`global_urls.db`, `global_landing_pages.db`, `reserved.db`)
/// and returns a list of detected issues categorized per safety policies.
pub fn scan(
system_conn: &Connection,
_system_conn: &Connection,
users_conn: &Connection,
data_dir: &Path,
slug_filter: Option<&str>,
) -> Result<Vec<RegistryIssue>, Box<dyn std::error::Error>> {
use chrono::{DateTime, Utc};
let topology = Topology::new(data_dir);
let mut issues = Vec::new();
// 1. Check duplicate slugs (only if not filtering by single slug)
if slug_filter.is_none() {
let total_count: i64 =
system_conn.query_row("SELECT COUNT(*) FROM global_slugs;", [], |r| r.get(0))?;
let distinct_count: i64 = system_conn.query_row(
"SELECT COUNT(DISTINCT slug) FROM global_slugs;",
[],
|r| r.get(0),
)?;
if total_count != distinct_count {
issues.push(RegistryIssue {
slug: "*".to_string(),
target_type: "system".to_string(),
owner_user_id: 0,
database_path: data_dir.join("admin/system.db"),
target_id: "".to_string(),
issue_type: RegistryIssueType::DuplicateSlug,
description: format!(
"Duplicate slugs found in global_slugs table (total rows: {}, distinct slugs: {})",
total_count, distinct_count
),
});
}
let urls_path = topology.global_urls_db();
let pages_path = topology.global_landing_pages_db();
let reserved_path = topology.reserved_db();
if !urls_path.exists() || !pages_path.exists() || !reserved_path.exists() {
issues.push(RegistryIssue {
slug: "*".to_string(),
target_type: "system".to_string(),
owner_tenant_id: "".to_string(),
database_path: urls_path,
target_id: "".to_string(),
issue_type: RegistryIssueType::AccessFailure,
description: "One or more v0.8 slug databases are missing from disk".to_string(),
});
return Ok(issues);
}
// 2. Scan global slugs
let (query, params_string) = if let Some(slug) = slug_filter {
let urls_conn = Connection::open(&urls_path)?;
let pages_conn = Connection::open(&pages_path)?;
let reserved_conn = Connection::open(&reserved_path)?;
// 1. Scan global_urls.db
Self::scan_table(
&urls_conn,
users_conn,
&reserved_conn,
&pages_conn,
&topology,
"url",
slug_filter,
&mut issues,
)?;
// 2. Scan global_landing_pages.db
Self::scan_table(
&pages_conn,
users_conn,
&reserved_conn,
&urls_conn,
&topology,
"page",
slug_filter,
&mut issues,
)?;
Ok(issues)
}
#[allow(clippy::too_many_arguments)]
fn scan_table(
conn: &Connection,
users_conn: &Connection,
reserved_conn: &Connection,
other_conn: &Connection,
topology: &Topology,
target_type: &str,
slug_filter: Option<&str>,
issues: &mut Vec<RegistryIssue>,
) -> Result<(), Box<dyn std::error::Error>> {
let (query, params_vec) = if let Some(slug) = slug_filter {
(
"SELECT slug, owner_user_id, target_type, target_id, created_at, status FROM global_slugs WHERE slug = ?1;",
format!(
"SELECT slug, owner_tenant_id, target_id, created_at, status FROM global_{}s WHERE slug = ?1;",
if target_type == "url" { "url" } else { "landing_page" }
),
vec![slug.to_string()],
)
} else {
(
"SELECT slug, owner_user_id, target_type, target_id, created_at, status FROM global_slugs;",
format!(
"SELECT slug, owner_tenant_id, target_id, created_at, status FROM global_{}s;",
if target_type == "url" {
"url"
} else {
"landing_page"
}
),
vec![],
)
};
let mut stmt = system_conn.prepare(query)?;
let mut rows = stmt.query(rusqlite::params_from_iter(params_string))?;
let mut stmt = conn.prepare(&query)?;
let mut rows = stmt.query(rusqlite::params_from_iter(params_vec))?;
while let Some(row) = rows.next()? {
let slug: String = row.get(0)?;
let owner_user_id: i64 = row.get(1)?;
let target_type: String = row.get(2)?;
let target_id: String = row.get(3)?;
let created_at_str: String = row.get(4)?;
let status: String = row.get(5)?;
let owner_tenant_id_str: String = row.get(1)?;
let target_id: String = row.get(2)?;
let created_at_str: String = row.get(3)?;
let status: String = row.get(4)?;
let content_db_path = if owner_user_id == 1 {
data_dir.join("users").join("1").join("content.db")
} else {
data_dir
.join("users")
.join(owner_user_id.to_string())
.join("content.db")
let tenant_id_res = TenantId::parse(&owner_tenant_id_str);
let content_db_path = match tenant_id_res {
Ok(tid) => topology.tenant_dir(tid).join("content.db"),
Err(_) => topology.users_dir().join("_invalid").join("content.db"),
};
// Target type check
if target_type != "url" && target_type != "page" {
// 1. Conflict with reserved.db
let is_reserved: bool = reserved_conn
.query_row(
"SELECT EXISTS(SELECT 1 FROM reserved_slugs WHERE slug = ?1);",
[&slug],
|r| r.get(0),
)
.unwrap_or(false);
if is_reserved {
issues.push(RegistryIssue {
slug: slug.clone(),
target_type: target_type.clone(),
owner_user_id,
database_path: content_db_path.clone(),
target_type: target_type.to_string(),
owner_tenant_id: owner_tenant_id_str.clone(),
database_path: topology.reserved_db(),
target_id: target_id.clone(),
issue_type: RegistryIssueType::InvalidTargetType,
issue_type: RegistryIssueType::Conflict,
description: format!("Slug '{}' conflicts with a reserved system route", slug),
});
}
// 2. Conflict with the other slug database
let other_table = if target_type == "url" {
"global_landing_pages"
} else {
"global_urls"
};
let in_other: bool = other_conn
.query_row(
&format!("SELECT EXISTS(SELECT 1 FROM {other_table} WHERE slug = ?1);"),
[&slug],
|r| r.get(0),
)
.unwrap_or(false);
if in_other {
issues.push(RegistryIssue {
slug: slug.clone(),
target_type: target_type.to_string(),
owner_tenant_id: owner_tenant_id_str.clone(),
database_path: conn.path().map(PathBuf::from).unwrap_or_default(),
target_id: target_id.clone(),
issue_type: RegistryIssueType::Conflict,
description: format!(
"Slug '{}' has invalid target_type '{}'",
slug, target_type
"Slug '{}' exists in both global_urls.db and global_landing_pages.db",
slug
),
});
}
// Status check
if status != "active" && status != "disabled" && status != "reserving" {
// 3. Status check
if status != "active"
&& status != "disabled"
&& status != "reserving"
&& status != "retired"
{
issues.push(RegistryIssue {
slug: slug.clone(),
target_type: target_type.clone(),
owner_user_id,
database_path: content_db_path.clone(),
target_type: target_type.to_string(),
owner_tenant_id: owner_tenant_id_str.clone(),
database_path: conn.path().map(PathBuf::from).unwrap_or_default(),
target_id: target_id.clone(),
issue_type: RegistryIssueType::InvalidStatus,
description: format!("Slug '{}' has invalid status '{}'", slug, status),
});
}
// Check owner
let owner_exists: bool = users_conn
.query_row(
"SELECT EXISTS(SELECT 1 FROM users WHERE id = ?1);",
[owner_user_id],
|r| r.get(0),
)
.unwrap_or(false);
// If retired, no active target check is needed
if status == "retired" {
continue;
}
// 4. Owner tenant check in users.db
let tid = match tenant_id_res {
Ok(t) => t,
Err(_) => {
issues.push(RegistryIssue {
slug: slug.clone(),
target_type: target_type.to_string(),
owner_tenant_id: owner_tenant_id_str.clone(),
database_path: content_db_path,
target_id: target_id.clone(),
issue_type: RegistryIssueType::MissingTenant,
description: format!(
"Slug '{}' has invalid TenantId '{}'",
slug, owner_tenant_id_str
),
});
continue;
}
};
let owner_opt = crate::db::users::get_user_by_tenant_id(users_conn, tid)?;
let owner_exists = match owner_opt {
Some(ref u) => u.status != "deleted",
None => false,
};
if !owner_exists {
issues.push(RegistryIssue {
slug: slug.clone(),
target_type: target_type.clone(),
owner_user_id,
target_type: target_type.to_string(),
owner_tenant_id: owner_tenant_id_str.clone(),
database_path: content_db_path.clone(),
target_id: target_id.clone(),
issue_type: RegistryIssueType::MissingOwner,
issue_type: RegistryIssueType::MissingTenant,
description: format!(
"Slug '{}' references missing owner user ID {}",
slug, owner_user_id
"Slug '{}' references missing or deleted owner tenant '{}'",
slug, owner_tenant_id_str
),
});
continue;
}
// Stale warning check
// 5. Stale reservation check
if status == "reserving" {
if let Ok(created_at) = DateTime::parse_from_rfc3339(&created_at_str) {
let age = Utc::now().signed_duration_since(created_at.with_timezone(&Utc));
if age > chrono::Duration::try_minutes(15).unwrap_or_default() {
issues.push(RegistryIssue {
slug: slug.clone(),
target_type: target_type.clone(),
owner_user_id,
target_type: target_type.to_string(),
owner_tenant_id: owner_tenant_id_str.clone(),
database_path: content_db_path.clone(),
target_id: target_id.clone(),
issue_type: RegistryIssueType::StaleReservation,
@@ -170,19 +274,16 @@ impl RegistryValidator {
}
}
// Check target record exists for active / disabled (and reserving with target_id)
if status == "active"
|| status == "disabled"
|| (status == "reserving" && !target_id.is_empty())
{
// 6. Target record check in tenant content DB
if status == "active" || status == "disabled" {
if !content_db_path.exists() {
issues.push(RegistryIssue {
slug: slug.clone(),
target_type: target_type.clone(),
owner_user_id,
target_type: target_type.to_string(),
owner_tenant_id: owner_tenant_id_str.clone(),
database_path: content_db_path.clone(),
target_id: target_id.clone(),
issue_type: RegistryIssueType::MissingDatabase,
issue_type: RegistryIssueType::TrueOrphan,
description: format!(
"Slug '{}' owner content database does not exist at {:?}",
slug, content_db_path
@@ -190,47 +291,66 @@ impl RegistryValidator {
});
} else {
match Connection::open(&content_db_path) {
Ok(conn) => {
Ok(tenant_conn) => {
let exists = if target_type == "url" {
conn.query_row(
"SELECT EXISTS(SELECT 1 FROM urls WHERE id = ?1);",
[&target_id],
|r| r.get(0),
)
.unwrap_or(false)
} else if target_type == "page" {
conn.query_row(
"SELECT EXISTS(SELECT 1 FROM landing_pages WHERE id = ?1);",
[&target_id],
|r| r.get(0),
)
.unwrap_or(false)
tenant_conn
.query_row(
"SELECT EXISTS(SELECT 1 FROM urls WHERE id = ?1);",
[&target_id],
|r| r.get(0),
)
.unwrap_or(false)
} else {
false
tenant_conn
.query_row(
"SELECT EXISTS(SELECT 1 FROM landing_pages WHERE id = ?1);",
[&target_id],
|r| r.get(0),
)
.unwrap_or(false)
};
if !exists {
issues.push(RegistryIssue {
slug: slug.clone(),
target_type: target_type.clone(),
owner_user_id,
target_type: target_type.to_string(),
owner_tenant_id: owner_tenant_id_str.clone(),
database_path: content_db_path.clone(),
target_id: target_id.clone(),
issue_type: RegistryIssueType::MissingTarget,
description: format!("Slug '{}' (type: '{}', id: '{}') references missing target record in owner's content database", slug, target_type, target_id),
description: format!(
"Slug '{}' (type: '{}', id: '{}') references missing target record in tenant content database",
slug, target_type, target_id
),
});
}
}
Err(rusqlite::Error::SqliteFailure(err, _))
if err.code == rusqlite::ErrorCode::DatabaseCorrupt =>
{
issues.push(RegistryIssue {
slug: slug.clone(),
target_type: target_type.to_string(),
owner_tenant_id: owner_tenant_id_str.clone(),
database_path: content_db_path.clone(),
target_id: target_id.clone(),
issue_type: RegistryIssueType::CorruptDatabase,
description: format!(
"Slug '{}' owner content database is corrupt at {:?}",
slug, content_db_path
),
});
}
Err(e) => {
issues.push(RegistryIssue {
slug: slug.clone(),
target_type: target_type.clone(),
owner_user_id,
target_type: target_type.to_string(),
owner_tenant_id: owner_tenant_id_str.clone(),
database_path: content_db_path.clone(),
target_id: target_id.clone(),
issue_type: RegistryIssueType::MissingDatabase,
issue_type: RegistryIssueType::AccessFailure,
description: format!(
"Slug '{}' owner content database could not be opened: {}",
"Slug '{}' owner content database could not be accessed: {}",
slug, e
),
});
@@ -240,46 +360,6 @@ impl RegistryValidator {
}
}
// 3. Admin Content Reverse Consistency Check (Legacy DB)
// Check if tenant databases contain content for admin users incorrectly (isolated admin content)
if slug_filter.is_none() {
let mut stmt = users_conn.prepare(
"SELECT id, username FROM users WHERE account_type = 'admin' AND id != 1;",
)?;
let mut admin_rows = stmt.query([])?;
while let Some(row) = admin_rows.next()? {
let id: i64 = row.get(0)?;
let username: String = row.get(1)?;
let tenant_db_path = data_dir
.join("users")
.join(id.to_string())
.join("content.db");
if tenant_db_path.exists() {
if let Ok(tenant_conn) = Connection::open(&tenant_db_path) {
let url_count: i64 = tenant_conn
.query_row("SELECT COUNT(*) FROM urls;", [], |r| r.get(0))
.unwrap_or(0);
let page_count: i64 = tenant_conn
.query_row("SELECT COUNT(*) FROM landing_pages;", [], |r| r.get(0))
.unwrap_or(0);
if url_count > 0 || page_count > 0 {
issues.push(RegistryIssue {
slug: "*".to_string(),
target_type: "system".to_string(),
owner_user_id: id,
database_path: tenant_db_path.clone(),
target_id: "".to_string(),
issue_type: RegistryIssueType::TenantAdminHasIsolatedContent,
description: 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(issues)
Ok(())
}
}