Feat: Updating the Privacy Providers, moving them into the appropriate Modules, Updating Privacy views

This commit is contained in:
2026-09-16 18:51:20 +03:00
parent fdd1899c34
commit 910fa94395
14 changed files with 621 additions and 51 deletions
+38 -2
View File
@@ -5,19 +5,23 @@ namespace Modules\Core\Privacy\Services;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;
use Lunar\Base\LunarUser;
use Lunar\Models\Customer;
use Modules\Core\Auth\Models\Staff;
use Modules\Core\Privacy\Contracts\PersonalDataProvider;
use Modules\Core\Privacy\DTOs\CustomerSubject;
use Modules\Core\Privacy\DTOs\ErasureReport;
use Modules\Core\Privacy\DTOs\ProviderErasureResult;
use Modules\Core\Privacy\DTOs\UserSubject;
use Modules\Core\Privacy\Enums\ErasureOutcome;
use Modules\Core\Privacy\Enums\ErasureRequestStatus;
use Modules\Core\Privacy\Enums\ExportRequestStatus;
use Modules\Core\Privacy\Events\UserErasureRequested;
use Modules\Core\Privacy\Jobs\ExportDataSubjectJob;
use Modules\Core\Privacy\Models\DataErasureRequest;
use Modules\Core\Privacy\Models\DataExportRequest;
use Throwable;
/**
* Entry point for right-of-access and right-of-erasure requests, split into two
@@ -244,15 +248,30 @@ class PrivacyService
* called directly for a request that hasn't passed its grace period, since
* that defeats the point of the window; ProcessErasureRequestsCommand
* enforces isDue() before calling this.
*
* Each provider call is caught individually — a provider throwing (a bug,
* an unexpected DB state) converts to ErasureOutcome::Failed rather than
* aborting the whole array_map, so one broken provider never discards
* every OTHER provider's already-completed erasure for this same request.
* Without this, the $request->update() below would never run at all on a
* throw, silently leaving providers that already succeeded unrecorded and
* the request stuck Pending forever. Logged via Log::error() so a thrown
* provider is still visible to staff, not just swallowed into "Failed."
*/
public function completeErasure(DataErasureRequest $request): ErasureReport
{
if ($request->isForCustomer()) {
$subject = new CustomerSubject(customerId: $request->subject_id);
$results = array_map(fn ($provider) => $provider->eraseForCustomer($subject), $this->manager->providers());
$results = array_map(
fn (PersonalDataProvider $provider) => $this->safeErase($provider, 'eraseForCustomer', $subject),
$this->manager->providers()
);
} else {
$subject = new UserSubject(userId: $request->subject_id, email: $request->email);
$results = array_map(fn ($provider) => $provider->eraseForUser($subject), $this->manager->providers());
$results = array_map(
fn (PersonalDataProvider $provider) => $this->safeErase($provider, 'eraseForUser', $subject),
$this->manager->providers()
);
}
$report = new ErasureReport($subject, $results);
@@ -282,4 +301,21 @@ class PrivacyService
'deactivated_at' => $deactivated ? now() : null,
]);
}
/**
* @param 'eraseForCustomer'|'eraseForUser' $method
*/
private function safeErase(PersonalDataProvider $provider, string $method, CustomerSubject|UserSubject $subject): ProviderErasureResult
{
try {
return $provider->{$method}($subject);
} catch (Throwable $e) {
Log::error("Privacy provider {$provider->name()}::{$method}() threw during erasure", [
'provider' => $provider->name(),
'exception' => $e,
]);
return new ProviderErasureResult($provider->name(), ErasureOutcome::Failed, $e->getMessage());
}
}
}