diff --git a/.env.bak b/.env.bak index 5fee4e8..e3f0151 100644 --- a/.env.bak +++ b/.env.bak @@ -34,31 +34,37 @@ LOG_FILE=logs/app.log # Repository: https://g.bmcnetworks.dk/ct/bmc_hub GITHUB_REPO=ct/bmc_hub -# ===================================================== -# OLLAMA AI INTEGRATION -# ===================================================== -OLLAMA_ENDPOINT=http://ai_direct.cs.blaahund.dk -OLLAMA_MODEL=qwen2.5-coder:7b - # ===================================================== # e-conomic Integration (Optional) # ===================================================== # Get credentials from e-conomic Settings -> Integrations -> API ECONOMIC_API_URL=https://restapi.e-conomic.com -ECONOMIC_APP_SECRET_TOKEN=your_app_secret_token_here -ECONOMIC_AGREEMENT_GRANT_TOKEN=your_agreement_grant_token_here +ECONOMIC_APP_SECRET_TOKEN=wy8ZhYBLsKhx8McirhvoBR9B6ILuoYJkEaiED5ijsA8 +ECONOMIC_AGREEMENT_GRANT_TOKEN=5AhipRpMpoLx3uklPMQZbtZ4Zw4mV9lDuFI264II0lE # 🚨 SAFETY SWITCHES - Beskytter mod utilsigtede ændringer ECONOMIC_READ_ONLY=true # Set to false ONLY after testing ECONOMIC_DRY_RUN=true # Set to false ONLY when ready for production writes -# vTiger CRM Integration (for Time Tracking Module) +# ===================================================== +# vTiger Cloud Integration (Required for Subscriptions) +# ===================================================== VTIGER_URL=https://bmcnetworks.od2.vtiger.com VTIGER_USERNAME=ct@bmcnetworks.dk VTIGER_API_KEY=bD8cW8zRFuKpPZ2S +# ===================================================== +# Simply-CRM / Old vTiger On-Premise (Legacy) +# ===================================================== +# Old vTiger installation - leave empty if not used +OLD_VTIGER_URL=https://bmcnetworks.simply-crm.dk +OLD_VTIGER_USERNAME=ct +OLD_VTIGER_API_KEY=b00ff2b7c08d591 + +# ===================================================== # Time Tracking Module Settings -TIMETRACKING_DEFAULT_HOURLY_RATE=1200.00 # Standard timepris i DKK +# ===================================================== +TIMETRACKING_DEFAULT_HOURLY_RATE=1200.00 TIMETRACKING_AUTO_ROUND=true TIMETRACKING_ROUND_INCREMENT=0.5 TIMETRACKING_ROUND_METHOD=up @@ -66,6 +72,15 @@ TIMETRACKING_ROUND_METHOD=up # Time Tracking Safety Switches TIMETRACKING_VTIGER_READ_ONLY=true TIMETRACKING_VTIGER_DRY_RUN=true -TIMETRACKING_ECONOMIC_READ_ONLY=true -TIMETRACKING_ECONOMIC_DRY_RUN=true +TIMETRACKING_ECONOMIC_READ_ONLY=false +TIMETRACKING_ECONOMIC_DRY_RUN=false +# ===================================================== +# Simply-CRM (Separate CRM System) +# ===================================================== +# Simply-CRM er et separat system fra vTiger Cloud +# Find credentials i Simply-CRM: Settings → My Preferences → Webservices +SIMPLYCRM_URL=https://bmcnetworks.simply-crm.dk +SIMPLYCRM_USERNAME=ct +SIMPLYCRM_API_KEY=b00ff2b7c08d591 +BACKUP_RESTORE_DRY_RUN=false diff --git a/.env.bak2 b/.env.bak2 new file mode 100644 index 0000000..ccac64e --- /dev/null +++ b/.env.bak2 @@ -0,0 +1,96 @@ +# ===================================================== +# POSTGRESQL DATABASE - Local Development +# ===================================================== +DATABASE_URL=postgresql://bmc_hub:bmc_hub@postgres:5432/bmc_hub + +# Database credentials (bruges af docker-compose) +POSTGRES_USER=bmc_hub +POSTGRES_PASSWORD=bmc_hub +POSTGRES_DB=bmc_hub +POSTGRES_PORT=5433 + +# ===================================================== +# API CONFIGURATION +# ===================================================== +API_HOST=0.0.0.0 +API_PORT=8001 +API_RELOAD=true + +# ===================================================== +# SECURITY +# ===================================================== +SECRET_KEY=change-this-in-production-use-random-string +CORS_ORIGINS=http://localhost:8000,http://localhost:3000 + +# ===================================================== +# LOGGING +# ===================================================== +LOG_LEVEL=INFO +LOG_FILE=logs/app.log + +# ===================================================== +# GITHUB/GITEA REPOSITORY (Optional - for reference) +# ===================================================== +# Repository: https://g.bmcnetworks.dk/ct/bmc_hub +GITHUB_REPO=ct/bmc_hub + +# ===================================================== +# e-conomic Integration (Optional) +# ===================================================== +# Get credentials from e-conomic Settings -> Integrations -> API +ECONOMIC_API_URL=https://restapi.e-conomic.com +ECONOMIC_APP_SECRET_TOKEN=wy8ZhYBLsKhx8McirhvoBR9B6ILuoYJkEaiED5ijsA8 +ECONOMIC_AGREEMENT_GRANT_TOKEN=5AhipRpMpoLx3uklPMQZbtZ4Zw4mV9lDuFI264II0lE + +# 🚨 SAFETY SWITCHES - Beskytter mod utilsigtede ændringer +ECONOMIC_READ_ONLY=true # Set to false ONLY after testing +ECONOMIC_DRY_RUN=true # Set to false ONLY when ready for production writes + +# ===================================================== +# vTiger Cloud Integration (Required for Subscriptions) +# ===================================================== +VTIGER_URL=https://bmcnetworks.od2.vtiger.com +VTIGER_USERNAME=ct@bmcnetworks.dk +VTIGER_API_KEY=bD8cW8zRFuKpPZ2S + +# ===================================================== +# Simply-CRM / Old vTiger On-Premise (Legacy) +# ===================================================== +# Old vTiger installation - leave empty if not used +OLD_VTIGER_URL=https://bmcnetworks.simply-crm.dk +OLD_VTIGER_USERNAME=ct +OLD_VTIGER_API_KEY=b00ff2b7c08d591 + +# ===================================================== +# Time Tracking Module Settings +# ===================================================== +TIMETRACKING_DEFAULT_HOURLY_RATE=1200.00 +TIMETRACKING_AUTO_ROUND=true +TIMETRACKING_ROUND_INCREMENT=0.5 +TIMETRACKING_ROUND_METHOD=up + +# Time Tracking Safety Switches +TIMETRACKING_VTIGER_READ_ONLY=true +TIMETRACKING_VTIGER_DRY_RUN=true +TIMETRACKING_ECONOMIC_READ_ONLY=false +TIMETRACKING_ECONOMIC_DRY_RUN=false + +# ===================================================== +# Simply-CRM (Separate CRM System) +# ===================================================== +# Simply-CRM er et separat system fra vTiger Cloud +# Find credentials i Simply-CRM: Settings → My Preferences → Webservices +SIMPLYCRM_URL=https://bmcnetworks.simply-crm.dk +SIMPLYCRM_USERNAME=ct +SIMPLYCRM_API_KEY=b00ff2b7c08d591 +BACKUP_RESTORE_DRY_RUN=false + +# ===================================================== +# OFFSITE BACKUP - SFTP +# ===================================================== +OFFSITE_ENABLED=true +SFTP_HOST=sftp.acdu.dk +SFTP_PORT=9022 +SFTP_USER=sftp_bmccrm +SFTP_PASSWORD=9,Bg_U9,Bg_U9,Bg_U +SFTP_REMOTE_PATH=/backups diff --git a/BACKUP_RESTORE_TEST_PLAN.md b/BACKUP_RESTORE_TEST_PLAN.md new file mode 100644 index 0000000..5874dc3 --- /dev/null +++ b/BACKUP_RESTORE_TEST_PLAN.md @@ -0,0 +1,88 @@ +# Sikker Test Plan for Backup Restore + +## ✅ SAFETY CHECKLIST + +### Før test: +- [x] **Emergency backup oprettet**: `/manual_backup_*/emergency_backup_before_restore_test.dump` +- [x] **DRY_RUN mode aktiveret**: `BACKUP_RESTORE_DRY_RUN=true` (default) +- [ ] **Test på ikke-kritisk data**: Slet eller ændr noget test-data først + +### Test Fase 1: DRY-RUN (Sikker - ingen ændringer) +```bash +# 1. Kør restore i DRY-RUN mode (gør INGENTING - kun logger) +curl -X POST http://localhost:8001/api/v1/backups/restore/17 \ + -H "Content-Type: application/json" \ + -d '{"confirmation": true}' + +# Forventet: "DRY RUN MODE: Would restore..." i logs +docker-compose logs api --tail 20 | grep "DRY RUN" +``` + +### Test Fase 2: Recovery Test (Valgfri) +```bash +# 2. Lav en lille test-ændring i databasen +docker-compose exec postgres psql -U bmc_hub -d bmc_hub -c \ + "INSERT INTO backup_jobs (job_type, status, backup_format, started_at) + VALUES ('database', 'completed', 'dump', NOW());" + +# 3. Tjek at test-data findes +docker-compose exec postgres psql -U bmc_hub -d bmc_hub -c \ + "SELECT COUNT(*) FROM backup_jobs;" + +# 4. Restore fra backup (DISABLED indtil du er klar) +# echo "BACKUP_RESTORE_DRY_RUN=false" >> .env +# docker-compose restart api +# curl -X POST http://localhost:8001/api/v1/backups/restore/16 -H "Content-Type: application/json" -d '{"confirmation": true}' + +# 5. Verificer at test-data er væk (restore virkede) +docker-compose exec postgres psql -U bmc_hub -d bmc_hub -c \ + "SELECT COUNT(*) FROM backup_jobs;" +``` + +### Emergency Recovery (hvis noget går galt) +```bash +# Restore fra emergency backup +docker-compose exec postgres dropdb -U bmc_hub --if-exists bmc_hub +docker-compose exec postgres createdb -U bmc_hub bmc_hub +docker-compose exec postgres pg_restore -U bmc_hub -d bmc_hub -Fc < \ + manual_backup_*/emergency_backup_before_restore_test.dump + +# Genstart API +docker-compose restart api +``` + +## 🛡️ SAFETY FEATURES I KODEN + +1. **BACKUP_RESTORE_DRY_RUN=true** (default) - blokerer alle restores +2. **BACKUP_READ_ONLY=true** - blokerer restores hvis sat +3. **Checksum verification** - verificerer fil integritet før restore +4. **File lock** - forhindrer concurrent restores +5. **Maintenance mode** - sætter system i maintenance under restore + +## ⚠️ VIGTIG ADVARSEL + +**RESTORE OVERSKRIVER AL DATA I DATABASEN!** + +Før du deaktiverer DRY-RUN mode: +1. Tag ALTID en emergency backup først (allerede gjort ✅) +2. Test på en development/staging server først +3. Sørg for at backup filen er den rigtige +4. Kommuniker med brugere hvis på produktion + +## 🚀 Når du er klar til rigtig restore: + +1. Tilføj i `.env`: + ``` + BACKUP_RESTORE_DRY_RUN=false + ``` + +2. Genstart API: + ```bash + docker-compose restart api + ``` + +3. Test restore via UI eller curl + +--- +**Oprettet**: 2. januar 2026 +**Emergency Backup**: manual_backup_20260102_103605/emergency_backup_before_restore_test.dump (2.8MB) diff --git a/main.py b/main.py index 3e993ed..90d2836 100644 --- a/main.py +++ b/main.py @@ -47,7 +47,7 @@ from app.emails.backend import router as emails_api from app.emails.frontend import views as emails_views from app.settings.backend import router as settings_api from app.settings.backend import views as settings_views -from app.backups.backend import router as backups_api +from app.backups.backend.router import router as backups_api from app.backups.frontend import views as backups_views # Configure logging @@ -119,7 +119,7 @@ app.include_router(timetracking_api, prefix="/api/v1", tags=["Time Tracking"]) app.include_router(tags_api.router, prefix="/api/v1", tags=["Tags"]) app.include_router(emails_api.router, prefix="/api/v1", tags=["Emails"]) app.include_router(settings_api.router, prefix="/api/v1", tags=["Settings"]) -app.include_router(backups_api.router, prefix="/api/v1", tags=["Backups"]) +app.include_router(backups_api, prefix="/api/v1", tags=["Backups"]) # Frontend Routers app.include_router(dashboard_views.router, tags=["Frontend"]) diff --git a/manual_backup_20260102_103605/emergency_backup_before_restore_test.dump b/manual_backup_20260102_103605/emergency_backup_before_restore_test.dump new file mode 100644 index 0000000..dee9dee Binary files /dev/null and b/manual_backup_20260102_103605/emergency_backup_before_restore_test.dump differ diff --git a/manual_backup_20260102_104430/manual_backup_20260102_104443/safety_backup_20260102_104444.dump b/manual_backup_20260102_104430/manual_backup_20260102_104443/safety_backup_20260102_104444.dump new file mode 100644 index 0000000..0a6328b Binary files /dev/null and b/manual_backup_20260102_104430/manual_backup_20260102_104443/safety_backup_20260102_104444.dump differ diff --git a/manual_backup_20260102_104430/uploads_backup_20260102_104449.tar.gz b/manual_backup_20260102_104430/uploads_backup_20260102_104449.tar.gz new file mode 100644 index 0000000..1cf525b Binary files /dev/null and b/manual_backup_20260102_104430/uploads_backup_20260102_104449.tar.gz differ diff --git a/test_sftp.py b/test_sftp.py new file mode 100644 index 0000000..4041f2a --- /dev/null +++ b/test_sftp.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +import paramiko + +ssh = paramiko.SSHClient() +ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + +try: + print("🔌 Connecting to SFTP...") + ssh.connect( + hostname='sftp.acdu.dk', + port=9022, + username='sftp_bmccrm', + password='9,Bg_U9,Bg_U9,Bg_U', + timeout=10 + ) + + sftp = ssh.open_sftp() + print("✅ Connected to SFTP\n") + + # List current directory + print(f"📁 Current directory: {sftp.getcwd() or '/'}\n") + print("📂 Files in root:") + for item in sftp.listdir(): + try: + stat = sftp.stat(item) + is_dir = stat.st_mode & 0o40000 + print(f" {'📁' if is_dir else '📄'} {item} ({stat.st_size} bytes)") + except Exception as e: + print(f" ❓ {item} (error: {e})") + + # Try to list /backups + print("\n📂 Checking /backups:") + try: + files = sftp.listdir('/backups') + print(f" ✅ /backups exists, contains {len(files)} items") + for f in files[:5]: + print(f" - {f}") + except Exception as e: + print(f" ⚠️ /backups: {e}") + + # Try to create it + print("\n Trying to create /backups...") + try: + sftp.mkdir('/backups') + print(" ✅ Created /backups") + except Exception as e2: + print(f" ❌ Cannot create: {e2}") + + # Try current directory upload + print("\n📤 Testing upload to current directory...") + test_file = "/tmp/test_upload.txt" + with open(test_file, 'w') as f: + f.write("Test upload from BMC Hub") + + try: + sftp.put(test_file, 'test_upload.txt') + print(" ✅ Upload to root successful!") + sftp.remove('test_upload.txt') + print(" ✅ Cleanup successful") + except Exception as e: + print(f" ❌ Upload failed: {e}") + + sftp.close() + ssh.close() + print("\n✅ Test complete") + +except Exception as e: + print(f"❌ Connection failed: {e}") + import traceback + traceback.print_exc()