Proje dosyaları eklendi

This commit is contained in:
2026-08-29 15:53:53 +03:00
commit d3313400ca
440 changed files with 24827 additions and 0 deletions
@@ -0,0 +1,61 @@
<?php
namespace App\Http\Controllers;
use App\Models\EarnApiToken;
use App\Services\EarnApiAccessLogger;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\RateLimiter;
class EarnDeviceController extends Controller
{
public function bind(Request $request, EarnApiAccessLogger $logs): JsonResponse
{
$token = $request->attributes->get('earn_api_token');
abort_unless($token instanceof EarnApiToken, 401);
$hash = $request->attributes->get('earn_device_hash');
$preview = $request->attributes->get('earn_device_preview');
$result = DB::transaction(function () use ($token, $hash, $preview, $request): array {
$locked = EarnApiToken::query()->whereKey($token->id)->lockForUpdate()->firstOrFail();
if ($locked->device_id_hash !== null) {
$match = hash_equals($locked->device_id_hash, $hash);
if ($match) {
$locked->forceFill(['last_ip' => $request->ip(), 'last_seen_at' => now()])->save();
}
return ['token' => $locked, 'match' => $match];
}
$locked->forceFill(['device_id_hash' => $hash, 'device_id_preview' => $preview, 'device_bound_at' => now(),
'first_ip' => $request->ip(), 'last_ip' => $request->ip(), 'last_seen_at' => now()])->save();
return ['token' => $locked, 'match' => true, 'created' => true];
}, 3);
if (! $result['match']) {
$failedKey = 'earn-api:failed-device:'.hash('sha256', $result['token']->id.'|'.$request->ip());
$failedLimit = (int) config('earn.api.failed_device_per_minute');
if (RateLimiter::tooManyAttempts($failedKey, $failedLimit)) {
$logs->record($request, 'rate_limited', 429, 'RATE_LIMITED', $result['token'], $hash, $preview);
return $this->error('RATE_LIMITED', 429)->header('Retry-After', (string) RateLimiter::availableIn($failedKey));
}
RateLimiter::hit($failedKey, 60);
$result['token']->forceFill(['device_mismatch_seen' => true])->saveQuietly();
$logs->record($request, 'device_mismatch', 409, 'DEVICE_MISMATCH', $result['token'], $hash, $preview);
return $this->error('DEVICE_MISMATCH', 409);
}
$already = ! ($result['created'] ?? false);
$logs->record($request, $already ? 'device_bind_repeated' : 'device_bound', 200, null, $result['token'], $hash, $preview);
return response()->json(['success' => true, 'data' => ['device_bound' => true, 'already_bound' => $already,
'device_id_preview' => $result['token']->device_id_preview, 'bound_at' => $result['token']->device_bound_at?->toISOString()]])
->header('Cache-Control', 'no-store');
}
private function error(string $code, int $status): JsonResponse
{
return response()->json(['success' => false, 'error' => ['code' => $code, 'message' => __('earn_api.'.$code)]], $status)->header('Cache-Control', 'no-store');
}
}