insert([ 'id' => $id, 'user_id' => $userId, 'temp_path' => $tempPath, 'status' => $status, 'ip' => $ip, 'created_at' => $createdAt->toDateTimeString(), 'progress' => 0, 'failure_reason' => null, ]); return new UploadSessionData($id, $userId, $tempPath, $status, $ip, $createdAt, 0, null); } public function get(string $id): ?UploadSessionData { $row = DB::table('uploads_sessions')->where('id', $id)->first(); if (! $row) { return null; } return new UploadSessionData( (string) $row->id, (int) $row->user_id, (string) $row->temp_path, (string) $row->status, (string) $row->ip, CarbonImmutable::parse($row->created_at), (int) ($row->progress ?? 0), $row->failure_reason ? (string) $row->failure_reason : null ); } public function getOrFail(string $id): UploadSessionData { $session = $this->get($id); if (! $session) { throw new RuntimeException('Upload session not found.'); } return $session; } public function updateStatus(string $id, string $status): void { DB::table('uploads_sessions')->where('id', $id)->update([ 'status' => $status, ]); } public function updateProgress(string $id, int $progress): void { DB::table('uploads_sessions')->where('id', $id)->update([ 'progress' => max(0, min(100, $progress)), ]); } public function updateFailureReason(string $id, ?string $reason): void { DB::table('uploads_sessions')->where('id', $id)->update([ 'failure_reason' => $reason, ]); } public function updateTempPath(string $id, string $tempPath): void { DB::table('uploads_sessions')->where('id', $id)->update([ 'temp_path' => $tempPath, ]); } public function countActiveForUser(int $userId): int { $terminal = [ UploadSessionStatus::PROCESSED, UploadSessionStatus::QUARANTINED, UploadSessionStatus::CANCELLED, ]; return (int) DB::table('uploads_sessions') ->where('user_id', $userId) ->whereNotIn('status', $terminal) ->count(); } public function countForUserSince(int $userId, CarbonImmutable $since): int { return (int) DB::table('uploads_sessions') ->where('user_id', $userId) ->where('created_at', '>=', $since->toDateTimeString()) ->count(); } }