Add SFTP connection and file management script
- Implemented a Python script using Paramiko to connect to an SFTP server. - Added functionality to list files in the current directory and check for the existence of a '/backups' directory. - Included error handling for directory listing and creation. - Implemented a test upload feature to verify file upload capabilities. - Added cleanup for uploaded test files and ensured proper connection closure.
This commit is contained in:
parent
e45b1ed19e
commit
1b5085de21
39
.env.bak
39
.env.bak
@ -34,31 +34,37 @@ LOG_FILE=logs/app.log
|
|||||||
# Repository: https://g.bmcnetworks.dk/ct/bmc_hub
|
# Repository: https://g.bmcnetworks.dk/ct/bmc_hub
|
||||||
GITHUB_REPO=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)
|
# e-conomic Integration (Optional)
|
||||||
# =====================================================
|
# =====================================================
|
||||||
# Get credentials from e-conomic Settings -> Integrations -> API
|
# Get credentials from e-conomic Settings -> Integrations -> API
|
||||||
ECONOMIC_API_URL=https://restapi.e-conomic.com
|
ECONOMIC_API_URL=https://restapi.e-conomic.com
|
||||||
ECONOMIC_APP_SECRET_TOKEN=your_app_secret_token_here
|
ECONOMIC_APP_SECRET_TOKEN=wy8ZhYBLsKhx8McirhvoBR9B6ILuoYJkEaiED5ijsA8
|
||||||
ECONOMIC_AGREEMENT_GRANT_TOKEN=your_agreement_grant_token_here
|
ECONOMIC_AGREEMENT_GRANT_TOKEN=5AhipRpMpoLx3uklPMQZbtZ4Zw4mV9lDuFI264II0lE
|
||||||
|
|
||||||
# 🚨 SAFETY SWITCHES - Beskytter mod utilsigtede ændringer
|
# 🚨 SAFETY SWITCHES - Beskytter mod utilsigtede ændringer
|
||||||
ECONOMIC_READ_ONLY=true # Set to false ONLY after testing
|
ECONOMIC_READ_ONLY=true # Set to false ONLY after testing
|
||||||
ECONOMIC_DRY_RUN=true # Set to false ONLY when ready for production writes
|
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_URL=https://bmcnetworks.od2.vtiger.com
|
||||||
VTIGER_USERNAME=ct@bmcnetworks.dk
|
VTIGER_USERNAME=ct@bmcnetworks.dk
|
||||||
VTIGER_API_KEY=bD8cW8zRFuKpPZ2S
|
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
|
# 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_AUTO_ROUND=true
|
||||||
TIMETRACKING_ROUND_INCREMENT=0.5
|
TIMETRACKING_ROUND_INCREMENT=0.5
|
||||||
TIMETRACKING_ROUND_METHOD=up
|
TIMETRACKING_ROUND_METHOD=up
|
||||||
@ -66,6 +72,15 @@ TIMETRACKING_ROUND_METHOD=up
|
|||||||
# Time Tracking Safety Switches
|
# Time Tracking Safety Switches
|
||||||
TIMETRACKING_VTIGER_READ_ONLY=true
|
TIMETRACKING_VTIGER_READ_ONLY=true
|
||||||
TIMETRACKING_VTIGER_DRY_RUN=true
|
TIMETRACKING_VTIGER_DRY_RUN=true
|
||||||
TIMETRACKING_ECONOMIC_READ_ONLY=true
|
TIMETRACKING_ECONOMIC_READ_ONLY=false
|
||||||
TIMETRACKING_ECONOMIC_DRY_RUN=true
|
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
|
||||||
|
|||||||
96
.env.bak2
Normal file
96
.env.bak2
Normal file
@ -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
|
||||||
88
BACKUP_RESTORE_TEST_PLAN.md
Normal file
88
BACKUP_RESTORE_TEST_PLAN.md
Normal file
@ -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)
|
||||||
4
main.py
4
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.emails.frontend import views as emails_views
|
||||||
from app.settings.backend import router as settings_api
|
from app.settings.backend import router as settings_api
|
||||||
from app.settings.backend import views as settings_views
|
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
|
from app.backups.frontend import views as backups_views
|
||||||
|
|
||||||
# Configure logging
|
# 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(tags_api.router, prefix="/api/v1", tags=["Tags"])
|
||||||
app.include_router(emails_api.router, prefix="/api/v1", tags=["Emails"])
|
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(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
|
# Frontend Routers
|
||||||
app.include_router(dashboard_views.router, tags=["Frontend"])
|
app.include_router(dashboard_views.router, tags=["Frontend"])
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
70
test_sftp.py
Normal file
70
test_sftp.py
Normal file
@ -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()
|
||||||
Loading…
Reference in New Issue
Block a user