KESİNLEŞEN KARARLAR
İçindekiler
1 Git Backup Al KRİTİK - İLK ADIM
Herhangi bir kod değişikliğinden ÖNCE mutlaka backup al!
# 1. Mevcut durumu kontrol et
cd /var/www/vhosts/muzibu.com/httpdocs
git status
git log --oneline -5
# 2. Varsa uncommitted değişiklikleri commit et
git add .
git commit -m "🔧 CHECKPOINT: Before Bunny Storage integration"
# 3. Backup branch oluştur
git branch backup-before-bunny-$(date +%Y%m%d)
git push origin backup-before-bunny-$(date +%Y%m%d)
# 4. Yeni feature branch oluştur
git checkout -b feature/bunny-storage-integration
- • Backup branch'i PUSH et (remote'da da olsun)
- • Branch adını not al:
backup-before-bunny-YYYYMMDD - • Sorun çıkarsa bu branch'e dönülecek
2 Bunny Storage Zone Oluştur
Bunny Panel'e Git
https://panel.bunny.net → Storage → Add Storage Zone
Storage Zone Ayarları
| Zone Name: | muzibu-audio |
| Main Region: | Frankfurt (DE) - Türkiye'ye yakın |
| Replication: | None (tek bölge yeterli, local backup var) |
API Key Al
Storage Zone oluştuktan sonra → FTP & API Access → Password
Klasör Yapısı Oluştur
Bunny File Manager'dan veya API ile:
muzibu-audio/
├── hls/ # HLS dosyaları buraya
└── songs/
├── mp3_128/ # 128k fallback
└── mp3_64/ # 64k fallback
3 Pull Zone'u Storage Zone'a Bağla
Mevcut muzibuweb Pull Zone'u Storage Zone'a bağla:
Seçenek A: Yeni Pull Zone (Önerilen)
- 1. Bunny Panel → CDN → Add Pull Zone
- 2. Name:
muzibu-audio-cdn - 3. Origin Type: Storage Zone seç
- 4. Storage Zone:
muzibu-audioseç - 5. Create
Bu şekilde audio dosyaları için ayrı CDN URL olur: muzibu-audio-cdn.b-cdn.net
Seçenek B: Mevcut Pull Zone'a Linked
- 1. muzibuweb Pull Zone → General → Linked Storage Zone
- 2.
muzibu-audioseç - 3. URL prefix:
/bunny-audio/
Seçenek A: https://muzibu-audio-cdn.b-cdn.net/hls/123/master.m3u8
Seçenek B: https://muzibuweb.b-cdn.net/bunny-audio/hls/123/master.m3u8
4 Kod Değişiklikleri (8 Dosya + 3 Yeni)
app/Services/Bunny/BunnyStorageService.php
<?php
namespace App\Services\Bunny;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class BunnyStorageService
{
private $apiKey;
private $storageZone;
private $region;
private $baseUrl;
public function __construct()
{
$this->apiKey = config('services.bunny.api_key');
$this->storageZone = config('services.bunny.storage_zone');
$this->region = config('services.bunny.region', 'de');
// Frankfurt: storage.bunnycdn.com, diğer bölgeler: {region}.storage.bunnycdn.com
$this->baseUrl = $this->region === 'de'
? 'https://storage.bunnycdn.com'
: "https://{$this->region}.storage.bunnycdn.com";
}
/**
* Tek dosya yükle
*/
public function upload($localPath, $remotePath): bool
{
if (!file_exists($localPath)) {
Log::error("Bunny upload failed: File not found", ['path' => $localPath]);
return false;
}
$url = "{$this->baseUrl}/{$this->storageZone}/{$remotePath}";
$response = Http::withHeaders([
'AccessKey' => $this->apiKey,
'Content-Type' => 'application/octet-stream',
])->withBody(
file_get_contents($localPath),
'application/octet-stream'
)->put($url);
if ($response->successful()) {
Log::info("Bunny upload success", ['remote' => $remotePath]);
return true;
}
Log::error("Bunny upload failed", [
'remote' => $remotePath,
'status' => $response->status(),
'body' => $response->body()
]);
return false;
}
/**
* Klasör yükle (enc.bin hariç!)
*/
public function uploadDirectory($localDir, $remoteDir, $exclude = ['enc.bin', 'enc.keyinfo']): bool
{
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($localDir)
);
$success = true;
foreach ($files as $file) {
if ($file->isDir()) continue;
$filename = $file->getFilename();
// Şifreleme dosyalarını ATLA!
if (in_array($filename, $exclude)) {
Log::debug("Bunny: Skipping excluded file", ['file' => $filename]);
continue;
}
$relativePath = str_replace($localDir . '/', '', $file->getPathname());
$remotePath = $remoteDir . '/' . $relativePath;
if (!$this->upload($file->getPathname(), $remotePath)) {
$success = false;
}
}
return $success;
}
/**
* Dosya sil
*/
public function delete($remotePath): bool
{
$url = "{$this->baseUrl}/{$this->storageZone}/{$remotePath}";
$response = Http::withHeaders([
'AccessKey' => $this->apiKey,
])->delete($url);
return $response->successful();
}
/**
* Klasör sil (recursive)
*/
public function deleteDirectory($remoteDir): bool
{
// Bunny API: klasör sonuna / ekle
$url = "{$this->baseUrl}/{$this->storageZone}/{$remoteDir}/";
$response = Http::withHeaders([
'AccessKey' => $this->apiKey,
])->delete($url);
return $response->successful();
}
/**
* CDN URL döndür
*/
public function getCdnUrl($remotePath): string
{
return config('services.bunny.cdn_url') . '/' . ltrim($remotePath, '/');
}
}
Değişecek Dosyalar Özeti
| Dosya | Değişiklik |
|---|---|
ConvertToHLSJob.php |
HLS sonrası Bunny'e yükle (enc.bin hariç) |
HLSService.php |
MP3 fallback'ları Bunny'e yükle |
SongObserver.php |
forceDelete'te Bunny'den de sil |
SongStreamController.php |
HLS URL'lerini Bunny CDN'den döndür |
Song.php |
getHlsUrl() Bunny URL desteği |
config/services.php |
Bunny config'leri ekle |
config/filesystems.php |
Bunny disk tanımı |
config/app.php |
BunnyServiceProvider ekle |
app/Console/Commands/MigrateToBunnyCommand.php
Mevcut şarkıları batch olarak Bunny'e yükler.
// Kullanım örnekleri:
# Tüm şarkıları yükle (tam gaz)
php artisan muzibu:migrate-to-bunny --all
# 100'lük chunk'lar halinde
php artisan muzibu:migrate-to-bunny --chunk=100
# Belirli ID aralığı
php artisan muzibu:migrate-to-bunny --from=1 --to=1000
# Sadece belirli şarkı
php artisan muzibu:migrate-to-bunny --song=123
# Test modu (yüklemez, sadece listeler)
php artisan muzibu:migrate-to-bunny --all --dry-run
# İlerleme takibi
php artisan muzibu:migrate-to-bunny --all --progress
⚠️ enc.bin ve enc.keyinfo dosyaları OTOMATİK OLARAK HARİÇ TUTULUR!
5 ENV Ayarları
# ===========================================
# BUNNY STORAGE ZONE
# ===========================================
# Ana switch - false iken Bunny devre dışı, local çalışır
BUNNY_STORAGE_ENABLED=false
# Storage Zone API (Bunny Panel → Storage → FTP & API Access)
BUNNY_STORAGE_API_KEY=your-storage-zone-api-key-here
BUNNY_STORAGE_ZONE=muzibu-audio
BUNNY_STORAGE_REGION=de
# CDN URL (Pull Zone bağlandıktan sonra)
BUNNY_CDN_URL=https://muzibu-audio-cdn.b-cdn.net
# ===========================================
# HİBRİT MOD AYARLARI
# ===========================================
# Mod: hybrid (her ikisi) | bunny_only | local_only
BUNNY_MODE=hybrid
# Bunny başarısız olursa local'e düş
BUNNY_FALLBACK_TO_LOCAL=true
# Orijinal MP3'leri de Bunny'e yükle? (opsiyonel, genelde false)
BUNNY_UPLOAD_ORIGINALS=false
- 1. İlk başta
BUNNY_STORAGE_ENABLED=false - 2. Kodları deploy et
- 3. Migration'ı çalıştır
- 4. Test et
- 5. Sonra
BUNNY_STORAGE_ENABLED=true
6 Batch Migration (2.1TB Yükleme)
📊 Boyut Tahmini
| Şarkı sayısı: | ~30,000 |
| Per şarkı: | ~70 MB |
| Toplam: | ~2.1 TB |
⏱️ Süre Tahmini
| Upload hızı: | ~100 Mbps (tam gaz) |
| 2.1TB süresi: | ~48 saat |
| Sunucu etkisi: | Minimal (güçlü sunucu) |
Adım Adım Migration
# 1. Önce dry-run ile kontrol et
php artisan muzibu:migrate-to-bunny --all --dry-run
# 2. Küçük bir test (ilk 10 şarkı)
php artisan muzibu:migrate-to-bunny --from=1 --to=10
# 3. Bunny panel'den dosyaları kontrol et
# 4. Tam migration başlat (screen içinde)
screen -S bunny-migration
php artisan muzibu:migrate-to-bunny --all --progress
# Screen'den çıkmak: Ctrl+A, D
# Screen'e dönmek: screen -r bunny-migration
- ✅ enc.bin YÜKLENMEZ - Otomatik hariç tutulur
- ✅ enc.keyinfo YÜKLENMEZ - Otomatik hariç tutulur
- ✅ Local dosyalar SİLİNMEZ - Sadece kopyalama
- ✅ Hata olursa devam eder - Başarısız dosyaları loglar
7 Test Senaryoları
# Browser DevTools → Network tab
# Bir şarkı çal ve kontrol et:
✅ master.m3u8 → https://muzibu-audio-cdn.b-cdn.net/hls/123/master.m3u8
✅ playlist.m3u8 → https://muzibu-audio-cdn.b-cdn.net/hls/123/playlist.m3u8
✅ segment-*.ts → https://muzibu-audio-cdn.b-cdn.net/hls/123/segment-000.ts
✅ encryption key → https://muzibu.com/hls-key/muzibu/songs/123 (SUNUCUDAN!)
# Segment'i direkt indirmeye çalış
curl https://muzibu-audio-cdn.b-cdn.net/hls/123/segment-000.ts -o test.ts
# Eğer şifrelenmiş ise:
file test.ts
# Output: "data" (çünkü encrypted, decode edilemez)
# Şifresiz olsaydı:
# Output: "MPEG transport stream data"
# .env'de geçici olarak:
BUNNY_CDN_URL=https://yanlis-url.b-cdn.net
# Şarkı çal → Bunny fail → Local'e düşmeli
# Network tab'da local URL'ler görünmeli
# Sonra düzelt:
BUNNY_CDN_URL=https://muzibu-audio-cdn.b-cdn.net
# Admin'den yeni şarkı yükle
# HLS convert bekle
# Kontrol et:
# 1. Local'de var mı?
ls storage/tenant1001/app/public/muzibu/hls/[yeni_id]/
# 2. Bunny'de var mı?
# Bunny Panel → Storage → muzibu-audio → hls/[yeni_id]/
# 3. enc.bin Bunny'de YOK olmalı!
# Admin'den bir test şarkısını kalıcı sil (force delete)
# Kontrol et:
# 1. Local'den silindi mi?
ls storage/tenant1001/app/public/muzibu/hls/[silinen_id]/
# "No such file or directory" olmalı
# 2. Bunny'den silindi mi?
# Bunny Panel → Storage → hls/[silinen_id]/ → Olmamalı
✅ Go Live Öncesi Checklist
- ☐ HLS Bunny'den çalıyor
- ☐ Key sunucudan geliyor
- ☐ Şifreleme çalışıyor
- ☐ MP3 fallback çalışıyor
- ☐ Yeni şarkı dual-write çalışıyor
- ☐ Silme her iki yerden çalışıyor
- ☐ enc.bin Bunny'de YOK
- ☐ Fallback to local çalışıyor
8 DNS Geçişi (Cloudflare → Bunny)
Storage Zone entegrasyonu ve DNS geçişi AYRI adımlar. Storage Zone çalıştıktan sonra DNS geçişi yapılır. Bu rapor Storage Zone odaklı. DNS geçişi için mevcut Pull Zone raporuna bak.
TTL Düşür
Cloudflare DNS → muzibu.com → TTL: 300 (5 dakika) → 2 saat bekle
DNS Değiştir
muzibu.com → CNAME → muzibuweb.b-cdn.net
Cloudflare Proxy Kapat
Orange cloud → Grey cloud (DNS only)
Bunny SSL Aktif Et
Bunny → Hostnames → muzibu.com → Enable SSL
9 Go Live!
# 1. Son kontroller
php artisan config:clear
php artisan cache:clear
php artisan view:clear
# 2. Bunny'i aktif et
# .env dosyasında:
BUNNY_STORAGE_ENABLED=true
# 3. Config cache
php artisan config:cache
# 4. Test şarkı çal
# Network tab'dan Bunny URL'lerini doğrula
10 İzleme ve Metrikler
📊 Bunny Statistics
- Cache HIT oranı: %90+ olmalı
- Bandwidth kullanımı
- Response time
- Error rate: %1'in altında
📋 Log Kontrolü
# Bunny upload hataları
grep "Bunny upload failed" storage/logs/laravel.log
# Player hataları
tail -f storage/logs/player-errors.log
KRİZ YÖNETİMİ — Sorun Çıkarsa Ne Yapılır?
🔥 KRİZ 1: Bunny Tamamen Çöktü
Şarkılar çalmıyor, Bunny CDN erişilemiyor.
# HIZLI ÇÖZÜM: Bunny'i devre dışı bırak (30 saniye)
# 1. .env'de:
BUNNY_STORAGE_ENABLED=false
# 2. Cache temizle
php artisan config:cache
php artisan cache:clear
# Sistem otomatik olarak LOCAL'e döner!
# Kullanıcılar şarkı dinlemeye devam eder.
⚠️ KRİZ 2: Bazı Şarkılar Çalmıyor
Migration eksik kalmış veya upload hatası var.
# 1. Hangi şarkılar sorunlu bul
grep "Bunny upload failed" storage/logs/laravel.log | tail -20
# 2. O şarkıları tekrar yükle
php artisan muzibu:migrate-to-bunny --song=123
php artisan muzibu:migrate-to-bunny --song=456
# 3. Veya fallback aktif ise zaten local'den çalar
⚡ KRİZ 3: Şifreleme Bozuldu
Şarkılar çalıyor ama ses bozuk/gürültü geliyor.
# Bu enc.bin'in yanlışlıkla Bunny'e yüklendiği anlamına gelir!
# 1. Bunny'den enc.bin'leri sil
# Bunny Panel → Storage → hls/ → enc.bin dosyalarını bul ve sil
# 2. Veya Bunny'i kapat, local'e dön
BUNNY_STORAGE_ENABLED=false
php artisan config:cache
# 3. Sorun çözülene kadar local'den devam et
🔄 KRİZ 4: Kod Hatası - Geri Al
Kod değişikliklerinde kritik hata var.
# 1. Backup branch'e dön
git checkout backup-before-bunny-YYYYMMDD
# 2. Deploy et
php artisan config:cache
php artisan cache:clear
# 3. Sistemi eski haline getir
# Bunny'deki dosyalar kalır, zarar vermez
📞 Acil Durum Kontrol Listesi
- 1. Panik yapma, fallback var!
- 2.
BUNNY_STORAGE_ENABLED=false→ Anında local'e döner - 3. Logları kontrol et
- 4. Gerekirse git backup'a dön
- 5. Local dosyalar HER ZAMAN yerinde!
Sıkça Sorulan Sorular
Key sunucudan gelmesi yavaşlatır mı?
Hayır. Key sadece 16 byte ve şarkı başına 1 kez istenir. HLS.js key'i cache'ler, aynı şarkıda tekrar istemez. Toplam gecikme ~50ms, fark edilmez. Üstelik segment'ler Bunny'den geldiği için net kazanç var.
enc.bin'i Bunny'e yüklesek ne olur?
GÜVENLİK RİSKİ! enc.bin şifreleme anahtarı. Bunny public CDN, herkes indirebilir. Anahtar ele geçerse şifreleme anlamsız olur. Bu yüzden enc.bin ve enc.keyinfo ASLA Bunny'e yüklenmemeli.
Batch migration sunucuyu yavaşlatır mı?
Hayır. Sadece dosya okuma + HTTP upload. CPU/RAM kullanımı minimal. Upload bandwidth ayrı, kullanıcı trafiği etkilenmez. Yine de gece yapmak en güvenlisi.
Bunny çökerse ne olur?
Fallback devreye girer.
BUNNY_FALLBACK_TO_LOCAL=true olduğu sürece,
Bunny erişilemezse otomatik olarak local dosyalardan servis edilir.
Local dosyalar HER ZAMAN yerinde!
Aylık maliyet ne kadar artar?
~$21/ay Storage. Storage: 2.1TB × $0.01 = ~$21/ay. Bandwidth zaten Pull Zone'da hesaplanmıştı. Cloudflare'a göre hala $600-800/ay tasarruf!