Medya ve depolama
Medya, bir CMF'nin en çok saldırıya uğrayan yüzeylerinden biridir: kullanıcı dosya yükler ve sunucu onu sunar. CPalius yükleme hattını tek bir kapıya bağlar ve her adımı sertleştirir.
Yükleme: tek kapı
Hiçbir kod doğrudan dosya yazmaz; AssetManager::upload() yazma için tek geçittir. Her yükleme şu denetimlerden geçer:
- MIME doğrulaması: tür dosya uzantısına değil, dosyanın içeriğine (
finfo) bakılarak belirlenir. - İzin listesi:
MimeTypeAllowlistçekirdeğe gömülüdür; modüller bu listeyi genişletemez. - Uzantı türetme: diskteki uzantı, istemcinin gönderdiği dosya adından değil, doğrulanmış MIME'dan sunucuda türetilir.
evil.php.jpgadıyla gelen bir dosyajpgolarak, rastgele adla saklanır. - İçerik özeti (hash) ile tekilleştirme: aynı içerik ikinci kez yüklenirse aynı varlık yeniden kullanılır.
- Çalıştırılamayan klasör: dosyalar
public/uploadsaltına yazılır ve oradaki.htaccesskatmanlı bir korumayla betik çalıştırmayı, MIME sezmeyi ve içerik gömmeyi engeller.
İzinli türler
| Aile | Türler |
|---|---|
| Görsel | JPEG, PNG, GIF, WebP, AVIF, BMP, TIFF, HEIC/HEIF, ICO |
| Belge | Yalnızca PDF |
| Video | MP4, WebM, OGG video, QuickTime (MOV), Matroska (MKV) |
| Ses | Bilerek dışarıda: haritaya açıkça eklenene kadar |
SVG, HTML, arşivler ve application/octet-stream bilinçli olarak dışarıda tutulur. SVG içinde betik taşıyabilir, HTML sayfa gibi çalışır, arşivler içerik denetimini atlatır.
İzin listesi çekirdeğe aittir. Bir modül kendi çağrısında daralma talep edebilir (restrictedTo([...]), $allowedMimePrefixes, $maxBytes); izin listesini genişletemez.
Medya kütüphanesi
Media modülü (Studio → Medya) yüklenen dosyaları listeler, arar ve medya seçici sunar. Seçici diğer ekranlardan çağrılır:
// tek seçim
window.CPaliusMediaPicker.open((asset) => { /* asset.id, asset.url, asset.originalName */ });
// çoklu seçim
window.CPaliusMediaPicker.open((assets) => assets.forEach(add), { multiple: true });
Bir varlığın önizleme bilgisi için: GET /admin/media/api/assets/{id}/meta → {id, url, name, mime}.
Görsel/dosya alanları bu seçiciyi field-media betiğiyle otomatik kullanır; bkz. Alan sistemi.
Görsel küçük resimleri
ImageProcessor, istenen boyutta görseli ilk istekte üretir ve public/uploads/cache/ altına yazar. Sonraki her istekte dosyayı doğrudan web sunucusu sunar, PHP devreye girmez. Modlar kırpma (crop) ve sığdırma biçimindedir.
Şablonda cp_thumb süzgeci kullanılır: {{ asset|cp_thumb(400, 300) }} (varsayılan kırpma) ya da {{ asset|cp_thumb(400, 300, 'fit') }}. Kaynak olarak varlık nesnesi, kimliği ya da yol verilebilir. Varlık silinince türevler purge ile temizlenir.
CDN
CDN anahtarı çok basit bir sözleşme izler: cdn.base_url yalnızca /uploads önekinin yerine geçer.
| Kurulum | cdn.base_url |
|---|---|
| Sizin sunucunuzun önünde bir çekme bölgesi (pull-zone) | https://cdn.example.com/uploads |
| Herkese açık kova (R2 / S3 / Spaces) | https://pub-xxxx.r2.dev |
CDN anahtarı uzak depolamadan bağımsızdır. Çekme bölgesi hiçbir yükleme gerektirmez: önbellekte olmayan dosyayı sizin sunucunuzdan çeker. Kova zorunluluğu koşmak, en ucuz ve en yaygın kurulumu ifade edilemez bırakırdı. cdn.images_only (varsayılan açık) yalnızca görsellerin CDN'den sunulmasını sağlar.
Dosya adresleri her zaman AssetUrlGenerator ile üretilir; modülünüzde "/uploads/".$key yazmayın.
Uzak depolama: S3, R2, FTP
AACP → Bakım → Depolama ekranında üç hedef tanımlanabilir:
| Hedef | Ayarlar (öne çıkanlar) |
|---|---|
| S3 uyumlu | endpoint, region (varsayılan eu-central-1), bucket, anahtar/gizli anahtar, prefix, path_style |
| Cloudflare R2 | region (auto), bucket, anahtar/gizli anahtar, prefix |
| FTP | host, port (21), kullanıcı/parola, path, pasif mod, SSL, zaman aşımı |
Hedef arayüzü bilerek minimaldir: put, has, delete, test. CPalius uzaktan okumaz; bu, "uzak dosya her istekte okunabilir olsun" bağımlılığını doğurmamak için bilinçli bir karardır.
Canlı yazma testi
Bir hedef, şu anki yapılandırmayla canlı bir yazma sınaması geçmeden kullanılmaz. Makul görünen kimlik bilgileri kanıt değildir: yalnız ListBucket izni veren ama PutObject'u yasaklayan bir kova politikası, salt okunur ev dizinli bir FTP hesabı ya da yanlış yazılmış bir uç nokta, ancak dosya yazıp geri okuyan bir testle yakalanır. Kaydet ve sına düğmesi bunu yapar; sonuç saklanır.
Medya kopyalama (offload)
Uzak depolama açılırsa yüklenen medya uzak hedefe kopyalanır (taşınmaz):
- Yerel dosya kalır ve asıl kopya olarak kalır. Görsel işleme GD ile diskten okur; uzak asıl dosya, her küçük resim üretiminde ağdan indirmek demek olurdu.
public/uploads/.htaccesskorumaları baytlarla birlikte kovaya taşınmaz; orada koruma, operatörün yönettiği bir kova politikasıdır.- Dosya yedeği medyayı içermez hale gelmesin diye yerel kopya silinmez.
Sonuç: uzak kopya bir teslimat optimizasyonudur, taşınma değil. Bu yüzden her hata kurtarılabilirdir: offload() istisna fırlatmaz, bool döner; bir S3 kesintisi çalışan bir görsel yüklemesini 500'e çevirmez.
| Ayar | Varsayılan |
|---|---|
storage.media.sweep_enabled |
kapalı |
storage.media.sweep_batch |
200 |
sweep zamanlanmış görevi (core.media_offload_sweep, günlük 03:40) henüz uzağa gitmemiş dosyaları toplu olarak gönderir; ilk açılışta mevcut kütüphaneyi taşımanın yoludur.
Yedekler için depolama
Aynı hedefler yedek gönderimi için de kullanılır; ayrıntı ve farklı davranış (orada hata gürültülüdür) için bkz. Yedekleme ve bakım.
Modül yazarı için
// Yükleme: tek yetkili kapı
$asset = $assetManager->upload($uploadedFile, allowedMimePrefixes: ['image/'], maxBytes: 5 * 1024 * 1024);
// Adres: her zaman jeneratör
$url = $assetUrlGenerator->forKey($asset->getStorageKey());
- Yüklemeyi kendiniz yazmayın,
AssetManagerkullanın. - Dosya türü kısıtını çağrıda daraltın (
image/gibi). - Dosya adı olarak kullanıcının gönderdiğini kullanmayın;
originalNameyalnızca görüntü içindir. - Adres için
AssetUrlGenerator, boyut için küçük resim yardımcısı kullanın.