Test Sunucusu için Tam Detaylı Uygulama Rehberi
public static function findOrCreateByFingerprint(int $userId, array $data): self
{
$fingerprint = self::generateFingerprint($data);
// ❌ SORUN: Sadece fingerprint ile arıyor (user_id kontrolü yok!)
$profile = self::where('fingerprint', $fingerprint)->first();
if ($profile) {
// ❌ SORUN: Başka kullanıcının device'ını ÇALıyor!
if ($profile->user_id !== $userId) {
$profile->update(['user_id' => $userId]);
}
return $profile;
}
// ...
}
| Kullanıcı | Kullandığı Device | Device Sahibi | Yanlış Kayıt | IP Kanıtı |
|---|---|---|---|---|
| Always (2905) | 3401 |
Ahmet (2899) | 3 play |
Play IP: 176.41.33.58
Device IP: 88.230.97.118
|
| Soner (2916) | 3073 |
Sait (2431) | 2 play |
Play IP: 85.105.46.138
Device IP: 88.248.29.26
|
mysql -u root -p'4p4CH3u$3r' tenant_muzibu_1528d0 -e "
SELECT * FROM muzibu_song_plays
WHERE (user_id = 2905 AND device_profile_id = 3401)
OR (user_id = 2916 AND device_profile_id = 3073)
ORDER BY created_at;
" > /tmp/device_profile_yanlış_kayitlar.sql
mysqldump -u root -p'4p4CH3u$3r' tenant_muzibu_1528d0 \
muzibu_device_profiles > /tmp/device_profiles_backup.sql
mysql -u root -p'4p4CH3u$3r' tenant_muzibu_1528d0 -e "
SELECT
user_id,
(SELECT name FROM users WHERE id = sp.user_id) as user_name,
device_profile_id,
COUNT(*) as will_delete
FROM muzibu_song_plays sp
WHERE (user_id = 2905 AND device_profile_id = 3401)
OR (user_id = 2916 AND device_profile_id = 3073)
GROUP BY user_id, device_profile_id;
"
mysql -u root -p'4p4CH3u$3r' tenant_muzibu_1528d0 -e "
DELETE FROM muzibu_song_plays
WHERE (user_id = 2905 AND device_profile_id = 3401)
OR (user_id = 2916 AND device_profile_id = 3073);
"
php artisan make:migration add_composite_unique_to_device_profiles --path=Modules/Muzibu/database/migrations/tenant
php artisan tenants:migrate --force
SELECT
sp.user_id,
u.name,
sp.device_profile_id,
dp.user_id as device_owner,
CASE WHEN sp.user_id = dp.user_id THEN '✅' ELSE '❌' END as match
FROM muzibu_song_plays sp
JOIN users u ON sp.user_id = u.id
JOIN muzibu_device_profiles dp ON sp.device_profile_id = dp.device_profile_id
WHERE sp.user_id IN (2905, 2899, 2916)
ORDER BY sp.created_at DESC
LIMIT 10;
php artisan cache:clear && \
php artisan config:clear && \
php artisan route:clear && \
php artisan view:clear
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
public function up(): void
{
Schema::table('muzibu_device_profiles', function (Blueprint $table) {
// 1. Eski index'i kaldır
$table->dropIndex('muzibu_device_profiles_fingerprint_index');
// 2. Yeni composite unique index ekle
$table->unique(['user_id', 'fingerprint'], 'unique_user_device');
});
}
public function down(): void
{
Schema::table('muzibu_device_profiles', function (Blueprint $table) {
// Rollback: Composite unique kaldır, eski index geri ekle
$table->dropUnique('unique_user_device');
$table->index('fingerprint');
});
}
};
public static function findOrCreateByFingerprint(int $userId, array $data): self
{
$fingerprint = self::generateFingerprint($data);
// Önce fingerprint ile ara (unique olduğu için)
$profile = self::where('fingerprint', $fingerprint)->first();
if ($profile) {
// Cihaz zaten kayıtlı - user_id'yi güncelle (farklı kullanıcı aynı cihazı kullanabilir)
if ($profile->user_id !== $userId) {
$profile->update(['user_id' => $userId]); // ❌ SORUN!
}
return $profile;
}
// Yeni cihaz - oluştur
try {
return self::create(array_merge($data, [
'fingerprint' => $fingerprint,
'user_id' => $userId,
]));
} catch (\Illuminate\Database\QueryException $e) {
// ...
}
}
public static function findOrCreateByFingerprint(int $userId, array $data): self
{
$fingerprint = self::generateFingerprint($data);
// ✅ user_id VE fingerprint ile ara
$profile = self::where('fingerprint', $fingerprint)
->where('user_id', $userId)
->first();
if ($profile) {
// Cihaz zaten kayıtlı, döndür
return $profile;
}
// Yeni cihaz - oluştur
try {
return self::create(array_merge($data, [
'fingerprint' => $fingerprint,
'user_id' => $userId,
]));
} catch (\Illuminate\Database\QueryException $e) {
// Race condition: Başka bir request aynı anda oluşturmuş olabilir
if ($e->getCode() === '23000') {
$profile = self::where('fingerprint', $fingerprint)
->where('user_id', $userId)
->first();
if ($profile) {
return $profile;
}
}
throw $e;
}
}
-- Duplicate kayıtları bul
SELECT user_id, fingerprint, COUNT(*) as cnt
FROM muzibu_device_profiles
GROUP BY user_id, fingerprint
HAVING cnt > 1;
-- En eskilerini tut, diğerlerini sil
-- (Manuel kontrol gerekir!)
localStorage.removeItem('muzibu_device_profile_id') → Sayfayı yenile
-- 1. Migration geri al
php artisan migrate:rollback --step=1 --path=Modules/Muzibu/database/migrations/tenant
-- 2. Backup'tan geri yükle
mysql -u root -p'4p4CH3u$3r' tenant_muzibu_1528d0 < /tmp/device_profiles_backup.sql
-- 3. Model kodunu eski haline getir (git reset)