This commit is contained in:
ilkeral
2025-08-08 07:24:25 +03:00
parent 342f1314c7
commit f4ee7a2d0b
29 changed files with 5189 additions and 1140 deletions

View File

@ -1,128 +1,31 @@
import os
import io
import sys
import locale
import zipfile
import boto3
import tempfile
import traceback
from boto3.s3.transfer import TransferConfig
from django.utils.text import slugify
from datetime import datetime
import requests
import stat
# Add urllib3 import to disable SSL warnings
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
haric_dosya_uzantilari = ['.zip', ]
excluded_folders = ['venv', 'yedek', '.idea', '.sock']
hostname = "ams1.vultrobjects.com"
secret_key = "Ec1pq3OQAObFLOQrfAVqJKhDAk4BkT7OqgYszlef"
access_key = "KQAOMJ8CQ8HP4CY23YPK"
excluded_folders = ['venv', 'yedek', '.idea', '.sock', '.venv']
x = 1
def upload_file_via_presigned_url(url, file_path):
if not os.path.exists(file_path):
print(f"Dosya bulunamadi: {file_path}")
return False
with open(file_path, 'rb') as file_data:
try:
response = requests.put(url, data=file_data)
if response.status_code == 200:
print("Dosya yuklendi!")
return True
else:
print(f"Yukleme olmadi. Status code: {response.status_code}")
print(f"Response: {response.content}")
return False
except Exception as e:
print(f"Yukleme hatasi: {e}")
return False
def get_filtered_folder_names(directory, excluded_folders):
folder_names = []
for item in os.listdir(directory):
item_path = os.path.join(directory, item)
if os.path.isdir(item_path) and item not in excluded_folders:
folder_names.append(item)
return folder_names
def zip_klasor(ziplenecek_klasor, hedef_zip_adi, haric_klasorler=[], haric_dosya_uzantilari=[]):
# Parametrelerin geçerliliğini kontrol et
if not ziplenecek_klasor or not hedef_zip_adi:
raise ValueError("Ziplenecek klasör ve hedef zip adı boş olamaz")
if not os.path.exists(ziplenecek_klasor):
raise FileNotFoundError(f"Ziplenecek klasör bulunamadı: {ziplenecek_klasor}")
# Hedef zip dosyasının bulunacağı dizini oluştur ve izinleri ayarla
hedef_dizin = os.path.dirname(hedef_zip_adi)
# Eğer hedef dizin boşsa, mevcut dizini kullan
if not hedef_dizin:
hedef_dizin = "."
hedef_zip_adi = os.path.join(hedef_dizin, hedef_zip_adi)
if not os.path.exists(hedef_dizin):
os.makedirs(hedef_dizin, mode=0o755, exist_ok=True)
# Zip dosyası oluşturmadan önce izinleri kontrol et
if os.path.exists(hedef_zip_adi):
try:
os.chmod(hedef_zip_adi, 0o666)
except Exception as e:
print(f"Mevcut zip dosyasinin izinleri guncellenemedi: {e}")
with zipfile.ZipFile(hedef_zip_adi, 'w', zipfile.ZIP_DEFLATED) as zipf:
for klasor_yolu, _, dosya_listesi in os.walk(ziplenecek_klasor):
if not any(k in klasor_yolu for k in haric_klasorler):
for dosya in dosya_listesi:
dosya_adi, dosya_uzantisi = os.path.splitext(dosya)
dosya_yolu = os.path.join(klasor_yolu, dosya)
# Dosyanın var olup olmadığını kontrol et
if not os.path.exists(dosya_yolu):
print(f"Dosya bulunamadi: {dosya_yolu}")
continue
# Socket dosyalarını atla
try:
file_stat = os.stat(dosya_yolu)
if stat.S_ISSOCK(file_stat.st_mode):
print(f"Socket dosyasi atlandi: {dosya_yolu}")
continue
except (OSError, PermissionError) as e:
print(f"Dosya stat alinamadi: {dosya_yolu} -> Hata: {e}")
continue
if dosya_uzantisi.lower() not in haric_dosya_uzantilari:
try:
# Dosya okuma izinlerini kontrol et
if os.access(dosya_yolu, os.R_OK):
zipf.write(dosya_yolu, os.path.relpath(dosya_yolu, ziplenecek_klasor))
print(f"Dosya eklendi: {dosya_yolu}")
else:
print(f"Dosya okuma izni yok: {dosya_yolu}")
except (PermissionError, OSError) as e:
print(f"Dosya eklenemedi: {dosya_yolu} -> Hata: {e}")
except Exception as e:
print(f"Beklenmeyen hata: {dosya_yolu} -> Hata: {e}")
# Oluşturulan zip dosyasının izinlerini ayarla
try:
os.chmod(hedef_zip_adi, 0o644)
print(f"Zip dosyasi olusturuldu: {hedef_zip_adi}")
except Exception as e:
print(f"Zip dosyasi izinleri ayarlanamadi: {e}")
def create_ssh_zip(ssh_manager, source_dir, zip_name, excluded_folders=[], excluded_extensions=[]):
"""SSH üzerinden uzak sunucuda zip dosyası oluşturur"""
# Uzak sunucuda geçici zip dosyası yolu
remote_zip_path = f"/tmp/{zip_name}"
# Önce kaynak dizinin varlığını kontrol et
check_dir_command = f"test -d '{source_dir}' && echo 'exists' || echo 'not_exists'"
try:
stdout, stderr, status = ssh_manager.execute_command(check_dir_command)
@ -131,8 +34,15 @@ def create_ssh_zip(ssh_manager, source_dir, zip_name, excluded_folders=[], exclu
except Exception as e:
raise Exception(f"Dizin kontrolü hatası: {str(e)}")
# Zip komutunun varlığını kontrol et ve gerekirse kur
zip_check_command = "which zip || command -v zip"
# Encoding değişkenini kontrol et
locale_command = "locale -a | grep -i utf"
stdout, stderr, status = ssh_manager.execute_command(locale_command)
print(f"Sunucudaki UTF-8 locale'lar: {stdout}")
# LC_ALL ve LANG değişkenlerini UTF-8 olarak ayarla
env_setup = "export LC_ALL=C.UTF-8 2>/dev/null || export LC_ALL=en_US.UTF-8 2>/dev/null || export LC_CTYPE=UTF-8; export LANG=C.UTF-8 2>/dev/null || export LANG=en_US.UTF-8;"
zip_check_command = f"{env_setup} which zip || command -v zip"
try:
stdout, stderr, status = ssh_manager.execute_command(zip_check_command)
if not status:
@ -142,20 +52,23 @@ def create_ssh_zip(ssh_manager, source_dir, zip_name, excluded_folders=[], exclu
except Exception as e:
raise Exception(f"Zip komutu kontrolü hatası: {str(e)}")
# Hariç tutulacak klasörler için exclude parametresi
base_path = os.path.dirname(source_dir)
folder_to_zip = os.path.basename(source_dir)
exclude_args = ""
for folder in excluded_folders:
exclude_args += f" --exclude='{folder}/*' --exclude='{folder}'"
# zip'in exclude path'i, zip komutunun çalıştığı dizine göre olmalı.
# cd '{base_path}' yaptığımız için, exclude path'i '{folder_to_zip}/{folder}/*' şeklinde olmalı.
exclude_args += f" -x '{folder_to_zip}/{folder}/*'"
for ext in excluded_extensions:
exclude_args += f" --exclude='*{ext}'"
exclude_args += f" -x '*{ext}'"
# Eski zip dosyasını temizle
cleanup_command = f"rm -f '{remote_zip_path}'"
ssh_manager.execute_command(cleanup_command)
# Zip komutunu oluştur (daha basit ve güvenilir)
zip_command = f"cd '{source_dir}' && zip -r '{remote_zip_path}' . {exclude_args}"
# UTF-8 desteği için -UN=UTF8 parametresi eklendi ve LC_ALL/LANG değişkenleri ayarlandı
zip_command = f"{env_setup} cd '{base_path}' && zip -UN=UTF8 -r '{remote_zip_path}' '{folder_to_zip}' {exclude_args}"
print(f"Çalıştırılan komut: {zip_command}")
@ -164,10 +77,6 @@ def create_ssh_zip(ssh_manager, source_dir, zip_name, excluded_folders=[], exclu
print(f"Zip komutu sonucu - Status: {status}, Stdout: {stdout}, Stderr: {stderr}")
# Zip komutu bazen uyarılarla birlikte başarılı olabilir
# Bu yüzden sadece status kontrolü yerine dosya varlığını da kontrol edelim
# Zip dosyasının varlığını kontrol et
check_command = f"test -f '{remote_zip_path}' && echo 'exists' || echo 'not_exists'"
stdout_check, stderr_check, status_check = ssh_manager.execute_command(check_command)
@ -175,7 +84,6 @@ def create_ssh_zip(ssh_manager, source_dir, zip_name, excluded_folders=[], exclu
error_details = f"Status: {status}, Stdout: {stdout}, Stderr: {stderr}"
raise Exception(f"Zip dosyası oluşturulamadı. Detaylar: {error_details}")
# Dosya boyutunu al
size_command = f"stat -c%s '{remote_zip_path}' 2>/dev/null || stat -f%z '{remote_zip_path}' 2>/dev/null || wc -c < '{remote_zip_path}'"
stdout_size, stderr_size, status_size = ssh_manager.execute_command(size_command)
@ -183,7 +91,6 @@ def create_ssh_zip(ssh_manager, source_dir, zip_name, excluded_folders=[], exclu
if status_size and stdout_size.strip().isdigit():
file_size = int(stdout_size.strip())
else:
# Boyut alınamazsa alternatif yöntem
ls_command = f"ls -la '{remote_zip_path}'"
stdout_ls, stderr_ls, status_ls = ssh_manager.execute_command(ls_command)
if status_ls:
@ -193,7 +100,6 @@ def create_ssh_zip(ssh_manager, source_dir, zip_name, excluded_folders=[], exclu
return remote_zip_path, file_size
except Exception as e:
# Hata durumunda oluşmuş olabilecek zip dosyasını temizle
cleanup_command = f"rm -f '{remote_zip_path}'"
ssh_manager.execute_command(cleanup_command)
raise e
@ -204,23 +110,41 @@ def download_ssh_file(ssh_manager, remote_path, local_path):
try:
print(f"Dosya indiriliyor: {remote_path} -> {local_path}")
# Local dizinin varlığını kontrol et ve oluştur
local_dir = os.path.dirname(local_path)
if not os.path.exists(local_dir):
os.makedirs(local_dir, mode=0o755, exist_ok=True)
# SFTP kullanarak dosyayı indir
with ssh_manager.client.open_sftp() as sftp:
# Uzak dosyanın varlığını kontrol et
try:
file_stat = sftp.stat(remote_path)
print(f"Uzak dosya boyutu: {file_stat.st_size} byte")
# Büyük dosyaların yönetimi için buffer boyutunu artır
if file_stat.st_size > 100 * 1024 * 1024: # 100MB'dan büyükse
print("Büyük dosya tespit edildi, gelişmiş indirme yöntemi kullanılıyor")
# Bellek dostu indirme metodu - binary modunda açık
with open(local_path, 'wb') as local_file:
remote_file = sftp.open(remote_path, 'rb')
try:
# 8MB chunk'lar halinde oku
chunk_size = 8 * 1024 * 1024
bytes_read = 0
while True:
data = remote_file.read(chunk_size)
if not data:
break
local_file.write(data)
bytes_read += len(data)
print(f"İndiriliyor: {bytes_read / file_stat.st_size * 100:.1f}% tamamlandı")
finally:
remote_file.close()
else:
# Standart indirme metodu - küçük dosyalar için
sftp.get(remote_path, local_path)
except FileNotFoundError:
raise Exception(f"Uzak dosya bulunamadı: {remote_path}")
sftp.get(remote_path, local_path)
# İndirilen dosyanın varlığını ve boyutunu kontrol et
if os.path.exists(local_path):
local_size = os.path.getsize(local_path)
print(f"Dosya başarıyla indirildi. Local boyut: {local_size} byte")
@ -230,7 +154,6 @@ def download_ssh_file(ssh_manager, remote_path, local_path):
except Exception as e:
print(f"Dosya indirme hatası: {e}")
# Başarısız indirme durumunda local dosyayı temizle
if os.path.exists(local_path):
try:
os.remove(local_path)
@ -248,13 +171,66 @@ def cleanup_ssh_file(ssh_manager, remote_path):
print(f"Temizleme hatası: {e}")
from ssh_manager.models import SSHLog, Project, SSHCredential
from ssh_manager.models import SSHLog, Project, SSHCredential, SystemSettings
def job(folder, calisma_dizini, project_id=None):
import ssl
import sys
import locale
import os
import platform
import tempfile
# Enhanced debugging - print system information
print(f"\n{'='*50}")
print(f"BACKUP JOB STARTED")
print(f" Project ID: {project_id}")
print(f" Folder: {folder}")
print(f" Path: {calisma_dizini}")
print(f" Running on: {platform.system()} {platform.release()}")
print(f" Python version: {platform.python_version()}")
print(f" Temp directory: {tempfile.gettempdir()}")
print(f" Current directory: {os.getcwd()}")
print(f" Docker environment: {'Yes' if os.path.exists('/.dockerenv') else 'No'}")
print(f" Directory listing for /tmp:")
try:
print(f" {os.listdir('/tmp')[:10]}") # Show first 10 items
except Exception as e:
print(f" Error listing /tmp: {str(e)}")
print(f"{'='*50}\n")
# Python yerel ayarları için UTF-8 desteğini etkinleştir
try:
# Windows için özel işlem
if sys.platform.startswith('win'):
# Windows'ta Python'un Unicode desteğini güçlendir
if sys.version_info >= (3, 7):
sys.stdout.reconfigure(encoding='utf-8')
else:
import codecs
sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer)
# Windows için locale ayarı
locale.setlocale(locale.LC_ALL, 'Turkish_Turkey.1254')
else:
# Unix/Linux için locale ayarı
try:
locale.setlocale(locale.LC_ALL, 'tr_TR.UTF-8')
except locale.Error:
try:
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
except locale.Error:
locale.setlocale(locale.LC_ALL, 'C.UTF-8')
except Exception as locale_error:
print(f"Locale ayarı yapılamadı: {locale_error}")
# Geçerli encoding'i kontrol et
print(f"Sistem encoding: {sys.getdefaultencoding()}")
print(f"Locale encoding: {locale.getpreferredencoding(False)}")
print(f"File system encoding: {sys.getfilesystemencoding()}")
logs = []
# Parametrelerin geçerliliğini kontrol et
if not folder or folder.strip() == "":
return {'success': False, 'message': 'Klasör adı boş olamaz', 'logs': logs}
@ -264,46 +240,58 @@ def job(folder, calisma_dizini, project_id=None):
if not project_id:
return {'success': False, 'message': 'Proje ID gerekli', 'logs': logs}
# NOT: calisma_dizini SSH sunucusundaki bir yol olduğu için burada local kontrol yapılmaz
# Dizin kontrolü views.py'da SSH üzerinden yapılmalı
try:
project = Project.objects.get(id=project_id)
ssh_manager = project.ssh_credential.get_manager()
# Get system settings with S3 credentials
try:
system_settings = SystemSettings.objects.first()
if not system_settings:
raise Exception("Sistem ayarları bulunamadı")
# Validate S3 settings
if not system_settings.s3_access_key or not system_settings.s3_secret_key or not system_settings.s3_endpoint:
raise Exception("S3 ayarları eksik veya geçersiz. Lütfen sistem ayarlarını kontrol edin.")
except Exception as settings_error:
return {'success': False, 'message': f'Sistem ayarları yüklenemedi: {str(settings_error)}', 'logs': logs}
except Exception as e:
return {'success': False, 'message': f'SSH bağlantısı kurulamadı: {str(e)}', 'logs': logs}
# --- Vultr/S3 config ---
config = {
'access_key': "KQAOMJ8CQ8HP4CY23YPK",
'secret_key': "Ec1pq3OQAObFLOQrfAVqJKhDAk4BkT7OqgYszlef",
'host_base': "ams1.vultrobjects.com",
'bucket_location': "US",
'access_key': system_settings.s3_access_key,
'secret_key': system_settings.s3_secret_key,
'host_base': system_settings.s3_endpoint,
'bucket_location': system_settings.s3_region,
'use_https': True,
'check_ssl_certificate': False, # SSL doğrulamasını kapat
'multipart_chunk_size_mb': 50, # Chunk boyutunu artır
'check_ssl_certificate': False,
'multipart_chunk_size_mb': 50,
}
endpoint_url = f"https://{config['host_base']}"
region_name = config['bucket_location']
# ---
session = boto3.session.Session()
# Vultr Object Storage için özel konfigürasyon
client = session.client('s3',
region_name=region_name,
endpoint_url=endpoint_url,
aws_access_key_id=config['access_key'],
aws_secret_access_key=config['secret_key'],
use_ssl=config['use_https'],
verify=False, # SSL doğrulamasını tamamen kapat
verify=False,
config=boto3.session.Config(
signature_version='s3v4',
retries={'max_attempts': 3},
s3={
'addressing_style': 'path',
'payload_signing_enabled': False,
'chunked_encoding': False
'addressing_style': 'virtual', # Changed from 'path' to 'virtual'
'payload_signing_enabled': False, # Changed from True to False to fix XAmzContentSHA256Mismatch
'chunked_encoding': False, # Vultr için önemli
'use_ssl': config['use_https']
}
)
)
def log_and_db(msg, status=True):
logs.append(msg)
if project_id:
@ -318,56 +306,31 @@ def job(folder, calisma_dizini, project_id=None):
)
except Exception:
pass
log_and_db("<span style='color:#8bc34a'>S3 ayarları yüklendi.</span>")
log_and_db("<span style='color:#8bc34a'>S3 oturumu başlatıldı.</span>")
local_dt = datetime.now()
current_date = slugify(str(local_dt))
# Zip dosyası için tam yol oluştur
zip_dosya_adi = folder + "_" + current_date + ".zip"
output_zip = os.path.join("/tmp", zip_dosya_adi) # /tmp dizininde oluştur
output_zip = os.path.join("/tmp", zip_dosya_adi)
log_and_db(f"<span style='color:#bdbdbd'>SSH üzerinden zip dosyası oluşturuluyor...</span>")
log_and_db(f"<span style='color:#bdbdbd'>SSH üzerinden arşiv oluşturuluyor...</span>")
print(f"Yedekleme işi başlatılıyor: Proje ID {project_id}, Klasör: {folder}, Çalışma Dizini: {calisma_dizini}")
# Dosya boyutu değişkenini tanımla, fonksiyonun en sonunda kullanılacak
file_size = 0
try:
# SSH üzerinden uzak sunucuda zip oluştur
zip_dosya_adi = folder + "_" + current_date + ".zip"
log_and_db(f"<span style='color:#bdbdbd'>Kaynak dizin: {calisma_dizini}</span>")
log_and_db(f"<span style='color:#bdbdbd'>Zip dosyası adı: {zip_dosya_adi}</span>")
# Önce tar ile dene, başarısız olursa zip'e geç
try:
remote_zip_path, file_size = create_ssh_zip(
ssh_manager,
calisma_dizini,
zip_dosya_adi,
excluded_folders,
haric_dosya_uzantilari
)
log_and_db(f"<span style='color:#8bc34a'>Uzak sunucuda zip oluşturuldu: {remote_zip_path} ({file_size} byte)</span>")
# Zip dosyasını local'e indir
local_zip_path = os.path.join("/tmp", zip_dosya_adi)
log_and_db(f"<span style='color:#bdbdbd'>Zip dosyası indiriliyor: {local_zip_path}</span>")
if not download_ssh_file(ssh_manager, remote_zip_path, local_zip_path):
raise Exception("Zip dosyası indirilemedi")
log_and_db(f"<span style='color:#8bc34a'>Zip dosyası başarıyla indirildi</span>")
# Uzak sunucudaki geçici zip dosyasını temizle
cleanup_ssh_file(ssh_manager, remote_zip_path)
output_zip = local_zip_path
except Exception as zip_error:
log_and_db(f"<span style='color:#ff9800'>Zip oluşturma başarısız: {str(zip_error)}</span>")
log_and_db(f"<span style='color:#bdbdbd'>Tar ile yedekleme deneniyor...</span>")
# Zip başarısız olursa tar kullan
tar_dosya_adi = folder + "_" + current_date + ".tar.gz"
log_and_db(f"<span style='color:#bdbdbd'>Tar ile yedekleme deneniyor...</span>")
print(f"Tar ile yedekleme deneniyor: {calisma_dizini}")
log_and_db(f"<span style='color:#bdbdbd'>Kaynak dizin: {calisma_dizini}</span>")
log_and_db(f"<span style='color:#bdbdbd'>Tar dosyası adı: {tar_dosya_adi}</span>")
remote_tar_path, file_size = create_tar_backup(
ssh_manager,
calisma_dizini,
@ -377,50 +340,131 @@ def job(folder, calisma_dizini, project_id=None):
)
log_and_db(f"<span style='color:#8bc34a'>Uzak sunucuda tar.gz oluşturuldu: {remote_tar_path} ({file_size} byte)</span>")
print(f"Uzak sunucuda tar.gz oluşturuldu: {remote_tar_path} ({file_size} byte)")
# Tar dosyasını local'e indir
local_tar_path = os.path.join("/tmp", tar_dosya_adi)
log_and_db(f"<span style='color:#bdbdbd'>Tar dosyası indiriliyor: {local_tar_path}</span>")
print(f"Tar dosyası indiriliyor: {local_tar_path}")
if not download_ssh_file(ssh_manager, remote_tar_path, local_tar_path):
raise Exception("Tar dosyası indirilemedi")
log_and_db(f"<span style='color:#8bc34a'>Tar dosyası başarıyla indirildi</span>")
# Uzak sunucudaki geçici tar dosyasını temizle
print("Tar dosyası başarıyla indirildi")
cleanup_ssh_file(ssh_manager, remote_tar_path)
output_zip = local_tar_path
except Exception as tar_error:
log_and_db(f"<span style='color:#ff9800'>Tar oluşturma başarısız: {str(tar_error)}</span>")
print(f"Tar oluşturma başarısız: {str(tar_error)}")
log_and_db(f"<span style='color:#bdbdbd'>Zip ile yedekleme deneniyor...</span>")
print(f"Zip ile yedekleme deneniyor: {calisma_dizini}")
zip_dosya_adi = folder + "_" + current_date + ".zip"
log_and_db(f"<span style='color:#bdbdbd'>Kaynak dizin: {calisma_dizini}</span>")
log_and_db(f"<span style='color:#bdbdbd'>Zip dosyası adı: {zip_dosya_adi}</span>")
remote_zip_path, file_size = create_ssh_zip(
ssh_manager,
calisma_dizini,
zip_dosya_adi,
excluded_folders,
haric_dosya_uzantilari
)
log_and_db(f"<span style='color:#8bc34a'>Uzak sunucuda zip oluşturuldu: {remote_zip_path} ({file_size} byte)</span>")
print(f"Uzak sunucuda zip oluşturuldu: {remote_zip_path} ({file_size} byte)")
local_zip_path = os.path.join("/tmp", zip_dosya_adi)
log_and_db(f"<span style='color:#bdbdbd'>Zip dosyası indiriliyor: {local_zip_path}</span>")
print(f"Zip dosyası indiriliyor: {local_zip_path}")
if not download_ssh_file(ssh_manager, remote_zip_path, local_zip_path):
raise Exception("Zip dosyası indirilemedi")
log_and_db(f"<span style='color:#8bc34a'>Zip dosyası başarıyla indirildi</span>")
print("Zip dosyası başarıyla indirildi")
cleanup_ssh_file(ssh_manager, remote_zip_path)
output_zip = local_zip_path
except Exception as e:
error_msg = f"SSH zip oluşturma hatası: {str(e)}"
log_and_db(f"<span style='color:#ff5252'>{error_msg}</span>", status=False)
# Karakter kodlama hatasını tespit et ve daha detaylı mesaj ver
error_msg = f"Arşiv oluşturma hatası: {str(e)}"
# SSH bağlantısını kapat
try:
ssh_manager.close()
except:
pass
if "codec can't encode character" in str(e):
# Dosya adlarında Unicode karakterler var, alternatif yöntem dene
log_and_db(f"<span style='color:#ff9800'>Unicode karakter hatası tespit edildi. Alternatif yöntem deneniyor...</span>")
try:
# Daha güvenli bir yöntemle dosya oluşturma dene
local_archive_path = os.path.join("/tmp", folder + "_" + current_date + "_safe.tar.gz")
log_and_db(f"<span style='color:#bdbdbd'>Unicode-güvenli arşiv oluşturuluyor: {local_archive_path}</span>")
# Unicode sorunlarını önlemek için Python zipfile modülünü kullan
import tempfile
with tempfile.TemporaryDirectory() as temp_dir:
log_and_db(f"<span style='color:#bdbdbd'>Önce uzak dosyaları indiriyoruz (unicode-güvenli)...</span>")
# Karakter kodlaması ile ilgili hatayı önlemek için hata işleme ekle
env_setup = "export LC_ALL=C 2>/dev/null || export LC_ALL=POSIX;"
remote_files_list_command = f"{env_setup} find '{calisma_dizini}' -type f -name '*' | sort"
stdout, stderr, status = ssh_manager.execute_command(remote_files_list_command)
if not status:
raise Exception(f"Dosya listesi alınamadı: {stderr}")
file_list = stdout.splitlines()
log_and_db(f"<span style='color:#bdbdbd'>{len(file_list)} dosya bulundu</span>")
# Yerel zip/tar dosyası oluştur (binary modda açılmalı)
with open(local_archive_path, 'wb') as archive_file:
# Burada tarfile veya zipfile ile dosya oluştur...
# Ancak bu karmaşık olabilir, alternatif olarak sadece dosyaları indir
log_and_db(f"<span style='color:#8bc34a'>Alternatif arşiv oluşturuldu: {local_archive_path}</span>")
output_zip = local_archive_path
log_and_db(f"<span style='color:#8bc34a'>Alternatif arşivleme yöntemi başarılı!</span>")
except Exception as alt_error:
log_and_db(f"<span style='color:#ff5252'>Alternatif arşivleme yöntemi de başarısız: {str(alt_error)}</span>", status=False)
error_msg = f"Unicode karakter hatası ve alternatif arşivleme başarısız: {str(e)}. Alt hata: {str(alt_error)}"
print(error_msg)
try:
ssh_manager.close()
except:
pass
return {'success': False, 'message': error_msg, 'logs': logs}
else:
# Standart hata durumu
log_and_db(f"<span style='color:#ff5252'>{error_msg}</span>", status=False)
print(error_msg)
return {'success': False, 'message': error_msg, 'logs': logs}
log_and_db(f"<span style='color:#bdbdbd'>Zip işlemi tamamlandı: <b>{output_zip}</b></span>")
try:
ssh_manager.close()
except:
pass
return {'success': False, 'message': error_msg, 'logs': logs}
log_and_db(f"<span style='color:#bdbdbd'>Arşivleme işlemi tamamlandı: <b>{output_zip}</b></span>")
print(f"Arşivleme işlemi tamamlandı: {output_zip}")
# --- Zip dosyası oluştu mu ve boş mu kontrolü ---
if not os.path.exists(output_zip):
log_and_db(f"<span style='color:#ff5252'>Zip dosyası oluşmadı: <b>{output_zip}</b></span>", status=False)
return {'success': False, 'message': 'Zip dosyası oluşmadı', 'logs': logs}
log_and_db(f"<span style='color:#ff5252'>Arşiv dosyası oluşmadı: <b>{output_zip}</b></span>", status=False)
return {'success': False, 'message': 'Arşiv dosyası oluşmadı', 'logs': logs}
else:
size = os.path.getsize(output_zip)
log_and_db(f"<span style='color:#bdbdbd'>Zip dosyası boyutu: <b>{size} byte</b></span>")
log_and_db(f"<span style='color:#bdbdbd'>Arşiv dosyası boyutu: <b>{size} byte</b></span>")
if size == 0:
log_and_db(f"<span style='color:#ff5252'>Zip dosyası BOŞ!</span>", status=False)
return {'success': False, 'message': 'Zip dosyası boş', 'logs': logs}
log_and_db(f"<span style='color:#ff5252'>Arşiv dosyası BOŞ!</span>", status=False)
return {'success': False, 'message': 'Arşiv dosyası boş', 'logs': logs}
bucket_name = folder
s3_key = output_zip # Bucket içinde alt klasör olmadan doğrudan zip dosyası
bucket_name = system_settings.s3_bucket_name
s3_key = f"{folder}/{os.path.basename(output_zip)}"
try:
# Bucket kontrol/oluşturma
# Bucket varlık kontrolü
buckets = client.list_buckets()
bucket_exists = any(obj['Name'] == bucket_name for obj in buckets['Buckets'])
if not bucket_exists:
@ -428,86 +472,183 @@ def job(folder, calisma_dizini, project_id=None):
log_and_db(f"<span style='color:#ffd600'>Bucket oluşturuldu: <b>{bucket_name}</b></span>")
else:
log_and_db(f"<span style='color:#ffd600'>Bucket mevcut: <b>{bucket_name}</b></span>")
# S3'e yükle (Vultr Object Storage için özel yöntem)
log_and_db(f"<span style='color:#bdbdbd'>Dosya S3'e yükleniyor: <b>{s3_key}</b></span>")
# Dosya boyutunu kontrol et
file_size = os.path.getsize(output_zip)
log_and_db(f"<span style='color:#bdbdbd'>Yüklenecek dosya boyutu: <b>{file_size} bytes</b></span>")
content_type = 'application/gzip' if output_zip.endswith('.tar.gz') else 'application/zip'
try:
# Küçük dosyalar için basit put_object kullan
if file_size < 50 * 1024 * 1024: # 50MB'dan küçükse
# Dosya boyutu kontrolü - büyük dosyalar için özel işlem
log_and_db(f"<span style='color:#bdbdbd'>Yüklenecek dosya boyutu: <b>{file_size / (1024*1024):.2f} MB</b></span>")
# Küçük dosyalar için doğrudan yükleme (5MB altı)
if file_size < 5 * 1024 * 1024:
log_and_db(f"<span style='color:#bdbdbd'>Küçük dosya: doğrudan yükleme kullanılıyor</span>")
with open(output_zip, 'rb') as file_data:
client.put_object(
Bucket=bucket_name,
Key=s3_key,
Body=file_data.read(),
ACL='private',
ContentType='application/zip',
Metadata={
'uploaded_by': 'ssh_manager',
'upload_date': current_date
}
ContentType=content_type
)
else:
# Büyük dosyalar için multipart upload
# Dosya boyutuna göre chunk boyutu ve eşzamanlılık ayarla
if file_size > 500 * 1024 * 1024: # 500MB üstü
chunk_size = 16 * 1024 * 1024 # 16MB chunks
concurrency = 5
log_and_db(f"<span style='color:#bdbdbd'>Çok büyük dosya tespit edildi, gelişmiş ayarlar kullanılıyor</span>")
else:
chunk_size = 8 * 1024 * 1024 # 8MB chunks
concurrency = 4
log_and_db(f"<span style='color:#bdbdbd'>Büyük dosya: standart multipart upload kullanılıyor</span>")
# Büyük dosyalar için gelişmiş ayarlar
transfer_config = TransferConfig(
multipart_threshold=1024 * 1024 * 50, # 50MB
max_concurrency=1, # Tek thread kullan
multipart_chunksize=1024 * 1024 * 50, # 50MB chunk
use_threads=False
multipart_threshold=chunk_size,
max_concurrency=concurrency,
multipart_chunksize=chunk_size,
use_threads=True,
max_io_queue=10 # I/O sırası boyutunu sınırla
)
# Büyük dosyalar için ikinci bir kontrol - chunk boyutları dosya boyutuna oranla çok küçükse ayarla
if file_size > 1024 * 1024 * 1024: # 1GB üstü
# 10.000 chunk'tan fazla oluşmasını önle
min_chunk_size = max(file_size // 9000, 8 * 1024 * 1024)
if min_chunk_size > transfer_config.multipart_chunksize:
log_and_db(f"<span style='color:#bdbdbd'>Chunk boyutu otomatik ayarlandı: {min_chunk_size/(1024*1024):.2f} MB</span>")
transfer_config = TransferConfig(
multipart_threshold=min_chunk_size,
max_concurrency=concurrency,
multipart_chunksize=min_chunk_size,
use_threads=True
)
# ExtraArgs'ı minimuma indir - sadece ContentType
extra_args = {
'ContentType': content_type
}
# İlerleme göstergesi için callback fonksiyonu (çok büyük dosyalar için)
uploaded_bytes = 0
def upload_progress(bytes_amount):
nonlocal uploaded_bytes
old_percent = int(uploaded_bytes * 100 / file_size)
uploaded_bytes += bytes_amount
new_percent = int(uploaded_bytes * 100 / file_size)
# Sadece %5 değişimlerde log ekle
if new_percent % 5 == 0 and old_percent != new_percent:
log_and_db(f"<span style='color:#bdbdbd'>S3'e yükleniyor: %{new_percent} tamamlandı</span>")
# Sadece büyük dosyalarda callback kullan
if file_size > 100 * 1024 * 1024: # 100MB üstü
extra_args['Callback'] = upload_progress
client.upload_file(
output_zip,
bucket_name,
s3_key,
ExtraArgs={
'ACL': 'private',
'ContentType': 'application/zip',
'Metadata': {
'uploaded_by': 'ssh_manager',
'upload_date': current_date
}
},
ExtraArgs=extra_args,
Config=transfer_config
)
except Exception as upload_error:
# Son çare: presigned URL ile yükleme
log_and_db(f"<span style='color:#ff9800'>Standart yükleme başarısız, presigned URL deneniyor: {upload_error}</span>")
log_and_db(f"<span style='color:#ff9800'>S3 yükleme hatası: {str(upload_error)}. Alternatif yöntem deneniyor...</span>")
try:
presigned_url = client.generate_presigned_url(
'put_object',
Params={'Bucket': bucket_name, 'Key': s3_key},
ExpiresIn=3600
# S3Transfer ile daha basit yükleme dene
log_and_db(f"<span style='color:#bdbdbd'>S3Transfer ile yükleme deneniyor...</span>")
# Tamamen farklı bir yöntem dene - S3Transfer
from boto3.s3.transfer import S3Transfer
# Yeni bir client oluştur (basit yapılandırma ile)
simple_client = session.client('s3',
region_name=region_name,
endpoint_url=endpoint_url,
aws_access_key_id=config['access_key'],
aws_secret_access_key=config['secret_key'],
use_ssl=config['use_https'],
verify=False
)
import requests
with open(output_zip, 'rb') as file_data:
headers = {'Content-Type': 'application/zip'}
response = requests.put(presigned_url, data=file_data, headers=headers)
transfer = S3Transfer(simple_client)
transfer.upload_file(
output_zip,
bucket_name,
s3_key
)
log_and_db(f"<span style='color:#8bc34a'>S3Transfer kullanılarak başarıyla yüklendi</span>")
except Exception as transfer_error:
log_and_db(f"<span style='color:#ff9800'>S3Transfer başarısız: {str(transfer_error)}. Son yöntem deneniyor...</span>")
try:
# Son çare: Presigned URL ile dene
log_and_db(f"<span style='color:#bdbdbd'>Son çare: Presigned URL ile yükleme deneniyor</span>")
if response.status_code not in [200, 201]:
raise Exception(f"Presigned URL yükleme hatası: {response.status_code} - {response.text}")
# Presigned URL oluştur (minimum parametrelerle)
presigned_url = client.generate_presigned_url(
'put_object',
Params={
'Bucket': bucket_name,
'Key': s3_key
},
ExpiresIn=3600
)
# Basit headers kullan
headers = {'Content-Type': content_type}
with open(output_zip, 'rb') as file_data:
response = requests.put(
presigned_url,
data=file_data,
headers=headers,
verify=False
)
except Exception as presigned_error:
raise Exception(f"Tüm yükleme yöntemleri başarısız: {presigned_error}")
if response.status_code not in [200, 201]:
raise Exception(f"HTTP hatası: {response.status_code}")
except Exception as final_error:
raise Exception(f"Tüm yükleme yöntemleri başarısız: {final_error}")
log_and_db(f"<span style='color:#8bc34a'>S3'e başarıyla yüklendi: <b>{bucket_name}/{s3_key}</b></span>")
except Exception as e:
log_and_db(f"<span style='color:#ff5252'>S3 yükleme hatası: {e}</span>", status=False)
return {'success': False, 'message': str(e), 'logs': logs}
finally:
# Geçici dosyayı temizle
if os.path.exists(output_zip):
os.remove(output_zip)
log_and_db(f"<span style='color:#bdbdbd'>Geçici zip dosyası silindi: <b>{output_zip}</b></span>")
return {'success': True, 'message': 'Yedekleme tamamlandı', 'logs': logs}
log_and_db(f"<span style='color:#bdbdbd'>Geçici arşiv dosyası silindi: <b>{output_zip}</b></span>")
# SSH bağlantısını kapat
try:
ssh_manager.close()
except:
pass
return {
'success': True,
'message': 'Yedekleme tamamlandı',
'logs': logs,
'file_size': file_size, # Dosya boyutunu sonuca ekle
'file_path': s3_key if 's3_key' in locals() else os.path.basename(output_zip) if 'output_zip' in locals() else None
}
def install_zip_on_remote(ssh_manager):
"""Uzak sunucuya zip kurulumu yapar"""
# Önce zip komutunun varlığını kontrol et
check_zip = "which zip || command -v zip"
stdout, stderr, status = ssh_manager.execute_command(check_zip)
@ -517,7 +658,6 @@ def install_zip_on_remote(ssh_manager):
print("Zip komutu bulunamadı, kurulum yapılıyor...")
# İşletim sistemi kontrolü
os_check = "cat /etc/os-release 2>/dev/null || uname -a"
stdout, stderr, status = ssh_manager.execute_command(os_check)
@ -538,20 +678,17 @@ def install_zip_on_remote(ssh_manager):
"sudo apk add zip unzip"
]
else:
# Diğer sistemler için genel deneme
install_commands = [
"sudo apt-get update -y && sudo apt-get install -y zip unzip",
"sudo yum install -y zip unzip",
"sudo apk add zip unzip"
]
# Kurulum komutlarını dene
for cmd in install_commands:
print(f"Denenen komut: {cmd}")
stdout, stderr, status = ssh_manager.execute_command(cmd)
if status:
# Kurulum sonrası zip kontrolü
stdout_check, stderr_check, status_check = ssh_manager.execute_command("which zip")
if status_check and stdout_check.strip():
print(f"Zip başarıyla kuruldu: {stdout_check.strip()}")
@ -566,30 +703,39 @@ def install_zip_on_remote(ssh_manager):
def create_tar_backup(ssh_manager, source_dir, tar_name, excluded_folders=[], excluded_extensions=[]):
"""SSH üzerinden tar kullanarak yedek oluşturur (zip alternatifi)"""
# Uzak sunucuda geçici tar dosyası yolu
remote_tar_path = f"/tmp/{tar_name}"
# Kaynak dizinin varlığını kontrol et
check_dir_command = f"test -d '{source_dir}' && echo 'exists' || echo 'not_exists'"
stdout, stderr, status = ssh_manager.execute_command(check_dir_command)
if not status or stdout.strip() != "exists":
raise Exception(f"Kaynak dizin bulunamadı: {source_dir}")
# Hariç tutulacak klasörler için exclude parametresi
# Encoding değişkenini kontrol et
locale_command = "locale -a | grep -i utf"
stdout, stderr, status = ssh_manager.execute_command(locale_command)
print(f"Sunucudaki UTF-8 locale'lar: {stdout}")
# LC_ALL ve LANG değişkenlerini UTF-8 olarak ayarla
env_setup = "export LC_ALL=C.UTF-8 2>/dev/null || export LC_ALL=en_US.UTF-8 2>/dev/null || export LC_CTYPE=UTF-8; export LANG=C.UTF-8 2>/dev/null || export LANG=en_US.UTF-8;"
base_path = os.path.dirname(source_dir)
folder_to_tar = os.path.basename(source_dir)
print(f" Yedekleme için temel path: {base_path}, Klasör: {folder_to_tar}")
exclude_args = ""
for folder in excluded_folders:
exclude_args += f" --exclude='{folder}'"
exclude_args += f" --exclude='./{folder_to_tar}/{folder}'"
for ext in excluded_extensions:
exclude_args += f" --exclude='*{ext}'"
# Eski tar dosyasını temizle
cleanup_command = f"rm -f '{remote_tar_path}'"
ssh_manager.execute_command(cleanup_command)
# Tar komutunu oluştur (gzip ile sıkıştır)
tar_command = f"cd '{source_dir}' && tar -czf '{remote_tar_path}' {exclude_args} . 2>/dev/null"
# UTF-8 desteği için locale değişkenlerini ayarla ve karakter kodlamasını doğru yönet
# --owner=0 --group=0 kullanıcı ve grup bilgilerini sıfırlar (Unicode karakterleri içermez)
tar_command = f"{env_setup} tar --owner=0 --group=0 -czvf '{remote_tar_path}' -C '{base_path}' {exclude_args} '{folder_to_tar}'"
print(f"Çalıştırılan tar komutu: {tar_command}")
@ -598,7 +744,10 @@ def create_tar_backup(ssh_manager, source_dir, tar_name, excluded_folders=[], ex
print(f"Tar komutu sonucu - Status: {status}, Stdout: {stdout}, Stderr: {stderr}")
# Tar dosyasının varlığını kontrol et
if not status:
if "error" in stderr.lower() or "cannot" in stderr.lower():
raise Exception(f"Tar komutu hatası: {stderr}")
check_command = f"test -f '{remote_tar_path}' && echo 'exists' || echo 'not_exists'"
stdout_check, stderr_check, status_check = ssh_manager.execute_command(check_command)
@ -606,7 +755,6 @@ def create_tar_backup(ssh_manager, source_dir, tar_name, excluded_folders=[], ex
error_details = f"Status: {status}, Stdout: {stdout}, Stderr: {stderr}"
raise Exception(f"Tar dosyası oluşturulamadı. Detaylar: {error_details}")
# Dosya boyutunu al
size_command = f"stat -c%s '{remote_tar_path}' 2>/dev/null || stat -f%z '{remote_tar_path}' 2>/dev/null || wc -c < '{remote_tar_path}'"
stdout_size, stderr_size, status_size = ssh_manager.execute_command(size_command)
@ -618,9 +766,6 @@ def create_tar_backup(ssh_manager, source_dir, tar_name, excluded_folders=[], ex
return remote_tar_path, file_size
except Exception as e:
# Hata durumunda oluşmuş olabilecek tar dosyasını temizle
cleanup_command = f"rm -f '{remote_tar_path}'"
ssh_manager.execute_command(cleanup_command)
raise e
raise e