feat(storage): complete phase 3 branding foundation
This commit is contained in:
@@ -289,19 +289,19 @@ Hedef klasör yapısı:
|
||||
|
||||
Dosya checklist'i:
|
||||
|
||||
- [ ] Dosya metadata tablosu oluşturuldu.
|
||||
- [ ] Relative path dışında mutlak kullanıcı girdisi kullanılmıyor.
|
||||
- [ ] Path traversal koruması var.
|
||||
- [ ] Dosya boyutu limiti var.
|
||||
- [ ] MIME allowlist var.
|
||||
- [ ] Gerekli türlerde magic-byte doğrulaması var.
|
||||
- [ ] SVG kabul ediliyorsa sanitizasyon kararı uygulandı.
|
||||
- [ ] Yetkili upload Route Handler yazıldı.
|
||||
- [ ] Yetkili download Route Handler yazıldı.
|
||||
- [ ] Public branding asset'leri ayrı ve kontrollü sunuluyor.
|
||||
- [ ] Project asset erişimi owner/project/client ilişkisiyle doğrulanıyor.
|
||||
- [ ] Dosya silme ve DB metadata işlemi tutarlı.
|
||||
- [ ] Backup içine upload klasörü dahil.
|
||||
- [x] Dosya metadata tablosu oluşturuldu.
|
||||
- [x] Relative path dışında mutlak kullanıcı girdisi kullanılmıyor.
|
||||
- [x] Path traversal koruması var.
|
||||
- [x] Dosya boyutu limiti var.
|
||||
- [x] MIME allowlist var.
|
||||
- [x] Gerekli türlerde magic-byte doğrulaması var.
|
||||
- [x] SVG kabul ediliyorsa sanitizasyon kararı uygulandı (ilk sürümde SVG reddediliyor).
|
||||
- [x] Yetkili upload Route Handler yazıldı.
|
||||
- [x] Yetkili download Route Handler yazıldı.
|
||||
- [x] Public branding asset'leri ayrı ve kontrollü sunuluyor.
|
||||
- [x] Project asset erişimi owner/project/client ilişkisiyle doğrulanıyor.
|
||||
- [x] Dosya silme ve DB metadata işlemi tutarlı.
|
||||
- [x] Backup içine upload klasörü dahil.
|
||||
|
||||
## 11. Instance özelleştirme ve tema
|
||||
|
||||
@@ -351,15 +351,15 @@ type BrandingSettings = {
|
||||
Tema checklist'i:
|
||||
|
||||
- [ ] Poyraz UI semantic tokenları kullanılıyor.
|
||||
- [ ] Marka ayarları root layout'ta server-side okunuyor.
|
||||
- [ ] İlk render sırasında tema/renk parlaması yok.
|
||||
- [ ] Primary ve accent renk girdileri doğrulanıyor.
|
||||
- [ ] Metin/zemin kontrastı kontrol ediliyor.
|
||||
- [x] Marka ayarları root layout'ta server-side okunuyor.
|
||||
- [x] İlk render sırasında tema/renk parlaması yok.
|
||||
- [x] Primary ve accent renk girdileri doğrulanıyor.
|
||||
- [x] Metin/zemin kontrastı kontrol ediliyor.
|
||||
- [ ] Hard-coded brand renkleri feature sayfalarına dağılmıyor.
|
||||
- [ ] Açık ve koyu modda logo fallback'i var.
|
||||
- [ ] Logo kaldırma ve varsayılana dönme desteği var.
|
||||
- [ ] Branding ayarlarına yalnızca freelancer/admin yazabiliyor.
|
||||
- [ ] Client portal aynı instance markasını güvenli biçimde kullanıyor.
|
||||
- [x] Açık ve koyu modda logo fallback'i var.
|
||||
- [x] Logo kaldırma ve varsayılana dönme desteği var.
|
||||
- [x] Branding ayarlarına yalnızca freelancer/admin yazabiliyor.
|
||||
- [x] Client portal aynı instance markasını güvenli biçimde kullanıyor.
|
||||
|
||||
## 12. Poyraz UI v3 stratejisi
|
||||
|
||||
@@ -717,13 +717,13 @@ Mümkün olduğunda küçük ve doğrudan test araçları tercih edilir; test al
|
||||
- [x] Client private task erişim negatif testi
|
||||
- [x] Revision project-client eşleşme negatif testi
|
||||
- [x] Revision quota testi
|
||||
- [ ] File upload MIME/size testi
|
||||
- [ ] Path traversal negatif testi
|
||||
- [x] File upload MIME/size testi
|
||||
- [x] Path traversal negatif testi
|
||||
- [x] Backup oluşturma testi
|
||||
- [ ] Restore ve checksum testi
|
||||
- [x] Restore ve checksum testi
|
||||
- [ ] Supabase import fixture testi
|
||||
- [ ] API response/error contract testi
|
||||
- [ ] Kritik sayfalar için SSR smoke testi
|
||||
- [x] API response/error contract testi
|
||||
- [x] Kritik sayfalar için SSR smoke testi
|
||||
|
||||
### 19.2. Her faz sonunda çalıştırılacak kalite kapıları
|
||||
|
||||
@@ -843,13 +843,23 @@ Faz 2 tamamlanma notu (2026-07-16):
|
||||
|
||||
Amaç: Supabase Storage yerine güvenli local filesystem ve instance özelleştirmesi sağlamak.
|
||||
|
||||
- [ ] File metadata schema tamamlandı.
|
||||
- [ ] Upload/download servisleri tamamlandı.
|
||||
- [ ] Avatar desteği tamamlandı.
|
||||
- [ ] Branding asset desteği tamamlandı.
|
||||
- [ ] Project asset desteği tamamlandı.
|
||||
- [ ] Instance branding schema ve service tamamlandı.
|
||||
- [ ] Server-rendered token uygulaması tamamlandı.
|
||||
- [x] File metadata schema tamamlandı.
|
||||
- [x] Upload/download servisleri tamamlandı.
|
||||
- [x] Avatar desteği tamamlandı.
|
||||
- [x] Branding asset desteği tamamlandı.
|
||||
- [x] Project asset desteği tamamlandı.
|
||||
- [x] Instance branding schema ve service tamamlandı.
|
||||
- [x] Server-rendered token uygulaması tamamlandı.
|
||||
|
||||
Faz 3 tamamlanma notu (2026-07-16):
|
||||
|
||||
- `files` ve `instance_branding` tabloları; owner/resource/visibility constraint'leri ve SHA-256 metadata ile eklendi.
|
||||
- Local file servisi 5 MiB limit, MIME allowlist, magic-byte kontrolü, SVG reddi, root-bound relative path, symlink koruması ve geri alınabilir upload/delete sırası uygular.
|
||||
- Authenticated upload/download/delete, kontrollü public branding asset ve owner-only branding Route Handler'ları standart API envelope ile eklendi.
|
||||
- Avatar subject, private/portal project asset ve referenced-only public branding authorization kuralları gerçek SQLite/filesystem ve Next.js HTTP smoke testleriyle doğrulandı.
|
||||
- Instance adı, logo/icon, primary/accent, color mode ve radius; root layout metadata/CSS tokenları ile dinamik web manifest'e server-side uygulanıyor.
|
||||
- Backup uploads ağacını kapsıyor; restore artık path, symlink, byte size, manifest completeness ve SHA-256 checksum doğrulaması yapıyor.
|
||||
- Tasarım, güvenlik ve test ayrıntıları `phase-3-storage-branding.md` belgesinde kaydedildi.
|
||||
|
||||
Çıkış kriteri: Logo, avatar ve project asset için Supabase Storage gerekmiyor.
|
||||
|
||||
@@ -971,22 +981,22 @@ Amaç: React Native geliştirmesine başlamadan önce instance keşif ve stabil
|
||||
|
||||
### Özelleştirme
|
||||
|
||||
- [ ] Instance adı değiştirilebiliyor.
|
||||
- [ ] Logo yüklenebiliyor.
|
||||
- [ ] Favicon/ikon yüklenebiliyor.
|
||||
- [ ] Primary renk değiştirilebiliyor.
|
||||
- [ ] Accent renk değiştirilebiliyor.
|
||||
- [ ] Varsayılan tema değiştirilebiliyor.
|
||||
- [ ] Radius yoğunluğu değiştirilebiliyor.
|
||||
- [ ] Portal markası uygulanıyor.
|
||||
- [x] Instance adı değiştirilebiliyor.
|
||||
- [x] Logo yüklenebiliyor.
|
||||
- [x] Favicon/ikon yüklenebiliyor.
|
||||
- [x] Primary renk değiştirilebiliyor.
|
||||
- [x] Accent renk değiştirilebiliyor.
|
||||
- [x] Varsayılan tema değiştirilebiliyor.
|
||||
- [x] Radius yoğunluğu değiştirilebiliyor.
|
||||
- [x] Portal markası uygulanıyor.
|
||||
|
||||
### Operasyon
|
||||
|
||||
- [ ] Docker kurulumu çalışıyor.
|
||||
- [ ] Persistent volume doğrulandı.
|
||||
- [ ] Migration güvenli.
|
||||
- [ ] Backup çalışıyor.
|
||||
- [ ] Restore ve checksum doğrulaması çalışıyor.
|
||||
- [x] Backup çalışıyor.
|
||||
- [x] Restore ve checksum doğrulaması çalışıyor.
|
||||
- [ ] Health endpoint'leri çalışıyor.
|
||||
- [ ] Upgrade dokümantasyonu hazır.
|
||||
- [ ] Rollback planı hazır.
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
---
|
||||
title: Faz 3 Yerel Storage ve Instance Branding
|
||||
description: Güvenli local filesystem, dosya metadata, authorized file route'ları, backup checksum ve server-rendered branding sözleşmesi.
|
||||
status: complete
|
||||
last_updated: 2026-07-16
|
||||
---
|
||||
|
||||
# Faz 3 Yerel Storage ve Instance Branding
|
||||
|
||||
Faz 3, avatar, branding ve project asset içeriklerinin Supabase Storage yerine instance'ın persistent data volume'ünde saklanabileceği güvenli backend temelini kurar. Mevcut feature sayfalarının bu servislere taşınması ilgili dikey sayfa fazlarında yapılacaktır; file ve branding çekirdeğinin kendisi Supabase, browser veya UI bağımlılığı taşımaz.
|
||||
|
||||
## Veri modeli
|
||||
|
||||
`0004_fancy_baron_zemo.sql` iki tablo ekler:
|
||||
|
||||
- `files`: owner/uploader, avatar subject veya project ilişkisi, tür, visibility, relative storage path, MIME, byte size ve SHA-256 metadata'sı;
|
||||
- `instance_branding`: tek instance kaydı, uygulama isimleri, logo/icon file referansları, primary/accent renkleri, color mode, radius ve portal metinleri.
|
||||
|
||||
`files` tablosu tür-kaynak-visibility kombinasyonlarını SQLite CHECK constraint'iyle sınırlar. Mutlak path, `..`, sıfır/negatif byte size ve geçersiz checksum metadata seviyesinde de reddedilir. File foreign key'leri `restrict` kullanır; bir user veya project silinmesi physical dosyayı atlayarak orphan üretemez. Branding file referansları dosya silindiğinde `set null` olur.
|
||||
|
||||
## Filesystem sözleşmesi
|
||||
|
||||
Dosyalar yalnızca aşağıdaki servis tarafından üretilen relative path'lerde tutulur:
|
||||
|
||||
```text
|
||||
uploads/
|
||||
avatars/<generated-id>.<detected-extension>
|
||||
branding/<generated-id>.<detected-extension>
|
||||
project-assets/<generated-id>.<detected-extension>
|
||||
```
|
||||
|
||||
Actor, project ID veya original filename path üretiminde kullanılmaz. `resolveStoragePath` absolute path, backslash, boş segment, `.` ve `..` segmentlerini reddeder; çözülmüş path'in uploads root altında kaldığını ikinci kez doğrular.
|
||||
|
||||
Upload sırası:
|
||||
|
||||
1. Role/resource authorization ve policy doğrulanır.
|
||||
2. İçerik instance `tmp/` alanına `wx` ve `0600` ile yazılır.
|
||||
3. Hedef path'e overwrite etmeyen hard-link ile atomik publish edilir.
|
||||
4. File metadata ve avatar `user.image` değişikliği `BEGIN IMMEDIATE` transaction'da yazılır.
|
||||
5. Transaction başarısızsa yalnızca o işlemde oluşturulan physical dosya kaldırılır.
|
||||
|
||||
Delete işleminde dosya önce aynı data volume içindeki trash path'e taşınır, metadata transaction'ı tamamlanır, sonra trash kaldırılır. DB işlemi başarısızsa dosya eski yerine alınır. Avatar silinirse halen ilgili dosyayı gösteren `user.image` alanı da aynı transaction'da temizlenir.
|
||||
|
||||
Download sırasında metadata authorization yeniden uygulanır. File descriptor `O_NOFOLLOW` ile açılır; symbolic link izlenmez ve physical size metadata ile eşleşmelidir. Response `nosniff`, doğru Content-Type, byte length, ETag ve kontrollü cache header'ları taşır.
|
||||
|
||||
## Dosya politikası
|
||||
|
||||
- Maksimum dosya boyutu: 5 MiB.
|
||||
- Allowlist: JPEG, PNG, WebP ve avatar/project/logo için GIF.
|
||||
- Branding icon/favicon için yalnızca PNG kabul edilir; manifest MIME sözleşmesi sabit ve güvenli kalır.
|
||||
- MIME yalnızca browser beyanından alınmaz; JPEG/PNG/WebP/GIF magic byte imzası içerikten doğrulanır.
|
||||
- SVG ilk sürümde kabul edilmez. Böylece SVG script/external reference sanitizasyon bağımlılığı eklenmez.
|
||||
- Original filename yalnızca download adı olarak normalize edilir; storage path'e girmez.
|
||||
|
||||
## Authorization matrisi
|
||||
|
||||
| Kaynak | Upload | Authenticated read | Public read | Delete |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Avatar | Aktif owner veya client, yalnızca kendisi | Owner veya avatar subject | Yok | Owner veya avatar subject |
|
||||
| Branding logo/icon | Yalnızca owner | Owner | Yalnızca aktif branding kaydınca referanslanan dosya | Owner |
|
||||
| Private project asset | Yalnızca project owner | Owner | Yok | Owner |
|
||||
| Portal project asset | Yalnızca project owner | Owner veya projeye bağlı client | Yok | Owner |
|
||||
|
||||
Unauthorized resource varlığı sızdırmamak için cross-owner/cross-client read çoğunlukla `NOT_FOUND` döner. Branding public route'u `public_branding` visibility tek başına yeterli saymaz; dosyanın aktif `instance_branding` kaydındaki light logo, dark logo veya icon alanlarından birinde referanslanması gerekir.
|
||||
|
||||
Route sınırları:
|
||||
|
||||
- `POST /api/files`: authenticated multipart upload;
|
||||
- `GET /api/files/:id`: authenticated authorized download;
|
||||
- `DELETE /api/files/:id`: authorized delete;
|
||||
- `GET /api/branding/assets/:id`: kontrollü public branding asset;
|
||||
- `GET /api/branding`: public, secret içermeyen instance markası;
|
||||
- `PATCH /api/branding`: owner-only branding mutation.
|
||||
|
||||
## Branding ve ilk render
|
||||
|
||||
Primary/accent değerleri yalnızca altı haneli hex olarak kabul edilir ve normalize edilir. Her renk için siyah/beyaz foreground arasından WCAG contrast oranı yüksek olan seçilir; smoke test en az 4.5:1 oranını doğrular. Hover/pressed, ring ve radius tokenları server-side üretilir.
|
||||
|
||||
Root layout her request'te branding'i SQLite'tan okur ve semantic CSS custom property'lerini doğrudan `<html style>` üzerinde üretir. `data-color-mode` ve dark class ilk HTML'de bulunur; system dark tercihi CSS media query ile uygulanır. Bu nedenle token veya color mode için hydration sonrası browser düzeltmesi ve ilk render parlaması gerekmez.
|
||||
|
||||
Metadata title, Apple web app adı, theme color ve icon da branding'den üretilir. `manifest.webmanifest` dinamik olarak application name, short name, primary color ve icon referansını kullanır. Light/dark logo alanlarından biri boşsa diğeri fallback olur; file silmek foreign key `set null` ile varsayılan asset durumuna döner. Dashboard ve client portal aynı root layout tokenlarını kullanır.
|
||||
|
||||
## Backup ve restore
|
||||
|
||||
Backup mevcut davranışını koruyarak SQLite dosyasını ve `uploads/` ağacını aynı backup klasörüne kopyalar; manifest her dosya için size ve SHA-256 içerir. Restore artık kopyalamadan önce:
|
||||
|
||||
- manifest formatını ve her path'in backup root içinde kalmasını;
|
||||
- symlink bulunmadığını;
|
||||
- size ve SHA-256 eşleşmesini;
|
||||
- manifest dışında doğrulanmamış ek dosya bulunmadığını
|
||||
|
||||
kontrol eder. Bozuk veya sonradan değiştirilmiş bir upload içeren backup reddedilir.
|
||||
|
||||
## Doğrulama kapsamı
|
||||
|
||||
`npm run phase3:storage-smoke`, migration uygulanmış gerçek SQLite ve gerçek geçici filesystem üzerinde şunları doğrular:
|
||||
|
||||
- avatar, logo/icon ve private/portal project asset upload/read/delete;
|
||||
- client avatar subject, project-client visibility ve cross-owner negatifleri;
|
||||
- MIME allowlist, magic byte, 5 MiB limit ve SVG reddi;
|
||||
- absolute/traversal/backslash path reddi ve symlink takip etmeme;
|
||||
- DB CHECK constraint'leri ve physical/metadata delete tutarlılığı;
|
||||
- owner-only branding mutation, file-kind eşleşmesi, logo fallback ve file silme;
|
||||
- primary/accent normalization ve foreground contrast.
|
||||
|
||||
Uçtan uca auth smoke gerçek Next.js Route Handler'ları üzerinden anonymous error envelope, authenticated multipart upload, public referenced logo, client portal/private ayrımı, client avatar upload/delete, server-rendered token/metadata ve dinamik manifest'i test eder. Runtime backup smoke upload restore'unu ve bozuk checksum reddini doğrular.
|
||||
|
||||
| Kontrol | Sonuç |
|
||||
| --- | --- |
|
||||
| `npm run typecheck` | Başarılı |
|
||||
| Değişen Faz 3 TypeScript dosyalarında targeted ESLint | 0 error, 0 warning |
|
||||
| `npm run phase3:storage-smoke` | Başarılı |
|
||||
| `node scripts/phase1-auth-smoke.mjs` | Başarılı; file/branding HTTP ve SSR dahil |
|
||||
| `node scripts/phase1-smoke.mjs` | Başarılı; uploads backup/restore ve bozuk checksum reddi dahil |
|
||||
| `pnpm db:generate` | Schema drift yok |
|
||||
| `npm run build` | Başarılı; file/branding route'ları ve dinamik manifest üretildi |
|
||||
|
||||
Repo geneli lint, önceki fazlardan kayıtlı UI/AI baseline dosyalarında 31 error ve 18 warning ile açık kalır. Faz 3 dosyalarının targeted lint kontrolü temizdir.
|
||||
Reference in New Issue
Block a user