Suistimal Raporları — Bug Fix + Batch Planı

admin/muzibu/abuse-reports • Uygulama Planı v1

6
Bug
4
Tasarım
1
Yeni Özellik
1
Karar

Kritik Karar: Ping-Pong Pattern'i

Basit Anlatım

Sorun: İlk planda Ping-Pong "normal davranış" sayılıp kaldırılacaktı. Ama iki kişi aynı hesabı paylaşıp aynı anda dinlediğinde, dinleme kayıtları zamana göre sıralandığında IP adresleri doğal olarak 1→2→1→2→1→2 şeklinde değişiyor.

Neden önemli: Bu alternating (değişen) IP pattern'i, Ping-Pong tespitinin tam olarak aradığı şey. Eğer kaldırırsak, sırayla dinleyenleri (biri kapatıp diğeri açıyor) yakalayamayız.

Karar: Ping-Pong KALACAK. 3 pattern ile devam edilecek.

Teknik Detay

Ping-Pong

KALIYOR

IP/browser/platform'da A→B→A döngüsü

Sırayla dinleyenleri yakalar (çakışma olmasa bile)

detectPingPong($plays, $field)
Her field için: ip_address, browser, platform, device_key

Concurrent Different

KALIYOR

Aynı anda farklı fingerprint

Eş zamanlı dinleyenleri yakalar (zaman çakışması)

detectConcurrentDifferentSource($plays)
Farklı IP+browser+platform, aynı zaman dilimi

Split Stream

KALIYOR

Aynı fingerprint + overlap

1 PC'den 2 hoparlöre yönlendirmeyi yakalar

detectSplitStream($plays)
Aynı IP+browser+platform, farklı şarkılar çakışıyor
Skor hesaplama (max 1300): Ping-Pong: döngü × 100 (max 500) + Concurrent: örnek × 50 (max 500) + Split: örnek × 30 (max 300)

Temel Kural

1 Abonelik = 1 Aktif Stream

YASAK (Suistimal)

  • Aynı anda 2 farklı cihaz/IP'den dinleme (Concurrent)
  • Aynı cihazdan 2 ayrı stream açma (Split Stream)
  • IP/cihaz sürekli A→B→A değişimi (Ping-Pong)

NORMAL (B2B)

  • 15 saat, 7/24 dinleme (işletme)
  • Yüksek hacim (restoran)
  • Skip yok (arka plan müzik)
  • Gece dinleme (24 saat açık mekan)

Bug Listesi (6 Adet)

B1 apiStats() yanlış pattern key'leri kullanıyor
Kritik

Basit: İstatistik sayfasındaki "tespit edilen davranışlar" kartları tamamen yanlış isimlere bakıyor. rapid_skips, high_volume gibi eski key'ler arıyor ama veritabanında ping_pong, concurrent_different, split_stream var.

Teknik Detay

Dosya: AbuseReportController.php:166-193

// YANLIŞ (eski key'ler)
$patternCounts = [
    'rapid_skips' => 0, 'high_volume' => 0,
    'repeat_songs' => 0, 'multi_device' => 0,
    'suspicious_ip' => 0, 'no_sleep' => 0, 'bot_like' => 0,
];
// DOĞRU (gerçek pattern key'leri)
$patternCounts = [
    'ping_pong' => 0,
    'concurrent_different' => 0,
    'split_stream' => 0,
];

Ayrıca isset($patterns[$key])$patterns[$key]['detected'] ?? false olmalı

B2 formatScore() saniye formatı kullanıyor (skor puan!)
Orta

Basit: Skor aslında "puan" ama arayüz bunu dakika:saniye olarak gösteriyor. Örneğin skor 500 olunca "8m 20s" yazıyor, ama aslında "500 puan" olmalı.

Teknik Detay

Dosyalar:

  • index.blade.php:576-581 → formatScore() JS fonksiyonu
  • AbuseReport.php:193-198 → getAbuseScoreFormattedAttribute()
  • show.blade.php → abuse_score gösterimi
