fix(rust) : connexion impossible sur une base dont la table sessions est ancienne (#573)

L'ancien serveur gardait les sessions en mémoire : la table sessions de
production n'avait pas la forme attendue (sid, sess, expire) et la
connexion échouait (« column "sid" does not exist »). Au démarrage, une
telle table est renommée (sessions_ancienne_<date>, rien n'est supprimé),
son index aussi, puis la bonne table est créée.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ke2qyS8rgacP3ap5iezDbM
This commit is contained in:
LogiFlowandClaude Opus 5.5 authored and GitHub committed 2026-10-15 20:46:16 +02:00
1 parent bffdca1297
commit af6a854400
1 file changed
+37
+37
View File
@@ -115,6 +115,9 @@ const SCHEMA_STATEMENTS: &[(&str, &str)] = &[
/// Applique les migrations de schéma, puis chiffre les secrets encore en clair
pub async fn run_migrations(db: &PgPool, encryption_key: &str) {
if let Err(error) = set_aside_legacy_sessions_table(db).await {
tracing::error!(%error, "vérification de la table sessions en échec");
}
for (label, statement) in SCHEMA_STATEMENTS {
if let Err(error) = sqlx::query(statement).execute(db).await {
tracing::error!(%error, "migration « {label} » en échec");
@@ -128,6 +131,40 @@ pub async fn run_migrations(db: &PgPool, encryption_key: &str) {
tracing::info!("migrations de démarrage terminées");
}
/// Une base peut porter une table « sessions » d'une autre forme, jamais utilisée
/// (l'ancien serveur gardait les sessions en mémoire) : CREATE TABLE IF NOT EXISTS
/// la laisserait en place et toute connexion échouerait (« column "sid" does not
/// exist »). Elle est renommée, sans rien supprimer, et la bonne table est créée
/// ensuite par SCHEMA_STATEMENTS. Les sessions ne sont que des connexions ouvertes.
async fn set_aside_legacy_sessions_table(db: &PgPool) -> Result<(), sqlx::Error> {
let (exists, expected_columns): (bool, i64) = sqlx::query_as(
"SELECT to_regclass('sessions') IS NOT NULL,
(SELECT count(*) FROM information_schema.columns
WHERE table_schema = current_schema() AND table_name = 'sessions'
AND column_name IN ('sid', 'sess', 'expire'))",
)
.fetch_one(db)
.await?;
if !exists || expected_columns == 3 {
return Ok(());
}
let suffix = chrono::Utc::now().format("%Y%m%d%H%M%S");
let legacy = format!("sessions_ancienne_{suffix}");
let mut transaction = db.begin().await?;
sqlx::query(&format!("ALTER TABLE sessions RENAME TO {legacy}"))
.execute(&mut *transaction)
.await?;
// Un index de ce nom sur l'ancienne table empêcherait de le créer sur la nouvelle
sqlx::query(&format!(
"ALTER INDEX IF EXISTS \"IDX_session_expire\" RENAME TO \"IDX_session_expire_ancienne_{suffix}\""
))
.execute(&mut *transaction)
.await?;
transaction.commit().await?;
tracing::warn!(table = %legacy, "table sessions d'une autre forme mise de côté");
Ok(())
}
/// Mots de passe SMTP des magasins et jetons NocoDB encore en clair → enc:v1
async fn encrypt_plain_secrets(db: &PgPool, key: &str) -> Result<usize, sqlx::Error> {
let mut count = 0;