Curator Ownership, Privacy & Usage Tracking
Dokumen ini menjelaskan dua aspek penting dari Filament Curator di starter kit ini:
- Ownership & Privacy — siapa yang memiliki media dan siapa yang bisa melihatnya
- Usage Tracking — pelacakan penggunaan media untuk menjaga integritas data
Bagian 1: Ownership & Privacy (Blameable)
Starter kit ini mengimplementasikan sistem kepemilikan (ownership) dan privasi (privacy) pada Curator Media Manager untuk meningkatkan keamanan dan akuntabilitas data.
Fitur Utama
- Explicit Ownership: Setiap record media mencatat siapa yang mengunggahnya melalui kolom
created_by. - Granular Privacy: Media dapat diatur menjadi
PRIVATE,MEMBER, atauPUBLIC. - Automatic Visibility Sync: Sistem secara otomatis mensinkronkan status
privacy(logical) denganvisibility(physical) di storage (misalnya S3). - Policy Enforcement: Akses untuk mengedit dan menghapus media dibatasi hanya untuk pemilik, admin, atau super admin.
Arsitektur Database
Kolom-kolom berikut telah ditambahkan ke tabel curator (via migration):
created_by(UUID): Foreign key ke tabelusers.deleted_by(UUID): Siapa yang menghapus (Soft Delete).deleted_at(Timestamp): Waktu penghapusan.privacy(String): Menyimpan status privasi media (Default:private).
Model CuratorMedia
Model App\Models\CuratorMedia telah diperbarui dengan:
- Relationship: Method
creator()yang merujuk ke modelUser. - Casts: Properti
privacydi-cast ke enumApp\Enums\Privacy. - Auto-blame: Menggunakan event
creatinguntuk otomatis mengisicreated_bydengan ID user yang sedang login. - Visibility Sync: Menggunakan event
savinguntuk sinkronisasi dua arah otomatis antaraprivacydanvisibility(physical storage).
// app/Models/CuratorMedia.php
protected static function booted(): void
{
self::saving(function (self $media): void {
// Priority 1: If privacy is changed, visibility must follow.
if ($media->isDirty('privacy')) {
$media->visibility = $media->privacy === Privacy::PUBLIC ? 'public' : 'private';
}
// Priority 2: If visibility is changed, privacy must follow (to stay in sync).
elseif ($media->isDirty('visibility')) {
$media->privacy = $media->visibility === 'public' ? Privacy::PUBLIC : Privacy::PRIVATE;
}
// Priority 3: Always ensure they match based on privacy source of truth.
else {
$media->visibility = $media->privacy === Privacy::PUBLIC ? 'public' : 'private';
}
});
}
Enum Privacy
Enum App\Enums\Privacy mendefinisikan tiga level akses:
- PRIVATE (Default): Hanya dapat dilihat oleh
creator,admin, dansuper_admin. Physical visibility di storage adalahprivate. - MEMBER: Dapat dilihat oleh semua user yang sudah login. Physical visibility di storage adalah
private. - PUBLIC: Dapat dilihat oleh siapa saja. Physical visibility di storage akan otomatis menjadi
public(memungkinkan akses langsung tanpa signed URL pada S3).
Security Policy (Permission-Only)
App\Policies\CuratorMediaPolicy menangani otorisasi menggunakan sistem Permission secara ketat (Best Practice Spatie & Filament Shield). Policy mengikuti konvensi penamaan {Action}:{Model} yang digunakan di proyek ini.
Logika Akses
-
View:
- Public/Member privacy diizinkan otomatis.
- Private diizinkan jika punya permission
View:CuratorMediaatau (pemilik dan punyaViewOwn:CuratorMedia).
-
Update/Delete/Restore:
- Diizinkan jika punya permission
{Action}:CuratorMedia(Global). - Diizinkan jika pemilik (
created_by) dan punya permission{Action}Own:CuratorMedia(Ownership).
- Diizinkan jika punya permission
Hal ini memungkinkan Admin untuk memiliki semua permission (misalnya Update:CuratorMedia), sedangkan Member hanya diberikan permission *Own (misalnya UpdateOwn:CuratorMedia) saja.
Proteksi Integritas
Aksi Delete dan Force Delete akan selalu diblokir jika media tersebut masih tercatat digunakan oleh model lain, meskipun user memiliki permission lengkap. Hal ini dijamin melalui CheckMediaUsageAction.
Pemilihan Privacy di Form
Untuk mendukung pemilihan privasi saat mengunggah atau mengedit media, starter kit menggunakan CustomMediaForm di app/Filament/Curator/CustomMediaForm.php.
Form ini menambahkan field Select untuk privasi:
Select::make('privacy')
->label('Privacy')
->options(Privacy::class)
->default(Privacy::PUBLIC)
->required()
Bagian 2: Media Usage Tracking & Integrity
Starter kit ini dilengkapi dengan sistem pelacakan penggunaan media secara eksplisit untuk menjamin integritas data, mencegah "Broken Images", dan mendukung siklus hidup aset yang aman.
Mengapa Menggunakan Usage Tracking?
Secara default, Media Manager tidak tahu di mana file media digunakan. Sistem pelacakan ini memberikan manfaat:
- Integritas Data: Mencegah penghapusan media yang masih digunakan oleh model lain (Post, User, dll).
- Audit Aset: Mengetahui dengan pasti aset mana saja yang masih aktif.
- Soft Delete & Restore: Mendukung fitur "Recycle Bin" di mana media yang dihapus tidak langsung hilang, namun otomatis tersembunyi dari relasi model yang menggunakannya.
Arsitektur Database: Tabel curator_media_usages
Tabel ini mencatat relasi antara media dan model yang menggunakannya secara Polymorphic:
curator_media_id(UUID): Referensi ke media.model_id,model_type: Model domain (misalnyaApp\Models\Post).field_name: Nama kolom (misalnyathumbnail_curator_id).
Action Pattern untuk Usage Tracking
Sistem ini menggunakan pola Action Pattern dengan method handle() dan DB::transaction untuk menjamin atomisitas data.
1. SyncMediaUsageAction
Menyinkronkan penggunaan media saat model dibuat atau diperbarui:
resolve(SyncMediaUsageAction::class)->handle($model, 'field_name', $curatorId);
2. DeleteCuratorMediaAction
Melakukan Soft Delete pada media dan mencatat siapa yang melakukan penghapusan (deleted_by):
resolve(DeleteCuratorMediaAction::class)->handle($media, $user);
3. DeleteAllMediaUsagesAction
Membersihkan catatan penggunaan media saat model pemilik (User/Post) benar-benar dihapus (Force Delete).
Proteksi Penghapusan
App\Policies\CuratorMediaPolicy menerapkan aturan industri:
- Cegah Penghapusan: Media tidak dapat di-delete atau di-force delete jika statusnya masih tercatat digunakan (
CheckMediaUsageAction). - Otomatis Tersembunyi: Karena menggunakan
SoftDeletespada modelCuratorMedia, relasi Eloquent (seperti$user->avatarMedia) secara otomatis akan mengembalikannulljika media tersebut sedang berada di "Recycle Bin" (di-soft delete). Jika di-restore, maka akan muncul kembali secara otomatis.
Pengujian (Testing)
Fitur ini telah dilindungi dengan unit & feature tests menggunakan Pest:
tests/Feature/CuratorMediaTest.php: Menguji ownership, soft delete, blameable, sinkronisasi privasi, dan logika akses per level privasi.tests/Feature/MediaUsageTest.php: Menguji integritas tabel usage.
Untuk menjalankan tes:
php artisan test --compact --filter=CuratorMediaTest
php artisan test --compact --filter=MediaUsageTest