Proje: OkulUp CRM · Hub: OkulUp CRM — Decisions
CRM Tarafında S3 Public/Private Geçişi — Etki Analizi
Karar
OkulUp API’sinde bugün yapılan public/ ↔ private/ prefix ayrımı (2026-05-14-s3-public-private-hiyerarsi) için CRM’de kod değişikliği yapılmadı. Mevcut CRM kodu yeni bucket yapısı ile %100 uyumlu çalışıyor; sadece manuel smoke test gerekiyor.
Gerekçe — CRM ne tüketiyor?
Tüm src/ ağacında S3-arkalı URL render eden sadece 3 nokta var:
| Yer | Alan | API Sınıflandırması | Risk |
|---|---|---|---|
src/features/ParentStudentMatchingTab.jsx:366 | child.avatar_url (<img>) | public (public/avatars/... + legacy avatars/...) | ❌ Yok — kalıcı URL |
src/features/GalleriesTab.jsx:87,147 | media.thumbnail_url, media.file_url (<img> + <a target=_blank>) | public (public/galleries/...) | ❌ Yok — kalıcı URL |
src/pages/AttendancePage.jsx:159 (window.open) | response.data.download_url (CSV export) | private (private/exports/{userId}/, 15 dk presigned) | ❌ Yok — URL anında kullanılıyor |
CRM, API’nin private tarafında olan mesaj ekleri, tracking foto, ödev teslimi, belge talebi eki, ödeme dekontu alanlarını hiç render etmiyor. Bu modüller CRM’de var ama UI seviyesinde sadece metadata gösteriyor (örn. TrackingTab sadece kategori/durum/not gösterir; photo_path ekran dışında).
Persistence Audit (zustand + localStorage)
localStoragesadece şunları saklar:STORAGE_KEYS.token,sidebarOpen, favori action key’leri. URL yok.useDataStore(zustand) persist middleware kullanmıyor — refresh’te tüm data sıfırlanır.src/lib/helpers.js’te in-memoryrecentGetResponsesMap var (TTLGET_CACHE_TTL_MS = 4000). 4 saniye, 15 dakika TTL’in çok altında — sorun yok.- IndexedDB / react-query persistor / sessionStorage kullanılmıyor.
Sonuç: 15 dakikalık presigned URL’lerin “ölü URL persist edilmesi” senaryosu CRM’de teknik olarak imkansız.
CORS
Bugünkü kod presigned S3 URL’i hiçbir yerde fetch() veya XMLHttpRequest ile çekmiyor. Kullanılan yöntemler:
<img src>— CORS preflight tetiklemez (no-cors mode).<a target="_blank" href>— sadece navigation.window.open(downloadUrl)— sadece navigation.
Dolayısıyla bucket’a CORS policy eklenmesi şu an gerekmiyor. İleride CRM private içeriği fetch(presignedUrl).blob() ile çekmek isterse (örn. inline PDF önizleme, mesaj eki client-side caching), API decision notunda hazır olan CORS şablonuna http://127.0.0.1:4173 (dev) ve prod origin eklenmeli.
”Copy link” pattern’i
CRM’de navigator.clipboard, copyToClipboard, “linki kopyala” tarzı buton hiç yok (grep ile doğrulandı). UX gözden geçirmesi gereken yer yok.
Browser image cache
Public içerik (avatar, gallery) → kalıcı URL → cache normal çalışıyor. Private içerik render edilmediği için “her response yeni URL → cache miss → data harcaması” senaryosu da geçerli değil.
Manuel Test Checklist
Smoke test için kullanıcı şunlara bakmalı:
- Avatar render: Admin/manager login →
Veli-Öğrenci Eşleştirmeekranı →child.avatar_urlolan kayıtlar →<img>200 dönmeli (hem legacyavatars/...hem yenipublic/avatars/...URL’leri için). - Gallery thumbnail: Admin login →
Galeri Onayları→ grid’de cover görseli + tıklayınca yeni sekmede S3 URL 200 dönmeli. - Attendance export: Yoklama → “CSV İndir” → yeni sekmede S3 presigned URL açılıp dosya inmeli (içerik
private/exports/...). - 15 dakika sonra reload: Avatar/galeri public olduğu için 15 dk sonra bile çalışmalı.
- Console/network: Dev tools network tab’inde yeni
okulup-dev/private/...URL’lerinin response header’ındax-amz-...imza parametreleri olmalı (presigned olduğunu teyit eder).
Backward Compatibility Doğrulaması
API decision notu legacy path’leri (avatars/, events/, galleries/, announcements/, meal-menus/, assignments/) yeni bucket policy ile public-read tutuyor. CRM’deki mevcut DB kayıtlarında bu eski path’ler olan satırlar (örn. data migration öncesi yüklenmiş avatarlar) → API tarafında UserResource::resolveAvatarUrl() her iki prefix’i de tanıdığı için CRM’e yine geçerli URL döner. CRM hiçbir prefix tahmini yapmıyor — sadece response’tan geleni alıp <img src>’ye koyuyor. ✅
İleride Dikkat Edilecekler
Eğer CRM şu özelliklerden birini eklerse o noktada code change şart olur:
- Ödeme dekontu görüntüleme —
private/payments/.../receipts/. View açıldığında payment’ı refetch ederek taze URL almak gerek. - Belge talebi eki indirme —
private/document-requests/.../. Aynı pattern. - Ödev teslim indirme (öğretmen ekranı) —
private/submissions/.../. Aynı pattern. - Mesaj ekleri (admin moderation ekranı eklenirse) —
private/messages/.../. - Tracking fotoğraf görüntüleme —
private/tracking/.../.
Pattern: “view modal/sayfa açıldığında ilgili resource’u tekrar requestJson ile fetch et” yeterli — useDataStore’daki cached versiyon bayatlamış olabilir. Bucket’a CORS eklemek ancak fetch(presignedUrl) (blob/base64) yapılırsa gerekiyor; bugünkü pattern (<img>, <a>, window.open) için CORS gerekmiyor.
Etkilenen Dosyalar
Hiçbiri. Sadece audit yapıldı, kod değişmedi. Bu not bir “negative finding” kaydı.
Related
- 2026-05-14-s3-public-private-hiyerarsi — API tarafındaki temel karar
- 2026-04-17-kampuscep-mobile-featureler-crm-e-tasindi — CRM’in hangi modülleri tükettiğini özetleyen önceki kapsam notu