feat: Prepare Zemailnator for Dokploy deployment

- Add highly optimized Dockerfile with Nginx and PHP-FPM 8.4
- Add docker-compose.yml configured with Redis and MariaDB 10.11
- Implement entrypoint.sh and supervisord.conf for background workers
- Refactor legacy IMAP scripts into scheduled Artisan Commands
- Secure app by removing old routes with hardcoded basic auth credentials
- Configure email attachments to use Laravel Storage instead of insecure public/tmp
This commit is contained in:
idevakk
2026-02-28 23:17:39 +05:30
parent bf5b797cd8
commit c312ec3325
78 changed files with 750 additions and 360 deletions

View File

@@ -2,18 +2,17 @@
namespace App\Models;
use DateTimeImmutable;
use Illuminate\Support\Facades\Date;
use App\ColorPicker;
use Carbon\CarbonImmutable;
use DateTime;
use DateTimeImmutable;
use Ddeboer\Imap\ConnectionInterface;
use Ddeboer\Imap\Search\Date\Since;
use Ddeboer\Imap\Server;
use Exception;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Date;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
@@ -78,7 +77,7 @@ class Email extends Model
$sender = $message->getFrom();
$date = $message->getDate();
if (!$date instanceof DateTimeImmutable) {
if (! $date instanceof DateTimeImmutable) {
$date = new DateTime;
if ($message->getHeaders()->get('udate')) {
$date->setTimestamp($message->getHeaders()->get('udate'));
@@ -100,11 +99,11 @@ class Email extends Model
$obj = [];
$to = $message->getHeaders()->get('To') ? array_map(fn($entry): string => $entry->mailbox.'@'.$entry->host, $message->getHeaders()->get('To')) : [];
$to = $message->getHeaders()->get('To') ? array_map(fn ($entry): string => $entry->mailbox.'@'.$entry->host, $message->getHeaders()->get('To')) : [];
$cc = $message->getHeaders()->get('Cc') ? array_map(fn($entry): string => $entry->mailbox.'@'.$entry->host, $message->getHeaders()->get('Cc')) : [];
$cc = $message->getHeaders()->get('Cc') ? array_map(fn ($entry): string => $entry->mailbox.'@'.$entry->host, $message->getHeaders()->get('Cc')) : [];
$bcc = $message->getHeaders()->get('Bcc') ? array_map(fn($entry): string => $entry->mailbox.'@'.$entry->host, $message->getHeaders()->get('Bcc')) : [];
$bcc = $message->getHeaders()->get('Bcc') ? array_map(fn ($entry): string => $entry->mailbox.'@'.$entry->host, $message->getHeaders()->get('Bcc')) : [];
$messageTime = $message->getDate();
$utcTime = CarbonImmutable::instance($messageTime)->setTimezone('UTC')->toDateTimeString();
@@ -127,28 +126,27 @@ class Email extends Model
if ($message->hasAttachments()) {
$attachments = $message->getAttachments();
$directory = './tmp/attachments/'.$obj['id'].'/';
$directoryPath = 'attachments/'.$obj['id'];
if (!is_dir($directory)) {
mkdir($directory, 0777, true);
}
foreach ($attachments as $attachment) {
$filenameArray = explode('.', (string) $attachment->getFilename());
$extension = $filenameArray[count($filenameArray) - 1];
if (in_array($extension, $allowed)) {
if (! file_exists($directory.$attachment->getFilename())) {
$filePath = $directoryPath.'/'.$attachment->getFilename();
if (! \Illuminate\Support\Facades\Storage::disk('public')->exists($filePath)) {
try {
file_put_contents(
$directory.$attachment->getFilename(),
\Illuminate\Support\Facades\Storage::disk('public')->put(
$filePath,
$attachment->getDecodedContent()
);
} catch (Exception $e) {
Log::error($e->getMessage());
}
}
if ($attachment->getFilename() !== 'undefined') {
$url = config('app.settings.app_base_url').str_replace('./', '/', $directory.$attachment->getFilename());
$url = config('app.settings.app_base_url').'/storage/'.$filePath;
$structure = $attachment->getStructure();
if (isset($structure->id) && str_contains($obj['content'], trim($structure->id, '<>'))) {
$obj['content'] = str_replace('cid:'.trim($structure->id, '<>'), $url, $obj['content']);
@@ -159,9 +157,7 @@ class Email extends Model
];
}
}
}
}
$response['data'][] = $obj;
@@ -354,11 +350,9 @@ class Email extends Model
public static function deleteBulkAttachments(): void
{
$dir = public_path('/tmp/attachments');
try {
if (File::exists($dir)) {
File::cleanDirectory($dir);
if (\Illuminate\Support\Facades\Storage::disk('public')->exists('attachments')) {
\Illuminate\Support\Facades\Storage::disk('public')->deleteDirectory('attachments');
}
} catch (Exception $e) {
Log::error($e->getMessage());
@@ -431,6 +425,7 @@ class Email extends Model
$currentTime = Date::now('UTC');
$lastRecordTime = Date::parse($latestRecord->timestamp);
return $lastRecordTime->diffInMinutes($currentTime) < 5;
}