Laravel uygulamanıza bildirim eklemek için her kılavuz, genellikle Pusher veya Reverb’i kurarak, Echo’yu bağlayarak ve belki Redis’i çalıştırarak başlar. Bu, canlı bir akışa ihtiyacınız olduğunda iyi bir yapı sağlar. Ancak bir uygulama içi bildirim merkezi için, okunmamış bir rozetle çan ve bir gelen kutusu, bir kuyruk ve bir cron satırı ile gerçekleştirebileceğiniz bir şeydir.
Halkın kullanabileceği bir SaaS çekirdek olan LaraFoundry, üretim ortamında zaten çalışan bir CRM’den çıkarıyorum. Bu sistemin belirli bir kuralı vardır: sadece düz paylaşımlı barındırma altında çalışmalıdır. Zorunlu Redis olmadan, Horizon olmadan ve uzun süreli daemonlar olmadan. Bu nedenle, bildirim modülünü (faz 4.1, etiket v0.14.x) geliştirirken, gerçekten çalışır bir bildirim merkezi ve süper yönetici yayınları sunmam gerekiyordu. İşte bu süreç nasıl gelişti.
Çanın bir sokete ihtiyacı yok
Çanın bir sokete ihtiyacı yok
Okunmamış rozet bir anket olup, itme değildir. Çan, yalnızca sekme görünürken, hafif bir aralıkta küçük bir unread-count uç noktasına erişir. Açılır menüyü açtığınızda, son liste alınır, bir zamanlayıcıyla değil. Tam gelen kutusu ise normal bir sayfalama sayfasıdır.
Bu, uygulama içi bir merkez için gerçekten yeterlidir. Uygulamayı açık tutan bir kullanıcı, sayımının bir dakika içinde yenilendiğini görür ve çanı açtıklarında her şeyi anında görebilir. Gerçek zamanlı teslimat, daha sonra kanallar dikişine eklenebilecek güzel bir güncellemedir, ancak başlamak için ihtiyaç duyulan bir bağımlılık değildir. Herhangi bir hostta çalışan 90 yüzdeyi gönderip beklemek, çoğu küçük SaaS’in erişmediği altyapı üzerine takılmaktan daha iyidir.
İsteği bloke etmeyen yayınlar
İsteği bloke etmeyen yayınlar
İlginç kısım ise süper yöneticilerin yayınlarıdır. Bir operatör, yerel metin ile bir mesaj taslaklar, bir kitle seçer ve gönderir. Kitle filtreleri, çekirdek tarafından sahip olunabilen alan bağımsız olanlardır: doğrulanmış kullanıcılar, son zamanda aktif olan kullanıcılar veya belirli bir RBAC rolüne sahip kullanıcılar. Demografik hedefleme (ülke, yaş) ev sahibi sistemde kalır, çünkü bu veriler orada bulunur.
“Herkese gönder” işleminin bilgisiz versiyonu, isteğin içinde her eşleşen kullanıcıyı senkronize bir ekleme ekler. Büyük bir kullanıcı tabanında bu istekleri bloke eder ve belleği şişirir. Bu nedenle, gönderim bir işi kuyruğa alır, depolanan filtrelerden kitleyi çözümler, bunu kimlik sırasına göre gruplandırır ve her taneyi insertOrIgnore ile ekler.
// Bir yayın, yerel ve parçalı olarak dağıtılır. insertOrIgnore, tekrar denenen bir işi idempotent tutar, büyük bir ekleme olmaz.
$this->recipientQuery($notification)
->select('id')
->chunkById(1000, function ($users) use ($notification) {
DB::table('larafoundry_notification_user')->insertOrIgnore(
$users->map(fn ($user) => [
'notification_id' => $notification->id,
'user_id' => $user->id,
'created_at' => now(),
])->all()
);
});
Burada iki detay önemlidir. insertOrIgnore işlemi, benzersiz bir (notification_id, user_id) index’e karşı, tekrar eden bir işin asla iki katına çıkmasını sağlamaz. Ve iş, yayın sending durumundayken çalışır: bittiğinde ve sent‘e döndüğünde, bir tekrar erken döner ve “yayın gönderildi” olayını yeniden ateşlemez. Yayının yayılması idempotenttir, aynı zamanda denetim izi de öyledir. Tüm bu işlemler veritabanı kuyruğunda çalışır, böylece sadece bir queue:work cron altında çalışan tek bir hareket alanı olur.
Bir bildirim, başka birinin yazdığı içeriktir
Bir bildirim, başka birinin yazdığı içeriktir
Bu benim en çok önem verdiğim kısım. Bir yayın gövdesi ve sistem bildirimi, sonunda bir kullanıcının tarayıcısında render edilen depolanmış metin haline gelir. Bu, saldırgan bağlantılı bir giriş olduğu için ben de bu şekilde ele aldım.
Başlıklar ve gövdeler, asla v-html olarak render edilmez. Bir bildirim, eylemler taşıyabilir, ancak her eylem, önce ön uç tarafından ulaşmadan önce, yalnızca dahili, aynı kaynaktan gelen bir GET bağlantısı haline getirilir: tek bir ön önek, asla bir protokol göreceli //host, asla bir geri eğim yoktur, ki bu bir tarayıcı tarafından yerel olarak başka bir siteye normalleştirilir. HTTP yöntemi düşürülür, bu nedenle depolanan bir eylem asla bir POST veya DELETE’ye yönlendiremeyecektir.
Ve gelen kutusu kendine özgüdür. Her okuma ve her değişim, kullanıcının kendi ilişkisi aracılığıyla geçer, bu nedenle başka bir kullanıcının bildirim kimliğini istemek yalnızca hiçbir şey bulur ve 404 döner. Sizin olmayan bir satırı “okunmuş olarak işaretle” işlemini yapamazsınız.
Ev sahibi bildirimleri tek bir dikişle gönderir
Ev sahibi bildirimleri tek bir dikişle gönderir
Uygulamanız, bildirim satırlarını elle yazmamalıdır. Çeviri anahtarları ile tek bir servisi çağırır, bu sayede mesaj, okuma sırasında her alıcı için yerelleştirilir.
// Kendi alan olayınızdan uygulama içi bir bildirim gönderin. Terimler çeviri anahtarlarıdır, her alıcı için yerelleştirilmiştir.
app(NotificationService::class)->system(
users: $company->users,
code: 'success',
titleKey: 'Welcome to :company',
params: [=> $company->name],
);
Ev sahipliğinde bunu bir alan olayı ile bağladım: bir şirket oluşturulduğunda, sahibi bir karşılama bildirimi alır. Ev sahibi tarafındaki tüm entegrasyon küçüktür. Kullanıcı modeline gelen kutusu ilişkisi için bir trait ekleyin, migration’ı çalıştırın, sayfaları yayınlayın ve çan, çekirdek şablonda yer aldığı için üstte görünmeye başlar. Kendi olaylarınızdan göndermek, yukarıdaki altı satırdır.
Kanıt, havadan değil
Kanıt, havadan değil
Bunların hiçbiri inançla gönderilmez. Modül, arka uçta Pest ve ön uçta Vitest ile kapatılmıştır ve yayımlanmadan önce her ikisi de yeşil olmuş, ayrıca gelen kutusunun kapsamı, IDOR kontrolü, yayın çapraz yayılımı ve süper yönetici hariç tutma, gerçek bir kiracıda baştan sona doğrulanmıştır. Başlıkların ve gövdelerin XSS kaçışının kendi ön uç testleri vardır. Test sayısı bu aşamada birkaç düzine kadar arttı.
Halka açık inşa etmek, yeşil test setinin hikayenin bir parçası olduğu anlamına gelir, ara not değil.
Takip edin
Takip edin
Kaynak: Orijinal Makale


