HEX
Server: LiteSpeed
System: Linux s1049.use1.mysecurecloudhost.com 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: xedaptot (3356)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/xedaptot/ai.naniguide.com/app/Model/Attachment.php
<?php

namespace App\Model;

use App\Library\Traits\HasUid;
use Illuminate\Support\Facades\Redis;
use Symfony\Component\HttpFoundation\File\File;
use App\Library\Storage\StoragePathResolver;

class Attachment extends Asset
{
    protected $table = 'assets';

    public const TYPE_ATTACHMENT = 'attachment';

    public static function makeForCampaignOrEmail(
        Campaign|AutomationEmail $campaignOrEmail,
        File $file,
    ): static {
        $attachment = new static();

        $attachment->customer()->associate($campaignOrEmail->customer);
        $attachment->original_name = ($file instanceof \Illuminate\Http\UploadedFile) ? $file->getClientOriginalName() : $file->getFilename();
        $attachment->size = $file->getSize();
        $attachment->mime_type = $file->getMimeType();
        $attachment->type = static::TYPE_ATTACHMENT;
        $attachment->visibility = static::VISIBILITY_PRIVATE;

        $attachment->path = app(StoragePathResolver::class)->attachmentPath($campaignOrEmail->customer);

        if ($campaignOrEmail instanceof Campaign) {
            $attachment->campaign()->associate($campaignOrEmail);
        } else {
            $attachment->email()->associate($campaignOrEmail);
        }

        return $attachment;
    }

    /**
     * Association with campaign through campaign_id column.
     */
    public function campaign()
    {
        return $this->belongsTo(Campaign::class);
    }

    /**
     * Association with email through email_id column.
     */
    public function email()
    {
        return $this->belongsTo(AutomationEmail::class);
    }

    /**
     * Store this file in Redis (as cache).
     *
     * @param int $ttl  Time to live in seconds (default 5 min).
     * @return string   The cache ID used to retrieve the file.
     */
    public function cacheToRedis(int $ttl = 1800): string
    {
        $content = $this->getFileContent();

        $id = $this->getFileCacheId();
        $keyBody = "tmp:file:{$id}:body";
        $keyMeta = "tmp:file:{$id}:meta";

        $meta = [
            'mime' => $this->mime_type,
            'size' => strlen($content),
        ];

        // Store compressed body + metadata
        Redis::multi()
            ->setex($keyBody, $ttl, gzencode($content, 6))
            ->setex($keyMeta, $ttl, json_encode($meta))
            ->exec();

        return $id;
    }

    /**
     * Fetch a cached file from Redis.
     *
     * @param string $id   The cache ID returned by cacheToRedis().
     * @return array|null  [ 'bytes' => string, 'meta' => array ] or null if expired.
     */
    public function fetchFromRedis(): ?array
    {
        $id = $this->getFileCacheId();

        $keyBody = "tmp:file:{$id}:body";
        $keyMeta = "tmp:file:{$id}:meta";

        $body = Redis::get($keyBody);
        $meta = json_decode(Redis::get($keyMeta) ?? '{}', true);

        if (!$body) {
            return null; // expired or not found
        }

        return [
            'bytes' => gzdecode($body) ?: $body,
            'meta'  => $meta,
        ];
    }

    /**
     * Check if a cached file still exists in Redis.
     *
     * @param  string $id
     * @return bool
     */
    public function cacheExistsInRedis(): bool
    {
        $id = $this->getFileCacheId();
        $keyBody = "tmp:file:{$id}:body";
        return Redis::exists($keyBody) > 0;
    }

    public function getFileCacheId()
    {
        $id = "attachment-{$this->uid}";
        return $id;
    }

    public function makePartFromRedis(): ?\Symfony\Component\Mime\Part\DataPart
    {
        $data = $this->fetchFromRedis();
        if (!$data) {
            return null;
        }

        $bytes = $data['bytes']; // already decoded in fetchFromRedis()
        $mime  = $data['meta']['mime'];

        return new \Symfony\Component\Mime\Part\DataPart($bytes, $this->name, $mime);
    }

    public function makePartFromStorage(): \Symfony\Component\Mime\Part\DataPart
    {
        $fileContent = $this->getFileContent();
        return new \Symfony\Component\Mime\Part\DataPart($fileContent, $this->name, $this->mime_type);
    }

    public function clearCacheFromRedis(): int
    {
        $id = $this->getFileCacheId();
        $keyBody = "tmp:file:{$id}:body";
        $keyMeta = "tmp:file:{$id}:meta";

        return Redis::del([$keyBody, $keyMeta]);
    }
}