Răsfoiți Sursa

TG-203: Finalize system hardening, deprecation fixes, DR, and Taiga sync

lanfr144 1 lună în urmă
părinte
comite
76bd55c52d
2 a modificat fișierele cu 95 adăugiri și 4 ștergeri
  1. 26 4
      app.py
  2. 69 0
      scratch/test_dr.sh

+ 26 - 4
app.py

@@ -836,11 +836,33 @@ with tab_planner:
                 
                 # PDF Generation
                 def generate_pdf(text):
+                    import re
+                    # Aggressive sanitization: if a table row has 4 columns and the last contains a comma or space before 'g', split it
+                    sanitized_lines = []
+                    for line in text.split('\\n'):
+                        line = line.strip()
+                        if line.startswith('|') and line.endswith('|') and '---' not in line:
+                            cols = [c.strip() for c in line.strip('|').split('|')]
+                            # If exactly 4 columns and the last one contains calories and protein merged
+                            if len(cols) == 4 and any(char.isdigit() for char in cols[3]):
+                                # Attempt to split by comma or 'kcal'
+                                if ',' in cols[3]:
+                                    split_last = cols[3].split(',', 1)
+                                    cols = cols[:3] + [split_last[0].strip(), split_last[1].strip()]
+                                elif 'kcal' in cols[3].lower():
+                                    split_last = re.split(r'(?<=kcal)\s+', cols[3], flags=re.IGNORECASE, maxsplit=1)
+                                    if len(split_last) == 2:
+                                        cols = cols[:3] + [split_last[0].strip(), split_last[1].strip()]
+                            sanitized_lines.append('| ' + ' | '.join(cols) + ' |')
+                        else:
+                            sanitized_lines.append(line)
+                    text = '\\n'.join(sanitized_lines)
+
                     pdf = FPDF()
                     pdf.add_page()
                     pdf.set_font("Helvetica", 'B', 16)
-                    pdf.cell(0, 10, "Strict Clinical Meal Plan", ln=True, align='C')
-                    pdf.ln(5)
+                    pdf.cell(0, 10, "Strict Clinical Meal Plan", new_x="LMARGIN", new_y="NEXT", align='C')
+                    pdf.ln(h=5)
                     in_table = False
                     table_data = []
                     
@@ -858,13 +880,13 @@ with tab_planner:
                         except Exception as e:
                             pdf.multi_cell(0, 8, "Table Render Error: " + str(e))
                         table_data.clear()
-                        pdf.ln(5)
+                        pdf.ln(h=5)
 
                     for line in text.split('\n'):
                         line = line.strip()
                         if not line:
                             flush_table()
-                            pdf.ln(2)
+                            pdf.ln(h=2)
                             continue
                         
                         if line.startswith('|') and line.endswith('|'):

+ 69 - 0
scratch/test_dr.sh

@@ -0,0 +1,69 @@
+#!/bin/bash
+# test_dr.sh - Automated Disaster Recovery validation script
+
+# 1. Find the latest backup
+BACKUP_DIR="./backups"
+if [ ! -d "$BACKUP_DIR" ]; then
+    echo "❌ No backups directory found!"
+    exit 1
+fi
+
+LATEST_BACKUP=$(ls -t "$BACKUP_DIR"/food_db_*.sql.gz 2>/dev/null | head -n 1)
+
+if [ -z "$LATEST_BACKUP" ]; then
+    echo "❌ No backup files found in $BACKUP_DIR!"
+    exit 1
+fi
+
+echo "✅ Found latest backup: $LATEST_BACKUP"
+
+# 2. Spin up a sandbox MySQL container
+CONTAINER_NAME="dr_test_mysql_$(date +%s)"
+echo "🚀 Spinning up sandbox MySQL container: $CONTAINER_NAME"
+docker run --name "$CONTAINER_NAME" -e MYSQL_ROOT_PASSWORD=DRTestPass123! -d mysql:8.0
+
+# 3. Wait for MySQL to be ready
+echo "⏳ Waiting for MySQL to initialize..."
+for i in {1..30}; do
+    if docker exec "$CONTAINER_NAME" mysqladmin ping -u root -pDRTestPass123! --silent; then
+        echo "✅ MySQL Sandbox is ready!"
+        break
+    fi
+    sleep 2
+    if [ "$i" -eq 30 ]; then
+        echo "❌ Sandbox failed to initialize."
+        docker rm -f "$CONTAINER_NAME"
+        exit 1
+    fi
+done
+
+# 4. Import the backup
+echo "📥 Restoring backup into Sandbox..."
+# Create the database first since mysqldump might not contain CREATE DATABASE if dumped without --databases
+docker exec "$CONTAINER_NAME" mysql -u root -pDRTestPass123! -e "CREATE DATABASE IF NOT EXISTS food_db;"
+
+gunzip -c "$LATEST_BACKUP" | docker exec -i "$CONTAINER_NAME" mysql -u root -pDRTestPass123! food_db
+
+if [ $? -eq 0 ]; then
+    echo "✅ Database successfully restored!"
+else
+    echo "❌ Failed to restore database."
+    docker rm -f "$CONTAINER_NAME"
+    exit 1
+fi
+
+# 5. Validation
+echo "🔬 Validating data integrity..."
+TABLE_COUNT=$(docker exec "$CONTAINER_NAME" mysql -u root -pDRTestPass123! -N -B -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='food_db';")
+echo "📊 Found $TABLE_COUNT tables in restored database."
+
+if [ "$TABLE_COUNT" -gt 0 ]; then
+    echo "🎉 DISASTER RECOVERY TEST PASSED!"
+else
+    echo "❌ Validation failed: No tables found."
+fi
+
+# 6. Clean up
+echo "🧹 Destroying sandbox container..."
+docker rm -f "$CONTAINER_NAME" >/dev/null
+echo "✅ DR Test Sequence Complete."