Skip to content

Commit 247d4b8

Browse files
Extend Rust status and product security controls
1 parent a49b075 commit 247d4b8

5 files changed

Lines changed: 930 additions & 35 deletions

File tree

rust/iscy-backend/src/db_admin.rs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ pub struct DbAdminOutcome {
2424
pub seeded_demo: bool,
2525
}
2626

27+
#[derive(Debug, Clone)]
28+
pub struct DbMigrationStatus {
29+
pub database_kind: &'static str,
30+
pub applied_count: i64,
31+
pub expected_count: usize,
32+
pub latest_applied_version: Option<String>,
33+
pub latest_applied_at: Option<String>,
34+
pub expected_latest_version: Option<&'static str>,
35+
}
36+
2737
#[derive(Clone, Copy)]
2838
struct Migration {
2939
version: &'static str,
@@ -1287,6 +1297,110 @@ pub async fn run_db_admin_action(
12871297
bail!("Nicht unterstuetztes DATABASE_URL-Schema fuer Rust-DB-Admin");
12881298
}
12891299

1300+
pub async fn migration_status(database_url: &str) -> anyhow::Result<DbMigrationStatus> {
1301+
let normalized_url = normalize_database_url(database_url);
1302+
if normalized_url.starts_with("postgres://") || normalized_url.starts_with("postgresql://") {
1303+
let pool = PgPoolOptions::new()
1304+
.max_connections(1)
1305+
.connect(&normalized_url)
1306+
.await
1307+
.context("PostgreSQL-Verbindung fuer Rust-Migrationsstatus fehlgeschlagen")?;
1308+
let table_exists: bool =
1309+
sqlx::query_scalar("SELECT to_regclass('public.iscy_schema_migrations') IS NOT NULL")
1310+
.fetch_one(&pool)
1311+
.await
1312+
.context("PostgreSQL-Migrationstabelle konnte nicht geprueft werden")?;
1313+
if !table_exists {
1314+
return Ok(empty_migration_status("postgres"));
1315+
}
1316+
let applied_count: i64 =
1317+
sqlx::query_scalar("SELECT COUNT(*)::bigint FROM iscy_schema_migrations")
1318+
.fetch_one(&pool)
1319+
.await
1320+
.context("PostgreSQL-Migrationsanzahl konnte nicht gelesen werden")?;
1321+
let latest = sqlx::query(
1322+
"SELECT version, applied_at FROM iscy_schema_migrations ORDER BY version DESC LIMIT 1",
1323+
)
1324+
.fetch_optional(&pool)
1325+
.await
1326+
.context("PostgreSQL-letzte Migration konnte nicht gelesen werden")?;
1327+
return Ok(migration_status_from_row(
1328+
"postgres",
1329+
applied_count,
1330+
latest.map(|row| {
1331+
(
1332+
row.try_get::<String, _>("version"),
1333+
row.try_get::<String, _>("applied_at"),
1334+
)
1335+
}),
1336+
)?);
1337+
}
1338+
if normalized_url.starts_with("sqlite:") {
1339+
let options = SqliteConnectOptions::from_str(&normalized_url)
1340+
.context("SQLite-DATABASE_URL konnte nicht gelesen werden")?
1341+
.create_if_missing(false);
1342+
let pool = SqlitePoolOptions::new()
1343+
.max_connections(1)
1344+
.connect_with(options)
1345+
.await
1346+
.context("SQLite-Verbindung fuer Rust-Migrationsstatus fehlgeschlagen")?;
1347+
if !sqlite_table_exists(&pool, "iscy_schema_migrations").await? {
1348+
return Ok(empty_migration_status("sqlite"));
1349+
}
1350+
let applied_count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM iscy_schema_migrations")
1351+
.fetch_one(&pool)
1352+
.await
1353+
.context("SQLite-Migrationsanzahl konnte nicht gelesen werden")?;
1354+
let latest = sqlx::query(
1355+
"SELECT version, applied_at FROM iscy_schema_migrations ORDER BY version DESC LIMIT 1",
1356+
)
1357+
.fetch_optional(&pool)
1358+
.await
1359+
.context("SQLite-letzte Migration konnte nicht gelesen werden")?;
1360+
return Ok(migration_status_from_row(
1361+
"sqlite",
1362+
applied_count,
1363+
latest.map(|row| {
1364+
(
1365+
row.try_get::<String, _>("version"),
1366+
row.try_get::<String, _>("applied_at"),
1367+
)
1368+
}),
1369+
)?);
1370+
}
1371+
bail!("Nicht unterstuetztes DATABASE_URL-Schema fuer Rust-Migrationsstatus");
1372+
}
1373+
1374+
fn empty_migration_status(database_kind: &'static str) -> DbMigrationStatus {
1375+
DbMigrationStatus {
1376+
database_kind,
1377+
applied_count: 0,
1378+
expected_count: MIGRATIONS.len(),
1379+
latest_applied_version: None,
1380+
latest_applied_at: None,
1381+
expected_latest_version: MIGRATIONS.last().map(|migration| migration.version),
1382+
}
1383+
}
1384+
1385+
fn migration_status_from_row(
1386+
database_kind: &'static str,
1387+
applied_count: i64,
1388+
latest: Option<(Result<String, sqlx::Error>, Result<String, sqlx::Error>)>,
1389+
) -> Result<DbMigrationStatus, sqlx::Error> {
1390+
let (latest_applied_version, latest_applied_at) = match latest {
1391+
Some((version, applied_at)) => (Some(version?), Some(applied_at?)),
1392+
None => (None, None),
1393+
};
1394+
Ok(DbMigrationStatus {
1395+
database_kind,
1396+
applied_count,
1397+
expected_count: MIGRATIONS.len(),
1398+
latest_applied_version,
1399+
latest_applied_at,
1400+
expected_latest_version: MIGRATIONS.last().map(|migration| migration.version),
1401+
})
1402+
}
1403+
12901404
pub async fn run_sqlite_migrations(pool: &SqlitePool) -> anyhow::Result<Vec<&'static str>> {
12911405
sqlx::query(
12921406
r#"

0 commit comments

Comments
 (0)