From 18cff1b067ad0e57da2067864eaf0180e77e8d76 Mon Sep 17 00:00:00 2001 From: Calum Towers Date: Thu, 9 Jan 2025 17:37:32 +0000 Subject: [PATCH] wip --- .../VatsimNet/ProcessVatsimNetWebhook.php | 28 +++++++ .../Webhooks/MemberChangedAction.php | 74 +++++++++++++++++++ .../Webhooks/MemberCreatedAction.php | 44 +++++++++++ .../Mship/Concerns/HasQualifications.php | 2 +- config/services.php | 7 ++ routes/web-external.php | 16 +--- 6 files changed, 157 insertions(+), 14 deletions(-) create mode 100644 app/Http/Controllers/External/VatsimNet/ProcessVatsimNetWebhook.php create mode 100644 app/Jobs/ExternalServices/VatsimNet/Webhooks/MemberChangedAction.php create mode 100644 app/Jobs/ExternalServices/VatsimNet/Webhooks/MemberCreatedAction.php diff --git a/app/Http/Controllers/External/VatsimNet/ProcessVatsimNetWebhook.php b/app/Http/Controllers/External/VatsimNet/ProcessVatsimNetWebhook.php new file mode 100644 index 0000000000..061627b1ac --- /dev/null +++ b/app/Http/Controllers/External/VatsimNet/ProcessVatsimNetWebhook.php @@ -0,0 +1,28 @@ +header('Authorization') !== config('services.vatsim-net.webhook.key')) { + return response()->json([ + 'status' => 'forbidden', + ], 403); + } + + foreach (request()->json('actions') as $action) { + if (class_exists($class = config("services.vatsim-net.webhook.jobs.{$action['action']}"))) { + dispatch(new $class(request()->json('resource'), $action)); + // ->afterResponse(); + } + } + + return response()->json([ + 'status' => 'ok', + ]); + } +} diff --git a/app/Jobs/ExternalServices/VatsimNet/Webhooks/MemberChangedAction.php b/app/Jobs/ExternalServices/VatsimNet/Webhooks/MemberChangedAction.php new file mode 100644 index 0000000000..ba60941c63 --- /dev/null +++ b/app/Jobs/ExternalServices/VatsimNet/Webhooks/MemberChangedAction.php @@ -0,0 +1,74 @@ +memberId = $memberId; + $this->data = $data; + } + + public function handle() + { + foreach ($this->data['deltas'] as $delta) { + match ($delta['field']) { + 'id', 'name_first', 'name_last', 'email', 'reg_date' => $this->processAccountChange($delta['field'], $delta['after']), + 'rating' => $this->processAtcRatingChange($delta['after']), + 'pilotrating' => $this->processPilotRatingChange($delta['after']), + 'division_id', 'region_id' => $this->processStateChange($delta['after']), + default => null + }; + } + } + + private function processAccountChange(string $field, mixed $value): void + { + Account::firstWhere('id', $this->memberId)->update([ + $field => $value, + ]); + } + + private function processAtcRatingChange(mixed $value): void + { + Account::firstWhere('id', $this->memberId)->updateVatsimRatings(atcRating: $value); + } + + private function processPilotRatingChange(mixed $value): void + { + Account::firstWhere('id', $this->memberId)->updateVatsimRatings(pilotRating: $value); + } + + private function processStateChange(mixed $value): void + { + // if both a division and region is changed in the deltas + // this will run twice, which is not ideal + + $account = Account::with('states')->firstWhere('id', $this->memberId); + + $currentRegion = $account->primary_permanent_state->pivot->region; + $currentDivision = $account->primary_permanent_state->pivot->division; + + $regionChange = collect($this->data['deltas'])->firstWhere('field', 'region_id'); + $divisionChange = collect($this->data['deltas'])->firstWhere('field', 'division_id'); + + $account->updateDivision( + division: is_null($divisionChange) ? $currentDivision : $divisionChange['after'], + region: is_null($regionChange) ? $currentRegion : $regionChange['after'], + ); + } +} diff --git a/app/Jobs/ExternalServices/VatsimNet/Webhooks/MemberCreatedAction.php b/app/Jobs/ExternalServices/VatsimNet/Webhooks/MemberCreatedAction.php new file mode 100644 index 0000000000..459937654b --- /dev/null +++ b/app/Jobs/ExternalServices/VatsimNet/Webhooks/MemberCreatedAction.php @@ -0,0 +1,44 @@ +memberId = $memberId; + $this->data = $data; + } + + public function handle() + { + $account = Account::updateOrCreate(['id' => $this->getField('id')], [ + 'name_first' => $this->getField('name_first'), + 'name_last' => $this->getField('name_last'), + 'email' => $this->getField('email'), + 'joined_at' => $this->getField('reg_date'), + ]); + $account->updateVatsimRatings($this->getField('rating'), $this->getField('pilotrating')); + $account->updateDivision($this->getField('division_id'), $this->getField('region_id')); + $account->save(); + } + + private function getField(string $field) + { + return Arr::get(collect($this->data['deltas'])->firstWhere('field', $field), 'after'); + } +} diff --git a/app/Models/Mship/Concerns/HasQualifications.php b/app/Models/Mship/Concerns/HasQualifications.php index ab8875c5d8..b76f5cc07f 100644 --- a/app/Models/Mship/Concerns/HasQualifications.php +++ b/app/Models/Mship/Concerns/HasQualifications.php @@ -78,7 +78,7 @@ public function removeQualification(Qualification $qualification) * @param int|null $atcRating The VATSIM ATC rating * @param int|null $pilotRating The VATSIM pilot rating */ - public function updateVatsimRatings(?int $atcRating, ?int $pilotRating) + public function updateVatsimRatings(?int $atcRating = null, ?int $pilotRating = null) { if ($atcRating === 0) { $this->addNetworkBan('Network ban discovered via Cert login.'); diff --git a/config/services.php b/config/services.php index 04bce23fcc..f27e404d95 100644 --- a/config/services.php +++ b/config/services.php @@ -1,5 +1,8 @@ [ 'webhook' => [ 'key' => env('VATSIM_NET_WEBHOOK_KEY'), + 'jobs' => [ + 'member_created_action' => MemberCreatedAction::class, + 'member_changed_action' => MemberChangedAction::class, + ], ], 'api' => [ 'base' => env('VATSIM_API_BASE', 'https://api.vatsim.net/api/'), diff --git a/routes/web-external.php b/routes/web-external.php index dcab84be90..6b78839f89 100644 --- a/routes/web-external.php +++ b/routes/web-external.php @@ -1,5 +1,7 @@ 'external', 'as' => 'external.', @@ -9,19 +11,7 @@ 'prefix' => 'vatsim-net', 'as' => 'vatsim-net.', ], function () { - - Route::post('webhook', function () { - Log::info(print_r([ - 'Authorization' => request()->header('Authorization'), - 'User-Agent' => request()->header('User-Agent'), - 'Body' => request()->all(), - ], true)); - - return response()->json([ - 'status' => 'ok', - ]); - })->name('webhook'); - + Route::post('webhook', ProcessVatsimNetWebhook::class)->name('webhook'); }); });