// YANLIŞ
formatScore(seconds) {
    const m = Math.floor(seconds / 60);
    const s = seconds % 60;
    return m > 0 ? `${m}m ${s}s` : `${s}s`;
}
// DOĞRU
formatScore(score) {
    return score + ' puan';
}
B3 getPatternCount() tüm key'leri sayıyor (detected filtresi yok)
Orta

Basit: Pattern sayısı her zaman 3 görünüyor, çünkü "tespit edilenler" değil "tüm key'ler" sayılıyor. Temiz bir kullanıcıda bile "3 pattern" yazıyor.

Teknik Detay

Dosya: index.blade.php:592-595

// YANLIŞ
getPatternCount(report) {
    return Object.keys(report.patterns_json).length; // Her zaman 3!
}
// DOĞRU
getPatternCount(report) {
    if (!report.patterns_json) return 0;
    return Object.values(report.patterns_json)
        .filter(p => p.detected).length;
}
B4 show.blade overlap kartları eksik alanlar bekliyor
Kritik

Basit: Detay sayfasındaki overlap kartları device, platform, overlap_start, overlap_end, same_browser, same_ip gibi alanlar bekliyor ama service bunları üretmiyor. Kartlar boş görünüyor.

Teknik Detay

Dosya: AbuseDetectionService.php

detectConcurrentDifferentSource() ve detectSplitStream() sample yapıları show.blade'in beklediği alanları içermiyor.

Çözüm: Sample yapısına şu alanlar eklenecek:

'device', 'platform', 'start', 'end',
'overlap_start', 'overlap_end',
'same_browser', 'same_ip'

Ayrıca getUserTimelineData() return'e 'overlaps' key eklenecek.

B5 calculateDailyStats() overlaps key eksik
Orta

Basit: Günlük istatistiklerde "toplam çakışma" sayısı yok. Detay sayfasında günlük çakışma grafiği boş kalıyor.

Teknik Detay

Dosya: AbuseDetectionService.php:565-598

// Eklenecek
'overlaps' => $concurrentCount + $splitCount
B6 scan_date format hatası (saat gösteriliyor)
Düşük

Basit: show.blade'de tarama tarihi "28.02.2026 00:00" şeklinde saat ile gösteriliyor. Sadece tarih gösterilmeli: "28.02.2026"

Teknik Detay

Dosya: show.blade.php

format('d.m.Y H:i') → format('d.m.Y')

Tasarım Hataları (4 Adet)

D1 scanUser() status belirleme: hasAbuse → determineStatus

Basit: Şu anda herhangi bir pattern varsa direkt "abuse" yazıyor. Oysa skora göre kademeli olmalı (temiz / şüpheli / suistimal).

Teknik Detay
// YANLIŞ
$hasAbuse = $this->hasAnyPatternDetected($patterns);
$status = $hasAbuse ? 'abuse' : 'clean';
// DOĞRU
$abuseScore = $this->calculatePatternScore($patterns);
$status = AbuseReport::determineStatus($abuseScore);
D2 quickCheck() → Job'da kullanılmıyor

Basit: Tek fingerprint'li kullanıcılar (suistimal OLAMAZ) gereksiz yere Horizon'a gönderiliyor. Job'un başında quickCheck yapılıp CLEAN kaydedilmeli.

Teknik Detay

Dosya: ScanUserForAbuseJob.php:91-138

handle() başında quickCheck çağrılacak, tek fingerprint ise direkt CLEAN rapor oluşturulacak.

D3 THRESHOLD sabitleri yorum düzeltme

Basit: Threshold sabitleri "saniye cinsinden" yorumlu ama aslında "puan cinsinden". Yorum güncellenmeli.

D4 getAbuseScoreFormattedAttribute() saniye→puan

Basit: Model'deki skor formatlama saniye olarak yapıyor, puan olarak değiştirilecek.

Yeni Özellik: Tarama Batch Ayrımı

Basit Anlatım

Sorun: Şu anda her tarama aynı gün yapılan önceki taramayı eziyor. Sabah "Son 7 Gün" ile tarama yapıp akşam "Son 30 Gün" ile tarama yaparsanız, sabahki sonuçlar kaybolur.

