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,11 +2,11 @@
namespace Tests\Concerns;
use Exception;
use Illuminate\Support\Collection;
use App\Models\Blog;
use App\Models\Menu;
use App\Models\Plan;
use Exception;
use Illuminate\Support\Collection;
trait LoadsApplicationData
{
@@ -64,7 +64,7 @@ trait LoadsApplicationData
try {
$menus = cache()->remember('app_menus', now()->addHours(6), Menu::all(...));
$blogs = cache()->remember('app_blogs', now()->addHours(6), fn() => Blog::query()->where('is_published', 1)->get());
$blogs = cache()->remember('app_blogs', now()->addHours(6), fn () => Blog::query()->where('is_published', 1)->get());
$plans = cache()->remember('app_plans', now()->addHours(6), Plan::all(...));
} catch (Exception) {

View File

@@ -2,30 +2,30 @@
namespace Tests\Feature\Filament;
use App\Filament\Resources\TicketResource\Pages\ListTickets;
use App\Filament\Resources\TicketResource\Pages\EditTicket;
use App\Filament\Resources\TicketResource\RelationManagers\ResponsesRelationManager;
use App\Filament\Resources\TicketResource;
use App\Filament\Resources\PlanResource\Pages\ListPlans;
use App\Filament\Resources\PlanResource\Pages\EditPlan;
use App\Filament\Resources\BlogResource\Pages\ListBlogs;
use App\Filament\Resources\BlogResource;
use App\Filament\Resources\BlogResource\Pages\CreateBlog;
use App\Filament\Resources\BlogResource\Pages\EditBlog;
use App\Filament\Resources\CategoryResource\Pages\ListCategories;
use App\Filament\Resources\BlogResource\Pages\ListBlogs;
use App\Filament\Resources\CategoryResource\Pages\CreateCategory;
use App\Filament\Resources\CategoryResource\Pages\EditCategory;
use App\Filament\Resources\PageResource\Pages\ListPages;
use App\Filament\Resources\CategoryResource\Pages\ListCategories;
use App\Filament\Resources\MenuResource\Pages\CreateMenu;
use App\Filament\Resources\MenuResource\Pages\ListMenus;
use App\Filament\Resources\PageResource\Pages\CreatePage;
use App\Filament\Resources\PageResource\Pages\EditPage;
use App\Filament\Resources\MenuResource\Pages\ListMenus;
use App\Filament\Resources\MenuResource\Pages\CreateMenu;
use App\Filament\Resources\UserResource\Pages\ListUsers;
use App\Filament\Resources\UserResource;
use App\Filament\Resources\PageResource\Pages\ListPages;
use App\Filament\Resources\PlanResource;
use App\Filament\Resources\BlogResource;
use App\Filament\Resources\UserResource\Pages\CreateUser;
use App\Filament\Resources\BlogResource\Pages\CreateBlog;
use App\Filament\Resources\PlanResource\Pages\CreatePlan;
use App\Filament\Resources\PlanResource\Pages\EditPlan;
use App\Filament\Resources\PlanResource\Pages\ListPlans;
use App\Filament\Resources\TicketResource;
use App\Filament\Resources\TicketResource\Pages\CreateTicket;
use App\Filament\Resources\TicketResource\Pages\EditTicket;
use App\Filament\Resources\TicketResource\Pages\ListTickets;
use App\Filament\Resources\TicketResource\RelationManagers\ResponsesRelationManager;
use App\Filament\Resources\UserResource;
use App\Filament\Resources\UserResource\Pages\CreateUser;
use App\Filament\Resources\UserResource\Pages\ListUsers;
use App\Models\Blog;
use App\Models\Category;
use App\Models\Menu;
@@ -40,6 +40,7 @@ use Tests\TestCase;
class ResourcesTest extends TestCase
{
public $adminUser;
protected function setUp(): void
{
parent::setUp();

View File

@@ -2,12 +2,12 @@
namespace Tests\Feature\Filament;
use App\Filament\Resources\UserResource\RelationManagers\LogsRelationManager;
use App\Filament\Resources\UserResource\RelationManagers\UsageLogsRelationManager;
use App\Filament\Resources\UserResource;
use App\Filament\Resources\UserResource\Pages\CreateUser;
use App\Filament\Resources\UserResource\Pages\EditUser;
use App\Filament\Resources\UserResource\Pages\ListUsers;
use App\Filament\Resources\UserResource\RelationManagers\LogsRelationManager;
use App\Filament\Resources\UserResource\RelationManagers\UsageLogsRelationManager;
use App\Models\Log;
use App\Models\User;
use Livewire\Livewire;
@@ -16,6 +16,7 @@ use Tests\TestCase;
class UserResourceTest extends TestCase
{
public $adminUser;
protected function setUp(): void
{
parent::setUp();

View File

@@ -13,6 +13,7 @@ use Tests\TestCase;
class LoginTest extends TestCase
{
public $user;
protected function setUp(): void
{
parent::setUp();

View File

@@ -12,6 +12,7 @@ use Tests\TestCase;
class DashboardTest extends TestCase
{
public $user;
protected function setUp(): void
{
parent::setUp();

View File

@@ -2,13 +2,13 @@
namespace Tests\Feature\Livewire;
use App\Livewire\Home;
use App\Livewire\Frontend\Mailbox;
use Exception;
use App\Models\Blog;
use App\Livewire\Home;
use App\Livewire\ListBlog;
use App\Models\Blog;
use App\Models\Page;
use App\Models\ZEmail;
use Exception;
use Illuminate\Support\Facades\Cookie;
use Livewire\Livewire;
use Tests\TestCase;

View File

@@ -1,7 +1,7 @@
<?php
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
/*
|--------------------------------------------------------------------------
@@ -30,7 +30,7 @@ pest()->extend(TestCase::class)
|
*/
expect()->extend('toBeOne', fn() => $this->toBe(1));
expect()->extend('toBeOne', fn () => $this->toBe(1));
/*
|--------------------------------------------------------------------------

View File

@@ -9,6 +9,7 @@ use Tests\TestCase;
class ActivationKeyTest extends TestCase
{
public $user;
protected function setUp(): void
{
parent::setUp();

View File

@@ -2,17 +2,19 @@
namespace Tests\Unit\Models;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use App\Models\Blog;
use App\Models\Category;
use App\Models\User;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Tests\TestCase;
class BlogTest extends TestCase
{
private User|Collection $user;
public $category;
protected function setUp(): void
{
parent::setUp();

View File

@@ -2,9 +2,9 @@
namespace Tests\Unit\Models;
use Illuminate\Database\Eloquent\Model;
use App\Models\Blog;
use App\Models\Category;
use Illuminate\Database\Eloquent\Model;
use Tests\TestCase;
class CategoryTest extends TestCase

View File

@@ -2,10 +2,10 @@
namespace Tests\Unit\Models;
use Illuminate\Support\Facades\Date;
use App\Models\Email;
use Carbon\Carbon;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Date;
use Tests\TestCase;
class EmailTest extends TestCase

View File

@@ -9,6 +9,7 @@ use Tests\TestCase;
class LogTest extends TestCase
{
public $user;
protected function setUp(): void
{
parent::setUp();

View File

@@ -2,13 +2,14 @@
namespace Tests\Unit\Models;
use Illuminate\Database\Eloquent\Model;
use App\Models\Plan;
use Illuminate\Database\Eloquent\Model;
use Tests\TestCase;
class PlanTest extends TestCase
{
public $planData;
protected function setUp(): void
{
parent::setUp();

View File

@@ -10,6 +10,7 @@ use Tests\TestCase;
class PremiumEmailTest extends TestCase
{
public $user;
protected function setUp(): void
{
parent::setUp();

View File

@@ -2,17 +2,19 @@
namespace Tests\Unit\Models;
use Illuminate\Database\Eloquent\Model;
use App\Models\Ticket;
use App\Models\TicketResponse;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Tests\TestCase;
class TicketResponseTest extends TestCase
{
public $user;
public $ticket;
protected function setUp(): void
{
parent::setUp();

View File

@@ -2,17 +2,19 @@
namespace Tests\Unit\Models;
use Illuminate\Database\Eloquent\Model;
use App\Models\Ticket;
use App\Models\TicketResponse;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Tests\TestCase;
class TicketTest extends TestCase
{
public $user;
public $ticketData;
protected function setUp(): void
{
parent::setUp();

View File

@@ -9,6 +9,7 @@ use Tests\TestCase;
class UsageLogTest extends TestCase
{
public $user;
protected function setUp(): void
{
parent::setUp();

View File

@@ -2,25 +2,26 @@
namespace Tests\Unit\Models;
use Carbon\Carbon;
use Illuminate\Support\Facades\Hash;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Notifications\Notifiable;
use Laravel\Cashier\Billable;
use Laravel\Sanctum\HasApiTokens;
use Filament\Models\Contracts\FilamentUser;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Laravel\Sanctum\NewAccessToken;
use App\Models\Log;
use App\Models\Ticket;
use App\Models\UsageLog;
use App\Models\User;
use Carbon\Carbon;
use Filament\Models\Contracts\FilamentUser;
use Filament\Panel;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
use Laravel\Cashier\Billable;
use Laravel\Sanctum\HasApiTokens;
use Laravel\Sanctum\NewAccessToken;
use Tests\TestCase;
class UserTest extends TestCase
{
public $user;
protected function setUp(): void
{
parent::setUp();

View File

@@ -2,9 +2,9 @@
namespace Tests\Unit;
use Exception;
use App\Http\Controllers\WebhookController;
use App\NotifyMe;
use Exception;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
@@ -19,6 +19,7 @@ class TestNotifier
class NotifyMeTest extends TestCase
{
public $notifier;
protected function setUp(): void
{
parent::setUp();
@@ -42,7 +43,7 @@ class NotifyMeTest extends TestCase
$result = $this->notifier->sendTelegramNotification('Test message');
$this->assertTrue($result);
Http::assertSent(fn(array $request): bool => $request->url() === 'https://api.telegram.org/bottest_bot_token/sendMessage' &&
Http::assertSent(fn (array $request): bool => $request->url() === 'https://api.telegram.org/bottest_bot_token/sendMessage' &&
$request['chat_id'] === 'test_chat_id' &&
$request['text'] === 'Test message' &&
$request['parse_mode'] === 'HTML');
@@ -133,7 +134,7 @@ class NotifyMeTest extends TestCase
$htmlMessage = '<b>Bold text</b> and <i>italic text</i>';
$this->notifier->sendTelegramNotification($htmlMessage);
Http::assertSent(fn(array $request): bool => $request['parse_mode'] === 'HTML' &&
Http::assertSent(fn (array $request): bool => $request['parse_mode'] === 'HTML' &&
$request['text'] === $htmlMessage);
}