first commit
Some checks failed
Build / run (push) Has been cancelled

This commit is contained in:
maher
2025-10-29 11:42:25 +01:00
commit 703f50a09d
4595 changed files with 385164 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
<?php
namespace Common\Csv;
use Auth;
use Carbon\Carbon;
use Common\Core\BaseController;
use Illuminate\Http\Request;
use Storage;
use Symfony\Component\HttpFoundation\StreamedResponse;
class BaseCsvExportController extends BaseController
{
public function __construct(protected Request $request)
{
$this->middleware('auth');
}
public function download(CsvExport $csvExport): StreamedResponse
{
if (
!Auth::user()->hasPermission('admin') &&
$csvExport->user_id !== Auth::id()
) {
abort(403);
}
return Storage::download(
$csvExport->filePath(),
$csvExport->download_name,
);
}
protected function exportUsing(BaseCsvExportJob $exportJob)
{
$csvExport = CsvExport::where(
'cache_name',
$exportJob->cacheName(),
)->first();
if (
$csvExport &&
$csvExport->created_at->greaterThan(Carbon::now()->addMinutes(-30))
) {
return $this->success([
'downloadPath' => $csvExport->downloadLink(),
]);
}
$this->dispatch($exportJob);
return $this->success(['result' => 'jobQueued']);
}
}

92
common/Csv/BaseCsvExportJob.php Executable file
View File

@@ -0,0 +1,92 @@
<?php
namespace Common\Csv;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Str;
abstract class BaseCsvExportJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* @var resource
*/
private $csvStream;
/**
* @var array
*/
protected $headerKeys;
abstract protected function generateLines();
abstract public function cacheName(): string;
public function handle()
{
$this->csvStream = fopen('php://temp', 'w');
$cacheName = $this->cacheName();
CsvExport::where('cache_name', $cacheName)->delete();
$this->generateLines();
$csvExport = CsvExport::create([
'cache_name' => $cacheName,
'user_id' => $this->requesterId ?? null,
'download_name' => "$cacheName.csv",
'uuid' => Str::uuid(),
]);
$csvExport->storeFile($this->csvStream);
fclose($this->csvStream);
$this->sendNotification($csvExport);
}
protected function writeLineToCsv(array $data)
{
if (!$this->headerKeys) {
$this->buildCsvHeader($data);
}
$values = array_map(function ($value) {
if ($value instanceof Carbon) {
return $value->created_at->format('Y-m-d');
}
return $value;
}, array_values($data));
fputcsv($this->csvStream, $values);
}
protected function buildCsvHeader(array $lineData)
{
$this->headerKeys = array_map(function ($column) {
return Str::title(str_replace('_', ' ', $column));
}, array_keys($lineData));
fputcsv($this->csvStream, $this->headerKeys);
}
protected function notificationName(): string
{
return $this->cacheName();
}
protected function sendNotification(CsvExport $export)
{
if (!$this->requesterId) {
return;
}
User::find($this->requesterId)?->notify(
new CsvExportReadyNotif($export, $this->notificationName()),
);
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Common\Csv;
use Auth;
use Common\Auth\Jobs\ExportRolesCsv;
use Common\Auth\Jobs\ExportUsersCsv;
class CommonCsvExportController extends BaseCsvExportController
{
public function exportUsers()
{
return $this->exportUsing(new ExportUsersCsv(Auth::id()));
}
public function exportRoles()
{
return $this->exportUsing(new ExportRolesCsv(Auth::id()));
}
}

39
common/Csv/CsvExport.php Executable file
View File

@@ -0,0 +1,39 @@
<?php
namespace Common\Csv;
use Illuminate\Database\Eloquent\Model;
use Storage;
class CsvExport extends Model
{
protected $guarded = ['id'];
protected $casts = [
'id' => 'integer',
'user_id' => 'integer',
];
const MODEL_TYPE = 'csv_export';
public static function getModelTypeAttribute(): string
{
return self::MODEL_TYPE;
}
public function storeFile($stream): bool
{
Storage::delete($this->filePath());
return Storage::writeStream($this->filePath(), $stream);
}
public function filePath(): string
{
return "exports/csv/{$this->uuid}.csv";
}
public function downloadLink(): string
{
return url("csv/download/$this->id");
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace Common\Csv;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class CsvExportReadyNotif extends Notification
{
use Queueable;
public function __construct(
protected CsvExport $csvExport,
protected string $exportName
) {
}
public function via($notifiable): array
{
return ['mail', 'database'];
}
public function toMail($notifiable): MailMessage
{
return (new MailMessage())
->line($this->primaryLine())
->line(
__(
'This download link will only work if you are logged in as user who has requested the export and it will expire in one day.',
),
)
->action('Download', $this->csvExport->downloadLink());
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable): array
{
return [
'mainAction' => [
'Label' => 'Download',
'action' => $this->csvExport->downloadLink(),
],
'lines' => [
[
'content' => $this->primaryLine(),
],
[
'content' => __(
'This download link will expire in one day.',
),
],
],
];
}
protected function primaryLine(): string
{
return __('“:name“ CSV export is ready to download.', [
'name' => ucfirst($this->exportName),
]);
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace Common\Csv;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
class DeleteExpiredCsvExports extends Command
{
protected $signature = 'csvExports:delete';
protected $description = 'Deleted csv exports that are expired.';
public function handle(): int
{
$count = 0;
CsvExport::where(
'created_at',
'<',
Carbon::now()->addDays(-1),
)->chunkById(10, function (Collection $chunk) use ($count) {
$count += $chunk->count();
CsvExport::whereIn('id', $chunk->pluck('id'))->delete();
$filePaths = $chunk->map(function (CsvExport $export) {
return $export->filePath();
});
Storage::delete($filePaths);
});
$this->info("Deleted $count expired csv exports");
return Command::SUCCESS;
}
}