Çözüm: Her taramaya benzersiz bir "batch ID" verilecek. Böylece aynı gün birden fazla tarama yapılabilir ve sonuçlar karışmaz.

Kullanım: Liste sayfasında bir dropdown ile batch'ler arasında geçiş yapılabilecek. Varsayılan olarak son batch gösterilir.

Teknik Detay

Migration

Tablo: muzibu_abuse_reports

  • + scan_batch_id string(36) nullable — UUID
  • + scan_batch_label string(100) nullable — "Son 7 Gün — 28.02.2026 01:15"
  • + Index: scan_batch_id
  • + Unique: (user_id, scan_batch_id)

updateOrCreate Mantığı

AbuseReport::updateOrCreate(
    ['user_id' => $userId, 'scan_batch_id' => $scanBatchId],
    [/* rapor verileri */]
);

Eski: (user_id, scan_date) → aynı günde 1 rapor. Yeni: (user_id, scan_batch_id) → batch başına 1 rapor.

API Endpoint'leri

  • GET /api/batches — Son 20 batch listesi (label, tarih, rapor sayısı)
  • GET /api/list?batch_id=xxx — Batch'e göre filtreli raporlar
  • GET /api/stats?batch_id=xxx — Batch'e göre istatistikler

Performans Optimizasyonu

Basit: Concurrent ve Split Stream tespitinde her play'i diğer her play ile karşılaştırıyoruz (O(n²)). 1000 play'lik bir kullanıcıda bu 500.000 karşılaştırma demek. Sliding window ile azaltılacak.

Teknik Detay

Play'ler zaman sıralı. Bir şarkının max süresi ~10dk olsun. İç döngüde, başlangıç zamanları arası max süreyi geçerse break yaparak gereksiz karşılaştırmaları atlıyoruz.

// Sort by start time (zaten sıralı geliyor)
// Inner loop: break when gap > maxDuration
if ($p2['start']->diffInSeconds($p1['start']) > $maxDuration) break;

show.blade.php — Dark Tema Çakışması

Basit: Detay sayfasında dark tema zorla uygulanmış, admin panelin normal görünümü bozulmuş. Arka plan gradient ve form input'lar düzeltilecek.

Teknik Detay

.abuse-report-page background gradient kaldırılacak

Form input'lardaki zorla dark stil kaldırılacak

Container container-xl yapılacak

Kapsam Dışı (Bu Seferde Yapılmayacak)

E1 Otomatik periyodik tarama (cron)
E2 CSV/Excel export
E3 Arşiv/soft delete
E4 Real-time tarama progress

Değişecek Dosyalar (8 Dosya)

# Dosya Değişiklik Risk
1 migration/tenant/...add_scan_batch.php YENİ — 2 kolon + index + unique Orta
2 AbuseReport.php fillable, threshold, skor format, scope Düşük
3 AbuseDetectionService.php batch params, sample alanları, overlaps, sliding window Yüksek
4 ScanUserForAbuseJob.php batch params, quickCheck entegrasyonu Orta
5 AbuseReportController.php batch ID üretimi, apiStats fix, apiBatches Orta
6 routes/admin.php api/batches route Düşük
7 index.blade.php 3 pattern kartı, skor format, batch dropdown Orta
8 show.blade.php 3 pattern meta, skor format, dark tema fix Orta

Uygulama Sırası

  1. 1
    Migration scan_batch_id + scan_batch_label kolonları (3 aşamalı onay)
  2. 2
    AbuseReport.php fillable, threshold, skor format, batch scope
  3. 3
    AbuseDetectionService.php EN BÜYÜK DEĞİŞİKLİK — batch params, sample alanları, sliding window, overlaps
  4. 4
    ScanUserForAbuseJob.php batch params, quickCheck early exit
  5. 5
    AbuseReportController.php batch ID üretimi, apiStats fix, apiBatches
  6. 6
    routes/admin.php api/batches GET route
  7. 7
    index.blade.php 3 pattern kartı, formatScore, getPatternCount, batch dropdown
  8. 8
    show.blade.php 3 pattern meta, skor format, dark tema fix, batch bilgisi
  9. 9
    İzinler + Cache chown/chmod + cache:clear + config:cache + route:cache