Author SHA1 Message Date
MichaelandClaude Opus 5.5 97d63b2097 fix(dlc) : la liste DLC renvoyait 0 produit à cause d'anciennes lignes sans nom
En production, 378 anciens produits DLC (ids 260 à 944, créés en 2025) ont
une colonne name à NULL. Le serveur Rust la décodait en String : sqlx
échouait sur ces lignes et toute réponse qui en contenait finissait en 500
(liste vide sans fournisseur choisi, 0 pour Baby délice, Depot, Lidis,
My Candy Shop ; SLS Agency et XMLS n'en ont aucune et s'affichaient).

name devient Option<String> dans DlcProductsRow, comme le générateur le
produit quand il tourne contre la base de production (colonne nullable).
La validation à la création reste inchangée : name y est toujours requis.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 11:20:17 +02:00
LogiFlowandClaude Opus 5.5 af6a854400 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
2026-10-15 20:46:16 +02:00
2 changed files with 38 additions and 1 deletions

No files matched your search

+1 -1
View File
@@ -833,7 +833,7 @@ pub struct DlcProductsRow {
#[serde(rename = "id")]
pub id: i32,
#[serde(rename = "name")]
pub name: String,
pub name: Option<String>,
#[serde(rename = "productName")]
pub product_name: Option<String>,
#[serde(rename = "gencode")]
+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;