Merge branch 'premiumDash'

# Conflicts:
#	.gitignore
#	zsql/zemailnator.sql
This commit is contained in:
Gitea
2025-11-08 23:54:06 +05:30
110 changed files with 7744 additions and 173 deletions

2
.gitignore vendored
View File

@@ -22,3 +22,5 @@ yarn-error.log
/.vscode /.vscode
/.zed /.zed
/stripe.exe /stripe.exe
/public/tmp/attachments/
/public/tmp/premium/

9
.htaccess Normal file
View File

@@ -0,0 +1,9 @@
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www.zemail.me [NC]
RewriteRule ^(.*)$ https://zemail.me/$1 [L,R=301]
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI} !^public
RewriteRule ^(.*)$ public/$1 [L]
</IfModule>

View File

@@ -4,6 +4,7 @@ namespace App\Filament\Pages;
use App\Models\Setting; use App\Models\Setting;
use App\Models\ZEmail; use App\Models\ZEmail;
use Artisan;
use Ddeboer\Imap\Server; use Ddeboer\Imap\Server;
use Filament\Actions\Action; use Filament\Actions\Action;
use Filament\Forms\Components\Checkbox; use Filament\Forms\Components\Checkbox;
@@ -45,6 +46,8 @@ class Settings extends Page implements HasForms
$this->applyDefaults($imapSettings, [ $this->applyDefaults($imapSettings, [
'protocol' => 'imap', 'protocol' => 'imap',
'default_account' => 'default', 'default_account' => 'default',
'premium_protocol' => 'imap',
'premium_default_account' => 'default',
]); ]);
$this->applyDefaults($configurationSettings, [ $this->applyDefaults($configurationSettings, [
@@ -56,6 +59,10 @@ class Settings extends Page implements HasForms
$transformMap = [ $transformMap = [
'domains' => 'domain', 'domains' => 'domain',
'gmailUsernames' => 'gmailUsername', 'gmailUsernames' => 'gmailUsername',
'premium_domains' => 'premium_domain',
'premium_gmailUsernames' => 'premium_gmailUsername',
'outlookUsernames' => 'outlookUsername',
'premium_outlookUsernames' => 'premium_outlookUsername',
'forbidden_ids' => 'forbidden_id', 'forbidden_ids' => 'forbidden_id',
'blocked_domains' => 'blocked_domain', 'blocked_domains' => 'blocked_domain',
]; ];
@@ -145,7 +152,7 @@ class Settings extends Page implements HasForms
]), ]),
Section::make('Imap') Section::make('Public Mailbox Imap')
->description('Enter your imap server') ->description('Enter your imap server')
->collapsed() ->collapsed()
->schema([ ->schema([
@@ -164,6 +171,25 @@ class Settings extends Page implements HasForms
Checkbox::make('imap_settings.cc_check')->label('Check CC Field')->default(false)->helperText('If enabled, we will check the CC field as well while fetching mails.'), Checkbox::make('imap_settings.cc_check')->label('Check CC Field')->default(false)->helperText('If enabled, we will check the CC field as well while fetching mails.'),
]), ]),
Section::make('Premium Mailbox Imap')
->description('Enter your imap server')
->collapsed()
->schema([
TextInput::make('imap_settings.premium_host')->label('Hostname')->required(),
TextInput::make('imap_settings.premium_port')->label('Port')->required(),
Select::make('imap_settings.premium_encryption')->options([
'none' => 'None',
'ssl' => 'SSL',
'tls' => 'TLS'
]),
Checkbox::make('imap_settings.premium_validate_cert')->label('Validate Encryption Certificate')->default(false),
TextInput::make('imap_settings.premium_username')->label('Username')->required(),
TextInput::make('imap_settings.premium_password')->label('Password')->required(),
TextInput::make('imap_settings.premium_default_account')->label('Default Account')->placeholder('default'),
TextInput::make('imap_settings.premium_protocol')->label('Protocol')->placeholder('imap'),
Checkbox::make('imap_settings.premium_cc_check')->label('Check CC Field')->default(false)->helperText('If enabled, we will check the CC field as well while fetching mails.'),
]),
Section::make('Configuration') Section::make('Configuration')
->description('Enter your configuration') ->description('Enter your configuration')
->collapsed() ->collapsed()
@@ -230,6 +256,42 @@ class Settings extends Page implements HasForms
]), ]),
]), ]),
Section::make('Premium Domains & Gmail Usernames')
->collapsed()
->columns(2)
->schema([
Repeater::make('configuration_settings.premium_domains')
->statePath('configuration_settings.premium_domains')
->columnSpan(1)
->schema([
TextInput::make('premium_domain')->label('Premium Domain')->required()->maxLength(30),
]),
Repeater::make('configuration_settings.premium_gmailUsernames')
->statePath('configuration_settings.premium_gmailUsernames')
->columnSpan(1)
->schema([
TextInput::make('premium_gmailUsername')->label('Premium Gmail Username')->required()->maxLength(30),
]),
]),
Section::make('Public & Premium Outlook.com Usernames')
->collapsed()
->columns(2)
->schema([
Repeater::make('configuration_settings.outlookUsernames')
->statePath('configuration_settings.outlookUsernames')
->columnSpan(1)
->schema([
TextInput::make('outlookUsername')->label('Public Outlook Username')->required()->maxLength(30),
]),
Repeater::make('configuration_settings.premium_outlookUsernames')
->statePath('configuration_settings.premium_outlookUsernames')
->columnSpan(1)
->schema([
TextInput::make('premium_outlookUsername')->label('Premium Outlook Username')->required()->maxLength(30),
]),
]),
Section::make('Mailbox Configuration') Section::make('Mailbox Configuration')
->collapsed() ->collapsed()
->schema([ ->schema([
@@ -300,9 +362,31 @@ class Settings extends Page implements HasForms
Action::make('save') Action::make('save')
->label(__('filament-panels::resources/pages/edit-record.form.actions.save.label')) ->label(__('filament-panels::resources/pages/edit-record.form.actions.save.label'))
->submit('save'), ->submit('save'),
Action::make('flushCache')
->label(__('Flush Cache'))
->color('danger')
->icon('heroicon-m-no-symbol')
->action('flushCache')
->requiresConfirmation(),
]; ];
} }
public function flushCache(): void
{
try {
Artisan::call('cache:clear');
Notification::make()
->title('Cache flushed successfully')
->success()
->send();
} catch (\Exception $e) {
Notification::make()
->title('Error : '.$e->getMessage())
->danger()
->send();
}
}
public function save(): void public function save(): void
{ {
try { try {
@@ -318,14 +402,20 @@ class Settings extends Page implements HasForms
} }
foreach ([ foreach ([
'protocol' => 'imap', 'protocol' => 'imap',
'default_account' => 'default' 'default_account' => 'default',
'premium_protocol' => 'imap',
'premium_default_account' => 'default',
] as $key => $default) { ] as $key => $default) {
$data['imap_settings'][$key] ??= $default; $data['imap_settings'][$key] ??= $default;
} }
$pluckMap = [ $pluckMap = [
'domains' => 'domain', 'domains' => 'domain',
'premium_domains' => 'premium_domain',
'premium_gmailUsernames' => 'premium_gmailUsername',
'gmailUsernames' => 'gmailUsername', 'gmailUsernames' => 'gmailUsername',
'outlookUsernames' => 'outlookUsername',
'premium_outlookUsernames' => 'premium_outlookUsername',
'forbidden_ids' => 'forbidden_id', 'forbidden_ids' => 'forbidden_id',
'blocked_domains' => 'blocked_domain' 'blocked_domains' => 'blocked_domain'
]; ];

View File

@@ -79,6 +79,7 @@ class BlogResource extends Resource
FileUpload::make('post_image') FileUpload::make('post_image')
->label('Custom Image (Optional)') ->label('Custom Image (Optional)')
->disk('public')
->directory('media/posts') ->directory('media/posts')
->columnSpan(4) ->columnSpan(4)
->preserveFilenames() ->preserveFilenames()

View File

@@ -0,0 +1,101 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\PlanResource\Pages;
use App\Filament\Resources\PlanResource\RelationManagers;
use App\Models\Plan;
use Filament\Forms;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\KeyValue;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
use Filament\Forms\Set;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Columns\BooleanColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Illuminate\Support\Str;
use phpDocumentor\Reflection\Types\Boolean;
class PlanResource extends Resource
{
protected static ?string $model = Plan::class;
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
protected static ?string $navigationGroup = 'Web Master';
public static function form(Form $form): Form
{
return $form
->schema([
Section::make('Plan Information')
->description('Add a new plan')
->schema([
TextInput::make('name')->label('Page Name')
->required(),
TextInput::make('description'),
TextInput::make('product_id')->required(),
TextInput::make('pricing_id')->required(),
TextInput::make('price')->numeric()->required(),
TextInput::make('mailbox_limit')->numeric()->required(),
Select::make('monthly_billing')->options([
1 => 'Monthly',
0 => 'Yearly',
])->default(1)->required(),
KeyValue::make('details')
->label('Plan Details (Optional)')
->keyPlaceholder('Name')
->valuePlaceholder('Content')
->reorderable(),
]),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('name')->label('Name'),
TextColumn::make('product_id')->label('Product'),
TextColumn::make('pricing_id')->label('Pricing'),
TextColumn::make('price')->label('Price'),
BooleanColumn::make('monthly_billing')->label('Monthly Billing'),
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListPlans::route('/'),
'create' => Pages\CreatePlan::route('/create'),
'edit' => Pages\EditPlan::route('/{record}/edit'),
];
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Filament\Resources\PlanResource\Pages;
use App\Filament\Resources\PlanResource;
use Filament\Actions;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\CreateRecord;
class CreatePlan extends CreateRecord
{
protected static string $resource = PlanResource::class;
protected function getCreatedNotification(): ?Notification
{
return Notification::make()
->success()
->title('Plan created')
->body('Plan created successfully');
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Filament\Resources\PlanResource\Pages;
use App\Filament\Resources\PlanResource;
use Filament\Actions;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\EditRecord;
class EditPlan extends EditRecord
{
protected static string $resource = PlanResource::class;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
protected function getRedirectUrl(): ?string
{
return $this->getResource()::getUrl('index');
}
protected function getSavedNotification(): ?Notification
{
return Notification::make()
->success()
->title('Plan updated')
->body('Plan updated successfully');
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\PlanResource\Pages;
use App\Filament\Resources\PlanResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListPlans extends ListRecords
{
protected static string $resource = PlanResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace App\Filament\Widgets;
use App\Models\Log;
use App\Models\Meta;
use App\Models\User;
use DB;
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
use Filament\Widgets\StatsOverviewWidget\Stat;
class StatsOverview extends BaseWidget
{
protected function getStats(): array
{
return [
Stat::make('Total Users', $this->getUser()),
Stat::make('Paid Users', $this->getUserPaid()),
Stat::make('Logs Count', $this->getLogsCount()),
Stat::make('Total Mailbox', $this->getTotalMailbox()),
Stat::make('Emails Received', $this->getTotalEmailsReceived()),
];
}
private function getUser(): int
{
return User::all()->count();
}
private function getUserPaid(): int
{
return DB::table('subscriptions')
->where(['stripe_status' => 'active'])
->distinct('user_id')
->count('user_id');
}
private function getLogsCount(): int
{
return Log::all()->count();
}
private function getTotalMailbox(): int
{
return Meta::select('value')->where(['key' => 'email_ids_created'])->first()->value;
}
private function getTotalEmailsReceived(): int
{
return Meta::select('value')->where(['key' => 'messages_received'])->first()->value;
}
}

View File

@@ -2,8 +2,10 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\Premium;
use App\Models\ZEmail; use App\Models\ZEmail;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Session;
class AppController extends Controller class AppController extends Controller
{ {
@@ -48,6 +50,33 @@ class AppController extends Controller
} }
} }
public function switchP($email) {
if (Session::get('isInboxTypePremium')) {
Premium::setEmailP($email);
} else {
ZEmail::setEmail($email);
}
if (json_decode(config('app.settings.configuration_settings'))->disable_mailbox_slug) {
return redirect()->route('dashboard');
}
return redirect()->route('dashboard.premium');
}
public function deleteP($email = null) {
if ($email) {
if (Session::get('isInboxTypePremium')) {
$emails = Premium::getEmails();
Premium::removeEmail($email);
} else {
$emails = ZEmail::getEmails();
ZEmail::removeEmail($email);
}
return redirect()->route('dashboard.premium');
} else {
return redirect()->route('dashboard');
}
}
public function locale($locale) { public function locale($locale) {
if (in_array($locale, config('app.locales'))) { if (in_array($locale, config('app.locales'))) {
session(['locale' => $locale]); session(['locale' => $locale]);

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
use Illuminate\Http\RedirectResponse;
class VerifyEmailController extends Controller
{
/**
* Mark the authenticated user's email address as verified.
*/
public function __invoke(EmailVerificationRequest $request): RedirectResponse
{
if ($request->user()->hasVerifiedEmail()) {
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
}
if ($request->user()->markEmailAsVerified()) {
/** @var \Illuminate\Contracts\Auth\MustVerifyEmail $user */
$user = $request->user();
event(new Verified($user));
}
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Listeners;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Log;
use Laravel\Cashier\Events\WebhookReceived;
use Livewire\Livewire;
class StripeEventListener
{
/**
* Create the event listener.
*/
public function __construct()
{
//
}
/**
* Handle the event.
*/
public function handle(WebhookReceived $event): void
{
if ($event->payload['type'] === 'invoice.payment_succeeded') {
session()->flash('alert', ['type' => 'success', 'message' => 'Payment completed successfully.']);
Log::info('Payment succeeded');
}
if ($event->payload['type'] === 'customer.subscription.deleted') {
session()->flash('alert', ['type' => 'error', 'message' => 'Subscription canceled.']);
Log::info('Subscription canceled');
}
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Livewire\Actions;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
class Logout
{
/**
* Log the current user out of the application.
*/
public function __invoke()
{
Auth::guard('web')->logout();
Session::invalidate();
Session::regenerateToken();
return redirect('/');
}
}

183
app/Livewire/AddOn.php Normal file
View File

@@ -0,0 +1,183 @@
<?php
namespace App\Livewire;
use App\Models\ZEmail;
use Livewire\Component;
use Request;
use Session;
class AddOn extends Component
{
public $title = "";
public $description = "";
public $keywords = "";
public $route = "";
public $faqs = [];
public $faqSchema;
protected $listeners = ['fetchMessages' => 'checkIfAnyMessage'];
public function checkIfAnyMessage()
{
$email = ZEmail::getEmail();
$messages = ZEmail::getMessages($email);
if (count($messages['data']) > 0) {
return redirect()->route('mailbox');
}
}
public function mount()
{
$this->faqs = json_decode(file_get_contents(public_path('addOnFAQs.json')), true) ?? [];
$route = request()->route()->getName();
Session::put('addOn-route', $this->route);
if ($route == 'disposable-email') {
$this->title = 'Free Disposable Email | Temporary & Anonymous Email Service';
$this->description = 'Use our free disposable email service to stay private and spam-free. Instantly create a secure, anonymous, and temporary email address—no registration needed.';
$this->keywords = 'disposable email, free disposable email, temporary email, anonymous disposable email, temporary disposable email, secure disposable email, fake email, temp inbox, throwaway email';
$this->route = $route;
$this->faqs = $this->faqs[$route];
$this->faqSchema = [
"@context" => "https://schema.org",
"@type" => "FAQPage",
"mainEntity" => collect($this->faqs)->map(function ($faq) {
return [
"@type" => "Question",
"name" => $faq['title'],
"acceptedAnswer" => [
"@type" => "Answer",
"text" => $faq['content']
]
];
})->toArray()
];
} elseif ($route == 'disposable-gmail') {
$this->title = 'Disposable Gmail Email | Free Temporary Gmail Inbox Online';
$this->description = 'Create a free disposable Gmail email address. Use a temporary Gmail inbox for sign-ups, testing, or staying spam-free. No login or registration required.';
$this->keywords = 'disposable Gmail, temporary Gmail inbox, Gmail disposable email, free disposable Gmail email address, Gmail temp email, anonymous Gmail email';
$this->route = $route;
$this->faqs = $this->faqs[$route];
$this->faqSchema = [
"@context" => "https://schema.org",
"@type" => "FAQPage",
"mainEntity" => collect($this->faqs)->map(function ($faq) {
return [
"@type" => "Question",
"name" => $faq['title'],
"acceptedAnswer" => [
"@type" => "Answer",
"text" => $faq['content']
]
];
})->toArray()
];
} elseif ($route == 'disposable-outlook') {
$this->title = 'Disposable Outlook Email | Free Temporary Outlook Inbox';
$this->description = 'Get a free disposable Outlook email address instantly. Use a temporary Outlook inbox to avoid spam and protect your main account—no registration required.';
$this->keywords = 'disposable Outlook, Outlook disposable email, temporary Outlook inbox, free Outlook temp email, anonymous Outlook address, Outlook email for sign-ups';
$this->route = $route;
$this->faqs = $this->faqs[$route];
$this->faqSchema = [
"@context" => "https://schema.org",
"@type" => "FAQPage",
"mainEntity" => collect($this->faqs)->map(function ($faq) {
return [
"@type" => "Question",
"name" => $faq['title'],
"acceptedAnswer" => [
"@type" => "Answer",
"text" => $faq['content']
]
];
})->toArray()
];
} elseif ($route == 'disposable-yahoo') {
$this->title = 'Disposable Yahoo Mail | Free Temporary Yahoo Inbox';
$this->description = 'Create a disposable Yahoo Mail address instantly. Use a free, temporary Yahoo inbox to stay private, filter spam, and protect your real Yahoo account.';
$this->keywords = 'disposable Yahoo mail, Yahoo mail disposable email, temporary Yahoo inbox, free Yahoo temp email, Yahoo throwaway email';
$this->route = $route;
$this->faqs = $this->faqs[$route];
$this->faqSchema = [
"@context" => "https://schema.org",
"@type" => "FAQPage",
"mainEntity" => collect($this->faqs)->map(function ($faq) {
return [
"@type" => "Question",
"name" => $faq['title'],
"acceptedAnswer" => [
"@type" => "Answer",
"text" => $faq['content']
]
];
})->toArray()
];
} elseif ($route == 'gmailnator') {
$this->title = 'Gmailnator - Free Temporary Gmail Email Address Generator';
$this->description = 'Use Gmailnator to get a disposable Gmail address instantly. Perfect for one-time use, signups, or testing. No account needed. Try Gmailnator now.';
$this->keywords = 'gmailnator, gmailnator temp mail, gmailnator gmail, free gmailnator, gmailnator email address, gmailnator online, gmail temp mail, gmail fake email';
$this->route = $route;
$this->faqs = $this->faqs[$route];
$this->faqSchema = [
"@context" => "https://schema.org",
"@type" => "FAQPage",
"mainEntity" => collect($this->faqs)->map(function ($faq) {
return [
"@type" => "Question",
"name" => $faq['title'],
"acceptedAnswer" => [
"@type" => "Answer",
"text" => $faq['content']
]
];
})->toArray()
];
} elseif ($route == 'emailnator') {
$this->title = 'Emailnator - Free Disposable Temporary Email Service';
$this->description = 'Use Emailnator to create a free temporary email address instantly. No sign-up needed. Secure, fast, and spam-free inboxes for signups or testing.';
$this->keywords = 'emailnator, emailnator temp mail, temporary email, disposable email, free temp inbox, temp mail generator, receive email online';
$this->route = $route;
$this->faqs = $this->faqs[$route];
$this->faqSchema = [
"@context" => "https://schema.org",
"@type" => "FAQPage",
"mainEntity" => collect($this->faqs)->map(function ($faq) {
return [
"@type" => "Question",
"name" => $faq['title'],
"acceptedAnswer" => [
"@type" => "Answer",
"text" => $faq['content']
]
];
})->toArray()
];
} elseif ($route == 'temp-gmail') {
$this->title = 'Temp Gmail | Free Temporary Gmail Inbox for Instant Use';
$this->description = 'Use Temp Gmail to get a temporary Gmail address fast. No registration. Secure, spam-free inboxes for signups, testing, or quick email verification.';
$this->keywords = 'temp gmail, temporary gmail, disposable gmail, gmail temp email, gmail fake email, temporary gmail address, gmail for verification';
$this->route = $route;
$this->faqs = $this->faqs[$route];
$this->faqSchema = [
"@context" => "https://schema.org",
"@type" => "FAQPage",
"mainEntity" => collect($this->faqs)->map(function ($faq) {
return [
"@type" => "Question",
"name" => $faq['title'],
"acceptedAnswer" => [
"@type" => "Answer",
"text" => $faq['content']
]
];
})->toArray()
];
}
}
public function render()
{
return view('livewire.add-on');
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Livewire\Auth;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;
use Livewire\Attributes\Layout;
use Livewire\Component;
#[Layout('components.layouts.auth')]
class ConfirmPassword extends Component
{
public string $password = '';
/**
* Confirm the current user's password.
*/
public function confirmPassword(): void
{
$this->validate([
'password' => ['required', 'string'],
]);
if (! Auth::guard('web')->validate([
'email' => Auth::user()->email,
'password' => $this->password,
])) {
throw ValidationException::withMessages([
'password' => __('auth.password'),
]);
}
session(['auth.password_confirmed_at' => time()]);
$this->redirectIntended(default: route('dashboard', absolute: false), navigate: true);
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Livewire\Auth;
use Illuminate\Support\Facades\Password;
use Livewire\Attributes\Layout;
use Livewire\Component;
#[Layout('components.layouts.auth')]
class ForgotPassword extends Component
{
public string $email = '';
/**
* Send a password reset link to the provided email address.
*/
public function sendPasswordResetLink(): void
{
$this->validate([
'email' => ['required', 'string', 'email'],
]);
Password::sendResetLink($this->only('email'));
session()->flash('status', __('A reset link will be sent if the account exists.'));
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace App\Livewire\Auth;
use Illuminate\Auth\Events\Lockout;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Validate;
use Livewire\Component;
#[Layout('components.layouts.auth')]
class Login extends Component
{
#[Validate('required|string|email')]
public string $email = '';
#[Validate('required|string')]
public string $password = '';
public bool $remember = false;
/**
* Handle an incoming authentication request.
*/
public function login(): void
{
$this->validate();
$this->ensureIsNotRateLimited();
if (! Auth::attempt(['email' => $this->email, 'password' => $this->password], $this->remember)) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'email' => __('auth.failed'),
]);
}
RateLimiter::clear($this->throttleKey());
Session::regenerate();
$this->redirectIntended(default: route('dashboard', absolute: false), navigate: true);
}
/**
* Ensure the authentication request is not rate limited.
*/
protected function ensureIsNotRateLimited(): void
{
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
return;
}
event(new Lockout(request()));
$seconds = RateLimiter::availableIn($this->throttleKey());
throw ValidationException::withMessages([
'email' => __('auth.throttle', [
'seconds' => $seconds,
'minutes' => ceil($seconds / 60),
]),
]);
}
/**
* Get the authentication rate limiting throttle key.
*/
protected function throttleKey(): string
{
return Str::transliterate(Str::lower($this->email).'|'.request()->ip());
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Livewire\Auth;
use App\Models\User;
use Illuminate\Auth\Events\Registered;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules;
use Livewire\Attributes\Layout;
use Livewire\Component;
#[Layout('components.layouts.auth')]
class Register extends Component
{
public string $name = '';
public string $email = '';
public string $password = '';
public string $password_confirmation = '';
/**
* Handle an incoming registration request.
*/
public function register(): void
{
$validated = $this->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'lowercase', 'email:rfc,dns', 'max:255', 'indisposable', 'unique:'.User::class],
'password' => ['required', 'string', 'confirmed', Rules\Password::defaults()],
]);
$validated['password'] = Hash::make($validated['password']);
event(new Registered(($user = User::create($validated))));
Auth::login($user);
$this->redirect(route('dashboard', absolute: false), navigate: true);
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace App\Livewire\Auth;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Locked;
use Livewire\Component;
#[Layout('components.layouts.auth')]
class ResetPassword extends Component
{
#[Locked]
public string $token = '';
public string $email = '';
public string $password = '';
public string $password_confirmation = '';
/**
* Mount the component.
*/
public function mount(string $token): void
{
$this->token = $token;
$this->email = request()->string('email');
}
/**
* Reset the password for the given user.
*/
public function resetPassword(): void
{
$this->validate([
'token' => ['required'],
'email' => ['required', 'string', 'email'],
'password' => ['required', 'string', 'confirmed', Rules\Password::defaults()],
]);
// Here we will attempt to reset the user's password. If it is successful we
// will update the password on an actual user model and persist it to the
// database. Otherwise we will parse the error and return the response.
$status = Password::reset(
$this->only('email', 'password', 'password_confirmation', 'token'),
function ($user) {
$user->forceFill([
'password' => Hash::make($this->password),
'remember_token' => Str::random(60),
])->save();
event(new PasswordReset($user));
}
);
// If the password was successfully reset, we will redirect the user back to
// the application's home authenticated view. If there is an error we can
// redirect them back to where they came from with their error message.
if ($status != Password::PasswordReset) {
$this->addError('email', __($status));
return;
}
Session::flash('status', __($status));
$this->redirectRoute('login', navigate: true);
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Livewire\Auth;
use App\Livewire\Actions\Logout;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
use Livewire\Attributes\Layout;
use Livewire\Component;
#[Layout('components.layouts.auth')]
class VerifyEmail extends Component
{
/**
* Send an email verification notification to the user.
*/
public function sendVerification(): void
{
if (Auth::user()->hasVerifiedEmail()) {
$this->redirectIntended(default: route('dashboard', absolute: false), navigate: true);
return;
}
Auth::user()->sendEmailVerificationNotification();
Session::flash('status', 'verification-link-sent');
}
/**
* Log the current user out of the application.
*/
public function logout(Logout $logout): void
{
$logout();
$this->redirect('/', navigate: true);
}
}

View File

@@ -0,0 +1,142 @@
<?php
namespace App\Livewire\Dashboard;
use Livewire\Component;
use Session;
class Bulk extends Component
{
public $bulkEmails = [];
public $bulkCount = 1;
private $isSubscribed = false;
public function mount() {
$subscriptionCheck = auth()->user()->subscribedToProduct(config('app.plans')[0]['product_id']);
Session::put('isSubscribed', $subscriptionCheck);
}
public function generateBulk()
{
$this->validate([
'bulkCount' => 'required|integer|min:1|max:500',
]);
if (count($this->bulkEmails) > 0) {
$this->bulkEmails = [];
}
for ($i = 0; $i < $this->bulkCount; $i++) {
$this->bulkEmails[] = $this->randomEmail();
}
}
public function downloadBulk()
{
// Ensure there's something to download
if (empty($this->bulkEmails) || !is_array($this->bulkEmails)) {
return;
}
$filename = 'bulk_emails_' . now()->format('Ymd_His') . '.txt';
$content = implode(PHP_EOL, $this->bulkEmails);
return response()->streamDownload(function () use ($content) {
echo $content;
}, $filename);
}
private function randomEmail(): string
{
$domain = $this->getRandomDomain();
if ($domain == "gmail.com" || $domain == "googlemail.com") {
$uname = $this->getRandomGmailUser();
$uname_len = strlen($uname);
$len_power = $uname_len - 1;
$combination = pow(2,$len_power);
$rand_comb = mt_rand(1,$combination);
$formatted = implode(' ',str_split($uname));
$uname_exp = explode(' ', $formatted);
$bin = intval("");
for($i=0; $i<$len_power; $i++) {
$bin .= mt_rand(0,1);
}
$bin = explode(' ', implode(' ',str_split(strval($bin))));
$email = "";
for($i=0; $i<$len_power; $i++) {
$email .= $uname_exp[$i];
if($bin[$i]) {
$email .= ".";
}
}
$email .= $uname_exp[$i];
$gmail_rand = mt_rand(1,10);
if($gmail_rand > 5) {
$email .= "@gmail.com";
} else {
$email .= "@googlemail.com";
}
return $email;
} else {
return $this->generateRandomUsername().'@'.$domain;
}
}
private function generateRandomUsername(): string
{
$start = json_decode(config('app.settings.configuration_settings'))->random_username_length_min ?? 0;
$end = json_decode(config('app.settings.configuration_settings'))->random_username_length_max ?? 0;
if ($start == 0 && $end == 0) {
return $this->generatePronounceableWord();
}
return $this->generatedRandomBetweenLength($start, $end);
}
private function generatedRandomBetweenLength($start, $end): string
{
$length = rand($start, $end);
return $this->generateRandomString($length);
}
private function getRandomDomain() {
$domains = json_decode(config('app.settings.configuration_settings'))->premium_domains ?? [];
$count = count($domains);
return $count > 0 ? $domains[rand(1, $count) - 1] : '';
}
private function getRandomGmailUser() {
$gmailusername = json_decode(config('app.settings.configuration_settings'))->premium_gmailUsernames ?? [];
$count = count($gmailusername);
return $count > 0 ? $gmailusername[rand(1, $count) - 1] : '';
}
private function generatePronounceableWord(): string
{
$c = 'bcdfghjklmnprstvwz'; //consonants except hard to speak ones
$v = 'aeiou'; //vowels
$a = $c . $v; //both
$random = '';
for ($j = 0; $j < 2; $j++) {
$random .= $c[rand(0, strlen($c) - 1)];
$random .= $v[rand(0, strlen($v) - 1)];
$random .= $a[rand(0, strlen($a) - 1)];
}
return $random;
}
private function generateRandomString($length = 10): string
{
$characters = '0123456789abcdefghijklmnopqrstuvwxyz';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
public function render()
{
if (Session::get('isSubscribed')) {
return view('livewire.dashboard.bulk')->layout('components.layouts.dashboard');
} else {
return view('livewire.dashboard.not-subscribed')->layout('components.layouts.dashboard');
}
}
}

View File

@@ -0,0 +1,118 @@
<?php
namespace App\Livewire\Dashboard;
use Livewire\Component;
use Session;
use function Pest\Laravel\withoutMockingConsoleOutput;
class BulkGmail extends Component
{
public $email;
public int $quantity = 10;
public $bulkEmails = [];
public function mount() {
$subscriptionCheck = auth()->user()->subscribedToProduct(config('app.plans')[0]['product_id']);
Session::put('isSubscribed', $subscriptionCheck);
}
public function generateBulk(): void
{
$this->validate([
'email' => [
'required',
'email',
'regex:/^[^@\s]+@gmail\.com$/i',
],
'quantity' => 'required|integer|min:10|max:500',
]);
if (count($this->bulkEmails) > 0) {
$this->bulkEmails = [];
}
$this->bulkEmails = $this->generateDotVariants(explode("@", $this->email)[0], $this->quantity);
}
public function downloadBulk()
{
// Ensure there's something to download
if (empty($this->bulkEmails) || !is_array($this->bulkEmails)) {
return;
}
$filename = 'bulk_gmails_' . now()->format('Ymd_His') . '.txt';
$content = implode(PHP_EOL, $this->bulkEmails);
return response()->streamDownload(function () use ($content) {
echo $content;
}, $filename);
}
private function generateDotVariants($uname, $quantity)
{
$length = strlen($uname);
$positions = range(1, $length - 1);
$results = [];
// From 1 dot up to max possible
for ($dotCount = 1; $dotCount < $length; $dotCount++) {
$combinations = $this->combinations($positions, $dotCount);
// Reverse each combination for right-to-left priority
foreach (array_reverse($combinations) as $combo) {
$dotted = '';
$lastPos = 0;
foreach ($combo as $pos) {
$dotted .= substr($uname, $lastPos, $pos - $lastPos) . '.';
$lastPos = $pos;
}
$dotted .= substr($uname, $lastPos);
$results[] = $dotted . '@gmail.com';
if (count($results) >= $quantity) {
return $results;
}
}
}
return $results;
}
private function combinations(array $items, int $size): array
{
if ($size === 0) {
return [[]];
}
if (empty($items)) {
return [];
}
$combinations = [];
$head = $items[0];
$tail = array_slice($items, 1);
foreach ($this->combinations($tail, $size - 1) as $c) {
array_unshift($c, $head);
$combinations[] = $c;
}
foreach ($this->combinations($tail, $size) as $c) {
$combinations[] = $c;
}
return $combinations;
}
public function render()
{
if (Session::get('isSubscribed')) {
return view('livewire.dashboard.bulk-gmail')->layout('components.layouts.dashboard');
} else {
return view('livewire.dashboard.not-subscribed')->layout('components.layouts.dashboard');
}
}
}

View File

@@ -0,0 +1,207 @@
<?php
namespace App\Livewire\Dashboard;
use App\Models\UsageLog;
use Cache;
use Carbon\Carbon;
use DB;
use Exception;
use Illuminate\Http\Request;
use Livewire\Component;
class Dashboard extends Component
{
public $message;
public $usageLog;
public $subscription;
public $plans;
public function paymentStatus(Request $request)
{
$status = $request->route('status');
$currentUrl = $request->fullUrl();
if ($status == 'success') {
$this->syncSubscription();
return redirect()->route('dashboard')->with('status', 'success');
} elseif ($status == 'cancel') {
return redirect()->route('dashboard')->with('status', 'cancel');
}
}
private function checkForSubscriptionStatus(): bool
{
$redirect = false;
$user = auth()->user();
$userId = $user->id;
if ($user->subscribed()) {
$subscription = $user->subscriptions()->where(['stripe_status' => 'active'])->orderByDesc('updated_at')->first();
if ($subscription !== null) {
$subscriptionId = $subscription->stripe_id;
$cacheKey = "stripe_check_executed_user_{$userId}_{$subscriptionId}";
if (!Cache::has($cacheKey)) {
try {
$stripe = new \Stripe\StripeClient(config('cashier.secret'));
$subscriptionData = $stripe->subscriptions->retrieve($subscriptionId, []);
if ($subscriptionData !== null) {
$items = $subscriptionData->items->data[0];
if ($items !== null) {
$cancel_at_period_end = $subscriptionData->cancel_at_period_end;
$ends_at = $items->current_period_end;
$cancel_at = $subscriptionData->cancel_at;
$canceled_at = $subscriptionData->canceled_at;
$status = $subscriptionData->status;
if ($cancel_at_period_end) {
$final_ends_at = Carbon::createFromTimestamp($cancel_at)->toDateTimeString();
} else {
if ($cancel_at === null && $canceled_at !== null && $status === "canceled" && $cancel_at_period_end === false) {
//$final_ends_at = Carbon::createFromTimestamp($canceled_at)->toDateTimeString();
$final_ends_at = Carbon::now()->subDays(2)->toDateTimeString();
$redirect = true;
} elseif($status === "active" && $cancel_at !== null) {
$final_ends_at = Carbon::createFromTimestamp($cancel_at)->toDateTimeString();
} else {
$final_ends_at = null;
}
}
DB::table('subscriptions')
->where('stripe_id', $subscriptionId)
->update([
'stripe_status' => $status,
'ends_at' => $final_ends_at,
'updated_at' => Carbon::now()->toDateTimeString(),
]);
}
}
Cache::put($cacheKey, true, now()->addHour());
} catch (Exception $exception) {
\Log::error($exception->getMessage());
}
}
}
}
return $redirect;
}
private function syncSubscription(): void
{
$user = auth()->user();
$userId = $user->id;
if ($user->hasStripeId()) {
$stripe = new \Stripe\StripeClient(config('cashier.secret'));
$subscriptions = $stripe->subscriptions->all(['limit' => 1]);
if (!$subscriptions->isEmpty()) {
$data = $subscriptions->data[0];
$items = $subscriptions->data[0]->items->data[0];
$type = 'default';
$subscriptionId = $items->subscription;
$status = $data->status;
$cancel_at_period_end = $data->cancel_at_period_end;
$quantity = $items->quantity;
$stripe_price = $items->price->id;
$stripe_product = $items->price->product;
$ends_at = $items->current_period_end;
$subscriptionItemId = $items->id;
if ($cancel_at_period_end) {
$final_ends_at = Carbon::createFromTimestamp($ends_at)->toDateTimeString();
} else {
$final_ends_at = null;
}
try {
if ($status === "active") {
$subscriptionsTable = DB::table('subscriptions')->where(['stripe_id' => $subscriptionId])->first();
if ($subscriptionsTable == null) {
$subscriptionsTable = DB::table('subscriptions')->insert([
'user_id' => $userId,
'type' => $type,
'stripe_id' => $subscriptionId,
'stripe_status' => $status,
'stripe_price' => $stripe_price,
'quantity' => $quantity,
'ends_at' => $final_ends_at,
'created_at' => Carbon::now()->toDateTimeString(),
'updated_at' => Carbon::now()->toDateTimeString(),
]);
$subscriptionsTable = DB::table('subscriptions')->where(['stripe_id' => $subscriptionId])->first();
$subID = $subscriptionsTable->id;
$subscriptionItemsTable = DB::table('subscription_items')->where(['stripe_id' => $subscriptionItemId])->first();
if ($subscriptionItemsTable == null) {
$subscriptionItemsTable = DB::table('subscription_items')->insert([
'subscription_id' => $subID,
'stripe_id' => $subscriptionItemId,
'stripe_product' => $stripe_product,
'stripe_price' => $stripe_price,
'quantity' => $quantity,
'created_at' => Carbon::now()->toDateTimeString(),
'updated_at' => Carbon::now()->toDateTimeString(),
]);
}
}
}
} catch (Exception $exception) {
\Log::error($exception->getMessage());
}
}
}
}
public function mount(Request $request)
{
if($this->checkForSubscriptionStatus()) {
return redirect()->route('dashboard');
};
try {
$status = $request->session()->get('status');
if (isset($status)) {
if ($status == 'success') {
$this->message = ['type' => 'success', 'message' => 'Order completed successfully.'];
} else {
$this->message = ['type' => 'error', 'message' => 'Order cancelled.'];
}
$request->session()->forget('status');
}
} catch (\Exception $exception) {
}
if (auth()->user()->subscribedToProduct(config('app.plans')[0]['product_id'])) {
try {
$result = auth()->user()->subscriptions()->where(['stripe_status' => 'active'])->orderByDesc('updated_at')->first();
if ($result != null) {
$userPriceID = $result['items'][0]['stripe_price'];
$subscriptionEnd = $result['ends_at'];
$planName = null; // Default value if not found
foreach (config('app.plans') as $plan) {
if ($plan['pricing_id'] === $userPriceID) {
$planName = $plan['name'];
break;
}
}
$this->subscription['name'] = $planName;
$this->subscription['ends_at'] = $subscriptionEnd;
}
} catch (\Exception $e) {
\Log::error($e->getMessage());
}
}
$usageLog = UsageLog::where('user_id', auth()->user()->id)->first();
$this->usageLog = [
'emails_created_count' => $usageLog->emails_created_count ?? 0,
'emails_received_count' => $usageLog->emails_received_count ?? 0,
];
}
public function render()
{
return view('livewire.dashboard.dashboard')->layout('components.layouts.dashboard')->with('message', $this->message);
}
}

View File

@@ -0,0 +1,444 @@
<?php
namespace App\Livewire\Dashboard\Mailbox;
use App\ColorPicker;
use App\Models\Log;
use App\Models\Premium;
use App\Models\PremiumEmail;
use App\Models\UsageLog;
use App\Models\ZEmail;
use Artisan;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cookie;
use Livewire\Component;
use Session;
class Inbox extends Component
{
use ColorPicker;
public $messages = [];
public $deleted = [];
public $error = '';
public $email;
public $initial = false;
public $type;
public $overflow = false;
public $messageId;
public $list = false;
public $emails;
public $mailboxHistory;
public $emailsHistory;
public $username, $domain, $domains, $action;
public $email_limit = 20;
public bool $premium = true;
private $isSubscribed;
protected $listeners = ['updateEmail' => 'syncEmail', 'getEmail' => 'generateEmail', 'fetchMessages' => 'fetch', 'setMessageId' => 'setMessageId'];
public function mount(): void
{
$this->premium = Session::get('isInboxTypePremium', true);
if ($this->premium) {
$this->domains = json_decode(config('app.settings.configuration_settings'))->premium_domains ?? [];
$this->email = Premium::getEmail();
$this->emails = Premium::getEmails();
$this->initial = false;
$this->checkMultipleEmails();
$this->validateDomainInEmail();
} else {
$this->domains = json_decode(config('app.settings.configuration_settings'))->domains ?? [];
$this->email = ZEmail::getEmail();
$this->emails = ZEmail::getEmails();
$this->initial = false;
$this->checkMultipleEmails();
$this->validateDomainInEmail();
}
$this->emailsHistory = array_reverse(PremiumEmail::parseEmail(\auth()->user()->id)['data']) ?? [];
$subscriptionCheck = auth()->user()->subscribedToProduct(config('app.plans')[0]['product_id']);
Session::put('isSubscribed', $subscriptionCheck);
if($subscriptionCheck) {
try {
$result = auth()->user()->subscriptions()->where(['stripe_status' => 'active'])->orderByDesc('updated_at')->first();
$userPriceID = $result['items'][0]['stripe_price'];
$mailboxLimit = $this->email_limit;
foreach (config('app.plans') as $plan) {
if ($plan['pricing_id'] === $userPriceID) {
$mailboxLimit = $plan['mailbox_limit'];
break;
}
}
$this->email_limit = $mailboxLimit;
} catch (\Exception $e) {
\Log::error($e->getMessage());
}
}
if ($this->premium) {
$mailboxHistory = UsageLog::where(['user_id' => auth()->user()->id])->first();
$this->mailboxHistory = $mailboxHistory->emails_created_history ?? [];
} else {
$this->mailboxHistory = ZEmail::getEmails() ?? [];
}
}
private function checkMultipleEmails(): void
{
if (count($this->emails) == 0) {
$this->emails = [$this->email];
}
if (count($this->emails) > 1) {
$this->list = true;
} else {
$this->list = false;
}
}
public function switchEmail($email)
{
try {
if ($email != null) {
$data = explode('@', $email);
if (isset($data[1])) {
$domain = $data[1];
if ($this->premium) {
$domains = json_decode(config('app.settings.configuration_settings'))->premium_domains ?? [];
} else {
$domains = json_decode(config('app.settings.configuration_settings'))->domains ?? [];
}
if (!in_array($domain, $domains)) {
return $this->showAlert('error', __('This mailbox can not be recovered.'));
}
}
}
} catch (\Exception $exception) {
\Log::error($exception->getMessage());
}
return redirect()->route('switchP', ['email' => $email]);
}
public function syncEmail(): void
{
$this->email = Premium::getEmail();
$this->emails = Premium::getEmails();
if (count($this->emails) == 0) {
$this->dispatch('getEmail');
}
$this->checkMultipleEmails();
$this->dispatch('syncMailbox', $this->email);
$this->dispatch('fetchMessages');
}
public function generateEmail(): void
{
if ($this->email == null) {
Premium::generateRandomEmail();
}
$this->checkMultipleEmails();
$this->dispatch('updateEmail');
}
/* Actions */
public function create() {
if (!$this->username) {
return $this->showAlert('error', __('Please enter Username'));
}
$this->checkDomainInUsername();
if (strlen($this->username) < json_decode(config('app.settings.configuration_settings'))->custom_username_length_min || strlen($this->username) > json_decode(config('app.settings.configuration_settings'))->custom_username_length_max) {
return $this->showAlert('error', __('Username length cannot be less than') . ' ' . json_decode(config('app.settings.configuration_settings'))->custom_username_length_min . ' ' . __('and greater than') . ' ' . json_decode(config('app.settings.configuration_settings'))->custom_username_length_max);
}
if (!$this->domain) {
return $this->showAlert('error', __('Please Select a Domain'));
}
if (in_array($this->username, json_decode(config('app.settings.configuration_settings'))->forbidden_ids)) {
return $this->showAlert('error', __('Username not allowed'));
}
if (!$this->checkEmailLimit()) {
return $this->showAlert('error', __('You have reached daily limit of MAX ') . $this->email_limit . __(' temp mail'));
}
if (!$this->checkUsedEmail()) {
return $this->showAlert('error', __('Sorry! That email is already been used by someone else. Please try a different email address.'));
}
if ($this->premium) {
$this->email = Premium::createCustomEmail($this->username, $this->domain);
} else {
$this->email = ZEmail::createCustomEmail($this->username, $this->domain);
}
return redirect()->route('dashboard.premium');
}
public function random() {
if (!$this->checkEmailLimit()) {
return $this->showAlert('error', __('You have reached daily limit of maximum ') . $this->email_limit . __(' temp mail addresses.'));
}
if ($this->premium) {
$this->email = Premium::generateRandomEmail();
} else {
$this->email = ZEmail::generateRandomEmail();
}
return redirect()->route('dashboard.premium');
}
public function gmail() {
if (!$this->checkEmailLimit()) {
return $this->showAlert('error', __('You have reached daily limit of maximum ') . $this->email_limit . __(' temp mail addresses.'));
}
if ($this->premium) {
$this->email = Premium::generateRandomGmail();
} else {
$this->email = ZEmail::generateRandomGmail();
}
return redirect()->route('dashboard.premium');
}
public function outlook() {
if (!$this->checkEmailLimit()) {
return $this->showAlert('error', __('You have reached daily limit of maximum ') . $this->email_limit . __(' temp mail addresses.'));
}
if ($this->premium) {
$this->email = Premium::generateRandomOutlook();
} else {
$this->email = ZEmail::generateRandomOutlook();
}
return redirect()->route('dashboard.premium');
}
public function deleteEmail()
{
return redirect()->route('deleteP', ['email' => $this->email]);
}
private function showAlert($type, $message): void
{
$this->dispatch('showAlert', ['type' => $type, 'message' => $message]);
}
private function checkEmailLimit(): bool
{
$logs = Log::select('ip', 'email')->where('user_id', auth()->user()->id)->where('created_at', '>', Carbon::now()->subDay())->groupBy('email')->groupBy('ip')->get();
if (count($logs) >= $this->email_limit) {
return false;
}
return true;
}
private function checkUsedEmail(): bool
{
if (json_decode(config('app.settings.configuration_settings'))->disable_used_email) {
$check = Log::where('email', $this->user . '@' . $this->domain)->where('ip', '<>', request()->ip())->count();
if ($check > 0) {
return false;
}
return true;
}
return true;
}
private function checkDomainInUsername(): void
{
$parts = explode('@', $this->username);
if (isset($parts[1])) {
if (in_array($parts[1], $this->domains)) {
$this->domain = $parts[1];
}
$this->username = $parts[0];
}
}
private function validateDomainInEmail(): void
{
try {
if ($this->email != null) {
$data = explode('@', $this->email);
if (isset($data[1])) {
$domain = $data[1];
if ($this->premium) {
$domains = json_decode(config('app.settings.configuration_settings'))->premium_domains ?? [];
} else {
$domains = json_decode(config('app.settings.configuration_settings'))->domains ?? [];
}
if (!in_array($domain, $domains)) {
$key = array_search($this->email, $this->emails);
if ($this->premium) {
Premium::removeEmail($this->email);
} else {
ZEmail::removeEmail($this->email);
}
if ($key == 0 && count($this->emails) == 1 && json_decode(config('app.settings.configuration_settings'))->after_last_email_delete == 'redirect_to_homepage') {
redirect()->route('dashboard.premium');
} else {
redirect()->route('dashboard.premium');
}
}
}
}
} catch (\Exception $exception) {
\Log::error($exception->getMessage());
}
}
/* Mailbox */
public function fetch(): void
{
try {
$count = count($this->messages);
if ($count > 0) {
$this->messages = [];
}
$responses = [];
if ($this->premium) {
if (config('app.beta_feature') || !json_decode(config('app.settings.imap_settings'))->premium_cc_check) {
$responses = [
'to' => Premium::getMessages($this->email, 'to', $this->deleted),
'cc' => [
'data' => [],
'notifications' => []
]
];
} else {
$responses = [
'to' => Premium::getMessages($this->email, 'to', $this->deleted),
'cc' => Premium::getMessages($this->email, 'cc', $this->deleted)
];
}
} else {
if (config('app.beta_feature') || !json_decode(config('app.settings.imap_settings'))->cc_check) {
$responses = [
'to' => ZEmail::getMessages($this->email, 'to', $this->deleted),
'cc' => [
'data' => [],
'notifications' => []
]
];
} else {
$responses = [
'to' => ZEmail::getMessages($this->email, 'to', $this->deleted),
'cc' => ZEmail::getMessages($this->email, 'cc', $this->deleted)
];
}
}
$this->deleted = [];
$this->messages = array_merge($responses['to']['data'], $responses['cc']['data']);
$notifications = array_merge($responses['to']['notifications'], $responses['cc']['notifications']);
if (count($notifications)) {
if (!$this->overflow && count($this->messages) == $count) {
$this->overflow = true;
}
} else {
$this->overflow = false;
}
foreach ($notifications as $notification) {
$this->dispatch('showNewMailNotification', $notification);
}
Premium::incrementMessagesStats(count($notifications));
if ($this->email != null && count($this->messages) > 0) {
foreach ($this->messages as $message) {
PremiumEmail::createEmail($message, $this->email);
}
}
$this->emailsHistory = array_reverse(PremiumEmail::parseEmail(\auth()->user()->id)['data']) ?? [];
} catch (\Exception $e) {
if (Auth::check() && Auth::user()->level == 9) {
$this->error = $e->getMessage();
} else {
$this->error = 'Not able to connect to Mail Server';
}
}
$this->dispatch('stopLoader');
$this->initial = true;
}
public function delete($messageId) {
try {
$this->deleted[] = $messageId;
foreach ($this->messages as $key => $message) {
if ($message['id'] == $messageId) {
if ($this->premium) {
$directory = public_path('tmp/premium/attachments/') . $messageId;
} else {
$directory = public_path('tmp/attachments/') . $messageId;
}
$this->rrmdir($directory);
unset($this->messages[$key]);
}
}
} catch (
\Exception $exception
) {
\Illuminate\Support\Facades\Log::error($exception->getMessage());
}
}
public function toggleMode()
{
$this->premium = !$this->premium;
Session::put('isInboxTypePremium', $this->premium);
if ($this->premium) {
$this->domains = json_decode(config('app.settings.configuration_settings'))->premium_domains ?? [];
$this->email = Premium::getEmail();
$this->emails = Premium::getEmails();
$this->initial = false;
$this->checkMultipleEmails();
$this->validateDomainInEmail();
$mailboxHistory = UsageLog::where(['user_id' => auth()->user()->id])->first();
$this->mailboxHistory = $mailboxHistory->emails_created_history ?? [];
$this->messages = [];
} else {
$this->domains = json_decode(config('app.settings.configuration_settings'))->domains ?? [];
$this->email = ZEmail::getEmail();
$this->emails = ZEmail::getEmails();
$this->initial = false;
$this->checkMultipleEmails();
$this->validateDomainInEmail();
$this->mailboxHistory = array_reverse(ZEmail::getEmails()) ?? [];
$this->messages = [];
}
}
public function render()
{
if (Session::get('isSubscribed')) {
return view('livewire.dashboard.mailbox.inbox')->layout('components.layouts.dashboard');
} else {
return view('livewire.dashboard.not-subscribed')->layout('components.layouts.dashboard');
}
}
private function rrmdir($dir): void
{
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != "." && $object != "..") {
if (is_dir($dir . DIRECTORY_SEPARATOR . $object) && !is_link($dir . "/" . $object))
$this->rrmdir($dir . DIRECTORY_SEPARATOR . $object);
else
unlink($dir . DIRECTORY_SEPARATOR . $object);
}
}
rmdir($dir);
}
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace App\Livewire\Dashboard;
use Livewire\Component;
class NotSubscribed extends Component
{
public function render()
{
return view('livewire.dashboard.not-subscribed')->layout('components.layouts.dashboard');
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Livewire\Dashboard;
use Livewire\Component;
class Pricing extends Component
{
public $plans;
public function mount(): void
{
$this->plans = config('app.plans');
}
public function choosePlan($pricing_id): void
{
$this->redirect(route('checkout', $pricing_id));
}
public function render()
{
return view('livewire.dashboard.pricing');
}
}

View File

@@ -10,8 +10,6 @@ use Livewire\Component;
class Action extends Component class Action extends Component
{ {
public $username, $email, $emails, $domain, $domains, $action, $initial; public $username, $email, $emails, $domain, $domains, $action, $initial;
public function mount() { public function mount() {
$this->domains = json_decode(config('app.settings.configuration_settings'))->domains ?? []; $this->domains = json_decode(config('app.settings.configuration_settings'))->domains ?? [];
$this->email = ZEmail::getEmail(); $this->email = ZEmail::getEmail();
@@ -61,6 +59,14 @@ class Action extends Component
return redirect()->route('mailbox'); return redirect()->route('mailbox');
} }
public function outlook() {
if (!$this->checkEmailLimit()) {
return $this->showAlert('error', __('You have reached daily limit of maximum ') . json_decode(config('app.settings.configuration_settings'))->email_limit . __(' temp mail addresses.'));
}
$this->email = ZEmail::generateRandomOutlook();
return redirect()->route('mailbox');
}
public function deleteEmail() public function deleteEmail()
{ {
return redirect()->route('delete', ['email' => $this->email]); return redirect()->route('delete', ['email' => $this->email]);
@@ -68,6 +74,10 @@ class Action extends Component
private function showAlert($type, $message): void private function showAlert($type, $message): void
{ {
$check = json_decode(config('app.settings.configuration_settings'))->email_limit;
if (strpos($message, $check) !== false) {
$this->dispatch('promotePremium');
}
$this->dispatch('showAlert', ['type' => $type, 'message' => $message]); $this->dispatch('showAlert', ['type' => $type, 'message' => $message]);
} }

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Livewire\Settings;
use Livewire\Attributes\Layout;
use Livewire\Component;
#[Layout('components.layouts.dashboard')]
class Appearance extends Component
{
//
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Livewire\Settings;
use App\Livewire\Actions\Logout;
use Illuminate\Support\Facades\Auth;
use Livewire\Attributes\Layout;
use Livewire\Component;
#[Layout('components.layouts.dashboard')]
class DeleteUserForm extends Component
{
public string $password = '';
/**
* Delete the currently authenticated user.
*/
public function deleteUser(Logout $logout): void
{
$this->validate([
'password' => ['required', 'string', 'current_password'],
]);
tap(Auth::user(), $logout(...))->delete();
$this->redirect('/', navigate: true);
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Livewire\Settings;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules\Password as PasswordRule;
use Illuminate\Validation\ValidationException;
use Livewire\Attributes\Layout;
use Livewire\Component;
#[Layout('components.layouts.dashboard')]
class Password extends Component
{
public string $current_password = '';
public string $password = '';
public string $password_confirmation = '';
/**
* Update the password for the currently authenticated user.
*/
public function updatePassword(): void
{
try {
$validated = $this->validate([
'current_password' => ['required', 'string', 'current_password'],
'password' => ['required', 'string', PasswordRule::defaults(), 'confirmed'],
]);
} catch (ValidationException $e) {
$this->reset('current_password', 'password', 'password_confirmation');
throw $e;
}
Auth::user()->update([
'password' => Hash::make($validated['password']),
]);
$this->reset('current_password', 'password', 'password_confirmation');
$this->dispatch('password-updated');
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace App\Livewire\Settings;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Layout;
use Livewire\Component;
#[Layout('components.layouts.dashboard')]
class Profile extends Component
{
public string $name = '';
public string $email = '';
/**
* Mount the component.
*/
public function mount(): void
{
$this->name = Auth::user()->name;
$this->email = Auth::user()->email;
}
/**
* Update the profile information for the currently authenticated user.
*/
public function updateProfileInformation(): void
{
$user = Auth::user();
$validated = $this->validate([
'name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
'lowercase',
'email',
'max:255',
Rule::unique(User::class)->ignore($user->id),
],
]);
$user->fill($validated);
if ($user->isDirty('email')) {
$user->email_verified_at = null;
}
$user->save();
$this->dispatch('profile-updated', name: $user->name);
}
/**
* Send an email verification notification to the current user.
*/
public function resendVerificationNotification(): void
{
$user = Auth::user();
if ($user->hasVerifiedEmail()) {
$this->redirectIntended(default: route('dashboard', absolute: false));
return;
}
$user->sendEmailVerificationNotification();
Session::flash('status', 'verification-link-sent');
}
}

View File

@@ -16,6 +16,7 @@ class Log extends Model
* @var array * @var array
*/ */
protected $fillable = [ protected $fillable = [
'user_id',
'ip', 'ip',
'email', 'email',
]; ];

24
app/Models/Plan.php Normal file
View File

@@ -0,0 +1,24 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Plan extends Model
{
protected $fillable = [
'name',
'description',
'product_id',
'pricing_id',
'price',
'mailbox_limit',
'monthly_billing',
'details'
];
protected $casts = [
'details' => 'json',
'monthly_billing' => 'boolean',
];
}

535
app/Models/Premium.php Normal file
View File

@@ -0,0 +1,535 @@
<?php
namespace App\Models;
use App\ColorPicker;
use Carbon\Carbon;
use Ddeboer\Imap\Search\Email\Cc;
use Ddeboer\Imap\Search\Email\To;
use Ddeboer\Imap\SearchExpression;
use Ddeboer\Imap\Server;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cookie;
class Premium extends Model
{
use ColorPicker;
public static function connectMailBox($imap = null): \Ddeboer\Imap\ConnectionInterface
{
$imapDB = json_decode(config('app.settings.imap_settings'), true);
$imap = [
'host' => $imapDB['premium_host'],
'port' => $imapDB['premium_port'],
'encryption' => $imapDB['premium_encryption'],
'validate_cert' => $imapDB['premium_validate_cert'],
'username' => $imapDB['premium_username'],
'password' => $imapDB['premium_password'],
'default_account' => $imapDB['premium_default_account'],
'protocol' => $imapDB['premium_protocol'],
'cc_check' => $imapDB['premium_cc_check']
];
return ZEmail::connectMailBox($imap);
}
public static function fetchMessages($email, $type = 'to', $deleted = []): array
{
if ($email == null) {
return [
"data" => [],
"notifications" => []
];
}
$allowed = explode(',', 'doc,docx,xls,xlsx,ppt,pptx,xps,pdf,dxf,ai,psd,eps,ps,svg,ttf,zip,rar,tar,gzip,mp3,mpeg,wav,ogg,jpeg,jpg,png,gif,bmp,tif,webm,mpeg4,3gpp,mov,avi,mpegs,wmv,flx,txt');
$connection = self::connectMailBox();
$mailbox = $connection->getMailbox('INBOX');
$search = new SearchExpression();
if ($type == 'cc') {
$search->addCondition(new Cc($email));
} else {
$search->addCondition(new To($email));
}
$messages = $mailbox->getMessages($search, \SORTDATE, true);
$limit = json_decode(config('app.settings.configuration_settings'))->fetch_messages_limit ?? 15;
$count = 1;
$response = [
'data' => [],
'notifications' => []
];
foreach ($messages as $message) {
if (in_array($message->getNumber(), $deleted)) {
$message->delete();
continue;
}
$blocked = false;
$sender = $message->getFrom();
$date = $message->getDate();
if (!$date) {
$date = new \DateTime();
if ($message->getHeaders()->get('udate')) {
$date->setTimestamp($message->getHeaders()->get('udate'));
}
}
$datediff = new Carbon($date);
$content = '';
$contentText = '';
$html = $message->getBodyHtml();
$text = $message->getBodyText();
if ($text) {
$contentText = str_replace('<a', '<a target="blank"', str_replace(array("\r\n", "\n"), '', $text));
}
if ($html) {
$content = str_replace('<a', '<a target="blank"', $html);
} else {
$content = str_replace('<a', '<a target="blank"', str_replace(array("\r\n", "\n"), '<br/>', $text));
}
if (json_decode(config('app.settings.configuration_settings'))->enable_masking_external_link) {
$content = str_replace('href="', 'href="http://href.li/?', $content);
}
$obj = [];
$obj['subject'] = $message->getSubject();
$obj['sender_name'] = $sender->getName();
$obj['sender_email'] = $sender->getAddress();
$obj['timestamp'] = $message->getDate();
$obj['date'] = $date->format(json_decode(config('app.settings.configuration_settings'))->date_format ?? 'd M Y h:i A');
$obj['datediff'] = $datediff->diffForHumans();
$obj['id'] = $message->getNumber();
$obj['size'] = $message->getSize();
$obj['content'] = $content;
$obj['contentText'] = $contentText;
$obj['attachments'] = [];
$obj['is_seen'] = true;
$obj['sender_photo'] = self::chooseColor(strtoupper(substr($sender->getName() ?: $sender->getAddress(), 0, 1) ));
//Checking if Sender is Blocked
$domain = explode('@', $obj['sender_email'])[1];
$blocked = in_array($domain, json_decode(config('app.settings.configuration_settings'))->blocked_domains);
if ($blocked) {
$obj['subject'] = __('Blocked');
$obj['content'] = __('Emails from') . ' ' . $domain . ' ' . __('are blocked by Admin');
$obj['contentText'] = __('Emails from') . ' ' . $domain . ' ' . __('are blocked by Admin');
}
if ($message->hasAttachments() && !$blocked) {
$attachments = $message->getAttachments();
$directory = './tmp/premium/attachments/' . $obj['id'] . '/';
is_dir($directory) || mkdir($directory, 0777, true);
foreach ($attachments as $attachment) {
$filenameArray = explode('.', $attachment->getFilename());
$extension = $filenameArray[count($filenameArray) - 1];
if (in_array($extension, $allowed)) {
if (!file_exists($directory . $attachment->getFilename())) {
try {
file_put_contents(
$directory . $attachment->getFilename(),
$attachment->getDecodedContent()
);
} catch (\Exception $e) {
\Illuminate\Support\Facades\Log::error($e->getMessage());
}
}
if ($attachment->getFilename() !== 'undefined') {
$url = config('app.settings.app_base_url') . str_replace('./', '/', $directory . $attachment->getFilename());
$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']);
}
$obj['attachments'][] = [
'file' => $attachment->getFilename(),
'url' => $url
];
}
}
}
}
$response['data'][] = $obj;
if (!$message->isSeen()) {
$response['notifications'][] = [
'subject' => $obj['subject'],
'sender_name' => $obj['sender_name'],
'sender_email' => $obj['sender_email']
];
if (config('app.zemail_log')) {
file_put_contents(storage_path('logs/zemail.csv'), request()->ip() . "," . date("Y-m-d h:i:s a") . "," . $obj['sender_email'] . "," . $email . PHP_EOL, FILE_APPEND);
}
}
$message->markAsSeen();
if (++$count > $limit) {
break;
}
}
$response['data'] = array_reverse($response['data']);
$connection->expunge();
return $response;
}
public static function getMessages($email, $type = 'to', $deleted = []): array
{
return self::fetchMessages($email, $type, $deleted);
}
public static function deleteMessage($id): void
{
$connection = self::connectMailBox();
$mailbox = $connection->getMailbox('INBOX');
$mailbox->getMessage($id)->delete();
$connection->expunge();
}
public static function getEmail($generate = false) {
if (Cookie::has('p_email')) {
return Cookie::get('p_email');
} else {
return $generate ? self::generateRandomEmail() : null;
}
}
public static function getEmails() {
if (Cookie::has('p_emails')) {
return unserialize(Cookie::get('p_emails'));
} else {
return [];
}
}
public static function setEmail($email): void
{
$emails = unserialize(Cookie::get('p_emails'));
if (is_array($emails) && in_array($email, $emails)) {
Cookie::queue('p_email', $email, 43800);
}
}
public static function setEmailP($email): void
{
$usageLogs = UsageLog::where(['user_id' => auth()->user()->id])->first();
$emails = $usageLogs->emails_created_history;
if (is_array($emails) && in_array($email, $emails)) {
Cookie::queue('p_email', $email, 43800);
}
}
public static function removeEmail($email): void
{
$emails = self::getEmails();
$key = array_search($email, $emails);
if ($key !== false) {
array_splice($emails, $key, 1);
}
if (count($emails) > 0) {
self::setEmail($emails[0]);
Cookie::queue('p_emails', serialize($emails), 43800);
} else {
Cookie::queue('p_email', '', -1);
Cookie::queue('p_emails', serialize([]), -1);
}
}
public static function createCustomEmailFull($email): string
{
$data = explode('@', $email);
$username = $data[0];
if (strlen($username) < json_decode(config('app.settings.configuration_settings'))->custom_username_length_min || strlen($username) > json_decode(config('app.settings.configuration_settings'))->custom_username_length_max) {
$zemail = new Premium();
$username = $zemail->generateRandomUsername();
}
$domain = $data[1];
return self::createCustomEmail($username, $domain);
}
public static function createCustomEmail($username, $domain): string
{
$username = preg_replace('/[^a-zA-Z0-9+.]/', '', strtolower($username));
$settings = json_decode(config('app.settings.configuration_settings'), true);
$forbidden_ids = $settings['forbidden_ids'] ?? [];
$gmail_usernames = $settings['premium_gmailUsernames'] ?? [];
$outlook_usernames = $settings['premium_outlookUsernames'] ?? [];
$domains = $settings['premium_domains'] ?? [];
if (in_array($username, $forbidden_ids)) {
return self::generateRandomEmail(true);
}
if ($username === '' && in_array($domain, ['gmail.com', 'googlemail.com'])) {
return self::generateRandomGmail(true);
}
if ($username === '' && in_array($domain, ['outlook.com'])) {
return self::generateRandomOutlook(true);
}
$zemail = new Premium();
if ($username === '' && in_array($domain, $domains)) {
return $zemail->generateRandomUsername().'@'.$domain;
}
if (in_array($domain, ['outlook.com'])) {
if (str_contains($username, '+')) {
[$check_username, $post_username] = explode('+', $username, 2);
if (in_array($check_username, $outlook_usernames)) {
$email = $username . '@' . $domain;
} else {
$email = $zemail->getRandomOutlookUser() . '+' . $post_username . '@' . $domain;
}
} else {
$email = $zemail->getRandomOutlookUser() . '+' . $username . '@' . $domain;
}
self::storeEmail($email);
return $email;
}
if (in_array($domain, ['gmail.com', 'googlemail.com'])) {
if (str_contains($username, '+')) {
[$check_username, $post_username] = explode('+', $username, 2);
if (in_array($check_username, $gmail_usernames)) {
$email = $username . '@' . $domain;
} else {
$email = $zemail->getRandomGmailUser() . '+' . $post_username . '@' . $domain;
}
} elseif (str_contains($username, '.')) {
$check_username = str_replace('.', '', $username);
if (in_array($check_username, $gmail_usernames)) {
$email = $username . '@' . $domain;
} else {
$email = $zemail->generateRandomGmail() . '@' . $domain;
}
} else {
$email = $zemail->getRandomGmailUser() . '+' . $username . '@' . $domain;
}
self::storeEmail($email);
return $email;
}
// Handle other custom domains
if (!in_array($domain, $domains)) {
return self::generateRandomEmail(true);
}
$finalDomain = in_array($domain, $domains) ? $domain : ($domains[0] ?? 'example.com');
$email = $username . '@' . $finalDomain;
self::storeEmail($email);
return $email;
}
public static function generateRandomEmail($store = true): string
{
$zemail = new Premium();
$domain = $zemail->getRandomDomain();
if ($domain == "gmail.com") {
$rd = mt_rand(0,1);
if ($rd == 0) {
$email = $zemail->generateRandomGmail(false);
} else {
$email = $zemail->getRandomGmailUser().'+'.$zemail->generateRandomUsername().'@gmail.com';
}
} elseif ($domain == "googlemail.com") {
$rd = mt_rand(0,1);
if ($rd == 0) {
$email = $zemail->generateRandomGmail(false);
} else {
$email = $zemail->getRandomGmailUser().'+'.$zemail->generateRandomUsername().'@googlemail.com';
}
} elseif ($domain == "outlook.com") {
$email = $zemail->getRandomOutlookUser().'+'.$zemail->generateRandomUsername().'@outlook.com';
}
else {
$email = $zemail->generateRandomUsername() . '@' . $domain;
}
if ($store) {
self::storeEmail($email);
}
return $email;
}
public static function generateRandomGmail($store = true): string
{
$zemail = new Premium();
$uname = $zemail->getRandomGmailUser();
$uname_len = strlen($uname);
$len_power = $uname_len - 1;
$combination = pow(2,$len_power);
$rand_comb = mt_rand(1,$combination);
$formatted = implode(' ',str_split($uname));
$uname_exp = explode(' ', $formatted);
$bin = intval("");
for($i=0; $i<$len_power; $i++) {
$bin .= mt_rand(0,1);
}
$bin = explode(' ', implode(' ',str_split(strval($bin))));
$email = "";
for($i=0; $i<$len_power; $i++) {
$email .= $uname_exp[$i];
if($bin[$i]) {
$email .= ".";
}
}
$email .= $uname_exp[$i];
$gmail_rand = mt_rand(1,10);
if($gmail_rand > 5) {
$email .= "@gmail.com";
} else {
$email .= "@googlemail.com";
}
if ($store) {
self::storeEmail($email);
}
return $email;
}
public static function generateRandomOutlook($store = true): string
{
$zemail = new Premium();
$email = $zemail->getRandomOutlookUser().'+'.$zemail->generateRandomUsername().'@outlook.com';
if ($store) {
self::storeEmail($email);
}
return $email;
}
private static function storeEmail($email): void
{
Log::create([
'user_id' => auth()->user()->id,
'ip' => request()->ip(),
'email' => $email
]);
self::storeUsageLog($email);
Cookie::queue('p_email', $email, 43800);
$emails = Cookie::has('p_emails') ? unserialize(Cookie::get('p_emails')) : [];
if (!in_array($email, $emails)) {
self::incrementEmailStats();
$emails[] = $email;
Cookie::queue('p_emails', serialize($emails), 43800);
}
}
public static function incrementEmailStats($count = 1): void
{
Meta::incrementEmailIdsCreated($count);
self::incrementEmailIdsCreated($count);
}
public static function incrementMessagesStats($count = 1): void
{
Meta::incrementMessagesReceived($count);
self::incrementMessagesReceived($count);
}
private function generateRandomUsername(): string
{
$start = json_decode(config('app.settings.configuration_settings'))->random_username_length_min ?? 0;
$end = json_decode(config('app.settings.configuration_settings'))->random_username_length_max ?? 0;
if ($start == 0 && $end == 0) {
return $this->generatePronounceableWord();
}
return $this->generatedRandomBetweenLength($start, $end);
}
protected function generatedRandomBetweenLength($start, $end): string
{
$length = rand($start, $end);
return $this->generateRandomString($length);
}
private function getRandomDomain() {
$domains = json_decode(config('app.settings.configuration_settings'))->premium_domains ?? [];
$count = count($domains);
return $count > 0 ? $domains[rand(1, $count) - 1] : '';
}
private function getRandomGmailUser() {
$gmailusername = json_decode(config('app.settings.configuration_settings'))->premium_gmailUsernames ?? [];
$count = count($gmailusername);
return $count > 0 ? $gmailusername[rand(1, $count) - 1] : '';
}
private function getRandomOutlookUser() {
$outlook_username = json_decode(config('app.settings.configuration_settings'))->premium_outlookUsernames ?? [];
$count = count($outlook_username);
return $count > 0 ? $outlook_username[rand(1, $count) - 1] : '';
}
private function generatePronounceableWord(): string
{
$c = 'bcdfghjklmnprstvwz'; //consonants except hard to speak ones
$v = 'aeiou'; //vowels
$a = $c . $v; //both
$random = '';
for ($j = 0; $j < 2; $j++) {
$random .= $c[rand(0, strlen($c) - 1)];
$random .= $v[rand(0, strlen($v) - 1)];
$random .= $a[rand(0, strlen($a) - 1)];
}
return $random;
}
private function generateRandomString($length = 10): string
{
$characters = '0123456789abcdefghijklmnopqrstuvwxyz';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
/**
* Stats Handling Functions
*/
public static function incrementEmailIdsCreated($count = 1): void
{
$user = Auth::user();
if (!$user) {
return;
}
$usageLog = UsageLog::firstOrCreate(
['user_id' => $user->id],
['ip_address' => request()->ip()]
);
$usageLog->increment('emails_created_count', $count);
}
public static function incrementMessagesReceived($count = 1): void
{
$user = Auth::user();
if (!$user) {
return;
}
$usageLog = UsageLog::firstOrCreate(
['user_id' => $user->id],
['ip_address' => request()->ip()]
);
$usageLog->increment('emails_received_count', $count);
}
public static function storeUsageLog($email): void
{
try {
$user = Auth::user();
if (!$user) {
return;
}
$ip = request()->ip();
$usageLog = UsageLog::firstOrCreate(
['user_id' => $user->id],
['ip_address' => $ip]
);
$history = $usageLog->emails_created_history ?? [];
if (!in_array($email, $history)) {
$history[] = $email;
//$usageLog->emails_created_count += 1;
$usageLog->emails_created_history = $history;
$usageLog->save();
}
} catch (\Exception $exception) {
\Log::error($exception->getMessage());
}
}
}

160
app/Models/PremiumEmail.php Normal file
View File

@@ -0,0 +1,160 @@
<?php
namespace App\Models;
use App\ColorPicker;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Validator;
class PremiumEmail extends Model
{
use ColorPicker;
protected $fillable = [
'user_id',
'message_id',
'subject',
'from_name',
'from_email',
'to',
'cc',
'bcc',
'timestamp',
'body_text',
'body_html',
'is_seen',
'is_flagged',
'size',
'mailbox',
'raw_headers',
'raw_body',
'attachments',
];
protected $casts = [
'to' => 'array',
'cc' => 'array',
'bcc' => 'array',
'attachments' => 'array',
'timestamp' => 'datetime'
];
public static function createEmail($message, $email): void
{
$initialData = $message;
$utcTime = CarbonImmutable::instance($message['timestamp'])->setTimezone('UTC')->toDateTimeString();
$messageId = Carbon::parse($utcTime)->format('Ymd').$initialData['id'];
$userId = \auth()->user()->id;
$exists = PremiumEmail::where('user_id', $userId)->where('message_id', $messageId)->exists();
$data = [
'user_id' => $userId,
'message_id' => $messageId,
'subject' => $initialData['subject'],
'from_name' => $initialData['sender_name'],
'from_email' => $initialData['sender_email'],
'to' => ["$email"],
'cc' => [],
'bcc' => [],
'timestamp' => $utcTime,
'body_text' => $initialData['contentText'],
'body_html' => $initialData['content'],
'is_seen' => true,
'is_flagged' => false,
'size' => $initialData['size'] ?? 0,
'mailbox' => 'INBOX',
'raw_headers' => null,
'raw_body' => null,
'attachments' => $initialData['attachments'],
];
if (!$exists) {
PremiumEmail::create($data);
}
}
public static function fetchEmailFromDB($userId) {
$validator = Validator::make(['user_id' => $userId], [
'user_id' => 'required|integer'
]);
if ($validator->fails()) {
return [];
}
return self::whereJsonContains('user_id', $userId)->orderBy('timestamp', 'desc')->get();
}
public static function parseEmail($userId, $deleted = []): array
{
$messages = self::fetchEmailFromDB($userId);
$limit = 50;
$count = 1;
$response = [
'data' => [],
'notifications' => []
];
foreach ($messages as $message) {
if (in_array($message['message_id'], $deleted)) {
// If it exists, delete the matching record from the 'emails' table
Email::where('message_id', $message['message_id'])->delete();
continue;
}
$blocked = false;
$timestamp = $message['timestamp'];
$carbonTimestamp = Carbon::parse($timestamp, 'UTC');
$obj = [];
$obj['subject'] = $message['subject'];
$obj['to'] = $message['to'];
$obj['sender_name'] = $message['from_name'];
$obj['sender_email'] = $message['from_email'];
$obj['timestamp'] = $message['timestamp'];
$obj['date'] = $carbonTimestamp->format('d M Y h:i A');
$obj['datediff'] = $carbonTimestamp->diffForHumans(Carbon::now('UTC'));
$obj['id'] = $message['message_id'];
$obj['content'] = $message['body_html'];
$obj['contentText'] = $message['body_text'];
$obj['attachments'] = [];
$obj['is_seen'] = $message['is_seen'];
$obj['sender_photo'] = self::chooseColor(strtoupper(substr($message['from_name'] ?: $message['from_email'], 0, 1) ));
$domain = explode('@', $obj['sender_email'])[1];
$blocked = in_array($domain, json_decode(config('app.settings.configuration_settings'))->blocked_domains);
if ($blocked) {
$obj['subject'] = __('Blocked');
$obj['content'] = __('Emails from') . ' ' . $domain . ' ' . __('are blocked by Admin');
$obj['contentText'] = __('Emails from') . ' ' . $domain . ' ' . __('are blocked by Admin');
}
if (count($message['attachments']) > 0 && !$blocked) {
$obj['attachments'] = $message['attachments'];
}
$response['data'][] = $obj;
if (!$message['is_seen']) {
$response['notifications'][] = [
'subject' => $obj['subject'],
'sender_name' => $obj['sender_name'],
'sender_email' => $obj['sender_email']
];
if (config('app.zemail_log')) {
file_put_contents(storage_path('logs/zemail.csv'), request()->ip() . "," . date("Y-m-d h:i:s a") . "," . $obj['sender_email'] . "," . $email . PHP_EOL, FILE_APPEND);
}
}
PremiumEmail::where('message_id', $message['message_id'])->update(['is_seen' => true]);
if (++$count > $limit) {
break;
}
}
return $response;
}
}

23
app/Models/UsageLog.php Normal file
View File

@@ -0,0 +1,23 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class UsageLog extends Model
{
protected $fillable = [
'user_id',
'ip_address',
'emails_created_count',
'emails_received_count',
'emails_created_history',
'emails_received_history',
];
protected $casts = [
'emails_created_history' => 'array',
'emails_received_history' => 'array',
];
}

View File

@@ -8,11 +8,14 @@ use Filament\Panel;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Support\Str;
use Laravel\Cashier\Billable;
class User extends Authenticatable implements FilamentUser class User extends Authenticatable implements FilamentUser, MustVerifyEmail
{ {
/** @use HasFactory<\Database\Factories\UserFactory> */ /** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable; use HasFactory, Notifiable, Billable;
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.
@@ -48,6 +51,14 @@ class User extends Authenticatable implements FilamentUser
]; ];
} }
public function initials(): string
{
return Str::of($this->name)
->explode(' ')
->map(fn (string $name) => Str::of($name)->substr(0, 1))
->implode('');
}
public function canAccessPanel(Panel $panel): bool public function canAccessPanel(Panel $panel): bool
{ {
return str_ends_with($this->email, '@zemail.me') && $this->level === 9 && $this->hasVerifiedEmail(); return str_ends_with($this->email, '@zemail.me') && $this->level === 9 && $this->hasVerifiedEmail();

View File

@@ -114,6 +114,7 @@ class ZEmail extends Model
$settings = json_decode(config('app.settings.configuration_settings'), true); $settings = json_decode(config('app.settings.configuration_settings'), true);
$forbidden_ids = $settings['forbidden_ids'] ?? []; $forbidden_ids = $settings['forbidden_ids'] ?? [];
$gmail_usernames = $settings['gmailUsernames'] ?? []; $gmail_usernames = $settings['gmailUsernames'] ?? [];
$outlook_usernames = $settings['outlookUsernames'] ?? [];
$domains = $settings['domains'] ?? []; $domains = $settings['domains'] ?? [];
if (in_array($username, $forbidden_ids)) { if (in_array($username, $forbidden_ids)) {
@@ -124,13 +125,31 @@ class ZEmail extends Model
return ZEmail::generateRandomGmail(true); return ZEmail::generateRandomGmail(true);
} }
if ($username === '' && in_array($domain, ['outlook.com'])) {
return ZEmail::generateRandomOutlook(true);
}
$zemail = new ZEmail(); $zemail = new ZEmail();
if ($username === '' && in_array($domain, $domains)) { if ($username === '' && in_array($domain, $domains)) {
return $zemail->generateRandomUsername().'@'.$domain; return $zemail->generateRandomUsername().'@'.$domain;
} }
if (in_array($domain, ['outlook.com'])) {
if (str_contains($username, '+')) {
[$check_username, $post_username] = explode('+', $username, 2);
if (in_array($check_username, $outlook_usernames)) {
$email = $username . '@' . $domain;
} else {
$email = $zemail->getRandomOutlookUser() . '+' . $post_username . '@' . $domain;
}
} else {
$email = $zemail->getRandomOutlookUser() . '+' . $username . '@' . $domain;
}
ZEmail::storeEmail($email);
return $email;
}
if (in_array($domain, ['gmail.com', 'googlemail.com'])) { if (in_array($domain, ['gmail.com', 'googlemail.com'])) {
if (str_contains($username, '+')) { if (str_contains($username, '+')) {
@@ -190,7 +209,10 @@ class ZEmail extends Model
} else { } else {
$email = $zemail->getRandomGmailUser().'+'.$zemail->generateRandomUsername().'@googlemail.com'; $email = $zemail->getRandomGmailUser().'+'.$zemail->generateRandomUsername().'@googlemail.com';
} }
} else { } elseif ($domain == "outlook.com") {
$email = $zemail->getRandomOutlookUser().'+'.$zemail->generateRandomUsername().'@outlook.com';
}
else {
$email = $zemail->generateRandomUsername() . '@' . $domain; $email = $zemail->generateRandomUsername() . '@' . $domain;
} }
if ($store) { if ($store) {
@@ -235,6 +257,15 @@ class ZEmail extends Model
return $email; return $email;
} }
public static function generateRandomOutlook($store = true): string
{
$zemail = new ZEmail();
$email = $zemail->getRandomOutlookUser().'+'.$zemail->generateRandomUsername().'@outlook.com';
if ($store) {
ZEmail::storeEmail($email);
}
return $email;
}
private static function storeEmail($email): void private static function storeEmail($email): void
{ {
Log::create([ Log::create([
@@ -292,6 +323,11 @@ class ZEmail extends Model
$count = count($gmailusername); $count = count($gmailusername);
return $count > 0 ? $gmailusername[rand(1, $count) - 1] : ''; return $count > 0 ? $gmailusername[rand(1, $count) - 1] : '';
} }
private function getRandomOutlookUser() {
$outlook_username = json_decode(config('app.settings.configuration_settings'))->outlookUsernames ?? [];
$count = count($outlook_username);
return $count > 0 ? $outlook_username[rand(1, $count) - 1] : '';
}
private function generatePronounceableWord(): string private function generatePronounceableWord(): string
{ {
@@ -318,100 +354,3 @@ class ZEmail extends Model
} }
} }
// public static function createCustomEmail($username, $domain): string
// {
// $username = str_replace('[^a-zA-Z0-9]', '', strtolower($username));
// $forbidden_ids = json_decode(config('app.settings.configuration_settings'))->forbidden_ids ?? [];
// $gmail_usernames = json_decode(config('app.settings.configuration_settings'))->gmailUsernames ?? [];
// if (in_array($username, $forbidden_ids)) {
// return ZEmail::generateRandomEmail(true);
// }
// $zemail = new ZEmail();
// if($domain == "gmail.com") {
//
// if(str_contains($username, "+")){
// $check_username = explode("+", $username)[0];
// $post_username = explode("+", $username)[1];
// if (in_array($check_username, $gmail_usernames)) {
// $email = $username . '@gmail.com';
// ZEmail::storeEmail($email);
// return $email;
// } else {
//
// $email = $zemail->getRandomGmailUser().'+'.$post_username.'@gmail.com';
// ZEmail::storeEmail($email);
// return $email;
// }
// }
// elseif(str_contains($username, ".")){
// $check_username = str_replace(".","",$username);
//
// if (in_array($check_username, $gmail_usernames)) {
// $email = $username . '@gmail.com';
// ZEmail::storeEmail($email);
// return $email;
// } else {
//
// $email = $zemail->generateRandomGmail();
// ZEmail::storeEmail($email);
// return $email;
// }
//
// }
// else {
//
// $email = $zemail->getRandomGmailUser().'+'.$username.'@gmail.com';
// ZEmail::storeEmail($email);
// return $email;
// }
// } elseif($domain == "googlemail.com") {
// if(str_contains($username, "+")){
// $check_username = explode("+", $username)[0];
// $post_username = explode("+", $username)[1];
//
// if (in_array($check_username, $gmail_usernames)) {
// $email = $username . '@googlemail.com';
// ZEmail::storeEmail($email);
// return $email;
// } else {
//
// $email = $zemail->getRandomGmailUser().'+'.$post_username.'@googlemail.com';
// ZEmail::storeEmail($email);
// return $email;
// }
// }
// elseif(str_contains($username, ".")){
// $check_username = str_replace(".","",$username);
//
// if (in_array($check_username, $gmail_usernames)) {
// $email = $username . '@googlemail.com';
// ZEmail::storeEmail($email);
// return $email;
// } else {
//
// $email = $zemail->generateRandomGmail();
// ZEmail::storeEmail($email);
// return $email;
// }
// }
// else {
// $email = $zemail->getRandomGmailUser().'+'.$username.'@googlemail.com';
// ZEmail::storeEmail($email);
// return $email;
// }
// } else {
// $domains = json_decode(config('app.settings.configuration_settings'))->domains ?? [];
// if (in_array($domain, $domains)) {
// $email = $username . '@' . $domain;
// ZEmail::storeEmail($email);
// return $email;
// } else {
// $email = $username . '@' . $domains[0];
// ZEmail::storeEmail($email);
// return $email;
// }
//
// }
// }

View File

@@ -4,8 +4,10 @@ namespace App\Providers;
use App\Models\Blog; use App\Models\Blog;
use App\Models\Menu; use App\Models\Menu;
use App\Models\Plan;
use DB; use DB;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Laravel\Cashier\Cashier;
class AppServiceProvider extends ServiceProvider class AppServiceProvider extends ServiceProvider
{ {
@@ -33,8 +35,15 @@ class AppServiceProvider extends ServiceProvider
return Blog::where('is_published', 1)->get(); return Blog::where('is_published', 1)->get();
}); });
$plans = cache()->remember('app_plans', now()->addHours(6), function () {
return Plan::all();
});
config(['app.settings' => (array) $settings]); config(['app.settings' => (array) $settings]);
config(['app.menus' => $menus]); config(['app.menus' => $menus]);
config(['app.blogs' => $blogs]); config(['app.blogs' => $blogs]);
config(['app.plans' => $plans]);
Cashier::calculateTaxes();
} }
} }

View File

@@ -24,8 +24,8 @@ class DashPanelProvider extends PanelProvider
{ {
return $panel return $panel
->default() ->default()
->id('dash') ->id('0xdash')
->path('dash') ->path('0xdash')
->login() ->login()
->font('Poppins') ->font('Poppins')
->colors([ ->colors([
@@ -38,8 +38,8 @@ class DashPanelProvider extends PanelProvider
]) ])
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets') ->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
->widgets([ ->widgets([
Widgets\AccountWidget::class, //Widgets\AccountWidget::class,
Widgets\FilamentInfoWidget::class, //Widgets\FilamentInfoWidget::class,
]) ])
->middleware([ ->middleware([
EncryptCookies::class, EncryptCookies::class,

View File

@@ -14,6 +14,9 @@ return Application::configure(basePath: dirname(__DIR__))
$middleware->web(append: [ $middleware->web(append: [
\App\Http\Middleware\Locale::class, \App\Http\Middleware\Locale::class,
]); ]);
$middleware->validateCsrfTokens(except: [
'stripe/*',
]);
}) })
->withExceptions(function (Exceptions $exceptions) { ->withExceptions(function (Exceptions $exceptions) {
// //

24
cleanCron.php Normal file
View File

@@ -0,0 +1,24 @@
<?php
// Adjust this path if your file location is different
require __DIR__ . '/vendor/autoload.php';
// Bootstrap the Laravel application
$app = require_once __DIR__ . '/bootstrap/app.php';
// Make the Console Kernel instance
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
try {
// Run the Artisan command 'ping'
$exitCode = $kernel->call('cleanMail');
// Get the output of the command
$output = $kernel->output();
echo "Artisan command 'schedule:run' executed successfully. Exit code: $exitCode\n";
echo "Output:\n$output";
} catch (\Exception $e) {
echo "Error running Artisan command: " . $e->getMessage();
}

View File

@@ -7,13 +7,15 @@
"license": "MIT", "license": "MIT",
"require": { "require": {
"php": "^8.2", "php": "^8.2",
"ext-imap": "*",
"ddeboer/imap": "^1.14", "ddeboer/imap": "^1.14",
"filament/filament": "3.3", "filament/filament": "3.3",
"laravel/cashier": "^15.6",
"laravel/framework": "^12.0", "laravel/framework": "^12.0",
"laravel/tinker": "^2.10.1", "laravel/tinker": "^2.10.1",
"livewire/flux": "^2.1", "livewire/flux": "^2.1",
"livewire/livewire": "^3.6", "livewire/livewire": "^3.6",
"ext-imap": "*" "propaganistas/laravel-disposable-email": "^2.4"
}, },
"require-dev": { "require-dev": {
"barryvdh/laravel-debugbar": "^3.15", "barryvdh/laravel-debugbar": "^3.15",

411
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "b4dcaf149eb8c0291c1de5ccd42c8f94", "content-hash": "972e884837f3870524619dc37aa08d0f",
"packages": [ "packages": [
{ {
"name": "anourvalar/eloquent-serialize", "name": "anourvalar/eloquent-serialize",
@@ -2100,6 +2100,94 @@
}, },
"time": "2025-04-01T14:41:56+00:00" "time": "2025-04-01T14:41:56+00:00"
}, },
{
"name": "laravel/cashier",
"version": "v15.6.4",
"source": {
"type": "git",
"url": "https://github.com/laravel/cashier-stripe.git",
"reference": "8fe60cc71161ef06b6a1b23cffe886abf2a49b29"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/cashier-stripe/zipball/8fe60cc71161ef06b6a1b23cffe886abf2a49b29",
"reference": "8fe60cc71161ef06b6a1b23cffe886abf2a49b29",
"shasum": ""
},
"require": {
"ext-json": "*",
"illuminate/console": "^10.0|^11.0|^12.0",
"illuminate/contracts": "^10.0|^11.0|^12.0",
"illuminate/database": "^10.0|^11.0|^12.0",
"illuminate/http": "^10.0|^11.0|^12.0",
"illuminate/log": "^10.0|^11.0|^12.0",
"illuminate/notifications": "^10.0|^11.0|^12.0",
"illuminate/pagination": "^10.0|^11.0|^12.0",
"illuminate/routing": "^10.0|^11.0|^12.0",
"illuminate/support": "^10.0|^11.0|^12.0",
"illuminate/view": "^10.0|^11.0|^12.0",
"moneyphp/money": "^4.0",
"nesbot/carbon": "^2.0|^3.0",
"php": "^8.1",
"stripe/stripe-php": "^16.2",
"symfony/console": "^6.0|^7.0",
"symfony/http-kernel": "^6.0|^7.0",
"symfony/polyfill-intl-icu": "^1.22.1"
},
"require-dev": {
"dompdf/dompdf": "^2.0",
"mockery/mockery": "^1.0",
"orchestra/testbench": "^8.18|^9.0|^10.0",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^10.4|^11.5"
},
"suggest": {
"dompdf/dompdf": "Required when generating and downloading invoice PDF's using Dompdf (^1.0.1|^2.0).",
"ext-intl": "Allows for more locales besides the default \"en\" when formatting money values."
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Laravel\\Cashier\\CashierServiceProvider"
]
},
"branch-alias": {
"dev-master": "15.x-dev"
}
},
"autoload": {
"psr-4": {
"Laravel\\Cashier\\": "src/",
"Laravel\\Cashier\\Database\\Factories\\": "database/factories/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
},
{
"name": "Dries Vints",
"email": "dries@laravel.com"
}
],
"description": "Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.",
"keywords": [
"billing",
"laravel",
"stripe"
],
"support": {
"issues": "https://github.com/laravel/cashier/issues",
"source": "https://github.com/laravel/cashier"
},
"time": "2025-04-22T13:59:36+00:00"
},
{ {
"name": "laravel/framework", "name": "laravel/framework",
"version": "v12.10.2", "version": "v12.10.2",
@@ -2503,16 +2591,16 @@
}, },
{ {
"name": "league/commonmark", "name": "league/commonmark",
"version": "2.6.2", "version": "2.7.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/thephpleague/commonmark.git", "url": "https://github.com/thephpleague/commonmark.git",
"reference": "06c3b0bf2540338094575612f4a1778d0d2d5e94" "reference": "6fbb36d44824ed4091adbcf4c7d4a3923cdb3405"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/06c3b0bf2540338094575612f4a1778d0d2d5e94", "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/6fbb36d44824ed4091adbcf4c7d4a3923cdb3405",
"reference": "06c3b0bf2540338094575612f4a1778d0d2d5e94", "reference": "6fbb36d44824ed4091adbcf4c7d4a3923cdb3405",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2549,7 +2637,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "2.7-dev" "dev-main": "2.8-dev"
} }
}, },
"autoload": { "autoload": {
@@ -2606,7 +2694,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-04-18T21:09:27+00:00" "time": "2025-05-05T12:20:28+00:00"
}, },
{ {
"name": "league/config", "name": "league/config",
@@ -3349,6 +3437,96 @@
}, },
"time": "2024-03-31T07:05:07+00:00" "time": "2024-03-31T07:05:07+00:00"
}, },
{
"name": "moneyphp/money",
"version": "v4.7.0",
"source": {
"type": "git",
"url": "https://github.com/moneyphp/money.git",
"reference": "af048f0206d3b39b8fad9de6a230cedf765365fa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/moneyphp/money/zipball/af048f0206d3b39b8fad9de6a230cedf765365fa",
"reference": "af048f0206d3b39b8fad9de6a230cedf765365fa",
"shasum": ""
},
"require": {
"ext-bcmath": "*",
"ext-filter": "*",
"ext-json": "*",
"php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"require-dev": {
"cache/taggable-cache": "^1.1.0",
"doctrine/coding-standard": "^12.0",
"doctrine/instantiator": "^1.5.0 || ^2.0",
"ext-gmp": "*",
"ext-intl": "*",
"florianv/exchanger": "^2.8.1",
"florianv/swap": "^4.3.0",
"moneyphp/crypto-currencies": "^1.1.0",
"moneyphp/iso-currencies": "^3.4",
"php-http/message": "^1.16.0",
"php-http/mock-client": "^1.6.0",
"phpbench/phpbench": "^1.2.5",
"phpstan/extension-installer": "^1.4",
"phpstan/phpstan": "^2.1.9",
"phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^10.5.9",
"psr/cache": "^1.0.1 || ^2.0 || ^3.0",
"ticketswap/phpstan-error-formatter": "^1.1"
},
"suggest": {
"ext-gmp": "Calculate without integer limits",
"ext-intl": "Format Money objects with intl",
"florianv/exchanger": "Exchange rates library for PHP",
"florianv/swap": "Exchange rates library for PHP",
"psr/cache-implementation": "Used for Currency caching"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Money\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mathias Verraes",
"email": "mathias@verraes.net",
"homepage": "http://verraes.net"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com"
},
{
"name": "Frederik Bosch",
"email": "f.bosch@genkgo.nl"
}
],
"description": "PHP implementation of Fowler's Money pattern",
"homepage": "http://moneyphp.org",
"keywords": [
"Value Object",
"money",
"vo"
],
"support": {
"issues": "https://github.com/moneyphp/money/issues",
"source": "https://github.com/moneyphp/money/tree/v4.7.0"
},
"time": "2025-04-03T08:26:36+00:00"
},
{ {
"name": "monolog/monolog", "name": "monolog/monolog",
"version": "3.9.0", "version": "3.9.0",
@@ -4019,6 +4197,82 @@
], ],
"time": "2024-07-20T21:41:07+00:00" "time": "2024-07-20T21:41:07+00:00"
}, },
{
"name": "propaganistas/laravel-disposable-email",
"version": "2.4.14",
"source": {
"type": "git",
"url": "https://github.com/Propaganistas/Laravel-Disposable-Email.git",
"reference": "a1d50a51cb8ec13596a477e2a1bf35f47fa6b88d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Propaganistas/Laravel-Disposable-Email/zipball/a1d50a51cb8ec13596a477e2a1bf35f47fa6b88d",
"reference": "a1d50a51cb8ec13596a477e2a1bf35f47fa6b88d",
"shasum": ""
},
"require": {
"ext-json": "*",
"illuminate/cache": "^10.0|^11.0|^12.0",
"illuminate/config": "^10.0|^11.0|^12.0",
"illuminate/console": "^10.0|^11.0|^12.0",
"illuminate/contracts": "^10.0|^11.0|^12.0",
"illuminate/support": "^10.0|^11.0|^12.0",
"illuminate/validation": "^10.0|^11.0|^12.0",
"php": "^8.1"
},
"require-dev": {
"laravel/pint": "^1.14",
"mockery/mockery": "^1.4.2",
"orchestra/testbench": "*",
"phpunit/phpunit": "^10.5|^11.5.3"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Propaganistas\\LaravelDisposableEmail\\DisposableEmailServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Propaganistas\\LaravelDisposableEmail\\": "src/",
"Propaganistas\\LaravelDisposableEmail\\Tests\\": "tests/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Propaganistas",
"email": "Propaganistas@users.noreply.github.com"
}
],
"description": "Disposable email validator",
"keywords": [
"disposable",
"email",
"laravel",
"mail",
"temporary",
"throwaway",
"validator"
],
"support": {
"issues": "https://github.com/Propaganistas/Laravel-Disposable-Email/issues",
"source": "https://github.com/Propaganistas/Laravel-Disposable-Email/tree/2.4.14"
},
"funding": [
{
"url": "https://github.com/Propaganistas",
"type": "github"
}
],
"time": "2025-05-01T00:56:00+00:00"
},
{ {
"name": "psr/cache", "name": "psr/cache",
"version": "3.0.0", "version": "3.0.0",
@@ -5028,6 +5282,65 @@
], ],
"time": "2025-04-11T15:27:14+00:00" "time": "2025-04-11T15:27:14+00:00"
}, },
{
"name": "stripe/stripe-php",
"version": "v16.6.0",
"source": {
"type": "git",
"url": "https://github.com/stripe/stripe-php.git",
"reference": "d6de0a536f00b5c5c74f36b8f4d0d93b035499ff"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/d6de0a536f00b5c5c74f36b8f4d0d93b035499ff",
"reference": "d6de0a536f00b5c5c74f36b8f4d0d93b035499ff",
"shasum": ""
},
"require": {
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"php": ">=5.6.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "3.5.0",
"phpstan/phpstan": "^1.2",
"phpunit/phpunit": "^5.7 || ^9.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
}
},
"autoload": {
"psr-4": {
"Stripe\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Stripe and contributors",
"homepage": "https://github.com/stripe/stripe-php/contributors"
}
],
"description": "Stripe PHP Library",
"homepage": "https://stripe.com/",
"keywords": [
"api",
"payment processing",
"stripe"
],
"support": {
"issues": "https://github.com/stripe/stripe-php/issues",
"source": "https://github.com/stripe/stripe-php/tree/v16.6.0"
},
"time": "2025-02-24T22:35:29+00:00"
},
{ {
"name": "symfony/clock", "name": "symfony/clock",
"version": "v7.2.0", "version": "v7.2.0",
@@ -6204,6 +6517,90 @@
], ],
"time": "2024-09-09T11:45:10+00:00" "time": "2024-09-09T11:45:10+00:00"
}, },
{
"name": "symfony/polyfill-intl-icu",
"version": "v1.32.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-icu.git",
"reference": "763d2a91fea5681509ca01acbc1c5e450d127811"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/763d2a91fea5681509ca01acbc1c5e450d127811",
"reference": "763d2a91fea5681509ca01acbc1c5e450d127811",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"suggest": {
"ext-intl": "For best performance and support of other locales than \"en\""
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Intl\\Icu\\": ""
},
"classmap": [
"Resources/stubs"
],
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's ICU-related data and classes",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"icu",
"intl",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.32.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-12-21T18:38:29+00:00"
},
{ {
"name": "symfony/polyfill-intl-idn", "name": "symfony/polyfill-intl-idn",
"version": "v1.31.0", "version": "v1.31.0",

127
config/cashier.php Normal file
View File

@@ -0,0 +1,127 @@
<?php
use Laravel\Cashier\Console\WebhookCommand;
use Laravel\Cashier\Invoices\DompdfInvoiceRenderer;
return [
/*
|--------------------------------------------------------------------------
| Stripe Keys
|--------------------------------------------------------------------------
|
| The Stripe publishable key and secret key give you access to Stripe's
| API. The "publishable" key is typically used when interacting with
| Stripe.js while the "secret" key accesses private API endpoints.
|
*/
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
/*
|--------------------------------------------------------------------------
| Cashier Path
|--------------------------------------------------------------------------
|
| This is the base URI path where Cashier's views, such as the payment
| verification screen, will be available from. You're free to tweak
| this path according to your preferences and application design.
|
*/
'path' => env('CASHIER_PATH', 'stripe'),
/*
|--------------------------------------------------------------------------
| Stripe Webhooks
|--------------------------------------------------------------------------
|
| Your Stripe webhook secret is used to prevent unauthorized requests to
| your Stripe webhook handling controllers. The tolerance setting will
| check the drift between the current time and the signed request's.
|
*/
'webhook' => [
'secret' => env('STRIPE_WEBHOOK_SECRET'),
'tolerance' => env('STRIPE_WEBHOOK_TOLERANCE', 300),
'events' => WebhookCommand::DEFAULT_EVENTS,
],
/*
|--------------------------------------------------------------------------
| Currency
|--------------------------------------------------------------------------
|
| This is the default currency that will be used when generating charges
| from your application. Of course, you are welcome to use any of the
| various world currencies that are currently supported via Stripe.
|
*/
'currency' => env('CASHIER_CURRENCY', 'usd'),
/*
|--------------------------------------------------------------------------
| Currency Locale
|--------------------------------------------------------------------------
|
| This is the default locale in which your money values are formatted in
| for display. To utilize other locales besides the default en locale
| verify you have the "intl" PHP extension installed on the system.
|
*/
'currency_locale' => env('CASHIER_CURRENCY_LOCALE', 'en'),
/*
|--------------------------------------------------------------------------
| Payment Confirmation Notification
|--------------------------------------------------------------------------
|
| If this setting is enabled, Cashier will automatically notify customers
| whose payments require additional verification. You should listen to
| Stripe's webhooks in order for this feature to function correctly.
|
*/
'payment_notification' => env('CASHIER_PAYMENT_NOTIFICATION'),
/*
|--------------------------------------------------------------------------
| Invoice Settings
|--------------------------------------------------------------------------
|
| The following options determine how Cashier invoices are converted from
| HTML into PDFs. You're free to change the options based on the needs
| of your application or your preferences regarding invoice styling.
|
*/
'invoices' => [
'renderer' => env('CASHIER_INVOICE_RENDERER', DompdfInvoiceRenderer::class),
'options' => [
// Supported: 'letter', 'legal', 'A4'
'paper' => env('CASHIER_PAPER', 'letter'),
'remote_enabled' => env('CASHIER_REMOTE_ENABLED', false),
],
],
/*
|--------------------------------------------------------------------------
| Stripe Logger
|--------------------------------------------------------------------------
|
| This setting defines which logging channel will be used by the Stripe
| library to write log messages. You are free to specify any of your
| logging channels listed inside the "logging" configuration file.
|
*/
'logger' => env('CASHIER_LOGGER'),
];

100
config/disposable-email.php Normal file
View File

@@ -0,0 +1,100 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| JSON Source URLs
|--------------------------------------------------------------------------
|
| The source URLs yielding a list of disposable email domains. Change these
| to whatever source you like. Just make sure they all return a JSON array.
|
| A sensible default is provided using jsDelivr's services. jsDelivr is
| a free service, so there are no uptime or support guarantees.
|
*/
'sources' => [
'https://cdn.jsdelivr.net/gh/disposable/disposable-email-domains@master/domains.json',
],
/*
|--------------------------------------------------------------------------
| Fetch class
|--------------------------------------------------------------------------
|
| The class responsible for fetching the contents of the source url.
| The default implementation makes use of file_get_contents and
| json_decode and will probably suffice for most applications.
|
| If your application has different needs (e.g. behind a proxy) then you
| can define a custom fetch class here that carries out the fetching.
| Your custom class should implement the Fetcher contract.
|
*/
'fetcher' => \Propaganistas\LaravelDisposableEmail\Fetcher\DefaultFetcher::class,
/*
|--------------------------------------------------------------------------
| Storage Path
|--------------------------------------------------------------------------
|
| The location where the retrieved domains list should be stored locally.
| The path should be accessible and writable by the web server. A good
| place for storing the list is in the framework's own storage path.
|
*/
'storage' => storage_path('framework/disposable_domains.json'),
/*
|--------------------------------------------------------------------------
| Whitelist Configuration
|--------------------------------------------------------------------------
|
| Here you may define a list of whitelist domains that should be allowed.
| These domains will be removed from the list of disposable domains.
|
| Insert as "mydomain.com", without the @ symbol.
|
*/
'whitelist' => [],
/*
|--------------------------------------------------------------------------
| Include Subdomains
|--------------------------------------------------------------------------
|
| Determines whether subdomains should be validated based on the disposability
| status of their parent domains. Enabling this will treat any subdomain of
| a disposable domain as disposable too (e.g., 'temp.abc.com' if 'abc.com'
| is disposable).
|
*/
'include_subdomains' => false,
/*
|--------------------------------------------------------------------------
| Cache Configuration
|--------------------------------------------------------------------------
|
| Here you may define whether the disposable domains list should be cached.
| If you disable caching or when the cache is empty, the list will be
| fetched from local storage instead.
|
| You can optionally specify an alternate cache connection or modify the
| cache key as desired.
|
*/
'cache' => [
'enabled' => true,
'store' => 'default',
'key' => 'disposable_email:domains',
],
];

View File

@@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('stripe_id')->nullable()->index();
$table->string('pm_type')->nullable();
$table->string('pm_last_four', 4)->nullable();
$table->timestamp('trial_ends_at')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropIndex([
'stripe_id',
]);
$table->dropColumn([
'stripe_id',
'pm_type',
'pm_last_four',
'trial_ends_at',
]);
});
}
};

View File

@@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('subscriptions', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id');
$table->string('type');
$table->string('stripe_id')->unique();
$table->string('stripe_status');
$table->string('stripe_price')->nullable();
$table->integer('quantity')->nullable();
$table->timestamp('trial_ends_at')->nullable();
$table->timestamp('ends_at')->nullable();
$table->timestamps();
$table->index(['user_id', 'stripe_status']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('subscriptions');
}
};

View File

@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('subscription_items', function (Blueprint $table) {
$table->id();
$table->foreignId('subscription_id');
$table->string('stripe_id')->unique();
$table->string('stripe_product');
$table->string('stripe_price');
$table->integer('quantity')->nullable();
$table->timestamps();
$table->index(['subscription_id', 'stripe_price']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('subscription_items');
}
};

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('plans', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('description')->nullable();
$table->string('product_id')->collation('utf8_bin');
$table->string('pricing_id')->collation('utf8_bin');
$table->integer('price');
$table->integer('mailbox_limit')->default(15);
$table->boolean('monthly_billing')->default(true);
$table->longText('details')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('plans');
}
};

View File

@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('usage_logs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id');
$table->ipAddress('ip_address');
$table->integer('emails_created_count')->default(0);
$table->integer('emails_received_count')->default(0);
$table->json('emails_created_history')->nullable();
$table->json('emails_received_history')->nullable();
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('usage_logs');
}
};

View File

@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('logs', function (Blueprint $table) {
$table->unsignedBigInteger('user_id')->nullable()->after('id');
$table->foreign('user_id')->references('id')->on('users');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('logs', function (Blueprint $table) {
$table->dropForeign(['user_id']);
$table->dropColumn('user_id');
});
}
};

View File

@@ -0,0 +1,47 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('premium_emails', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->string('message_id')->unique()->index();
$table->string('subject')->nullable();
$table->string('from_name')->nullable();
$table->string('from_email');
$table->text('to');
$table->text('cc')->nullable();
$table->text('bcc')->nullable();
$table->dateTime('timestamp')->nullable();
$table->longText('body_text')->nullable();
$table->longText('body_html')->nullable();
$table->boolean('is_seen')->default(false);
$table->boolean('is_flagged')->default(false);
$table->unsignedBigInteger('size')->nullable();
$table->string('mailbox')->default('INBOX');
$table->longText('raw_headers')->nullable();
$table->longText('raw_body')->nullable();
$table->json('attachments')->nullable();
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('premium_emails');
}
};

41
dropattach.php Normal file
View File

@@ -0,0 +1,41 @@
<?php
function deleteFiles($dir) {
// Open the directory
if ($handle = opendir($dir)) {
// Loop through each file in the directory
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
$filePath = $dir . DIRECTORY_SEPARATOR . $file;
// If it's a directory, recursively call deleteFiles
if (is_dir($filePath)) {
deleteFiles($filePath);
} else {
// It's a file, attempt to delete it
if (unlink($filePath)) {
echo "File '$file' deleted successfully.<br>";
} else {
echo "Error deleting file '$file'.<br>";
}
}
}
}
// Close the directory handle
closedir($handle);
// Attempt to remove the directory itself
if (rmdir($dir)) {
echo "Directory '$dir' deleted successfully.<br>";
} else {
echo "Error deleting directory '$dir'.<br>";
}
}
}
$folderPath = '/home/u146541135/domains/zemail.me/public_html/public/tmp/attachments/';
// Ensure the folder path exists and is a directory
if (is_dir($folderPath)) {
deleteFiles($folderPath);
} else {
echo "Folder '$folderPath' does not exist or is not a directory.";
}
?>

41
dropattachP.php Normal file
View File

@@ -0,0 +1,41 @@
<?php
function deleteFiles($dir) {
// Open the directory
if ($handle = opendir($dir)) {
// Loop through each file in the directory
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
$filePath = $dir . DIRECTORY_SEPARATOR . $file;
// If it's a directory, recursively call deleteFiles
if (is_dir($filePath)) {
deleteFiles($filePath);
} else {
// It's a file, attempt to delete it
if (unlink($filePath)) {
echo "File '$file' deleted successfully.<br>";
} else {
echo "Error deleting file '$file'.<br>";
}
}
}
}
// Close the directory handle
closedir($handle);
// Attempt to remove the directory itself
if (rmdir($dir)) {
echo "Directory '$dir' deleted successfully.<br>";
} else {
echo "Error deleting directory '$dir'.<br>";
}
}
}
$folderPath = '/home/u146541135/domains/zemail.me/public_html/public/tmp/premium/attachments/';
// Ensure the folder path exists and is a directory
if (is_dir($folderPath)) {
deleteFiles($folderPath);
} else {
echo "Folder '$folderPath' does not exist or is not a directory.";
}
?>

80
dropmail.php Normal file
View File

@@ -0,0 +1,80 @@
<?php
// 1. Bootstrap Laravel
require __DIR__ . '/vendor/autoload.php';
$app = require_once __DIR__ . '/bootstrap/app.php';
// 2. Start Laravel container
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$kernel->bootstrap();
set_time_limit(0);
$newTimezone = 'Europe/London';
date_default_timezone_set($newTimezone);
$imapDB = json_decode(config('app.settings.imap_settings'), true);
// Mailbox credentials
$hostname = '{'.$imapDB['host'].':'.$imapDB['port'].'/ssl}INBOX';
$username = $imapDB['username'];
$password = $imapDB['password'];
// Connect to mailbox
$inbox = imap_open($hostname, $username, $password);
// Check for connection errors
if (!$inbox) {
die('Could not connect to mailbox: ' . imap_last_error());
}
// Get current time in Unix timestamp
$current_time = time();
// Search for messages older than one day
//$search_criteria = 'BEFORE "' . date('d-M-Y', strtotime('-3 hours', $current_time)) . '"';
//$messages = imap_search($inbox, $search_criteria);
$messages = imap_search($inbox, 'ALL');
$batch_size = 10;
$deleted_count = 0;
//if ($messages) {
// $chunks = array_chunk($messages, $batch_size);
// foreach ($chunks as $chunk) {
// foreach ($chunk as $message_number) {
// imap_delete($inbox, $message_number);
// }
// imap_expunge($inbox);
// $deleted_count += count($chunk);
// }
// echo $deleted_count . ' messages older than specified time have been deleted.';
//} else {
// echo 'No messages older than specified time found in mailbox.';
//}
if ($messages) {
$chunks = array_chunk($messages, $batch_size);
foreach ($chunks as $chunk) {
foreach ($chunk as $message_number) {
// Get message header to fetch internal date
$header = imap_headerinfo($inbox, $message_number);
$date_str = $header->date;
$msg_time = strtotime($date_str);
// Check if message is older than 3 hours
if ($msg_time !== false && ($current_time - $msg_time) > 2 * 3600) {
imap_delete($inbox, $message_number);
$deleted_count++;
}
}
imap_expunge($inbox);
}
echo $deleted_count . ' messages older than 2 hours have been deleted.';
} else {
echo 'No messages found in mailbox.';
}
// Close mailbox connection
imap_close($inbox);

57
dropmailP.php Normal file
View File

@@ -0,0 +1,57 @@
<?php
// 1. Bootstrap Laravel
require __DIR__ . '/vendor/autoload.php';
$app = require_once __DIR__ . '/bootstrap/app.php';
// 2. Start Laravel container
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$kernel->bootstrap();
set_time_limit(0);
$newTimezone = 'Europe/London';
date_default_timezone_set($newTimezone);
$imapDB = json_decode(config('app.settings.imap_settings'), true);
// Mailbox credentials
$hostname = '{'.$imapDB['premium_host'].':'.$imapDB['premium_port'].'/ssl}INBOX';
$username = $imapDB['premium_username'];
$password = $imapDB['premium_password'];
// Connect to mailbox
$inbox = imap_open($hostname, $username, $password);
// Check for connection errors
if (!$inbox) {
die('Could not connect to mailbox: ' . imap_last_error());
}
// Get current time in Unix timestamp
$current_time = time();
// Search for messages older than one day
$search_criteria = 'BEFORE "' . date('d-M-Y', strtotime('-3 hours', $current_time)) . '"';
$messages = imap_search($inbox, $search_criteria);
$batch_size = 10;
$deleted_count = 0;
if ($messages) {
$chunks = array_chunk($messages, $batch_size);
foreach ($chunks as $chunk) {
foreach ($chunk as $message_number) {
imap_delete($inbox, $message_number);
}
imap_expunge($inbox);
$deleted_count += count($chunk);
}
echo $deleted_count . ' messages older than specified time have been deleted.';
} else {
echo 'No messages older than specified time found in mailbox.';
}
// Close mailbox connection
imap_close($inbox);

View File

@@ -11,12 +11,12 @@ $kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
try { try {
// Run the Artisan command 'ping' // Run the Artisan command 'ping'
$exitCode = $kernel->call('ping'); $exitCode = $kernel->call('schedule:run');
// Get the output of the command // Get the output of the command
$output = $kernel->output(); $output = $kernel->output();
echo "Artisan command 'ping' executed successfully. Exit code: $exitCode\n"; echo "Artisan command 'schedule:run' executed successfully. Exit code: $exitCode\n";
echo "Output:\n$output"; echo "Output:\n$output";
} catch (\Exception $e) { } catch (\Exception $e) {

24
indisposableCron.php Normal file
View File

@@ -0,0 +1,24 @@
<?php
// Adjust this path if your file location is different
require __DIR__ . '/vendor/autoload.php';
// Bootstrap the Laravel application
$app = require_once __DIR__ . '/bootstrap/app.php';
// Make the Console Kernel instance
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
try {
// Run the Artisan command 'ping'
$exitCode = $kernel->call('disposable:update');
// Get the output of the command
$output = $kernel->output();
echo "Artisan command 'schedule:run' executed successfully. Exit code: $exitCode\n";
echo "Output:\n$output";
} catch (\Exception $e) {
echo "Error running Artisan command: " . $e->getMessage();
}

198
public/addOnFAQs.json Normal file
View File

@@ -0,0 +1,198 @@
{
"disposable-email": [
{
"title": "What is a disposable email?",
"content": "A disposable email is a temporary email address that can be used for short-term purposes like sign-ups, testing, or avoiding spam. It automatically expires after a set time."
},
{
"title": "Why should I use a disposable email address?",
"content": "Using a disposable email helps protect your personal inbox from spam, phishing, or unwanted marketing emails. Its a privacy-first solution."
},
{
"title": "Are disposable email services free?",
"content": "Yes, most disposable email services are free to use. They dont require sign-ups or personal information."
},
{
"title": "Can I send emails from a disposable address?",
"content": "Some services allow sending emails, but most focus on receiving emails only to reduce spam and abuse."
},
{
"title": "How long does a disposable email address last?",
"content": "Typically, disposable email addresses last from a few minutes to 24 hours, depending on the service provider."
},
{
"title": "Is it legal to use disposable email addresses?",
"content": "Yes, it is legal to use disposable emails for legitimate purposes like testing or protecting privacy. It's not intended for fraudulent use."
}
],
"disposable-gmail": [
{
"title": "What is a disposable Gmail email?",
"content": "A disposable Gmail email is a temporary Gmail address that lets you receive messages without needing to log in to Gmail or sign up."
},
{
"title": "Can I use a disposable Gmail for sign-ups?",
"content": "Yes, you can use it for sign-ups, app trials, or websites that require email verification but you don't want to share your real Gmail."
},
{
"title": "How do disposable Gmail addresses work?",
"content": "They simulate Gmail inboxes through public domain inboxes. You receive emails in a shared inbox thats cleared frequently."
},
{
"title": "Do I need a Google account for disposable Gmail?",
"content": "No, you dont need a Google account. These addresses are managed by third-party services that mimic Gmail inboxes."
},
{
"title": "Is using a temporary Gmail safe?",
"content": "Yes, as long as you use it for legal and ethical reasons. The inbox is public, so avoid receiving sensitive data."
}
],
"disposable-outlook": [
{
"title": "What is a disposable Outlook email?",
"content": "A disposable Outlook email provides a temporary email address that mimics an Outlook.com address without needing to create a Microsoft account."
},
{
"title": "Can I get a free temporary Outlook inbox?",
"content": "Yes, several services offer free access to Outlook-like temporary inboxes for quick sign-ups and verification emails."
},
{
"title": "Is disposable Outlook email secure?",
"content": "These inboxes are typically public and auto-deleted. They are secure for one-time use, but not for private communication."
},
{
"title": "Where can I use a disposable Outlook email?",
"content": "Use it for registering on forums, testing apps, or any place where you dont want to use your real Outlook address."
},
{
"title": "Can I receive attachments in a temporary Outlook email?",
"content": "Some disposable Outlook services support attachments, but many limit functionality for safety and storage reasons."
}
],
"disposable-yahoo": [
{
"title": "What is a disposable Yahoo email?",
"content": "A disposable Yahoo email is a temporary email address formatted like a Yahoo inbox, used for short-term purposes."
},
{
"title": "Is there a free way to get a Yahoo-style temporary email?",
"content": "Yes, you can use services that provide Yahoo-like disposable email addresses for receiving emails online."
},
{
"title": "Why use a disposable Yahoo email?",
"content": "It helps you avoid giving your personal Yahoo email to websites that may spam or sell your data."
},
{
"title": "Can I use a disposable Yahoo email for email verification?",
"content": "Yes, its commonly used to receive verification links for one-time use without exposing your real Yahoo account."
},
{
"title": "Do I need a Yahoo account to use these addresses?",
"content": "No, these are provided by third-party services and dont require a Yahoo login."
}
],
"gmailnator": [
{
"title": "What is Gmailnator?",
"content": "Gmailnator is a free service that gives you a temporary Gmail email address for receiving emails online without signing up."
},
{
"title": "Is Gmailnator safe to use?",
"content": "Yes, Gmailnator provides disposable inboxes that are automatically deleted after use, protecting your identity and email privacy."
},
{
"title": "Can I use Gmailnator for account verification?",
"content": "Yes, many users use Gmailnator to verify accounts, sign up for trials, or test web apps without revealing their personal Gmail address."
},
{
"title": "How long does a Gmailnator inbox last?",
"content": "Inbox lifespan varies, but most Gmailnator inboxes exist for a short duration and are cleaned regularly."
},
{
"title": "Is Gmailnator private?",
"content": "Gmailnator is not fully private as inboxes may be accessible to others. Avoid using it for confidential information."
},
{
"title": "What types of emails can I receive with Gmailnator?",
"content": "You can receive verification emails, sign-up confirmations, and basic transactional emails through Gmailnator."
}
],
"emailnator": [
{
"title": "What is Emailnator?",
"content": "Emailnator is a free disposable email service that lets you generate temporary email addresses for sign-ups, verification codes, or spam control. No registration required."
},
{
"title": "Is Emailnator safe to use?",
"content": "Yes, Emailnator is safe for temporary usage. It keeps your personal email private by offering short-lived, anonymous inboxes."
},
{
"title": "Can I use Emailnator for verification?",
"content": "Absolutely. Many users rely on Emailnator to receive verification emails from websites, apps, and newsletters."
},
{
"title": "How long do Emailnator inboxes last?",
"content": "Emailnator inboxes are active temporarily, usually lasting between 10 minutes and a few hours. They're automatically deleted after that to ensure privacy."
},
{
"title": "Do I need to sign up to use Emailnator?",
"content": "No. Emailnator is completely free and doesn't require any account registration. Just open the site, and your inbox is ready."
},
{
"title": "Does Emailnator support Gmail addresses?",
"content": "Yes, Emailnator uses Gmail domains that work with most sign-up forms, making it more reliable than traditional temp mail tools."
}
],
"temp-gmail": [
{
"title": "What is a temporary Gmail disposable email service?",
"content": "It is a service that generates temporary, throwaway email addresses resembling Gmail addresses, allowing users to receive emails such as sign-up verifications or newsletters without exposing their real email. These addresses are short-lived and automatically deleted after a set time to protect privacy."
},
{
"title": "How does Zemail work?",
"content": "Zemail provides free disposable Gmail email addresses instantly without requiring any registration. Users generate a temporary email address, use it to receive verification or other emails, and the inbox remains active for a limited time before automatic deletion."
},
{
"title": "Is it safe to use temporary email services like Zemail?",
"content": "Yes, these services are generally safe for temporary use as they keep personal emails private by offering short-lived, anonymous inboxes, but they should be used responsibly."
},
{
"title": "How long do temporary inboxes last?",
"content": "Typically, inboxes last from 10 minutes up to a few hours, sometimes longer depending on the service. After that, emails and inboxes are automatically deleted to maintain privacy."
},
{
"title": "Do I need to sign up to use these services?",
"content": "No, most disposable email services, including Zemail, do not require any registration or account creation. Temporary email addresses can be generated and used instantly."
},
{
"title": "Can I use these temporary emails to receive verification codes?",
"content": "Yes, they are widely used to receive verification emails from websites, apps, and newsletters, especially to avoid spam or protect your main email."
},
{
"title": "Do these services support sending emails?",
"content": "Most disposable email services primarily support receiving emails only. Some services allow sending emails, but many restrict outgoing mail to prevent abuse."
},
{
"title": "Are Gmail domains better than traditional disposable email domains?",
"content": "Gmail domains tend to bypass filters on websites that block typical disposable email domains, making them more reliable for sign-ups."
},
{
"title": "What are common use cases for temporary email addresses?",
"content": "Use cases include protecting your real inbox from spam, signing up for trials or newsletters without giving your real email, testing email workflows for developers, receiving one-time verification codes, and avoiding long-term tracking."
},
{
"title": "Can temporary emails be traced back to me?",
"content": "While temporary emails provide anonymity, service providers may keep logs and your IP or metadata might be traceable depending on access methods. Additional privacy tools like VPNs are recommended for full anonymity."
},
{
"title": "Are there premium versions of these services?",
"content": "Yes, some services like Zemail offer premium plans with features such as longer inbox duration, multiple inboxes, and real Gmail addresses for enhanced reliability and spam control."
}
]
}

BIN
public/images/crown.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

132
public/sitemap.xml Normal file
View File

@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<!-- created with Free Online Sitemap Generator www.xml-sitemaps.com -->
<url>
<loc>https://zemail.me/</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>1.00</priority>
</url>
<url>
<loc>https://zemail.me/faq</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://zemail.me/blog</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://zemail.me/cookies-policy</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://zemail.me/privacy-policy</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://zemail.me/terms-and-conditions</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://zemail.me/refund-policy</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://zemail.me/login</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://zemail.me/blog/creating-unlimited-disposable-gmail-addresses</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://zemail.me/blog/email-privacy-and-security</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://zemail.me/blog/email-signature</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://zemail.me/blog/email-marketing</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://zemail.me/blog/prevent-spam-and-protect-your-privacy-with-temporary-email-generators</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://zemail.me/blog/the-forever-free-disposable-email-service</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://zemail.me/blog/best-services-for-temporary-gmail-accounts</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.64</priority>
</url>
<url>
<loc>https://zemail.me/forgot-password</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.64</priority>
</url>
<url>
<loc>https://zemail.me/register</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.64</priority>
</url>
<url>
<loc>https://zemail.me/disposable-gmail</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://zemail.me/disposable-email</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://zemail.me/disposable-outlook</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://zemail.me/disposable-yahoo</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://zemail.me/emailnator</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://zemail.me/gmailnator</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://zemail.me/temp-gmail</loc>
<lastmod>2025-05-13T19:24:37+00:00</lastmod>
<priority>0.51</priority>
</url>
</urlset>

View File

@@ -8,6 +8,14 @@
background-color: #f72a25; background-color: #f72a25;
} }
.app-primary-bg {
background-color: #F14743;
}
.app-primary {
color: #F14743;
}
.btn-primary { .btn-primary {
color: white; color: white;
background-color: #4361ee; background-color: #4361ee;
@@ -28,6 +36,30 @@
background-color: #00AB55; background-color: #00AB55;
} }
.btn-outlook {
background-color: #0078D4; /* Outlook blue */
color: white;
border: 2px solid #0078D4;
transition: background-color 0.3s ease;
}
.btn-outlook:hover {
background-color: #005a9e; /* Darker blue on hover */
border-color: #005a9e;
}
.btn-gmail {
color: white;
background-color: #D93025; /* Gmail red */
border: 2px solid #D93025;
transition: background-color 0.3s ease;
}
.btn-gmail:hover {
background-color: #A52714; /* Darker red on hover */
border-color: #A52714;
}
.iframe-min-height { .iframe-min-height {
min-height: 70vh; min-height: 70vh;
} }
@@ -65,3 +97,92 @@
.magic-box { .magic-box {
scrollbar-width: none; scrollbar-width: none;
} }
.premium-btn {
perspective: 1000px;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 8px 20px;
background: linear-gradient(145deg, #f7b42f, #fdd832);
color: #fff;
font-size: 16px;
font-weight: bold;
text-transform: uppercase;
border: none;
border-radius: 5px;
cursor: pointer;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
overflow: hidden;
transition: transform 0.3s ease;
}
.premium-btn:hover {
transform: scale(1.05);
}
.premium-btn .crown,
.premium-btn .crown-bg1,
.premium-btn .crown-bg2 {
transform-style: preserve-3d;
position: absolute;
background: url('/images/crown.webp') no-repeat center center;
background-size: contain;
animation: rotateCrownY 2s linear infinite;
}
/* Main Crown */
.premium-btn .crown {
top: -20px;
left: 50%;
transform: translateX(-50%);
width: 40px;
height: 40px;
z-index: 3;
}
/* Background Crown 1 */
.premium-btn .crown-bg1 {
top: -10px;
left: 5%;
width: 25px;
height: 25px;
animation-duration: 3s;
opacity: 0.5;
z-index: 1;
}
/* Background Crown 2 */
.premium-btn .crown-bg2 {
bottom: -15px;
right: 15%;
width: 30px;
height: 30px;
animation-duration: 4s;
opacity: 0.4;
z-index: 1;
}
.premium-btn .btn-text {
z-index: 2;
}
@keyframes rotateCrownY {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.bg-pricing-modal {
background-color: #F3F4F6; /* Light mode background */
}
html.dark .bg-pricing-modal {
background-color: #101828; /* Dark mode */
}

View File

@@ -217,3 +217,24 @@ document.addEventListener('DOMContentLoaded', function () {
const fetchInterval = setInterval(fetchStoreEmail, 10000); const fetchInterval = setInterval(fetchStoreEmail, 10000);
}, 3000); }, 3000);
}); });
document.addEventListener('promotePremium', function () {
setTimeout(() => {
const event = new CustomEvent('modal-show', {
detail: {
name: 'premium',
scope: null
}
});
const showText = document.getElementById('premium-modal-limit');
showText.classList.remove('hidden');
const text1 = document.getElementById('focus-modal-text1');
text1.classList.remove('text-accent');
text1.classList.add('text-amber-500');
const text2 = document.getElementById('focus-modal-text2');
text2.classList.remove('text-accent');
text2.classList.add('text-amber-500');
document.dispatchEvent(event);
}, 500);
});

View File

@@ -0,0 +1,14 @@
@props([
'on',
])
<div
x-data="{ shown: false, timeout: null }"
x-init="@this.on('{{ $on }}', () => { clearTimeout(timeout); shown = true; timeout = setTimeout(() => { shown = false }, 2000); })"
x-show.transition.out.opacity.duration.1500ms="shown"
x-transition:leave.opacity.duration.1500ms
style="display: none"
{{ $attributes->merge(['class' => 'text-sm']) }}
>
{{ $slot->isEmpty() ? __('Saved.') : $slot }}
</div>

View File

@@ -0,0 +1 @@
<img src="{{ asset('images/logo.webp') }}" class="scale-150" alt="logo"/>

View File

@@ -0,0 +1,9 @@
@props([
'title',
'description',
])
<div class="flex w-full flex-col text-center">
<flux:heading size="xl">{{ $title }}</flux:heading>
<flux:subheading>{{ $description }}</flux:subheading>
</div>

View File

@@ -0,0 +1,9 @@
@props([
'status',
])
@if ($status)
<div {{ $attributes->merge(['class' => 'font-medium text-sm text-green-600']) }}>
{{ $status }}
</div>
@endif

View File

@@ -45,6 +45,14 @@
{{ __('Refresh') }} {{ __('Refresh') }}
</button> </button>
<flux:modal.trigger name="premium">
<button type="button" class="premium-btn mt-2">
<span class="crown"></span>
<span class="crown-bg1"></span>
<span class="crown-bg2"></span>
<span class="btn-text">Get Premium</span>
</button>
</flux:modal.trigger>
</flux:navlist> </flux:navlist>
<div id="sidebar-magic" class="items-center border-1 dark:border-zinc-800 border-zinc-200 hidden"> <div id="sidebar-magic" class="items-center border-1 dark:border-zinc-800 border-zinc-200 hidden">
<p class="px-6 py-4 text-sm dark:bg-zinc-900 bg-zinc-100 dark:text-white accent-zinc-700">Support us by disabling ad blockеrs on our site 🙏</p> <p class="px-6 py-4 text-sm dark:bg-zinc-900 bg-zinc-100 dark:text-white accent-zinc-700">Support us by disabling ad blockеrs on our site 🙏</p>
@@ -118,6 +126,26 @@
</flux:tooltip> </flux:tooltip>
</flux:modal.trigger> </flux:modal.trigger>
</div> </div>
@auth
<flux:menu.separator />
<button type="button" x-on:click="window.location.href = '{{ route('dashboard') }}'" class="premium-btn mt-2 w-full">
<span class="crown"></span>
<span class="crown-bg1"></span>
<span class="crown-bg2"></span>
<span class="btn-text">Login</span>
</button>
@endauth
@guest
<flux:menu.separator />
<flux:modal.trigger name="premium">
<button type="button" class="premium-btn mt-2 w-full">
<span class="crown"></span>
<span class="crown-bg1"></span>
<span class="crown-bg2"></span>
<span class="btn-text">Get Premium</span>
</button>
</flux:modal.trigger>
@endguest
</flux:menu> </flux:menu>
</flux:dropdown> </flux:dropdown>
</flux:navbar> </flux:navbar>
@@ -135,7 +163,7 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="w-1/2"> <div class="w-1/2">
<livewire:frontend.action action="random" /> <livewire:frontend.action action="outlook" />
</div> </div>
<div class="w-1/2"> <div class="w-1/2">
<livewire:frontend.action action="gmail" /> <livewire:frontend.action action="gmail" />
@@ -162,6 +190,73 @@
</div> </div>
</div> </div>
</flux:modal> </flux:modal>
<flux:modal name="premium" class="min-w-[22rem]" class="bg-pricing-modal">
<div class="space-y-6">
<div>
<div id="premium-modal-limit" class="mt-10 w-full justify-center hidden">
<p class=" font-mono text-amber-500 text-center">
Looks like you have reached the daily email generation limit, consider subscribing and access to premium features
</p>
</div>
<!-- From Uiverse.io by themrsami -->
<div class="w-full pt-5 px-5 pb-4 bg-zinc-100 dark:bg-zinc-900 rounded-3xl">
<div class="text-center mb-6">
<h5 class="text-2xl font-semibold text-gray-500 mb-3">Zemail Premium</h5>
<span class="block text-5xl font-bold text-accent mb-3">${{ config('app.plans')[0]->price ?? 10 }}</span>
<span class="block text-gray-400 font-medium mb-6">per month</span
>
<a class="relative group inline-block w-full py-4 px-6 text-center text-gray-50 hover:text-gray-500 bg-amber-400 font-semibold rounded-full overflow-hidden transition duration-200"
href="{{ route('dashboard') }}">
<div class="absolute top-0 right-full w-full h-full bg-white transform group-hover:translate-x-full group-hover:scale-102 transition duration-500"></div>
<span class="relative">Subscribe now</span>
</a>
</div>
<ul>
<li class="flex mb-4 items-center">
<flux:icon.check />
<span class="ml-2 text-accent wrap-normal">No Ads</span>
</li>
<li class="flex mb-4 items-center">
<flux:icon.check />
<span class="ml-2 text-accent wrap-normal">Dedicated premium domains</span>
</li>
<li class="flex mb-4 items-center">
<flux:icon.check />
<span id="focus-modal-text1" class="ml-2 text-accent wrap-normal">Unlimited email addresses on public domains</span>
</li>
<li class="flex mb-4 items-center">
<flux:icon.check />
<span id="focus-modal-text2" class="ml-2 text-accent wrap-normal">Up to 100 email addresses daily on premium domains</span>
</li>
<li class="flex mb-4 items-center">
<flux:icon.check />
<span class="ml-2 text-accent wrap-normal">Dedicated Premium Gmail Addresses</span>
</li>
<li class="flex mb-4 items-center">
<flux:icon.check />
<span class="ml-2 text-accent wrap-normal">100% Private address with full ownership</span>
</li>
<li class="flex mb-4 items-center">
<flux:icon.check />
<span class="ml-2 text-accent wrap-normal">History Upto 30 Days</span>
</li>
<li class="flex mb-4 items-center">
<flux:icon.check />
<span class="ml-2 text-accent wrap-normal">Enhanced privacy and security</span>
</li>
</ul>
</div>
</div>
<flux:separator text="or" />
<di class="mt-3 flex justify-center">
<p class="text-center ">Already subscribed?</p>&nbsp;<a class="text-amber-400" href="{{ route('dashboard') }}">Login now</a>
</di>
</div>
</flux:modal>
</flux:header> </flux:header>
<flux:main class="dark:bg-gray-900 bg-gray-100"> <flux:main class="dark:bg-gray-900 bg-gray-100">
@@ -212,6 +307,6 @@
}); });
</script> </script>
{!! config('app.settings.app_footer') !!} {!! config('app.settings.app_footer') !!}
@yield('custom_header') @yield('custom_footer')
</body> </body>
</html> </html>

View File

@@ -0,0 +1,3 @@
<x-layouts.auth.simple :title="$title ?? null">
{{ $slot }}
</x-layouts.auth.simple>

View File

@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="dark">
<head>
@include('partials.head')
</head>
<body class="min-h-screen bg-neutral-100 antialiased dark:bg-linear-to-b dark:from-neutral-950 dark:to-neutral-900">
<div class="bg-muted flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
<div class="flex w-full max-w-md flex-col gap-6">
<a href="{{ route('home') }}" class="flex flex-col items-center gap-2 font-medium" wire:navigate>
<span class="flex h-9 w-9 items-center justify-center rounded-md">
<x-app-logo-icon class="size-9 fill-current text-black dark:text-white" />
</span>
<span class="sr-only">{{ config('app.name', 'Laravel') }}</span>
</a>
<div class="flex flex-col gap-6">
<div class="rounded-xl border bg-white dark:bg-stone-950 dark:border-stone-800 text-stone-800 shadow-xs">
<div class="px-10 py-8">{{ $slot }}</div>
</div>
</div>
</div>
</div>
@fluxScripts
</body>
</html>

View File

@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="dark">
<head>
@include('partials.head')
</head>
<body class="min-h-screen bg-white antialiased dark:bg-linear-to-b dark:from-zinc-900 dark:to-zinc-900">
<div class="bg-background flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
<div class="flex w-full max-w-sm flex-col gap-2">
<a href="{{ route('home') }}" class="flex flex-col items-center gap-2 font-medium" wire:navigate>
<span class="flex h-9 w-9 mb-1 items-center justify-center rounded-md">
<x-app-logo-icon class="size-9 fill-current text-accent dark:text-white" />
</span>
<span class="sr-only">{{ config('app.name', 'Laravel') }}</span>
</a>
<div class="flex flex-col gap-6">
{{ $slot }}
</div>
</div>
</div>
@fluxScripts
</body>
</html>

View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="dark">
<head>
@include('partials.head')
</head>
<body class="min-h-screen bg-white antialiased dark:bg-linear-to-b dark:from-neutral-950 dark:to-neutral-900">
<div class="relative grid h-dvh flex-col items-center justify-center px-8 sm:px-0 lg:max-w-none lg:grid-cols-2 lg:px-0">
<div class="bg-muted relative hidden h-full flex-col p-10 text-white lg:flex dark:border-e dark:border-neutral-800">
<div class="absolute inset-0 bg-neutral-900"></div>
<a href="{{ route('home') }}" class="relative z-20 flex items-center text-lg font-medium" wire:navigate>
<span class="flex h-10 w-10 items-center justify-center rounded-md">
<x-app-logo-icon class="me-2 h-7 fill-current text-white" />
</span>
{{ config('app.name', 'Laravel') }}
</a>
@php
[$message, $author] = str(Illuminate\Foundation\Inspiring::quotes()->random())->explode('-');
@endphp
<div class="relative z-20 mt-auto">
<blockquote class="space-y-2">
<flux:heading size="lg">&ldquo;{{ trim($message) }}&rdquo;</flux:heading>
<footer><flux:heading>{{ trim($author) }}</flux:heading></footer>
</blockquote>
</div>
</div>
<div class="w-full lg:p-8">
<div class="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]">
<a href="{{ route('home') }}" class="z-20 flex flex-col items-center gap-2 font-medium lg:hidden" wire:navigate>
<span class="flex h-9 w-9 items-center justify-center rounded-md">
<x-app-logo-icon class="size-9 fill-current text-black dark:text-white" />
</span>
<span class="sr-only">{{ config('app.name', 'Laravel') }}</span>
</a>
{{ $slot }}
</div>
</div>
</div>
@fluxScripts
</body>
</html>

View File

@@ -0,0 +1,178 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@yield('title', config('app.settings.app_title'))</title>
<meta name="description" content="@yield('description', config('app.settings.app_description'))">
<meta name="keywords" content="@yield('keywords', config('app.settings.app_keyword'))">
@forelse (json_decode(config('app.settings.app_meta')) as $key => $value)
@if ($value)
<meta name="{{ $key }}" content="{{ $value }}">
@endif
@empty
@endforelse
@yield('metas')
@if (file_exists(public_path('build/manifest.json')) || file_exists(public_path('hot')))
@vite(['resources/css/app.css', 'resources/css/boil.css', 'resources/js/app.js', 'resources/js/boil.js'])
@endif
<link rel="icon" type="image/png" href="{{ asset('images/logo.webp') }}">
@fluxAppearance
@yield('custom_header')
</head>
<body class="min-h-screen bg-white dark:bg-zinc-800">
<flux:sidebar sticky stashable class="bg-zinc-50 dark:bg-zinc-900 border-r rtl:border-r-0 rtl:border-l border-zinc-200 dark:border-zinc-700">
<flux:sidebar.toggle class="lg:hidden" icon="x-mark" />
<a class="flex items-center px-2 py-2 cursor-pointer" href="{{ route('dashboard') }}">
<img src="{{ asset('images/zemail-logo-light.webp') }}" class="dark:hidden" alt="logo"/>
<img src="{{ asset('images/zemail-logo-dark.webp') }}" class="hidden dark:flex" alt="logo"/>
</a>
<flux:navlist variant="outline">
@php
$navItems = [
['label' => 'Dashboard', 'route' => 'dashboard'],
['label' => 'Generate Premium Email', 'route' => 'dashboard.premium'],
//['label' => '10 Minute Mail', 'route' => 'dashboard.10minute'],
['label' => 'Bulk Email Generator', 'route' => 'dashboard.bulk'],
['label' => 'Bulk Gmail Generator', 'route' => 'dashboard.bulkGmail'],
//['label' => 'Compose Email', 'route' => 'dashboard.compose'],
];
$currentRoute = Route::currentRouteName();
@endphp
@foreach ($navItems as $item)
<button type="button" x-on:click="window.location.href = '{{ route($item['route']) }}'" class="cursor-pointer h-10 lg:h-10 relative flex items-center gap-3 rounded-lg py-0 text-start w-full px-3 my-px text-zinc-500 dark:text-white/80 data-current:text-(--color-accent-content) hover:data-current:text-(--color-accent-content) data-current:bg-white dark:data-current:bg-white/[7%] data-current:border data-current:border-zinc-200 dark:data-current:border-transparent hover:text-zinc-800 dark:hover:text-white dark:hover:bg-white/[7%] hover:bg-zinc-800/5 {{ $currentRoute === $item['route'] ? 'border-1 border-dashed border-zinc-500' : '' }}" data-flux-navlist-item="data-flux-navlist-item">
<div class="flex-1 text-sm font-medium leading-none whitespace-nowrap [[data-nav-footer]_&amp;]:hidden [[data-nav-sidebar]_[data-nav-footer]_&amp;]:block" data-content="">
<div class="-ml-2 pl-2 py-2">
<div class="[:where(&amp;)]:text-sm [:where(&amp;)]:text-zinc-500 [:where(&amp;)]:dark:text-white/70 {{ $currentRoute === $item['route'] ? 'accent-zinc-500' : '' }}" data-flux-text="{{ $item['label'] }}">{{ $item['label'] }}</div>
</div>
</div>
</button>
@endforeach
</flux:navlist>
<flux:spacer />
<flux:navlist variant="outline">
<flux:separator variant="subtle" />
</flux:navlist>
<flux:dropdown position="bottom" align="start">
<flux:profile
:name="auth()->user()->name"
:initials="auth()->user()->initials()"
icon-trailing="chevrons-up-down"
/>
<flux:menu class="w-[220px]">
<flux:menu.radio.group>
<div class="p-0 text-sm font-normal">
<div class="flex items-center gap-2 px-1 py-1.5 text-start text-sm">
<span class="relative flex h-8 w-8 shrink-0 overflow-hidden rounded-lg">
<span
class="flex h-full w-full items-center justify-center rounded-lg bg-neutral-200 text-black dark:bg-neutral-700 dark:text-white"
>
{{ auth()->user()->initials() }}
</span>
</span>
<div class="grid flex-1 text-start text-sm leading-tight">
<span class="truncate font-semibold">{{ auth()->user()->name }}</span>
<span class="truncate text-xs">{{ auth()->user()->email }}</span>
</div>
</div>
</div>
</flux:menu.radio.group>
<flux:menu.separator />
<flux:menu.radio.group>
<flux:menu.item :href="route('settings.profile')" icon="cog" wire:navigate>{{ __('Settings') }}</flux:menu.item>
</flux:menu.radio.group>
<flux:menu.separator />
<form method="POST" action="{{ route('logout') }}" class="w-full">
@csrf
<flux:menu.item as="button" type="submit" icon="arrow-right-start-on-rectangle" class="w-full">
{{ __('Log Out') }}
</flux:menu.item>
</form>
</flux:menu>
</flux:dropdown>
</flux:sidebar>
<flux:header sticky container class="bg-zinc-50 dark:bg-zinc-900 border-b border-zinc-200 dark:border-zinc-700">
<div class="flex items-center w-full gap-4">
<div class="flex items-center gap-2 flex-grow overflow-hidden">
<flux:sidebar.toggle class="lg:hidden" icon="bars-3-bottom-left" inset="left" />
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<flux:navbar>
<flux:dropdown position="top" align="start">
<flux:tooltip content="Language">
<flux:button x-data x-on:click="" icon="language" variant="subtle" aria-label="Language" />
</flux:tooltip>
<flux:menu>
<span>
@foreach(config('app.locales') as $item => $language)
<span x-on:click="window.location.href ='{{ route('locale', $language) }}'" role="menuitem" class="cursor-pointer group dark:hover:bg-zinc-600/75 dark:hover:text-gray-300 dark:focus:bg-zinc-600/75 dark:focus:text-gray-300 flex items-center px-4 py-2 dark:text-gray-400 text-gray-700 hover:text-gray-900 focus:text-gray-900 text-sm leading-5 hover:bg-gray-100 focus:bg-gray-100 focus:outline-none truncate">{{ config('app.locales_text')[$item] }}</span>
@endforeach
</span>
</flux:menu>
</flux:dropdown>
<flux:tooltip content="Switch Theme">
<flux:button x-on:click="$flux.dark = ! $flux.dark" icon="sun" variant="subtle" aria-label="Toggle dark mode">
{{-- <flux:icon.sun x-show="$flux.appearance === 'dark'" variant="mini" />--}}
{{-- <flux:icon.moon x-show="$flux.appearance === 'light'" variant="mini" />--}}
{{-- <flux:icon.computer-desktop x-show="$flux.appearance === 'system'" variant="mini" />--}}
</flux:button>
</flux:tooltip>
</flux:navbar>
</div>
</div>
</flux:header>
<flux:main class="dark:bg-gray-900 bg-gray-100">
{{ $slot }}
</flux:main>
<!-- Toast Container -->
<div id="toast-container" class="fixed top-5 left-1/2 transform -translate-x-1/2 z-50 space-y-4"></div>
<p id="sidebar-magic" class="hidden"></p>
<p class="ionize hidden" id="gR7pT9xLwQ" sync="{{ config('app.auto_fetch_mail') }}">{{ csrf_token() }}</p>
<p class="hidden" id="copyEmailText">{{ __('Email ID Copied to Clipboard') }}</p>
@fluxScripts
<script>
document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('stopLoader', () => {
document.querySelectorAll('#refresh-icon').forEach(el => {
setTimeout(() => {
el.classList.remove('animate-spin');
}, 1000);
});
});
let counter = parseInt({{ json_decode(config('app.settings.configuration_settings'))->fetch_seconds }});
setInterval(() => {
if (counter === 0 && document.getElementById('imap-error') === null && !document.hidden) {
document.querySelectorAll('#refresh-icon').forEach(el => {
el.classList.add('animate-spin');
});
Livewire.dispatch('fetchMessages');
counter = parseInt({{ json_decode(config('app.settings.configuration_settings'))->fetch_seconds }});
}
counter--;
if(document.hidden) {
counter = 1;
}
}, 1000);
});
</script>
@yield('custom_footer')
</body>
</html>

View File

@@ -0,0 +1,12 @@
@props([
'id' => uniqid(),
])
<svg {{ $attributes }} fill="none">
<defs>
<pattern id="pattern-{{ $id }}" x="0" y="0" width="8" height="8" patternUnits="userSpaceOnUse">
<path d="M-1 5L5 -1M3 9L8.5 3.5" stroke-width="0.5"></path>
</pattern>
</defs>
<rect stroke="none" fill="url(#pattern-{{ $id }})" width="100%" height="100%"></rect>
</svg>

View File

@@ -0,0 +1,20 @@
<div class="flex items-start max-md:flex-col">
<div class="me-10 w-full pb-4 md:w-[220px]">
<flux:navlist>
<flux:navlist.item :href="route('settings.profile')" wire:navigate>{{ __('Profile') }}</flux:navlist.item>
<flux:navlist.item :href="route('settings.password')" wire:navigate>{{ __('Password') }}</flux:navlist.item>
<flux:navlist.item :href="route('settings.appearance')" wire:navigate>{{ __('Appearance') }}</flux:navlist.item>
</flux:navlist>
</div>
<flux:separator class="md:hidden" />
<div class="flex-1 self-stretch max-md:pt-6">
<flux:heading>{{ $heading ?? '' }}</flux:heading>
<flux:subheading>{{ $subheading ?? '' }}</flux:subheading>
<div class="mt-5 w-full max-w-lg">
{{ $slot }}
</div>
</div>
</div>

View File

@@ -0,0 +1,42 @@
{{-- Credit: Lucide (https://lucide.dev) --}}
@props([
'variant' => 'outline',
])
@php
if ($variant === 'solid') {
throw new \Exception('The "solid" variant is not supported in Lucide.');
}
$classes = Flux::classes('shrink-0')
->add(match($variant) {
'outline' => '[:where(&)]:size-6',
'solid' => '[:where(&)]:size-6',
'mini' => '[:where(&)]:size-5',
'micro' => '[:where(&)]:size-4',
});
$strokeWidth = match ($variant) {
'outline' => 2,
'mini' => 2.25,
'micro' => 2.5,
};
@endphp
<svg
{{ $attributes->class($classes) }}
data-flux-icon
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="{{ $strokeWidth }}"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
data-slot="icon"
>
<circle cx="12" cy="12" r="4" />
<path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-4 8" />
</svg>

View File

@@ -0,0 +1,42 @@
{{-- Credit: Lucide (https://lucide.dev) --}}
@props([
'variant' => 'outline',
])
@php
if ($variant === 'solid') {
throw new \Exception('The "solid" variant is not supported in Lucide.');
}
$classes = Flux::classes('shrink-0')
->add(match($variant) {
'outline' => '[:where(&)]:size-6',
'solid' => '[:where(&)]:size-6',
'mini' => '[:where(&)]:size-5',
'micro' => '[:where(&)]:size-4',
});
$strokeWidth = match ($variant) {
'outline' => 2,
'mini' => 2.25,
'micro' => 2.5,
};
@endphp
<svg
{{ $attributes->class($classes) }}
data-flux-icon
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="{{ $strokeWidth }}"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
data-slot="icon"
>
<path d="m7 15 5 5 5-5" />
<path d="m7 9 5-5 5 5" />
</svg>

View File

@@ -0,0 +1,43 @@
{{-- Credit: Lucide (https://lucide.dev) --}}
@props([
'variant' => 'outline',
])
@php
if ($variant === 'solid') {
throw new \Exception('The "solid" variant is not supported in Lucide.');
}
$classes = Flux::classes('shrink-0')
->add(match($variant) {
'outline' => '[:where(&)]:size-6',
'solid' => '[:where(&)]:size-6',
'mini' => '[:where(&)]:size-5',
'micro' => '[:where(&)]:size-4',
});
$strokeWidth = match ($variant) {
'outline' => 2,
'mini' => 2.25,
'micro' => 2.5,
};
@endphp
<svg
{{ $attributes->class($classes) }}
data-flux-icon
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="{{ $strokeWidth }}"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
data-slot="icon"
>
<circle cx="12" cy="12" r="10" />
<path d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8" />
<path d="M12 18V6" />
</svg>

View File

@@ -0,0 +1,43 @@
{{-- Credit: Lucide (https://lucide.dev) --}}
@props([
'variant' => 'outline',
])
@php
if ($variant === 'solid') {
throw new \Exception('The "solid" variant is not supported in Lucide.');
}
$classes = Flux::classes('shrink-0')
->add(match($variant) {
'outline' => '[:where(&)]:size-6',
'solid' => '[:where(&)]:size-6',
'mini' => '[:where(&)]:size-5',
'micro' => '[:where(&)]:size-4',
});
$strokeWidth = match ($variant) {
'outline' => 2,
'mini' => 2.25,
'micro' => 2.5,
};
@endphp
<svg
{{ $attributes->class($classes) }}
data-flux-icon
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="{{ $strokeWidth }}"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
data-slot="icon"
>
<circle cx="12" cy="12" r="10" />
<path d="m15 9-6 6" />
<path d="m9 9 6 6" />
</svg>

View File

@@ -0,0 +1,43 @@
{{-- Credit: Lucide (https://lucide.dev) --}}
@props([
'variant' => 'outline',
])
@php
if ($variant === 'solid') {
throw new \Exception('The "solid" variant is not supported in Lucide.');
}
$classes = Flux::classes('shrink-0')
->add(match($variant) {
'outline' => '[:where(&)]:size-6',
'solid' => '[:where(&)]:size-6',
'mini' => '[:where(&)]:size-5',
'micro' => '[:where(&)]:size-4',
});
$strokeWidth = match ($variant) {
'outline' => 2,
'mini' => 2.25,
'micro' => 2.5,
};
@endphp
<svg
{{ $attributes->class($classes) }}
data-flux-icon
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="{{ $strokeWidth }}"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
data-slot="icon"
>
<circle cx="12" cy="16" r="1" />
<rect x="3" y="10" width="18" height="12" rx="2" />
<path d="M7 10V7a5 5 0 0 1 10 0v3" />
</svg>

View File

@@ -0,0 +1,43 @@
{{-- Credit: Lucide (https://lucide.dev) --}}
@props([
'variant' => 'outline',
])
@php
if ($variant === 'solid') {
throw new \Exception('The "solid" variant is not supported in Lucide.');
}
$classes = Flux::classes('shrink-0')
->add(match($variant) {
'outline' => '[:where(&)]:size-6',
'solid' => '[:where(&)]:size-6',
'mini' => '[:where(&)]:size-5',
'micro' => '[:where(&)]:size-4',
});
$strokeWidth = match ($variant) {
'outline' => 2,
'mini' => 2.25,
'micro' => 2.5,
};
@endphp
<svg
{{ $attributes->class($classes) }}
data-flux-icon
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="{{ $strokeWidth }}"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
data-slot="icon"
>
<rect width="16" height="13" x="6" y="4" rx="2" />
<path d="m22 7-7.1 3.78c-.57.3-1.23.3-1.8 0L6 7" />
<path d="M2 8v11c0 1.1.9 2 2 2h14" />
</svg>

View File

@@ -0,0 +1,627 @@
@section('title'){{ $title.' | Zemail.me' }}@endsection
@section('description'){{ $description }}@endsection
@section('keywords'){{ $keywords }}@endsection
@section('custom_header')
@if (!empty($faqSchema))
<script type="application/ld+json">
{!! json_encode($faqSchema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) !!}
</script>
@endif
@endsection
<span>
<div class="container">
@if($route == "disposable-email")
<div class="flex justify-center items-center py-3">
<div class="flex items-center">
<div class="group relative mx-auto w-full overflow-hidden rounded-[16px]">
<div class="relative rounded-[15px] p-6">
<div class="space-y-2 w-full flex flex-col items-center text-center">
<div id="hero-section" class="relative self-center" >
<svg class="h-56 w-56" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1035 832" >
<path opacity=".1" d="M601 95c-66-2-129-20-189-39S291 15 227 4c-42-6-90-7-123 11S61 64 55 93c-4 21-6 44 5 64 8 14 23 26 33 39 34 47 10 105-28 150-17 22-37 42-51 65s-19 49-8 72c12 23 40 40 70 53 61 24 133 31 203 35 156 9 313 5 469 1 57-1 115-2 172-10 32-4 64-10 87-26 29-20 36-54 17-80-33-42-123-52-146-98-13-25 0-52 19-76 39-49 105-93 108-149 2-39-29-78-78-97-51-19-122-17-160 15-39 33-107 46-166 44ZM514.5 831.6c160.604 0 290.8-13.118 290.8-29.3 0-16.182-130.196-29.3-290.8-29.3s-290.8 13.118-290.8 29.3c0 16.182 130.196 29.3 290.8 29.3Z" fill="#F14743" style="transform: translate3d(0px, 11.4579px, 0px);"></path><path d="M249 799s-13-73-61-109c-20-15-35-36-42-60l-5-34" stroke="#535461" stroke-width="2" stroke-miterlimit="10" style="transform: translate3d(0px, 10.8015px, 0px);"></path>
<path d="M111 573c4 8 30 24 30 24s4-30 0-37c-4-8-14-12-23-8s-11 14-7 21Zm-3 55c8 5 39 4 39 4s-14-26-22-31-18-2-22 5-3 18 5 22Zm39 74c10 0 37-16 37-16s-27-16-36-16-17 7-17 16 7 16 16 16Zm43 49c9 2 39-9 39-9s-23-20-33-22-17 4-19 13 4 17 13 18Zm-10-132c-3 8-28 27-28 27s-7-29-3-38 12-12 21-9 13 12 10 20Zm49 59c-5 7-35 17-35 17s3-30 9-36c6-7 16-9 23-3 7 4 9 14 4 21l-1 1Zm40 63c-4 7-31 23-31 23s-3-30 2-38 14-11 22-7 12 14 7 22Z" fill="#F14743" style="transform: translate3d(0px, 8.66419px, 0px);"></path><path opacity=".25" d="M111 573c4 8 30 24 30 24s4-30 0-37c-4-8-14-12-23-8s-11 14-7 21Zm-3 55c8 5 39 4 39 4s-14-26-22-31-18-2-22 5-3 18 5 22Zm39 74c10 0 37-16 37-16s-27-16-36-16-17 7-17 16 7 16 16 16Zm43 49c9 2 39-9 39-9s-23-20-33-22-17 4-19 13 4 17 13 18Zm-10-132c-3 8-28 27-28 27s-7-29-3-38 12-12 21-9 13 12 10 20Zm49 59c-5 7-35 17-35 17s3-30 9-36c6-7 16-9 23-3 7 4 9 14 4 21l-1 1Zm40 63c-4 7-31 23-31 23s-3-30 2-38 14-11 22-7 12 14 7 22Z" fill="#000" style="transform: translate3d(0px, 9.40631px, 0px);"></path>
<path d="M247 797s20-72-9-123c-11-22-16-46-12-70 2-12 5-23 10-34" stroke="#535461" stroke-width="2" stroke-miterlimit="10" style="transform: translate3d(0px, 10.1179px, 0px);"></path>
<path d="M218 538c0 9 18 33 18 33s16-25 15-34-8-16-17-15-16 7-16 16Zm-26 49c5 7 34 19 34 19s-1-30-7-37c-5-7-15-9-23-4a15.005 15.005 0 0 0-6.919 10.106A14.99 14.99 0 0 0 192 587Zm4 83c9 3 40-1 40-1s-17-25-26-28-18 0-22 7 0 18 8 22Zm18 61c8 5 39 7 39 7s-12-28-20-33-18-3-23 4-4 17 3 22h1Zm47-124c-6 6-37 13-37 13s7-29 13-35c7-7 17-7 24-1 6 6 7 15 1 22l-1 1Zm21 72c-9 4-40 2-40 2s15-26 23-30 19-1 23 6c5 8 2 18-6 22Zm9 73c-7 5-39 9-39 9s11-29 18-34 18-4 24 3a15 15 0 0 1-3 22Z" fill="#F14743" style="transform: translate3d(0px, 7.94676px, 0px);"></path><path d="M579 214 317 384l171 308c1 3 4 5 7 6l155 37c5 2 11-1 13-6l77-160 84-112-199-236a35 35 0 0 0-46-7Z" fill="#DCDFED" style="transform: translate3d(0px, 5.38356px, 0px);"></path>
<path opacity=".1" d="m827 459-1 1-49 65-1 2-33 44-66 136-2 4-9 19c-2 5-8 8-13 7l-155-38c-3-1-6-3-8-6l-15-27-2-4-107-194-1-2-45-81 5-3 41-27h1l87-56 5-4 122-79c15-10 35-7 46 7l91 107 4 5 67 78v1l33 39 4 5 1 1Z" fill="#000" style="transform: translate3d(0px, 5.97558px, 0px);"></path>
<path opacity=".1" d="m786 412-8 56-2 17-2 36v3l-7 188a11.999 11.999 0 0 1-14 11l-78-17h-1l-200-45-4-1-110-24 3-169v-6l1-51v-57l87-56 6-3c80 8 187 24 259 35l4 5 66 78Z" fill="#000" ></path>
<path d="m795 346-9 66-9 60-2 17-2 36-7 191a11.999 11.999 0 0 1-14 11l-79-17-200-45-114-25 3-175 1-51 1-60v-62c19-1 50 1 87 5a5591.76 5591.76 0 0 1 344 49Z" fill="#F14743" ></path>
<path opacity=".1" d="m820 452-43 20-215 106-199-164-41-33-5 3 45 81-3 175 114 25 15 27c1 3 4 5 7 6l155 37c5 2 11-1 13-6l10-19 78 17c7 2 13-2 15-9v-2l7-191 50-67v-1l-3-5Z" fill="#000" style="transform: translate3d(0px, 6.60105px, 0px);"></path>
<path d="m317 384-18 305c0 6 4 11 9 13l474 105a11.994 11.994 0 0 0 9.442-2.391A11.997 11.997 0 0 0 796 796l28-339-262 129-245-202Z" fill="#DCDFED" style="transform: translate3d(0px, 7.25494px, 0px);"></path><path opacity=".1" d="m303 696 246-151c7-5 17-3 23 3l220 257c1 1 1 0 0 0L303 696c-2 1-2 0 0 0Z" fill="#000" style="transform: translate3d(0px, 4.28594px, 0px);"></path>
<path d="m304 696 244-147c8-5 18-3 24 4l218 251v3l-2 1-483-107-2-3 1-2Z" fill="#E3E5F1" style="transform: translate3d(0px, 4.81912px, 0px);"></path>
</svg>
</div>
<h1 class="text-2xl leading-9 tracking-tight text-gray-900 sm:text-2xl sm:leading-10 dark:text-gray-300" >Free Disposable Email Service</h1>
<span class="text-center text-gray-700 lg:max-w-2xl dark:text-gray-400 text-lg">Protect your privacy online with our <strong>disposable email</strong> solution. Create a <strong>temporary disposable email address</strong> instantly—no sign-up required. Stay anonymous and secure while avoiding spam and unwanted emails.</span>
</div>
</div>
</div>
</div>
</div>
<div class="flex justify-center items-center mt-4">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 p-1">
<!-- Card 1 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-600 hover:via-zinc-800 hover:to-zinc-700">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.shield-check/>
<p class="text-lg font-semibold dark:text-white text-accent-content">Secure Disposable Email</p>
<p class="font-md text-slate-500">
Our <strong>disposable email service</strong> is built for privacy. Messages are automatically deleted after 24 hours, helping you stay secure without leaving a trace.
</p>
</div>
</div>
</div>
</div>
<!-- Card 2 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-700 hover:via-zinc-800 hover:to-zinc-700">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.user />
<p class="text-lg font-semibold dark:text-white text-accent-content">Instant Disposable Email Address</p>
<p class="font-md text-slate-500">
Generate a <strong>free disposable email address</strong> instantly. Use it to sign up for websites, receive confirmation emails, or test services—then let it disappear when you're done.
</p>
</div>
</div>
</div>
</div>
<!-- Card 3 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-700 hover:via-zinc-800 hover:to-zinc-600">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.clock />
<p class="text-lg font-semibold dark:text-white text-accent-content">Fast & Anonymous Disposable Email</p>
<p class="font-md text-slate-500">Our <strong>anonymous disposable email service</strong> delivers messages instantly without asking for any personal data. Stay safe from spam and protect your main inbox at all times.</p>
</div>
</div>
</div>
</div>
</div>
</div>
@elseif($route == "disposable-gmail")
<div class="flex justify-center items-center py-3">
<div class="flex items-center">
<div class="group relative mx-auto w-full overflow-hidden rounded-[16px]">
<div class="relative rounded-[15px] p-6">
<div class="space-y-2 w-full flex flex-col items-center text-center">
<div id="hero-section" class="relative self-center" >
<svg class="h-56 w-56" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1035 832" >
<path opacity=".1" d="M601 95c-66-2-129-20-189-39S291 15 227 4c-42-6-90-7-123 11S61 64 55 93c-4 21-6 44 5 64 8 14 23 26 33 39 34 47 10 105-28 150-17 22-37 42-51 65s-19 49-8 72c12 23 40 40 70 53 61 24 133 31 203 35 156 9 313 5 469 1 57-1 115-2 172-10 32-4 64-10 87-26 29-20 36-54 17-80-33-42-123-52-146-98-13-25 0-52 19-76 39-49 105-93 108-149 2-39-29-78-78-97-51-19-122-17-160 15-39 33-107 46-166 44ZM514.5 831.6c160.604 0 290.8-13.118 290.8-29.3 0-16.182-130.196-29.3-290.8-29.3s-290.8 13.118-290.8 29.3c0 16.182 130.196 29.3 290.8 29.3Z" fill="#F14743" style="transform: translate3d(0px, 11.4579px, 0px);"></path><path d="M249 799s-13-73-61-109c-20-15-35-36-42-60l-5-34" stroke="#535461" stroke-width="2" stroke-miterlimit="10" style="transform: translate3d(0px, 10.8015px, 0px);"></path>
<path d="M111 573c4 8 30 24 30 24s4-30 0-37c-4-8-14-12-23-8s-11 14-7 21Zm-3 55c8 5 39 4 39 4s-14-26-22-31-18-2-22 5-3 18 5 22Zm39 74c10 0 37-16 37-16s-27-16-36-16-17 7-17 16 7 16 16 16Zm43 49c9 2 39-9 39-9s-23-20-33-22-17 4-19 13 4 17 13 18Zm-10-132c-3 8-28 27-28 27s-7-29-3-38 12-12 21-9 13 12 10 20Zm49 59c-5 7-35 17-35 17s3-30 9-36c6-7 16-9 23-3 7 4 9 14 4 21l-1 1Zm40 63c-4 7-31 23-31 23s-3-30 2-38 14-11 22-7 12 14 7 22Z" fill="#F14743" style="transform: translate3d(0px, 8.66419px, 0px);"></path><path opacity=".25" d="M111 573c4 8 30 24 30 24s4-30 0-37c-4-8-14-12-23-8s-11 14-7 21Zm-3 55c8 5 39 4 39 4s-14-26-22-31-18-2-22 5-3 18 5 22Zm39 74c10 0 37-16 37-16s-27-16-36-16-17 7-17 16 7 16 16 16Zm43 49c9 2 39-9 39-9s-23-20-33-22-17 4-19 13 4 17 13 18Zm-10-132c-3 8-28 27-28 27s-7-29-3-38 12-12 21-9 13 12 10 20Zm49 59c-5 7-35 17-35 17s3-30 9-36c6-7 16-9 23-3 7 4 9 14 4 21l-1 1Zm40 63c-4 7-31 23-31 23s-3-30 2-38 14-11 22-7 12 14 7 22Z" fill="#000" style="transform: translate3d(0px, 9.40631px, 0px);"></path>
<path d="M247 797s20-72-9-123c-11-22-16-46-12-70 2-12 5-23 10-34" stroke="#535461" stroke-width="2" stroke-miterlimit="10" style="transform: translate3d(0px, 10.1179px, 0px);"></path>
<path d="M218 538c0 9 18 33 18 33s16-25 15-34-8-16-17-15-16 7-16 16Zm-26 49c5 7 34 19 34 19s-1-30-7-37c-5-7-15-9-23-4a15.005 15.005 0 0 0-6.919 10.106A14.99 14.99 0 0 0 192 587Zm4 83c9 3 40-1 40-1s-17-25-26-28-18 0-22 7 0 18 8 22Zm18 61c8 5 39 7 39 7s-12-28-20-33-18-3-23 4-4 17 3 22h1Zm47-124c-6 6-37 13-37 13s7-29 13-35c7-7 17-7 24-1 6 6 7 15 1 22l-1 1Zm21 72c-9 4-40 2-40 2s15-26 23-30 19-1 23 6c5 8 2 18-6 22Zm9 73c-7 5-39 9-39 9s11-29 18-34 18-4 24 3a15 15 0 0 1-3 22Z" fill="#F14743" style="transform: translate3d(0px, 7.94676px, 0px);"></path><path d="M579 214 317 384l171 308c1 3 4 5 7 6l155 37c5 2 11-1 13-6l77-160 84-112-199-236a35 35 0 0 0-46-7Z" fill="#DCDFED" style="transform: translate3d(0px, 5.38356px, 0px);"></path>
<path opacity=".1" d="m827 459-1 1-49 65-1 2-33 44-66 136-2 4-9 19c-2 5-8 8-13 7l-155-38c-3-1-6-3-8-6l-15-27-2-4-107-194-1-2-45-81 5-3 41-27h1l87-56 5-4 122-79c15-10 35-7 46 7l91 107 4 5 67 78v1l33 39 4 5 1 1Z" fill="#000" style="transform: translate3d(0px, 5.97558px, 0px);"></path>
<path opacity=".1" d="m786 412-8 56-2 17-2 36v3l-7 188a11.999 11.999 0 0 1-14 11l-78-17h-1l-200-45-4-1-110-24 3-169v-6l1-51v-57l87-56 6-3c80 8 187 24 259 35l4 5 66 78Z" fill="#000" ></path>
<path d="m795 346-9 66-9 60-2 17-2 36-7 191a11.999 11.999 0 0 1-14 11l-79-17-200-45-114-25 3-175 1-51 1-60v-62c19-1 50 1 87 5a5591.76 5591.76 0 0 1 344 49Z" fill="#F14743" ></path>
<path opacity=".1" d="m820 452-43 20-215 106-199-164-41-33-5 3 45 81-3 175 114 25 15 27c1 3 4 5 7 6l155 37c5 2 11-1 13-6l10-19 78 17c7 2 13-2 15-9v-2l7-191 50-67v-1l-3-5Z" fill="#000" style="transform: translate3d(0px, 6.60105px, 0px);"></path>
<path d="m317 384-18 305c0 6 4 11 9 13l474 105a11.994 11.994 0 0 0 9.442-2.391A11.997 11.997 0 0 0 796 796l28-339-262 129-245-202Z" fill="#DCDFED" style="transform: translate3d(0px, 7.25494px, 0px);"></path><path opacity=".1" d="m303 696 246-151c7-5 17-3 23 3l220 257c1 1 1 0 0 0L303 696c-2 1-2 0 0 0Z" fill="#000" style="transform: translate3d(0px, 4.28594px, 0px);"></path>
<path d="m304 696 244-147c8-5 18-3 24 4l218 251v3l-2 1-483-107-2-3 1-2Z" fill="#E3E5F1" style="transform: translate3d(0px, 4.81912px, 0px);"></path>
</svg>
</div>
<h1 class="text-2xl leading-9 tracking-tight text-gray-900 sm:text-2xl sm:leading-10 dark:text-gray-300" >Free Disposable Gmail Email Address</h1>
<span class="text-center text-gray-700 lg:max-w-2xl dark:text-gray-400 text-lg">Instantly generate a <strong>disposable Gmail email address</strong> to protect your privacy. Use a <strong>temporary Gmail inbox</strong> for sign-ups, testing, and avoiding spam.</span>
</div>
</div>
</div>
</div>
</div>
<div class="flex justify-center items-center mt-4">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 p-1">
<!-- Card 1 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-600 hover:via-zinc-800 hover:to-zinc-700">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.shield-check/>
<p class="text-lg font-semibold dark:text-white text-accent-content">Secure Disposable Gmail Email</p>
<p class="font-md text-slate-500">
Our <strong>disposable Gmail email</strong> is automatically deleted after 24 hours. Keep your personal inbox clean while remaining anonymous and protected online.
</p>
</div>
</div>
</div>
</div>
<!-- Card 2 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-700 hover:via-zinc-800 hover:to-zinc-700">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.user />
<p class="text-lg font-semibold dark:text-white text-accent-content">Instant Gmail Disposable Email</p>
<p class="font-md text-slate-500">
Get a <strong>Gmail disposable email address</strong> instantly. No sign-up required—perfect for temporary use, testing, or online verifications as it comes with trusted domain.
</p>
</div>
</div>
</div>
</div>
<!-- Card 3 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-700 hover:via-zinc-800 hover:to-zinc-600">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.clock />
<p class="text-lg font-semibold dark:text-white text-accent-content">Instant Temporary Gmail Inbox</p>
<p class="font-md text-slate-500">Access your <strong>temporary Gmail inbox</strong> in seconds. Perfect for quick tasks without revealing your real Gmail account. Stay spam-free and private.</p>
</div>
</div>
</div>
</div>
</div>
</div>
@elseif($route == "disposable-outlook")
<div class="flex justify-center items-center py-3">
<div class="flex items-center">
<div class="group relative mx-auto w-full overflow-hidden rounded-[16px]">
<div class="relative rounded-[15px] p-6">
<div class="space-y-2 w-full flex flex-col items-center text-center">
<div id="hero-section" class="relative self-center" >
<svg class="h-56 w-56" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1035 832" >
<path opacity=".1" d="M601 95c-66-2-129-20-189-39S291 15 227 4c-42-6-90-7-123 11S61 64 55 93c-4 21-6 44 5 64 8 14 23 26 33 39 34 47 10 105-28 150-17 22-37 42-51 65s-19 49-8 72c12 23 40 40 70 53 61 24 133 31 203 35 156 9 313 5 469 1 57-1 115-2 172-10 32-4 64-10 87-26 29-20 36-54 17-80-33-42-123-52-146-98-13-25 0-52 19-76 39-49 105-93 108-149 2-39-29-78-78-97-51-19-122-17-160 15-39 33-107 46-166 44ZM514.5 831.6c160.604 0 290.8-13.118 290.8-29.3 0-16.182-130.196-29.3-290.8-29.3s-290.8 13.118-290.8 29.3c0 16.182 130.196 29.3 290.8 29.3Z" fill="#F14743" style="transform: translate3d(0px, 11.4579px, 0px);"></path><path d="M249 799s-13-73-61-109c-20-15-35-36-42-60l-5-34" stroke="#535461" stroke-width="2" stroke-miterlimit="10" style="transform: translate3d(0px, 10.8015px, 0px);"></path>
<path d="M111 573c4 8 30 24 30 24s4-30 0-37c-4-8-14-12-23-8s-11 14-7 21Zm-3 55c8 5 39 4 39 4s-14-26-22-31-18-2-22 5-3 18 5 22Zm39 74c10 0 37-16 37-16s-27-16-36-16-17 7-17 16 7 16 16 16Zm43 49c9 2 39-9 39-9s-23-20-33-22-17 4-19 13 4 17 13 18Zm-10-132c-3 8-28 27-28 27s-7-29-3-38 12-12 21-9 13 12 10 20Zm49 59c-5 7-35 17-35 17s3-30 9-36c6-7 16-9 23-3 7 4 9 14 4 21l-1 1Zm40 63c-4 7-31 23-31 23s-3-30 2-38 14-11 22-7 12 14 7 22Z" fill="#F14743" style="transform: translate3d(0px, 8.66419px, 0px);"></path><path opacity=".25" d="M111 573c4 8 30 24 30 24s4-30 0-37c-4-8-14-12-23-8s-11 14-7 21Zm-3 55c8 5 39 4 39 4s-14-26-22-31-18-2-22 5-3 18 5 22Zm39 74c10 0 37-16 37-16s-27-16-36-16-17 7-17 16 7 16 16 16Zm43 49c9 2 39-9 39-9s-23-20-33-22-17 4-19 13 4 17 13 18Zm-10-132c-3 8-28 27-28 27s-7-29-3-38 12-12 21-9 13 12 10 20Zm49 59c-5 7-35 17-35 17s3-30 9-36c6-7 16-9 23-3 7 4 9 14 4 21l-1 1Zm40 63c-4 7-31 23-31 23s-3-30 2-38 14-11 22-7 12 14 7 22Z" fill="#000" style="transform: translate3d(0px, 9.40631px, 0px);"></path>
<path d="M247 797s20-72-9-123c-11-22-16-46-12-70 2-12 5-23 10-34" stroke="#535461" stroke-width="2" stroke-miterlimit="10" style="transform: translate3d(0px, 10.1179px, 0px);"></path>
<path d="M218 538c0 9 18 33 18 33s16-25 15-34-8-16-17-15-16 7-16 16Zm-26 49c5 7 34 19 34 19s-1-30-7-37c-5-7-15-9-23-4a15.005 15.005 0 0 0-6.919 10.106A14.99 14.99 0 0 0 192 587Zm4 83c9 3 40-1 40-1s-17-25-26-28-18 0-22 7 0 18 8 22Zm18 61c8 5 39 7 39 7s-12-28-20-33-18-3-23 4-4 17 3 22h1Zm47-124c-6 6-37 13-37 13s7-29 13-35c7-7 17-7 24-1 6 6 7 15 1 22l-1 1Zm21 72c-9 4-40 2-40 2s15-26 23-30 19-1 23 6c5 8 2 18-6 22Zm9 73c-7 5-39 9-39 9s11-29 18-34 18-4 24 3a15 15 0 0 1-3 22Z" fill="#F14743" style="transform: translate3d(0px, 7.94676px, 0px);"></path><path d="M579 214 317 384l171 308c1 3 4 5 7 6l155 37c5 2 11-1 13-6l77-160 84-112-199-236a35 35 0 0 0-46-7Z" fill="#DCDFED" style="transform: translate3d(0px, 5.38356px, 0px);"></path>
<path opacity=".1" d="m827 459-1 1-49 65-1 2-33 44-66 136-2 4-9 19c-2 5-8 8-13 7l-155-38c-3-1-6-3-8-6l-15-27-2-4-107-194-1-2-45-81 5-3 41-27h1l87-56 5-4 122-79c15-10 35-7 46 7l91 107 4 5 67 78v1l33 39 4 5 1 1Z" fill="#000" style="transform: translate3d(0px, 5.97558px, 0px);"></path>
<path opacity=".1" d="m786 412-8 56-2 17-2 36v3l-7 188a11.999 11.999 0 0 1-14 11l-78-17h-1l-200-45-4-1-110-24 3-169v-6l1-51v-57l87-56 6-3c80 8 187 24 259 35l4 5 66 78Z" fill="#000" ></path>
<path d="m795 346-9 66-9 60-2 17-2 36-7 191a11.999 11.999 0 0 1-14 11l-79-17-200-45-114-25 3-175 1-51 1-60v-62c19-1 50 1 87 5a5591.76 5591.76 0 0 1 344 49Z" fill="#F14743" ></path>
<path opacity=".1" d="m820 452-43 20-215 106-199-164-41-33-5 3 45 81-3 175 114 25 15 27c1 3 4 5 7 6l155 37c5 2 11-1 13-6l10-19 78 17c7 2 13-2 15-9v-2l7-191 50-67v-1l-3-5Z" fill="#000" style="transform: translate3d(0px, 6.60105px, 0px);"></path>
<path d="m317 384-18 305c0 6 4 11 9 13l474 105a11.994 11.994 0 0 0 9.442-2.391A11.997 11.997 0 0 0 796 796l28-339-262 129-245-202Z" fill="#DCDFED" style="transform: translate3d(0px, 7.25494px, 0px);"></path><path opacity=".1" d="m303 696 246-151c7-5 17-3 23 3l220 257c1 1 1 0 0 0L303 696c-2 1-2 0 0 0Z" fill="#000" style="transform: translate3d(0px, 4.28594px, 0px);"></path>
<path d="m304 696 244-147c8-5 18-3 24 4l218 251v3l-2 1-483-107-2-3 1-2Z" fill="#E3E5F1" style="transform: translate3d(0px, 4.81912px, 0px);"></path>
</svg>
</div>
<h1 class="text-2xl leading-9 tracking-tight text-gray-900 sm:text-2xl sm:leading-10 dark:text-gray-300" >Free Disposable Outlook Email Address</h1>
<span class="text-center text-gray-700 lg:max-w-2xl dark:text-gray-400 text-lg">Get a <strong>disposable Outlook email address</strong> instantly. Use a <strong>temporary Outlook inbox</strong> to stay anonymous, avoid spam, and protect your real Outlook account.</span>
</div>
</div>
</div>
</div>
</div>
<div class="flex justify-center items-center mt-4">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 p-1">
<!-- Card 1 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-600 hover:via-zinc-800 hover:to-zinc-700">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.shield-check/>
<p class="text-lg font-semibold dark:text-white text-accent-content">Secure Disposable Outlook Email</p>
<p class="font-md text-slate-500">
Our <strong>temporary Outlook email</strong> is private, secure, and deleted after 24 hours. Use it to prevent spam and shield your main Outlook inbox.
</p>
</div>
</div>
</div>
</div>
<!-- Card 2 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-700 hover:via-zinc-800 hover:to-zinc-700">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.user />
<p class="text-lg font-semibold dark:text-white text-accent-content">Instant Outlook Disposable Email</p>
<p class="font-md text-slate-500">
Create a <strong>disposable Outlook email address</strong> on the fly—no login or personal details required. Perfect for quick verifications and one-time use.
</p>
</div>
</div>
</div>
</div>
<!-- Card 3 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-700 hover:via-zinc-800 hover:to-zinc-600">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.clock />
<p class="text-lg font-semibold dark:text-white text-accent-content">Instant Temporary Outlook Inbox</p>
<p class="font-md text-slate-500">Access a <strong>temporary Outlook-style inbox</strong> in seconds. Read messages instantly and stay free from long-term clutter and marketing emails.</p>
</div>
</div>
</div>
</div>
</div>
</div>
@elseif($route == "disposable-yahoo")
<div class="flex justify-center items-center py-3">
<div class="flex items-center">
<div class="group relative mx-auto w-full overflow-hidden rounded-[16px]">
<div class="relative rounded-[15px] p-6">
<div class="space-y-2 w-full flex flex-col items-center text-center">
<div id="hero-section" class="relative self-center" >
<svg class="h-56 w-56" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1035 832" >
<path opacity=".1" d="M601 95c-66-2-129-20-189-39S291 15 227 4c-42-6-90-7-123 11S61 64 55 93c-4 21-6 44 5 64 8 14 23 26 33 39 34 47 10 105-28 150-17 22-37 42-51 65s-19 49-8 72c12 23 40 40 70 53 61 24 133 31 203 35 156 9 313 5 469 1 57-1 115-2 172-10 32-4 64-10 87-26 29-20 36-54 17-80-33-42-123-52-146-98-13-25 0-52 19-76 39-49 105-93 108-149 2-39-29-78-78-97-51-19-122-17-160 15-39 33-107 46-166 44ZM514.5 831.6c160.604 0 290.8-13.118 290.8-29.3 0-16.182-130.196-29.3-290.8-29.3s-290.8 13.118-290.8 29.3c0 16.182 130.196 29.3 290.8 29.3Z" fill="#F14743" style="transform: translate3d(0px, 11.4579px, 0px);"></path><path d="M249 799s-13-73-61-109c-20-15-35-36-42-60l-5-34" stroke="#535461" stroke-width="2" stroke-miterlimit="10" style="transform: translate3d(0px, 10.8015px, 0px);"></path>
<path d="M111 573c4 8 30 24 30 24s4-30 0-37c-4-8-14-12-23-8s-11 14-7 21Zm-3 55c8 5 39 4 39 4s-14-26-22-31-18-2-22 5-3 18 5 22Zm39 74c10 0 37-16 37-16s-27-16-36-16-17 7-17 16 7 16 16 16Zm43 49c9 2 39-9 39-9s-23-20-33-22-17 4-19 13 4 17 13 18Zm-10-132c-3 8-28 27-28 27s-7-29-3-38 12-12 21-9 13 12 10 20Zm49 59c-5 7-35 17-35 17s3-30 9-36c6-7 16-9 23-3 7 4 9 14 4 21l-1 1Zm40 63c-4 7-31 23-31 23s-3-30 2-38 14-11 22-7 12 14 7 22Z" fill="#F14743" style="transform: translate3d(0px, 8.66419px, 0px);"></path><path opacity=".25" d="M111 573c4 8 30 24 30 24s4-30 0-37c-4-8-14-12-23-8s-11 14-7 21Zm-3 55c8 5 39 4 39 4s-14-26-22-31-18-2-22 5-3 18 5 22Zm39 74c10 0 37-16 37-16s-27-16-36-16-17 7-17 16 7 16 16 16Zm43 49c9 2 39-9 39-9s-23-20-33-22-17 4-19 13 4 17 13 18Zm-10-132c-3 8-28 27-28 27s-7-29-3-38 12-12 21-9 13 12 10 20Zm49 59c-5 7-35 17-35 17s3-30 9-36c6-7 16-9 23-3 7 4 9 14 4 21l-1 1Zm40 63c-4 7-31 23-31 23s-3-30 2-38 14-11 22-7 12 14 7 22Z" fill="#000" style="transform: translate3d(0px, 9.40631px, 0px);"></path>
<path d="M247 797s20-72-9-123c-11-22-16-46-12-70 2-12 5-23 10-34" stroke="#535461" stroke-width="2" stroke-miterlimit="10" style="transform: translate3d(0px, 10.1179px, 0px);"></path>
<path d="M218 538c0 9 18 33 18 33s16-25 15-34-8-16-17-15-16 7-16 16Zm-26 49c5 7 34 19 34 19s-1-30-7-37c-5-7-15-9-23-4a15.005 15.005 0 0 0-6.919 10.106A14.99 14.99 0 0 0 192 587Zm4 83c9 3 40-1 40-1s-17-25-26-28-18 0-22 7 0 18 8 22Zm18 61c8 5 39 7 39 7s-12-28-20-33-18-3-23 4-4 17 3 22h1Zm47-124c-6 6-37 13-37 13s7-29 13-35c7-7 17-7 24-1 6 6 7 15 1 22l-1 1Zm21 72c-9 4-40 2-40 2s15-26 23-30 19-1 23 6c5 8 2 18-6 22Zm9 73c-7 5-39 9-39 9s11-29 18-34 18-4 24 3a15 15 0 0 1-3 22Z" fill="#F14743" style="transform: translate3d(0px, 7.94676px, 0px);"></path><path d="M579 214 317 384l171 308c1 3 4 5 7 6l155 37c5 2 11-1 13-6l77-160 84-112-199-236a35 35 0 0 0-46-7Z" fill="#DCDFED" style="transform: translate3d(0px, 5.38356px, 0px);"></path>
<path opacity=".1" d="m827 459-1 1-49 65-1 2-33 44-66 136-2 4-9 19c-2 5-8 8-13 7l-155-38c-3-1-6-3-8-6l-15-27-2-4-107-194-1-2-45-81 5-3 41-27h1l87-56 5-4 122-79c15-10 35-7 46 7l91 107 4 5 67 78v1l33 39 4 5 1 1Z" fill="#000" style="transform: translate3d(0px, 5.97558px, 0px);"></path>
<path opacity=".1" d="m786 412-8 56-2 17-2 36v3l-7 188a11.999 11.999 0 0 1-14 11l-78-17h-1l-200-45-4-1-110-24 3-169v-6l1-51v-57l87-56 6-3c80 8 187 24 259 35l4 5 66 78Z" fill="#000" ></path>
<path d="m795 346-9 66-9 60-2 17-2 36-7 191a11.999 11.999 0 0 1-14 11l-79-17-200-45-114-25 3-175 1-51 1-60v-62c19-1 50 1 87 5a5591.76 5591.76 0 0 1 344 49Z" fill="#F14743" ></path>
<path opacity=".1" d="m820 452-43 20-215 106-199-164-41-33-5 3 45 81-3 175 114 25 15 27c1 3 4 5 7 6l155 37c5 2 11-1 13-6l10-19 78 17c7 2 13-2 15-9v-2l7-191 50-67v-1l-3-5Z" fill="#000" style="transform: translate3d(0px, 6.60105px, 0px);"></path>
<path d="m317 384-18 305c0 6 4 11 9 13l474 105a11.994 11.994 0 0 0 9.442-2.391A11.997 11.997 0 0 0 796 796l28-339-262 129-245-202Z" fill="#DCDFED" style="transform: translate3d(0px, 7.25494px, 0px);"></path><path opacity=".1" d="m303 696 246-151c7-5 17-3 23 3l220 257c1 1 1 0 0 0L303 696c-2 1-2 0 0 0Z" fill="#000" style="transform: translate3d(0px, 4.28594px, 0px);"></path>
<path d="m304 696 244-147c8-5 18-3 24 4l218 251v3l-2 1-483-107-2-3 1-2Z" fill="#E3E5F1" style="transform: translate3d(0px, 4.81912px, 0px);"></path>
</svg>
</div>
<h1 class="text-2xl leading-9 tracking-tight text-gray-900 sm:text-2xl sm:leading-10 dark:text-gray-300" >Free Disposable Yahoo Mail Address</h1>
<span class="text-center text-gray-700 lg:max-w-2xl dark:text-gray-400 text-lg">Instantly get a <strong>disposable Yahoo Mail address</strong> to stay private online. Use a <strong>temporary Yahoo Mail inbox</strong> to block spam, protect your data, and avoid clutter.</span>
</div>
</div>
</div>
</div>
</div>
<div class="flex justify-center items-center mt-4">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 p-1">
<!-- Card 1 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-600 hover:via-zinc-800 hover:to-zinc-700">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.shield-check/>
<p class="text-lg font-semibold dark:text-white text-accent-content">Secure Yahoo Mail Disposable Email</p>
<p class="font-md text-slate-500">
A <strong>Yahoo Mail disposable email address</strong> lets you protect your personal inbox. All emails are deleted automatically after 24 hours to ensure privacy.
</p>
</div>
</div>
</div>
</div>
<!-- Card 2 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-700 hover:via-zinc-800 hover:to-zinc-700">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.user />
<p class="text-lg font-semibold dark:text-white text-accent-content">Instant Disposable Yahoo Email</p>
<p class="font-md text-slate-500">
Create a <strong>temporary Yahoo inbox</strong> with no sign-up or login required. Ideal for quick signups, app testing, or filtering out unwanted messages.
</p>
</div>
</div>
</div>
</div>
<!-- Card 3 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-700 hover:via-zinc-800 hover:to-zinc-600">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.clock />
<p class="text-lg font-semibold dark:text-white text-accent-content">Temporary Yahoo Mail Inbox</p>
<p class="font-md text-slate-500">Access a fast and reliable <strong>disposable Yahoo-style inbox</strong>. Avoid long-term clutter and use it just for what you need—then walk away spam-free.</p>
</div>
</div>
</div>
</div>
</div>
</div>
@elseif($route == "gmailnator")
<div class="flex justify-center items-center py-3">
<div class="flex items-center">
<div class="group relative mx-auto w-full overflow-hidden rounded-[16px]">
<div class="relative rounded-[15px] p-6">
<div class="space-y-2 w-full flex flex-col items-center text-center">
<div id="hero-section" class="relative self-center" >
<svg class="h-56 w-56" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1035 832" >
<path opacity=".1" d="M601 95c-66-2-129-20-189-39S291 15 227 4c-42-6-90-7-123 11S61 64 55 93c-4 21-6 44 5 64 8 14 23 26 33 39 34 47 10 105-28 150-17 22-37 42-51 65s-19 49-8 72c12 23 40 40 70 53 61 24 133 31 203 35 156 9 313 5 469 1 57-1 115-2 172-10 32-4 64-10 87-26 29-20 36-54 17-80-33-42-123-52-146-98-13-25 0-52 19-76 39-49 105-93 108-149 2-39-29-78-78-97-51-19-122-17-160 15-39 33-107 46-166 44ZM514.5 831.6c160.604 0 290.8-13.118 290.8-29.3 0-16.182-130.196-29.3-290.8-29.3s-290.8 13.118-290.8 29.3c0 16.182 130.196 29.3 290.8 29.3Z" fill="#F14743" style="transform: translate3d(0px, 11.4579px, 0px);"></path><path d="M249 799s-13-73-61-109c-20-15-35-36-42-60l-5-34" stroke="#535461" stroke-width="2" stroke-miterlimit="10" style="transform: translate3d(0px, 10.8015px, 0px);"></path>
<path d="M111 573c4 8 30 24 30 24s4-30 0-37c-4-8-14-12-23-8s-11 14-7 21Zm-3 55c8 5 39 4 39 4s-14-26-22-31-18-2-22 5-3 18 5 22Zm39 74c10 0 37-16 37-16s-27-16-36-16-17 7-17 16 7 16 16 16Zm43 49c9 2 39-9 39-9s-23-20-33-22-17 4-19 13 4 17 13 18Zm-10-132c-3 8-28 27-28 27s-7-29-3-38 12-12 21-9 13 12 10 20Zm49 59c-5 7-35 17-35 17s3-30 9-36c6-7 16-9 23-3 7 4 9 14 4 21l-1 1Zm40 63c-4 7-31 23-31 23s-3-30 2-38 14-11 22-7 12 14 7 22Z" fill="#F14743" style="transform: translate3d(0px, 8.66419px, 0px);"></path><path opacity=".25" d="M111 573c4 8 30 24 30 24s4-30 0-37c-4-8-14-12-23-8s-11 14-7 21Zm-3 55c8 5 39 4 39 4s-14-26-22-31-18-2-22 5-3 18 5 22Zm39 74c10 0 37-16 37-16s-27-16-36-16-17 7-17 16 7 16 16 16Zm43 49c9 2 39-9 39-9s-23-20-33-22-17 4-19 13 4 17 13 18Zm-10-132c-3 8-28 27-28 27s-7-29-3-38 12-12 21-9 13 12 10 20Zm49 59c-5 7-35 17-35 17s3-30 9-36c6-7 16-9 23-3 7 4 9 14 4 21l-1 1Zm40 63c-4 7-31 23-31 23s-3-30 2-38 14-11 22-7 12 14 7 22Z" fill="#000" style="transform: translate3d(0px, 9.40631px, 0px);"></path>
<path d="M247 797s20-72-9-123c-11-22-16-46-12-70 2-12 5-23 10-34" stroke="#535461" stroke-width="2" stroke-miterlimit="10" style="transform: translate3d(0px, 10.1179px, 0px);"></path>
<path d="M218 538c0 9 18 33 18 33s16-25 15-34-8-16-17-15-16 7-16 16Zm-26 49c5 7 34 19 34 19s-1-30-7-37c-5-7-15-9-23-4a15.005 15.005 0 0 0-6.919 10.106A14.99 14.99 0 0 0 192 587Zm4 83c9 3 40-1 40-1s-17-25-26-28-18 0-22 7 0 18 8 22Zm18 61c8 5 39 7 39 7s-12-28-20-33-18-3-23 4-4 17 3 22h1Zm47-124c-6 6-37 13-37 13s7-29 13-35c7-7 17-7 24-1 6 6 7 15 1 22l-1 1Zm21 72c-9 4-40 2-40 2s15-26 23-30 19-1 23 6c5 8 2 18-6 22Zm9 73c-7 5-39 9-39 9s11-29 18-34 18-4 24 3a15 15 0 0 1-3 22Z" fill="#F14743" style="transform: translate3d(0px, 7.94676px, 0px);"></path><path d="M579 214 317 384l171 308c1 3 4 5 7 6l155 37c5 2 11-1 13-6l77-160 84-112-199-236a35 35 0 0 0-46-7Z" fill="#DCDFED" style="transform: translate3d(0px, 5.38356px, 0px);"></path>
<path opacity=".1" d="m827 459-1 1-49 65-1 2-33 44-66 136-2 4-9 19c-2 5-8 8-13 7l-155-38c-3-1-6-3-8-6l-15-27-2-4-107-194-1-2-45-81 5-3 41-27h1l87-56 5-4 122-79c15-10 35-7 46 7l91 107 4 5 67 78v1l33 39 4 5 1 1Z" fill="#000" style="transform: translate3d(0px, 5.97558px, 0px);"></path>
<path opacity=".1" d="m786 412-8 56-2 17-2 36v3l-7 188a11.999 11.999 0 0 1-14 11l-78-17h-1l-200-45-4-1-110-24 3-169v-6l1-51v-57l87-56 6-3c80 8 187 24 259 35l4 5 66 78Z" fill="#000" ></path>
<path d="m795 346-9 66-9 60-2 17-2 36-7 191a11.999 11.999 0 0 1-14 11l-79-17-200-45-114-25 3-175 1-51 1-60v-62c19-1 50 1 87 5a5591.76 5591.76 0 0 1 344 49Z" fill="#F14743" ></path>
<path opacity=".1" d="m820 452-43 20-215 106-199-164-41-33-5 3 45 81-3 175 114 25 15 27c1 3 4 5 7 6l155 37c5 2 11-1 13-6l10-19 78 17c7 2 13-2 15-9v-2l7-191 50-67v-1l-3-5Z" fill="#000" style="transform: translate3d(0px, 6.60105px, 0px);"></path>
<path d="m317 384-18 305c0 6 4 11 9 13l474 105a11.994 11.994 0 0 0 9.442-2.391A11.997 11.997 0 0 0 796 796l28-339-262 129-245-202Z" fill="#DCDFED" style="transform: translate3d(0px, 7.25494px, 0px);"></path><path opacity=".1" d="m303 696 246-151c7-5 17-3 23 3l220 257c1 1 1 0 0 0L303 696c-2 1-2 0 0 0Z" fill="#000" style="transform: translate3d(0px, 4.28594px, 0px);"></path>
<path d="m304 696 244-147c8-5 18-3 24 4l218 251v3l-2 1-483-107-2-3 1-2Z" fill="#E3E5F1" style="transform: translate3d(0px, 4.81912px, 0px);"></path>
</svg>
</div>
<h1 class="text-2xl leading-9 tracking-tight text-gray-900 sm:text-2xl sm:leading-10 dark:text-gray-300" >Gmailnator Free Disposable Gmail Email Address</h1>
<span class="text-center text-gray-700 lg:max-w-2xl dark:text-gray-400 text-lg">Use <strong>Gmailnator</strong> to instantly get a <strong>temporary Gmail email address</strong> for sign-ups, testing, or spam control. No registration. Just quick, secure, disposable Gmail inboxes.</span>
</div>
</div>
</div>
</div>
</div>
<div class="flex justify-center items-center mt-4">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 p-1">
<!-- Card 1 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-600 hover:via-zinc-800 hover:to-zinc-700">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.shield-check/>
<p class="text-lg font-semibold dark:text-white text-accent-content">Gmailnator Disposable Gmail Generator</p>
<p class="font-md text-slate-500">
Use <strong>Gmailnator</strong> to receive emails online with a throwaway Gmail address. Every inbox is temporary, private, and refreshed regularly—ideal for testing or avoiding spam.
</p>
</div>
</div>
</div>
</div>
<!-- Card 2 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-700 hover:via-zinc-800 hover:to-zinc-700">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.user />
<p class="text-lg font-semibold dark:text-white text-accent-content">Instant Gmailnator Temp Mail</p>
<p class="font-md text-slate-500">
Get your <strong>Gmailnator temp mail</strong> address in seconds. Emails arrive instantly ensuring your real email stays clean. Use for verification, app signups, or anything where you dont want to use your personal Gmail.
</p>
</div>
</div>
</div>
</div>
<!-- Card 3 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-700 hover:via-zinc-800 hover:to-zinc-600">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.clock />
<p class="text-lg font-semibold dark:text-white text-accent-content">Free Gmailnator Inbox for One-Time Use</p>
<p class="font-md text-slate-500">Your <strong>Gmailnator inbox</strong> is deleted after a short time. No inbox bloat. No trackers. Just clean, fast Gmail temp addresses that keep your identity safe.</p>
</div>
</div>
</div>
</div>
</div>
</div>
@elseif($route == "emailnator")
<div class="flex justify-center items-center py-3">
<div class="flex items-center">
<div class="group relative mx-auto w-full overflow-hidden rounded-[16px]">
<div class="relative rounded-[15px] p-6">
<div class="space-y-2 w-full flex flex-col items-center text-center">
<div id="hero-section" class="relative self-center" >
<svg class="h-56 w-56" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1035 832" >
<path opacity=".1" d="M601 95c-66-2-129-20-189-39S291 15 227 4c-42-6-90-7-123 11S61 64 55 93c-4 21-6 44 5 64 8 14 23 26 33 39 34 47 10 105-28 150-17 22-37 42-51 65s-19 49-8 72c12 23 40 40 70 53 61 24 133 31 203 35 156 9 313 5 469 1 57-1 115-2 172-10 32-4 64-10 87-26 29-20 36-54 17-80-33-42-123-52-146-98-13-25 0-52 19-76 39-49 105-93 108-149 2-39-29-78-78-97-51-19-122-17-160 15-39 33-107 46-166 44ZM514.5 831.6c160.604 0 290.8-13.118 290.8-29.3 0-16.182-130.196-29.3-290.8-29.3s-290.8 13.118-290.8 29.3c0 16.182 130.196 29.3 290.8 29.3Z" fill="#F14743" style="transform: translate3d(0px, 11.4579px, 0px);"></path><path d="M249 799s-13-73-61-109c-20-15-35-36-42-60l-5-34" stroke="#535461" stroke-width="2" stroke-miterlimit="10" style="transform: translate3d(0px, 10.8015px, 0px);"></path>
<path d="M111 573c4 8 30 24 30 24s4-30 0-37c-4-8-14-12-23-8s-11 14-7 21Zm-3 55c8 5 39 4 39 4s-14-26-22-31-18-2-22 5-3 18 5 22Zm39 74c10 0 37-16 37-16s-27-16-36-16-17 7-17 16 7 16 16 16Zm43 49c9 2 39-9 39-9s-23-20-33-22-17 4-19 13 4 17 13 18Zm-10-132c-3 8-28 27-28 27s-7-29-3-38 12-12 21-9 13 12 10 20Zm49 59c-5 7-35 17-35 17s3-30 9-36c6-7 16-9 23-3 7 4 9 14 4 21l-1 1Zm40 63c-4 7-31 23-31 23s-3-30 2-38 14-11 22-7 12 14 7 22Z" fill="#F14743" style="transform: translate3d(0px, 8.66419px, 0px);"></path><path opacity=".25" d="M111 573c4 8 30 24 30 24s4-30 0-37c-4-8-14-12-23-8s-11 14-7 21Zm-3 55c8 5 39 4 39 4s-14-26-22-31-18-2-22 5-3 18 5 22Zm39 74c10 0 37-16 37-16s-27-16-36-16-17 7-17 16 7 16 16 16Zm43 49c9 2 39-9 39-9s-23-20-33-22-17 4-19 13 4 17 13 18Zm-10-132c-3 8-28 27-28 27s-7-29-3-38 12-12 21-9 13 12 10 20Zm49 59c-5 7-35 17-35 17s3-30 9-36c6-7 16-9 23-3 7 4 9 14 4 21l-1 1Zm40 63c-4 7-31 23-31 23s-3-30 2-38 14-11 22-7 12 14 7 22Z" fill="#000" style="transform: translate3d(0px, 9.40631px, 0px);"></path>
<path d="M247 797s20-72-9-123c-11-22-16-46-12-70 2-12 5-23 10-34" stroke="#535461" stroke-width="2" stroke-miterlimit="10" style="transform: translate3d(0px, 10.1179px, 0px);"></path>
<path d="M218 538c0 9 18 33 18 33s16-25 15-34-8-16-17-15-16 7-16 16Zm-26 49c5 7 34 19 34 19s-1-30-7-37c-5-7-15-9-23-4a15.005 15.005 0 0 0-6.919 10.106A14.99 14.99 0 0 0 192 587Zm4 83c9 3 40-1 40-1s-17-25-26-28-18 0-22 7 0 18 8 22Zm18 61c8 5 39 7 39 7s-12-28-20-33-18-3-23 4-4 17 3 22h1Zm47-124c-6 6-37 13-37 13s7-29 13-35c7-7 17-7 24-1 6 6 7 15 1 22l-1 1Zm21 72c-9 4-40 2-40 2s15-26 23-30 19-1 23 6c5 8 2 18-6 22Zm9 73c-7 5-39 9-39 9s11-29 18-34 18-4 24 3a15 15 0 0 1-3 22Z" fill="#F14743" style="transform: translate3d(0px, 7.94676px, 0px);"></path><path d="M579 214 317 384l171 308c1 3 4 5 7 6l155 37c5 2 11-1 13-6l77-160 84-112-199-236a35 35 0 0 0-46-7Z" fill="#DCDFED" style="transform: translate3d(0px, 5.38356px, 0px);"></path>
<path opacity=".1" d="m827 459-1 1-49 65-1 2-33 44-66 136-2 4-9 19c-2 5-8 8-13 7l-155-38c-3-1-6-3-8-6l-15-27-2-4-107-194-1-2-45-81 5-3 41-27h1l87-56 5-4 122-79c15-10 35-7 46 7l91 107 4 5 67 78v1l33 39 4 5 1 1Z" fill="#000" style="transform: translate3d(0px, 5.97558px, 0px);"></path>
<path opacity=".1" d="m786 412-8 56-2 17-2 36v3l-7 188a11.999 11.999 0 0 1-14 11l-78-17h-1l-200-45-4-1-110-24 3-169v-6l1-51v-57l87-56 6-3c80 8 187 24 259 35l4 5 66 78Z" fill="#000" ></path>
<path d="m795 346-9 66-9 60-2 17-2 36-7 191a11.999 11.999 0 0 1-14 11l-79-17-200-45-114-25 3-175 1-51 1-60v-62c19-1 50 1 87 5a5591.76 5591.76 0 0 1 344 49Z" fill="#F14743" ></path>
<path opacity=".1" d="m820 452-43 20-215 106-199-164-41-33-5 3 45 81-3 175 114 25 15 27c1 3 4 5 7 6l155 37c5 2 11-1 13-6l10-19 78 17c7 2 13-2 15-9v-2l7-191 50-67v-1l-3-5Z" fill="#000" style="transform: translate3d(0px, 6.60105px, 0px);"></path>
<path d="m317 384-18 305c0 6 4 11 9 13l474 105a11.994 11.994 0 0 0 9.442-2.391A11.997 11.997 0 0 0 796 796l28-339-262 129-245-202Z" fill="#DCDFED" style="transform: translate3d(0px, 7.25494px, 0px);"></path><path opacity=".1" d="m303 696 246-151c7-5 17-3 23 3l220 257c1 1 1 0 0 0L303 696c-2 1-2 0 0 0Z" fill="#000" style="transform: translate3d(0px, 4.28594px, 0px);"></path>
<path d="m304 696 244-147c8-5 18-3 24 4l218 251v3l-2 1-483-107-2-3 1-2Z" fill="#E3E5F1" style="transform: translate3d(0px, 4.81912px, 0px);"></path>
</svg>
</div>
<h1 class="text-2xl leading-9 tracking-tight text-gray-900 sm:text-2xl sm:leading-10 dark:text-gray-300" >Emailnator Free Temp Gmail Email Generator</h1>
<span class="text-center text-gray-700 lg:max-w-2xl dark:text-gray-400 text-lg">Use <strong>Emailnator</strong> to generate a <strong>disposable email address</strong> instantly. No registration. No spam. Just quick, secure inboxes that work for signups, verifications, and more.</span>
</div>
</div>
</div>
</div>
</div>
<div class="flex justify-center items-center mt-4">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 p-1">
<!-- Card 1 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-600 hover:via-zinc-800 hover:to-zinc-700">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.shield-check/>
<p class="text-lg font-semibold dark:text-white text-accent-content">Emailnator Disposable Mailbox</p>
<p class="font-md text-slate-500">
<strong>Emailnator</strong> gives you quick access to <strong>one-time use email addresses</strong> with zero tracking. Ideal for keeping your personal inbox spam-free and secure.
</p>
</div>
</div>
</div>
</div>
<!-- Card 2 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-700 hover:via-zinc-800 hover:to-zinc-700">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.user />
<p class="text-lg font-semibold dark:text-white text-accent-content">Get Emailnator Temp Mail Now</p>
<p class="font-md text-slate-500">
Receive emails online using <strong>Emailnator temp mail</strong> instant inboxes that refresh frequently, perfect for one-time verifications or testing services.
</p>
</div>
</div>
</div>
</div>
<!-- Card 3 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-700 hover:via-zinc-800 hover:to-zinc-600">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.clock />
<p class="text-lg font-semibold dark:text-white text-accent-content">Clean Temporary Inboxes</p>
<p class="font-md text-slate-500">Enjoy a clutter-free inbox with <strong>Emailnator</strong>. No ads. No sign-up. Just fast, disposable email solutions that protect your real identity.</p>
</div>
</div>
</div>
</div>
</div>
</div>
@elseif($route == "temp-gmail")
<div class="flex justify-center items-center py-3">
<div class="flex items-center">
<div class="group relative mx-auto w-full overflow-hidden rounded-[16px]">
<div class="relative rounded-[15px] p-6">
<div class="space-y-2 w-full flex flex-col items-center text-center">
<div id="hero-section" class="relative self-center" >
<svg class="h-56 w-56" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1035 832" >
<path opacity=".1" d="M601 95c-66-2-129-20-189-39S291 15 227 4c-42-6-90-7-123 11S61 64 55 93c-4 21-6 44 5 64 8 14 23 26 33 39 34 47 10 105-28 150-17 22-37 42-51 65s-19 49-8 72c12 23 40 40 70 53 61 24 133 31 203 35 156 9 313 5 469 1 57-1 115-2 172-10 32-4 64-10 87-26 29-20 36-54 17-80-33-42-123-52-146-98-13-25 0-52 19-76 39-49 105-93 108-149 2-39-29-78-78-97-51-19-122-17-160 15-39 33-107 46-166 44ZM514.5 831.6c160.604 0 290.8-13.118 290.8-29.3 0-16.182-130.196-29.3-290.8-29.3s-290.8 13.118-290.8 29.3c0 16.182 130.196 29.3 290.8 29.3Z" fill="#F14743" style="transform: translate3d(0px, 11.4579px, 0px);"></path><path d="M249 799s-13-73-61-109c-20-15-35-36-42-60l-5-34" stroke="#535461" stroke-width="2" stroke-miterlimit="10" style="transform: translate3d(0px, 10.8015px, 0px);"></path>
<path d="M111 573c4 8 30 24 30 24s4-30 0-37c-4-8-14-12-23-8s-11 14-7 21Zm-3 55c8 5 39 4 39 4s-14-26-22-31-18-2-22 5-3 18 5 22Zm39 74c10 0 37-16 37-16s-27-16-36-16-17 7-17 16 7 16 16 16Zm43 49c9 2 39-9 39-9s-23-20-33-22-17 4-19 13 4 17 13 18Zm-10-132c-3 8-28 27-28 27s-7-29-3-38 12-12 21-9 13 12 10 20Zm49 59c-5 7-35 17-35 17s3-30 9-36c6-7 16-9 23-3 7 4 9 14 4 21l-1 1Zm40 63c-4 7-31 23-31 23s-3-30 2-38 14-11 22-7 12 14 7 22Z" fill="#F14743" style="transform: translate3d(0px, 8.66419px, 0px);"></path><path opacity=".25" d="M111 573c4 8 30 24 30 24s4-30 0-37c-4-8-14-12-23-8s-11 14-7 21Zm-3 55c8 5 39 4 39 4s-14-26-22-31-18-2-22 5-3 18 5 22Zm39 74c10 0 37-16 37-16s-27-16-36-16-17 7-17 16 7 16 16 16Zm43 49c9 2 39-9 39-9s-23-20-33-22-17 4-19 13 4 17 13 18Zm-10-132c-3 8-28 27-28 27s-7-29-3-38 12-12 21-9 13 12 10 20Zm49 59c-5 7-35 17-35 17s3-30 9-36c6-7 16-9 23-3 7 4 9 14 4 21l-1 1Zm40 63c-4 7-31 23-31 23s-3-30 2-38 14-11 22-7 12 14 7 22Z" fill="#000" style="transform: translate3d(0px, 9.40631px, 0px);"></path>
<path d="M247 797s20-72-9-123c-11-22-16-46-12-70 2-12 5-23 10-34" stroke="#535461" stroke-width="2" stroke-miterlimit="10" style="transform: translate3d(0px, 10.1179px, 0px);"></path>
<path d="M218 538c0 9 18 33 18 33s16-25 15-34-8-16-17-15-16 7-16 16Zm-26 49c5 7 34 19 34 19s-1-30-7-37c-5-7-15-9-23-4a15.005 15.005 0 0 0-6.919 10.106A14.99 14.99 0 0 0 192 587Zm4 83c9 3 40-1 40-1s-17-25-26-28-18 0-22 7 0 18 8 22Zm18 61c8 5 39 7 39 7s-12-28-20-33-18-3-23 4-4 17 3 22h1Zm47-124c-6 6-37 13-37 13s7-29 13-35c7-7 17-7 24-1 6 6 7 15 1 22l-1 1Zm21 72c-9 4-40 2-40 2s15-26 23-30 19-1 23 6c5 8 2 18-6 22Zm9 73c-7 5-39 9-39 9s11-29 18-34 18-4 24 3a15 15 0 0 1-3 22Z" fill="#F14743" style="transform: translate3d(0px, 7.94676px, 0px);"></path><path d="M579 214 317 384l171 308c1 3 4 5 7 6l155 37c5 2 11-1 13-6l77-160 84-112-199-236a35 35 0 0 0-46-7Z" fill="#DCDFED" style="transform: translate3d(0px, 5.38356px, 0px);"></path>
<path opacity=".1" d="m827 459-1 1-49 65-1 2-33 44-66 136-2 4-9 19c-2 5-8 8-13 7l-155-38c-3-1-6-3-8-6l-15-27-2-4-107-194-1-2-45-81 5-3 41-27h1l87-56 5-4 122-79c15-10 35-7 46 7l91 107 4 5 67 78v1l33 39 4 5 1 1Z" fill="#000" style="transform: translate3d(0px, 5.97558px, 0px);"></path>
<path opacity=".1" d="m786 412-8 56-2 17-2 36v3l-7 188a11.999 11.999 0 0 1-14 11l-78-17h-1l-200-45-4-1-110-24 3-169v-6l1-51v-57l87-56 6-3c80 8 187 24 259 35l4 5 66 78Z" fill="#000" ></path>
<path d="m795 346-9 66-9 60-2 17-2 36-7 191a11.999 11.999 0 0 1-14 11l-79-17-200-45-114-25 3-175 1-51 1-60v-62c19-1 50 1 87 5a5591.76 5591.76 0 0 1 344 49Z" fill="#F14743" ></path>
<path opacity=".1" d="m820 452-43 20-215 106-199-164-41-33-5 3 45 81-3 175 114 25 15 27c1 3 4 5 7 6l155 37c5 2 11-1 13-6l10-19 78 17c7 2 13-2 15-9v-2l7-191 50-67v-1l-3-5Z" fill="#000" style="transform: translate3d(0px, 6.60105px, 0px);"></path>
<path d="m317 384-18 305c0 6 4 11 9 13l474 105a11.994 11.994 0 0 0 9.442-2.391A11.997 11.997 0 0 0 796 796l28-339-262 129-245-202Z" fill="#DCDFED" style="transform: translate3d(0px, 7.25494px, 0px);"></path><path opacity=".1" d="m303 696 246-151c7-5 17-3 23 3l220 257c1 1 1 0 0 0L303 696c-2 1-2 0 0 0Z" fill="#000" style="transform: translate3d(0px, 4.28594px, 0px);"></path>
<path d="m304 696 244-147c8-5 18-3 24 4l218 251v3l-2 1-483-107-2-3 1-2Z" fill="#E3E5F1" style="transform: translate3d(0px, 4.81912px, 0px);"></path>
</svg>
</div>
<h1 class="text-2xl leading-9 tracking-tight text-gray-900 sm:text-2xl sm:leading-10 dark:text-gray-300" >Temp Gmail Instant Temporary Gmail Address Generator</h1>
<span class="text-center text-gray-700 lg:max-w-2xl dark:text-gray-400 text-lg">Get a free <strong>Temp Gmail</strong> address without sign-up. Perfect for quick email verification, testing, or blocking spam. Fast, disposable Gmail inboxes made for privacy and ease.</span>
</div>
</div>
</div>
</div>
</div>
<div class="flex justify-center items-center mt-4">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 p-1">
<!-- Card 1 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-600 hover:via-zinc-800 hover:to-zinc-700">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.shield-check/>
<p class="text-lg font-semibold dark:text-white text-accent-content">Secure Temp Gmail Address</p>
<p class="font-md text-slate-500">
Create a <strong>temporary Gmail email address</strong> in seconds. <strong>Temp Gmail</strong> gives you a private inbox to use without sharing your real Gmail. Ideal for sensitive signups or testing.
</p>
</div>
</div>
</div>
</div>
<!-- Card 2 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-700 hover:via-zinc-800 hover:to-zinc-700">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.user />
<p class="text-lg font-semibold dark:text-white text-accent-content">Instant Gmail Inbox for One-Time Use</p>
<p class="font-md text-slate-500">
Use <strong>Temp Gmail</strong> to receive messages instantly online. No delays, no sign-ups just a quick Gmail-based temp email for verifications or app logins.
</p>
</div>
</div>
</div>
</div>
<!-- Card 3 -->
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] hover:bg-gradient-to-r hover:from-zinc-700 hover:via-zinc-800 hover:to-zinc-600">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<flux:icon.clock />
<p class="text-lg font-semibold dark:text-white text-accent-content">Avoid Spam with Temp Gmail</p>
<p class="font-md text-slate-500">Keep your personal Gmail clutter-free. <strong>Temp Gmail</strong> lets you handle signups, downloads, or demo access using a disposable Gmail address that vanishes when you're done.</p>
</div>
</div>
</div>
</div>
</div>
</div>
@endif
@if($faqs)
<div class="mt-10 mb-3 w-full lg:max-w-2xl dark:text-white text-accent-content justify-self-center text-center text-xl" size="xl">
<p>----- Frequently Asked Questions -----</p>
</div>
<div class="mb-3"></div>
<div class="justify-center items-center py-3">
@foreach($faqs as $index => $faq)
<details
class="group border-s-4 border-gray-200 bg-gray-50 p-4 dark:border-gray-800 dark:bg-gray-900 [&_summary::-webkit-details-marker]:hidden"
open
>
<summary class="flex items-center justify-between gap-1.5 text-gray-900 dark:text-white">
<h2 class="text-lg text-gray-700 dark:text-gray-300 font-medium">{{ $faq['title'] }}</h2>
<svg
class="size-5 shrink-0 transition-transform duration-300 group-open:-rotate-180"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</summary>
<p class="pt-4 text-slate-500">
{{ $faq['content'] }}
</p>
</details>
@endforeach
</div>
@endif
<div class="mt-10 mb-3 w-full lg:max-w-2xl dark:text-white text-accent-content justify-self-center text-center text-xl" size="xl">
<p>----- Popular Articles -----</p>
</div>
<div class="mb-3"></div>
<div class="flex justify-center items-center py-3">
<div class="flex justify-center items-center">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 p-1">
@foreach(collect(config('app.blogs'))->take(6) as $blog)
<a href="{{ route('blog', $blog->slug) }}">
<div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] ease-in-out hover:bg-gradient-to-r hover:from-zinc-600 hover:via-zinc-800 hover:to-zinc-700">
<div class="group-hover:animate-spin-slow invisible absolute -top-40 -bottom-40 left-10 right-10 bg-gradient-to-r from-transparent via-gray-600 to-transparent group-hover:visible"></div>
<div class="relative rounded-[15px] dark:bg-zinc-900 bg-zinc-100 dark:text-white text-accent-content p-6">
<div class="space-y-4">
<p class="font-md text-slate-500">
<img src="{{ asset('storage/'.$blog->post_image) }}" class="card-img-top" alt="{{ $blog->slug }}">
</p>
<p class="text-lg font-semibold dark:text-white text-accent-content truncate">{{ $blog->post }}</p>
</div>
</div>
</div>
</div>
</a>
@endforeach
</div>
</div>
</div>
</div>
</span>

View File

@@ -0,0 +1,24 @@
<div class="flex flex-col gap-6">
<x-auth-header
:title="__('Confirm password')"
:description="__('This is a secure area of the application. Please confirm your password before continuing.')"
/>
<!-- Session Status -->
<x-auth-session-status class="text-center" :status="session('status')" />
<form wire:submit="confirmPassword" class="flex flex-col gap-6">
<!-- Password -->
<flux:input
wire:model="password"
:label="__('Password')"
type="password"
required
autocomplete="new-password"
:placeholder="__('Password')"
viewable
/>
<flux:button variant="primary" type="submit" class="w-full">{{ __('Confirm') }}</flux:button>
</form>
</div>

View File

@@ -0,0 +1,27 @@
@section('title'){{ __('Forget Password | ') }}@endsection
<div class="flex flex-col gap-6">
<x-auth-header :title="__('Forgot password')" :description="__('Enter your email to receive a password reset link')" />
<!-- Session Status -->
<x-auth-session-status class="text-center" :status="session('status')" />
<form wire:submit="sendPasswordResetLink" class="flex flex-col gap-6">
<!-- Email Address -->
<flux:input
wire:model="email"
:label="__('Email Address')"
type="email"
required
autofocus
placeholder="email@example.com"
viewable
/>
<flux:button variant="primary" type="submit" class="w-full">{{ __('Email password reset link') }}</flux:button>
</form>
<div class="space-x-1 rtl:space-x-reverse text-center text-sm text-zinc-400">
{{ __('Or, return to') }}
<flux:link :href="route('login')" wire:navigate>{{ __('log in') }}</flux:link>
</div>
</div>

View File

@@ -0,0 +1,53 @@
@section('title'){{ __('Login | ') }}@endsection
<div class="flex flex-col gap-6">
<x-auth-header :title="__('Log in to your account')" :description="__('Enter your email and password below to log in')" />
<!-- Session Status -->
<x-auth-session-status class="text-center" :status="session('status')" />
<form wire:submit="login" class="flex flex-col gap-6">
<!-- Email Address -->
<flux:input
wire:model="email"
:label="__('Email address')"
type="email"
required
autofocus
autocomplete="email"
placeholder="email@example.com"
/>
<!-- Password -->
<div class="relative">
<flux:input
wire:model="password"
:label="__('Password')"
type="password"
required
autocomplete="current-password"
:placeholder="__('Password')"
viewable
/>
@if (Route::has('password.request'))
<flux:link class="absolute end-0 top-0 text-sm" :href="route('password.request')" wire:navigate>
{{ __('Forgot your password?') }}
</flux:link>
@endif
</div>
<!-- Remember Me -->
<flux:checkbox wire:model="remember" :label="__('Remember me')" />
<div class="flex items-center justify-end">
<flux:button variant="primary" type="submit" class="w-full">{{ __('Log in') }}</flux:button>
</div>
</form>
@if (Route::has('register'))
<div class="space-x-1 rtl:space-x-reverse text-center text-sm text-zinc-600 dark:text-zinc-400">
{{ __('Don\'t have an account?') }}
<flux:link :href="route('register')" wire:navigate>{{ __('Sign up') }}</flux:link>
</div>
@endif
</div>

View File

@@ -0,0 +1,63 @@
@section('title'){{ __('Register | ') }}@endsection
<div class="flex flex-col gap-6">
<x-auth-header :title="__('Create an account')" :description="__('Enter your details below to create your account')" />
<!-- Session Status -->
<x-auth-session-status class="text-center" :status="session('status')" />
<form wire:submit="register" class="flex flex-col gap-6">
<!-- Name -->
<flux:input
wire:model="name"
:label="__('Name')"
type="text"
required
autofocus
autocomplete="name"
:placeholder="__('Full name')"
/>
<!-- Email Address -->
<flux:input
wire:model="email"
:label="__('Email address')"
type="email"
required
autocomplete="email"
placeholder="email@example.com"
/>
<!-- Password -->
<flux:input
wire:model="password"
:label="__('Password')"
type="password"
required
autocomplete="new-password"
:placeholder="__('Password')"
viewable
/>
<!-- Confirm Password -->
<flux:input
wire:model="password_confirmation"
:label="__('Confirm password')"
type="password"
required
autocomplete="new-password"
:placeholder="__('Confirm password')"
viewable
/>
<div class="flex items-center justify-end">
<flux:button type="submit" variant="primary" class="w-full">
{{ __('Create account') }}
</flux:button>
</div>
</form>
<div class="space-x-1 rtl:space-x-reverse text-center text-sm text-zinc-600 dark:text-zinc-400">
{{ __('Already have an account?') }}
<flux:link :href="route('login')" wire:navigate>{{ __('Log in') }}</flux:link>
</div>
</div>

View File

@@ -0,0 +1,45 @@
<div class="flex flex-col gap-6">
<x-auth-header :title="__('Reset password')" :description="__('Please enter your new password below')" />
<!-- Session Status -->
<x-auth-session-status class="text-center" :status="session('status')" />
<form wire:submit="resetPassword" class="flex flex-col gap-6">
<!-- Email Address -->
<flux:input
wire:model="email"
:label="__('Email')"
type="email"
required
autocomplete="email"
/>
<!-- Password -->
<flux:input
wire:model="password"
:label="__('Password')"
type="password"
required
autocomplete="new-password"
:placeholder="__('Password')"
viewable
/>
<!-- Confirm Password -->
<flux:input
wire:model="password_confirmation"
:label="__('Confirm password')"
type="password"
required
autocomplete="new-password"
:placeholder="__('Confirm password')"
viewable
/>
<div class="flex items-center justify-end">
<flux:button type="submit" variant="primary" class="w-full">
{{ __('Reset password') }}
</flux:button>
</div>
</form>
</div>

View File

@@ -0,0 +1,21 @@
<div class="mt-4 flex flex-col gap-6">
<flux:text class="text-center">
{{ __('Please verify your email address by clicking on the link we just emailed to you.') }}
</flux:text>
@if (session('status') == 'verification-link-sent')
<flux:text class="text-center font-medium !dark:text-green-400 !text-green-600">
{{ __('A new verification link has been sent to the email address you provided during registration.') }}
</flux:text>
@endif
<div class="flex flex-col items-center justify-between space-y-3">
<flux:button wire:click="sendVerification" variant="primary" class="w-full">
{{ __('Resend verification email') }}
</flux:button>
<flux:link class="text-sm cursor-pointer" wire:click="logout">
{{ __('Log out') }}
</flux:link>
</div>
</div>

View File

@@ -0,0 +1,69 @@
@section('title'){{ __('Bulk Gmail Generator - Zemail.me') }}@endsection
<span>
<div class="flex-1 overflow-hidden rounded-xl border border-neutral-200 dark:border-neutral-700 dark:bg-white/[0.03] sm:px-8 sm:py-8 lg:p-12 p-2">
<div class="text-center">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-400 py-2">Bulk Gmail Generator</h2>
<!-- Upper Part: Input + Button -->
<div class="w-full max-w-xl mx-auto mb-2">
<div class="flex rounded-lg overflow-hidden border border-neutral-300 dark:border-neutral-600 bg-white dark:bg-zinc-800">
<input
type="email"
max="500"
wire:model="email"
class="w-full px-4 py-2 bg-white dark:bg-zinc-800 text-zinc-900 dark:text-zinc-100 placeholder-zinc-500 focus:outline-none"
placeholder="Enter your real gmail address"
/>
<button
wire:click="generateBulk"
class="cursor-pointer px-5 py-2 bg-[#4361EE] text-white transition-colors dark:bg-[#4361EE] "
>
Generate
</button>
@if (!empty($bulkEmails))
<button
type="button"
wire:click="downloadBulk"
class="cursor-pointer px-5 py-2 bg-[#00AB55] text-white transition-colors dark:bg-[#00AB55] "
>
<flux:icon.download />
</button>
@endif
</div>
<div x-data="{ quantity: @entangle('quantity') }" class="my-2 flex flex-col sm:flex-row sm:items-center justify-between mt-3 space-y-1 sm:space-y-0">
<label class="text-sm text-gray-700 dark:text-gray-300">Quantity: <span x-text="quantity"></span></label>
<input
type="range"
min="10"
max="500"
step="1"
x-model="quantity"
wire:model.debounce.150ms="quantity"
class="w-full sm:w-2/3"
/>
</div>
@error('quantity')
<span class="text-sm text-red-600 dark:text-red-400">{{ $message }}</span>
@enderror
@error('email')
<span class="text-sm text-red-600 dark:text-red-400">{{ $message }}</span>
@enderror
</div>
@if(count($bulkEmails) > 0)
<div class="w-full flex justify-center my-3 mt-12">
<span class="text-shadow-yellow-200">UNIQUE GMAIL GENERATED</span> : {{ count($bulkEmails)}}
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
@foreach($bulkEmails as $email)
<div class="truncate px-4 py-2 bg-white dark:bg-zinc-800 rounded shadow text-zinc-900 dark:text-zinc-100">
{{ $email }}
</div>
@endforeach
</div>
@endif
</div>
</div>
</span>

View File

@@ -0,0 +1,51 @@
@section('title'){{ __('Bulk Email Generator - Zemail.me') }}@endsection
<span>
<div class="flex-1 overflow-hidden rounded-xl border border-neutral-200 dark:border-neutral-700 dark:bg-white/[0.03] sm:px-8 sm:py-8 lg:p-12 p-2">
<div class="text-center">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-400 py-2">Bulk Email Generator</h2>
<!-- Upper Part: Input + Button -->
<div class="w-full max-w-xl mx-auto mb-2">
<div class="flex rounded-lg overflow-hidden border border-neutral-300 dark:border-neutral-600 bg-white dark:bg-zinc-800">
<input
type="number"
max="500"
wire:model="bulkCount"
class="w-full px-4 py-2 bg-white dark:bg-zinc-800 text-zinc-900 dark:text-zinc-100 placeholder-zinc-500 focus:outline-none"
placeholder="Enter a number (max 500)"
oninput="this.value = Math.min(500, Math.max(0, parseInt(this.value) || ''))"
/>
<button
wire:click="generateBulk"
class="cursor-pointer px-5 py-2 bg-[#4361EE] text-white transition-colors dark:bg-[#4361EE] "
>
Generate
</button>
@if (!empty($bulkEmails))
<button
type="button"
wire:click="downloadBulk"
class="cursor-pointer px-5 py-2 bg-[#00AB55] text-white transition-colors dark:bg-[#00AB55] "
>
<flux:icon.download />
</button>
@endif
</div>
@error('bulkCount')
<span class="text-sm text-red-600 dark:text-red-400">{{ $message }}</span>
@enderror
</div>
@if(count($bulkEmails) > 0)
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
@foreach($bulkEmails as $email)
<div class="truncate px-4 py-2 bg-white dark:bg-zinc-800 rounded shadow text-zinc-900 dark:text-zinc-100">
{{ $email }}
</div>
@endforeach
</div>
@endif
</div>
</div>
</span>

View File

@@ -0,0 +1,57 @@
@section('title'){{ __('Dashboard - Zemail.me') }}@endsection
<div class="flex h-full w-full flex-1 flex-col gap-4 rounded-xl">
<div class="grid auto-rows-min gap-4 md:grid-cols-2">
<article class="flex items-center gap-4 rounded-lg border border-gray-100 bg-white p-6 dark:border-gray-800 dark:bg-white/[0.03]">
<span class="rounded-full bg-[#F04743]/20 p-3 text-[#F04743] dark:bg-[#F04743]/20 dark:text-[#F04743]">
<flux:icon.at-sign />
</span>
<div>
<p class="text-2xl font-medium text-gray-900 dark:text-white">{{ $usageLog['emails_created_count'] }}</p>
<p class="text-sm text-gray-500 dark:text-gray-400">Mailbox Created</p>
</div>
</article>
<article class="flex items-center gap-4 rounded-lg border border-gray-100 bg-white p-6 dark:border-gray-800 dark:bg-white/[0.03]">
<span class="rounded-full bg-[#F04743]/20 p-3 text-[#F04743] dark:bg-[#F04743]/20 dark:text-[#F04743]">
<flux:icon.mails />
</span>
<div>
<p class="text-2xl font-medium text-gray-900 dark:text-white">{{ $usageLog['emails_received_count'] }}</p>
<p class="text-sm text-gray-500 dark:text-gray-400">Emails Received</p>
</div>
</article>
</div>
@if(auth()->user()->subscribedToProduct(config('app.plans')[0]['product_id']))
<article class="flex items-center gap-4 rounded-lg border border-gray-100 bg-white p-6 dark:border-gray-800 dark:bg-white/[0.03]">
<div>
<p class="text-sm text-gray-500 dark:text-gray-400">Your <span class="text-accent-content font-bold">{{ $subscription['name'] ?? "" }}</span> subscription is active and will be
@if($subscription['ends_at'] ?? "") end at<span class="app-primary"> {{ \Carbon\Carbon::make($subscription['ends_at'])->toFormattedDayDateString() ?? "" }}.</span>
@else auto-renew as per the plan you chose.
@endif
To manage you subscription <a href="{{ route('billing') }}" target="_blank" class="text-blue-500">Click here</a></p>
</div>
</article>
@else
<div class="flex-1 overflow-hidden rounded-xl border border-neutral-200 dark:border-neutral-700 dark:bg-white/[0.03]">
<livewire:dashboard.pricing />
</div>
@endif
<script>
document.addEventListener('DOMContentLoaded', function () {
// Check if session flash data exists
@if(session()->has('alert'))
setTimeout(function() {
// Emitting showAlert event with type and message from session
Livewire.emit('showAlert', {
type: '{{ session('alert')['type'] }}',
message: '{{ session('alert')['message'] }}'
});
}, 2000); // 2000ms = 2 seconds delay
@endif
});
</script>
</div>

View File

@@ -0,0 +1,483 @@
@section('title'){{ __('Generate Premium Email - Zemail.me') }}@endsection
<span>
<div class="flex-1 overflow-hidden rounded-xl border border-neutral-200 dark:border-neutral-700 dark:bg-white/[0.03] sm:px-8 sm:py-8 lg:p-12">
<div class="text-center mb-6">
<div class="flex items-center justify-center gap-3 sm:gap-4">
{{-- Public Label --}}
<span class="text-xs sm:text-sm font-medium @if($premium === false) text-gray-800 dark:text-gray-100 font-semibold @else text-gray-400 dark:text-gray-500 @endif ">
Public
</span>
{{-- Toggle Switch --}}
<label class="relative inline-flex items-center cursor-pointer">
<input
type="checkbox"
wire:click="toggleMode"
class="sr-only peer"
@checked($premium)
>
<div class="w-8 h-4 bg-gray-300 dark:bg-gray-600 rounded-full peer-checked:bg-[#F14743] dark:peer-checked:bg-[#F14743] transition-colors duration-300"></div>
<div class="absolute left-0.5 top-0.5 w-3 h-3 bg-white border border-gray-300 dark:border-gray-700 rounded-full transition-transform duration-300 peer-checked:translate-x-4"></div>
</label>
{{-- Premium Label --}}
<span class="text-xs sm:text-sm font-medium @if($premium === true) text-[#F14743] font-semibold @else text-gray-400 dark:text-gray-500 @endif">
Premium
</span>
</div>
</div>
<div class="rounded-xl border dark:border-white/[0.1] border-black/[0.3] p-6 shadow-xs ring-white/[0.5] lg:w-1/2 mx-auto">
<div class="text-center">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-400">Generate {{ $premium ? 'Premium' : 'Public' }} Email</h2>
<div class="space-y-6">
<div class="my-6">
<div class="w-auto relative block group/input cursor-pointer" x-on:click="$dispatch('copyEmail')">
<div id="copyEmail" role="textbox" aria-readonly="true" class="truncate w-auto cursor-pointer rounded-lg block disabled:shadow-none dark:shadow-none appearance-none text-base sm:text-sm py-2 h-10 leading-[1.375rem] ps-3 pe-3 bg-zinc-800/5 dark:bg-white/10 dark:disabled:bg-white/[7%] text-zinc-700 placeholder-zinc-500 disabled:placeholder-zinc-400 dark:text-zinc-200 dark:placeholder-white/60 dark:disabled:placeholder-white/40 border-0 text-center">{{ $email ?? __('Create your own Temp Mail') }}</div>
<div class="absolute top-0 bottom-0 flex items-center gap-x-1.5 pe-3 end-0 text-xs text-zinc-400"></div>
</div>
</div>
<div>
<form wire:submit.prevent="create" method="post">
<div>
<div class="mt-2">
<div class="flex items-center rounded-md dark:bg-zinc-700 pl-3 outline-1 -outline-offset-1 outline-zinc-200 has-[input:focus-within]:outline-2 has-[input:focus-within]:-outline-offset-2 dark:has-[input:focus-within]:outline-white has-[input:focus-within]:outline-zinc-900">
<div class="shrink-0 text-base text-gray-500 select-none sm:text-sm/6 mr-1.5">
<flux:icon.mail variant="mini"/>
</div>
<input type="text" name="username" id="username" autocomplete="false" wire:model.defer="username" class="block min-w-0 grow py-2 pr-3 pl-1 text-base dark:text-white text-gray-900 placeholder:text-gray-400 focus:outline-none sm:text-sm/6" placeholder="{{ __('Enter Username') }}">
<div class="grid shrink-0 grid-cols-1 focus-within:relative">
<select id="domain" name="domain" aria-label="Domain" wire:model="domain" class="col-start-1 row-start-1 w-full appearance-none rounded-md py-1.5 pr-7 pl-3 text-base text-gray-500 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 dark:focus:outline-white focus:outline-zinc-900 sm:text-sm/6">
<option class="dark:bg-zinc-700 dark:text-white bg-zinc-100 accent-gray-700" selected>{{ __('Select Domain') }}</option>
@foreach(array_reverse($domains) as $domain)
<option x-on:click="$refs.domain.value = '{{ $domain }}'; $wire.setDomain('{{ $domain }}')" class="dark:bg-zinc-700 dark:text-white bg-zinc-100 accent-gray-700">{{ $domain }}</option>
@endforeach
</select>
<svg class="pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-gray-500 sm:size-4" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" data-slot="icon">
<path fill-rule="evenodd" d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" />
</svg>
</div>
</div>
</div>
</div>
<div class="flex my-6">
<div class="flex w-full">
<div class="w-1/2">
<flux:button wire:click="random()" class="cursor-pointer w-full btn-warning" variant="filled">{{ __('Random') }} Email</flux:button>
</div>
<div class="w-1/2">
<flux:button class="w-full ml-2 btn-success cursor-pointer" type="submit" variant="primary">{{ __('Create') }}</flux:button>
</div>
</div>
</div>
</form>
</div>
<flux:separator text="or" />
<div class="flex w-full">
<div class="w-1/2">
<flux:button wire:click="outlook()" class="cursor-pointer w-full btn-outlook" type="submit" variant="filled">Disposable Gmail</flux:button>
</div>
<div class="w-1/2">
<flux:button wire:click="gmail()" class="cursor-pointer w-full ml-2 btn-gmail" type="submit" variant="filled">Disposable Gmail</flux:button>
</div>
</div>
</div>
</div>
</div>
<div x-data="{ currentTab: 0 }">
<div class="m-auto my-4 px-1 max-w-full max-lg:min-w-fit lg:max-w-96 flex justify-center">
<div class="w-full lg:px-12">
<ui-tabs class="block inline-flex p-1 rounded-lg bg-zinc-800/5 dark:bg-white/10 h-10 p-1 w-full" data-flux-tabs="" role="tablist">
<button type="button" x-on:click="currentTab = 0;" class="flex cursor-pointer whitespace-nowrap flex-1 justify-center items-center gap-2 rounded-md data-selected:shadow-xs text-sm font-medium text-zinc-600 hover:text-zinc-800 dark:hover:text-white dark:text-white/70 data-selected:text-zinc-800 dark:data-selected:text-white data-selected:bg-white dark:data-selected:bg-white/20 px-4" data-flux-tab="data-flux-tab" :data-selected="currentTab === 0 ? '' : null" role="tab">
Inbox
</button>
<button type="button" x-on:click="currentTab = 1;" class="flex cursor-pointer whitespace-nowrap flex-1 justify-center items-center gap-2 rounded-md data-selected:shadow-xs text-sm font-medium text-zinc-600 hover:text-zinc-800 dark:hover:text-white dark:text-white/70 data-selected:text-zinc-800 dark:data-selected:text-white data-selected:bg-white dark:data-selected:bg-white/20 px-4" data-flux-tab="data-flux-tab" :data-selected="currentTab === 1 ? '' : null" role="tab">
Mailbox
</button>
<button type="button" x-on:click="currentTab = 2;" class="flex cursor-pointer whitespace-nowrap flex-1 justify-center items-center gap-2 rounded-md data-selected:shadow-xs text-sm font-medium text-zinc-600 hover:text-zinc-800 dark:hover:text-white dark:text-white/70 data-selected:text-zinc-800 dark:data-selected:text-white data-selected:bg-white dark:data-selected:bg-white/20 px-4" data-flux-tab="data-flux-tab" :data-selected="currentTab === 2 ? '' : null" role="tab">
Email
</button>
</ui-tabs>
</div>
</div>
@if($email !== null)
<div class="rounded-xl border dark:border-white/[0.1] border-black/[0.3]" x-show="currentTab === 0">
<div class="mt-4 w-full text-center">
<div class="flex flex-full flex-row">
<div class="w-1/3"><flux:button class="w-3/4 cursor-pointer" x-on:click="$dispatch('copyEmail')">Copy</flux:button></div>
<div class="w-1/3">
<button type="button" class="relative items-center font-medium justify-center gap-2 whitespace-nowrap disabled:opacity-75 dark:disabled:opacity-75 disabled:cursor-default disabled:pointer-events-none h-10 text-sm rounded-lg px-4 inline-flex bg-white hover:bg-zinc-50 dark:bg-zinc-700 dark:hover:bg-zinc-600/75 text-zinc-800 dark:text-white border border-zinc-200 hover:border-zinc-200 border-b-zinc-300/80 dark:border-zinc-600 dark:hover:border-zinc-600 shadow-xs [[data-flux-button-group]_&amp;]:border-s-0 [:is([data-flux-button-group]>&amp;:first-child,_[data-flux-button-group]_:first-child>&amp;)]:border-s-[1px] w-3/4 cursor-pointer" data-flux-button="data-flux-button" data-flux-group-target="data-flux-group-target" x-on:click="Livewire.dispatch('fetchMessages'); document.getElementById('refresh-icon').classList.add('animate-spin')">
<svg id="refresh-icon" class="shrink-0 [:where(&amp;)]:size-4" data-flux-icon="" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" data-slot="icon">
<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"></path>
<path d="M21 3v5h-5"></path>
<path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"></path>
<path d="M8 16H3v5"></path>
</svg>
Refresh
</button>
</div>
<div class="w-1/3">
<flux:button class="w-3/4 cursor-pointer inbox-btn" wire:click="deleteEmail()" type="submit">{{ __('Delete') }}</flux:button>
</div>
</div>
</div>
<div class="w-full mt-4">
<div x-data="{ show: false, id: 0 }" class="mx-3">
@if($messages)
<div class="mailbox">
<div x-show="!show" class="list">
<div class="mb-3"></div>
@foreach(array_reverse($messages) as $i => $message)
<div class="inbox-list cursor-pointer" x-on:click="show = true; id = {{ $message['id'] }};" data-id="{{ $message['id'] }}">
<div class="block rounded-lg bg-white shadow-md dark:bg-zinc-700 text-left">
<div class="flex items-center px-4 py-4 sm:px-6">
<div class="flex flex-1 items-center min-w-0">
<div class="flex-shrink-0">
<span class="relative inline-block">
{{-- <img src="{{ asset('images/user.webp') }}" class="size-12" alt="inbox-logo" />--}}
<span class="{{ $message['sender_photo']['dark'] }} {{ $message['sender_photo']['light'] }} inline-flex items-center justify-center shrink-0 select-none overflow-hidden rounded-full align-middle size-11 text-[22px] transition">
<span id="sender-logo" class="font-medium leading-none text-gray-100 dark:text-gray-300 dark:group-hover:text-gray-200 group-hover:text-gray-700 truncate">{{ strtoupper(substr($message['sender_name'] ?: $message['sender_email'], 0, 1) ) }}</span>
</span>
@if(!$message['is_seen'])
<span class="shadow-solid absolute bottom-0 right-0 block w-3 h-3 dark:text-gray-500 text-white bg-amber-300 dark:bg-amber-400 rounded-full"></span>
@endif
</span>
</div>
<div class="flex-1 px-4 min-w-0 md:grid md:gap-4 md:grid-cols-2">
<div>
<div class="dark:text-accent text-accent-content text-sm font-medium leading-5 truncate">
{{ $message['sender_name'] }}
</div>
<div class="flex items-center mt-2 dark:text-gray-400 text-gray-500 text-sm leading-5">
<svg fill="currentColor" viewBox="0 0 20 20"
class="flex-shrink-0 mr-1.5 w-5 h-5 text-gray-400">
<path fill-rule="evenodd"
d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884zM18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z"
clip-rule="evenodd"></path>
</svg>
<span class="truncate">{{ $message['sender_email'] }}</span></div>
</div>
<div class="hidden md:block">
<div>
<div class="dark:text-gray-300 text-gray-900 text-sm leading-5 truncate">
{{ $message['subject'] }}
</div>
<div
class="flex items-center mt-2 text-gray-400 dark:text-gray-400 text-sm leading-5 truncate">
{{ Str::limit($message['contentText'], 100) }}
</div>
</div>
</div>
</div>
</div>
<div>
<svg fill="currentColor" viewBox="0 0 20 20" class="w-5 h-5 text-gray-400">
<path fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"></path>
</svg>
</div>
</div>
</div>
</div>
@endforeach
</div>
<div x-show="show" class="message mx-2 mb-2 ">
@foreach(array_reverse($messages) as $message)
<div x-show="id === {{ $message['id'] }}" id="message-{{ $message['id'] }}">
<textarea class="hidden">To: {{ $this->email ?? "" }}&#13;From: "{{ $message['sender_name'] }}" <{{ $message['sender_email'] }}>&#13;Subject: {{ $message['subject'] }}&#13;Date: {{ $message['date'] }}&#13;Content-Type: text/html&#13;&#13;{{ $message['content'] }}</textarea>
<span>
<div class="-mx-2">
<nav class="flex items-center cursor-pointer" x-on:click="show = false">
<flux:icon.chevron-left variant="mini"/>
<flux:text>{{ __('Get back to MailBox') }}</flux:text>
</nav>
<div class="mt-2 md:flex md:items-center md:justify-between">
<div class="flex-1 min-w-0">
<h2 class="dark:text-gray-300 text-gray-900 text-2xl font-bold leading-7 sm:truncate">
{{ $message['subject'] }}
</h2>
</div>
<div class="flex flex-shrink-0 mt-4 overflow-y-auto md:ml-4 md:mt-0 gap-2">
<flux:button iconVariant="mini" iconLeading="download" x-on:click="$dispatch('downloadFile', { download_id: {{ $message['id'] }} })" class="cursor-pointer">Download</flux:button>
{{-- <flux:button iconVariant="mini" iconLeading="file">Source</flux:button>--}}
<flux:button iconVariant="mini" iconLeading="printer" x-on:click="$dispatch('printFile', { print_id: {{ $message['id'] }} })" class="cursor-pointer">Print</flux:button>
<flux:button iconVariant="mini" iconLeading="trash" x-on:click="show = false; id = 0; document.querySelector(`[data-id='{{ $message['id'] }}']`).remove()" wire:click="delete({{ $message['id'] }})" class="cursor-pointer" style="color: #F14743;">{{ __('Delete') }}</flux:button>
</div>
</div>
<div class="mt-4 px-4 py-5 bg-white border-b border-gray-200 dark:border-gray-900 shadow overflow-hidden sm:px-6 sm:rounded-md">
<div class="flex flex-wrap items-center justify-between -ml-4 -mt-4 sm:flex-nowrap">
<div class="ml-4 mt-4">
<div class="flex items-center">
<div class="flex-shrink-0">
<span class="{{ $message['sender_photo']['dark'] }} {{ $message['sender_photo']['light'] }} inline-flex items-center justify-center shrink-0 select-none overflow-hidden rounded-full align-middle size-11 text-[22px] transition">
<span id="sender-logo" class="font-medium leading-none text-gray-100 dark:text-gray-300 dark:group-hover:text-gray-200 group-hover:text-gray-700 truncate">{{ strtoupper(substr($message['sender_name'] ?: $message['sender_email'], 0, 1) ) }}</span>
</span>
</div>
<div class="ml-4">
<div class="text-gray-700 text-lg font-medium leading-6">
{{ $message['sender_name'] }}
<span class="text-gray-700 text-sm font-normal leading-5">{{ $message['sender_email'] }}</span>
</div>
<div class="flex items-center mt-2 text-gray-500 text-sm leading-5">
<svg fill="currentColor" viewBox="0 0 20 20" class="flex-shrink-0 mr-1.5 w-5 h-5 text-gray-400">
<path fill-rule="evenodd" d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884zM18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" clip-rule="evenodd"></path>
</svg>
<span class="truncate"><a href="mailto:{{ $message['sender_email'] }}"
class="ml-1">{{ $email ?? "" }};</a></span></div>
</div>
</div>
</div>
<div class="flex flex-shrink-0 ml-4 mt-4">
<time datetime="2025-04-24T10:11:55+00:00" class="text-gray-500 truncate">
{{ $message['datediff'] }}
</time>
</div>
</div>
<div class="px-4 py-5 sm:px-6">
<iframe srcdoc="{{ $message['content'] }}" class="w-full iframe-min-height">
</iframe>
</div>
@if (count($message['attachments']) > 0)
<div class="grid grid-cols-1 mt-2 text-sm leading-5 text-gray-900 lg:grid-cols-4 md:grid-cols-3 sm:grid-cols-2">
@foreach ($message['attachments'] as $attachment)
<button class="mb-2 mr-2 border border-gray-200 rounded-md text-sm leading-5 hover:text-gray-500 cursor-pointer" onclick="window.open('{{ $attachment['url'] }}', '_blank')">
<div class="py-3 pl-3 pr-4">
<div class="flex flex-1 items-center">
<svg class="h-5 w-5 flex-shrink-0 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M8 4a3 3 0 00-3 3v4a5 5 0 0010 0V7a1 1 0 112 0v4a7 7 0 11-14 0V7a5 5 0 0110 0v4a3 3 0 11-6 0V7a1 1 0 012 0v4a1 1 0 102 0V7a3 3 0 00-3-3z" clip-rule="evenodd"></path>
</svg>
<span class="ml-2 flex-1 truncate">{{ $attachment['file'] }}</span>
</div>
</div>
</button>
@endforeach
</div>
@endif
</div>
</div>
</span>
</div>
@endforeach
</div>
</div>
@else
<div class="mb-3"></div>
<div class="mb-3 block rounded-b-lg bg-white shadow-md dark:bg-zinc-700 items-center">
<div class="flex mailbox-min-height w-full items-center justify-center px-4 py-4 sm:px-6">
<div class="waitingBox flex flex-col items-center relative -space-y-4">
<div class="absolute -mt-4 inset-0 flex justify-center items-center animate-spin duration-2000">
<flux:icon.refresh-cw variant="mini" style="color: #ffffdf" />
</div>
<svg width="120" height="124" viewBox="0 0 92 87" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M26 54.37V38.9C26.003 37.125 26.9469 35.4846 28.48 34.59L43.48 25.84C45.027 24.9468 46.933 24.9468 48.48 25.84L63.48 34.59C65.0285 35.4745 65.9887 37.1167 66 38.9V54.37C66 57.1314 63.7614 59.37 61 59.37H31C28.2386 59.37 26 57.1314 26 54.37Z" fill="#8C92A5"></path>
<path d="M46 47.7L26.68 36.39C26.2325 37.1579 25.9978 38.0312 26 38.92V54.37C26 57.1314 28.2386 59.37 31 59.37H61C63.7614 59.37 66 57.1314 66 54.37V38.9C66.0022 38.0112 65.7675 37.1379 65.32 36.37L46 47.7Z" fill="#CDCDD8"></path>
<path d="M27.8999 58.27C28.7796 58.9758 29.8721 59.3634 30.9999 59.37H60.9999C63.7613 59.37 65.9999 57.1314 65.9999 54.37V38.9C65.9992 38.0287 65.768 37.1731 65.3299 36.42L27.8999 58.27Z" fill="#E5E5F0"></path>
</svg>
<flux:text>{{ $initial ? __('Empty Inbox') : __('Fetching') }}</flux:text>
</div>
</div>
</div>
@endif
</div>
</div>
</div>
@endif
<div class="rounded-xl border dark:border-white/[0.1] border-black/[0.3]" x-show="currentTab === 1">
<div class="w-full text-center">
<div class="p-2 block rounded-xl items-center">
<h2 class="pt-3 text-lg font-semibold mb-4 text-center text-black dark:text-white">Generated Mailbox Addresses</h2>
<!-- Responsive Email List -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-left px-2">
@foreach(array_reverse($mailboxHistory) as $email)
<div wire:click="switchEmail('{{ $email }}')" class="cursor-pointer bg-gray-200 dark:bg-zinc-700 rounded-lg p-4 text-sm text-black dark:text-white flex items-center justify-between">
<span class="truncate">{{ $email }}</span>
<span class="text-gray-500 dark:text-gray-300 text-base"><flux:icon.arrow-right-circle /></span>
</div>
@endforeach
</div>
</div>
</div>
</div>
<div class="rounded-xl border dark:border-white/[0.1] border-black/[0.3]" x-show="currentTab === 2">
<div class="w-full text-center">
<div class="p-2 block rounded-xl items-center">
<h2 class="pt-6 text-lg font-semibold mb-4 text-center text-black dark:text-white">Email History</h2>
<!-- Responsive Email List -->
<div class="w-full mt-4">
<div x-data="{ show: false, id: 0 }" class="mx-3">
@if($emailsHistory)
<div class="mailbox">
<div x-show="!show" class="list">
<div class="mb-3"></div>
@foreach(array_reverse($emailsHistory) as $i => $message)
<div class="inbox-list cursor-pointer" x-on:click="show = true; id = {{ $message['id'] }};" data-id="{{ $message['id'] }}">
<div class="block rounded-lg bg-white shadow-md dark:bg-zinc-700 text-left">
<div class="flex items-center px-4 py-4 sm:px-6">
<div class="flex flex-1 items-center min-w-0">
<div class="flex-shrink-0">
<span class="relative inline-block">
{{-- <img src="{{ asset('images/user.webp') }}" class="size-12" alt="inbox-logo" />--}}
<span class="{{ $message['sender_photo']['dark'] }} {{ $message['sender_photo']['light'] }} inline-flex items-center justify-center shrink-0 select-none overflow-hidden rounded-full align-middle size-11 text-[22px] transition">
<span id="sender-logo" class="font-medium leading-none text-gray-100 dark:text-gray-300 dark:group-hover:text-gray-200 group-hover:text-gray-700 truncate">{{ strtoupper(substr($message['sender_name'] ?: $message['sender_email'], 0, 1) ) }}</span>
</span>
@if(!$message['is_seen'])
<span class="shadow-solid absolute bottom-0 right-0 block w-3 h-3 dark:text-gray-500 text-white bg-amber-300 dark:bg-amber-400 rounded-full"></span>
@endif
</span>
</div>
<div class="flex-1 px-4 min-w-0 md:grid md:gap-4 md:grid-cols-2">
<div>
<div class="dark:text-accent text-accent-content text-sm font-medium leading-5 truncate">
{{ $message['sender_name'] }}
</div>
<div class="flex items-center mt-2 dark:text-gray-400 text-gray-500 text-sm leading-5">
<svg fill="currentColor" viewBox="0 0 20 20"
class="flex-shrink-0 mr-1.5 w-5 h-5 text-gray-400">
<path fill-rule="evenodd"
d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884zM18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z"
clip-rule="evenodd"></path>
</svg>
<span class="truncate">{{ $message['sender_email'] }}</span></div>
</div>
<div class="hidden md:block">
<div>
<div class="dark:text-gray-300 text-gray-900 text-sm leading-5 truncate">
{{ $message['subject'] }}
</div>
<div
class="flex items-center mt-2 text-gray-400 dark:text-gray-400 text-sm leading-5 truncate">
{{ Str::limit($message['contentText'], 100) }}
</div>
</div>
</div>
</div>
</div>
<div>
<svg fill="currentColor" viewBox="0 0 20 20" class="w-5 h-5 text-gray-400">
<path fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"></path>
</svg>
</div>
</div>
</div>
</div>
@endforeach
</div>
<div x-show="show" class="message mx-2 mb-2 ">
@foreach(array_reverse($emailsHistory) as $message)
<div x-show="id === {{ $message['id'] }}" id="message-{{ $message['id'] }}">
<textarea class="hidden">To: {{ $this->email ?? "" }}&#13;From: "{{ $message['sender_name'] }}" <{{ $message['sender_email'] }}>&#13;Subject: {{ $message['subject'] }}&#13;Date: {{ $message['date'] }}&#13;Content-Type: text/html&#13;&#13;{{ $message['content'] }}</textarea>
<span>
<div class="-mx-2">
<nav class="flex items-center cursor-pointer" x-on:click="show = false">
<flux:icon.chevron-left variant="mini"/>
<flux:text>{{ __('Get back to MailBox') }}</flux:text>
</nav>
<div class="mt-2 md:flex md:items-center md:justify-between">
<div class="flex-1 min-w-0">
<h2 class="dark:text-gray-300 text-gray-900 text-2xl font-bold leading-7 sm:truncate">
{{ $message['subject'] }}
</h2>
</div>
<div class="flex flex-shrink-0 mt-4 overflow-y-auto md:ml-4 md:mt-0 gap-2">
<flux:button iconVariant="mini" iconLeading="download" x-on:click="$dispatch('downloadFile', { download_id: {{ $message['id'] }} })" class="cursor-pointer">Download</flux:button>
{{-- <flux:button iconVariant="mini" iconLeading="file">Source</flux:button>--}}
<flux:button iconVariant="mini" iconLeading="printer" x-on:click="$dispatch('printFile', { print_id: {{ $message['id'] }} })" class="cursor-pointer">Print</flux:button>
<flux:button iconVariant="mini" iconLeading="trash" x-on:click="show = false; id = 0; document.querySelector(`[data-id='{{ $message['id'] }}']`).remove()" wire:click="delete({{ $message['id'] }})" class="cursor-pointer" style="color: #F14743;">{{ __('Delete') }}</flux:button>
</div>
</div>
<div class="mt-4 px-4 py-5 bg-white border-b border-gray-200 dark:border-gray-900 shadow overflow-hidden sm:px-6 sm:rounded-md">
<div class="flex flex-wrap items-center justify-between -ml-4 -mt-4 sm:flex-nowrap">
<div class="ml-4 mt-4">
<div class="flex items-center">
<div class="flex-shrink-0">
<span class="{{ $message['sender_photo']['dark'] }} {{ $message['sender_photo']['light'] }} inline-flex items-center justify-center shrink-0 select-none overflow-hidden rounded-full align-middle size-11 text-[22px] transition">
<span id="sender-logo" class="font-medium leading-none text-gray-100 dark:text-gray-300 dark:group-hover:text-gray-200 group-hover:text-gray-700 truncate">{{ strtoupper(substr($message['sender_name'] ?: $message['sender_email'], 0, 1) ) }}</span>
</span>
</div>
<div class="ml-4">
<div class="text-gray-700 text-lg font-medium leading-6">
{{ $message['sender_name'] }}
<span class="text-gray-700 text-sm font-normal leading-5">{{ $message['sender_email'] }}</span>
</div>
<div class="flex items-center mt-2 text-gray-500 text-sm leading-5">
<svg fill="currentColor" viewBox="0 0 20 20" class="flex-shrink-0 mr-1.5 w-5 h-5 text-gray-400">
<path fill-rule="evenodd" d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884zM18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" clip-rule="evenodd"></path>
</svg>
<span class="truncate"><a href="mailto:{{ $message['sender_email'] }}"
class="ml-1">{{ $message['to'][0] ?? "" }};</a></span></div>
</div>
</div>
</div>
<div class="flex flex-shrink-0 ml-4 mt-4">
<time datetime="2025-04-24T10:11:55+00:00" class="text-gray-500 truncate">
{{ $message['datediff'] }}
</time>
</div>
</div>
<div class="px-4 py-5 sm:px-6">
<iframe srcdoc="{{ $message['content'] }}" class="w-full iframe-min-height">
</iframe>
</div>
@if (count($message['attachments']) > 0)
<div class="grid grid-cols-1 mt-2 text-sm leading-5 text-gray-900 lg:grid-cols-4 md:grid-cols-3 sm:grid-cols-2">
@foreach ($message['attachments'] as $attachment)
<button class="mb-2 mr-2 border border-gray-200 rounded-md text-sm leading-5 hover:text-gray-500 cursor-pointer" onclick="window.open('{{ $attachment['url'] }}', '_blank')">
<div class="py-3 pl-3 pr-4">
<div class="flex flex-1 items-center">
<svg class="h-5 w-5 flex-shrink-0 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M8 4a3 3 0 00-3 3v4a5 5 0 0010 0V7a1 1 0 112 0v4a7 7 0 11-14 0V7a5 5 0 0110 0v4a3 3 0 11-6 0V7a1 1 0 012 0v4a1 1 0 102 0V7a3 3 0 00-3-3z" clip-rule="evenodd"></path>
</svg>
<span class="ml-2 flex-1 truncate">{{ $attachment['file'] }}</span>
</div>
</div>
</button>
@endforeach
</div>
@endif
</div>
</div>
</span>
</div>
@endforeach
</div>
</div>
@else
<div class="mb-3"></div>
<div class="mb-3 block rounded-b-lg bg-white shadow-md dark:bg-zinc-700 items-center">
<div class="flex mailbox-min-height w-full items-center justify-center px-4 py-4 sm:px-6">
<div class="waitingBox flex flex-col items-center relative -space-y-4">
<div class="absolute -mt-4 inset-0 flex justify-center items-center animate-spin duration-2000">
<flux:icon.refresh-cw variant="mini" style="color: #ffffdf" />
</div>
<svg width="120" height="124" viewBox="0 0 92 87" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M26 54.37V38.9C26.003 37.125 26.9469 35.4846 28.48 34.59L43.48 25.84C45.027 24.9468 46.933 24.9468 48.48 25.84L63.48 34.59C65.0285 35.4745 65.9887 37.1167 66 38.9V54.37C66 57.1314 63.7614 59.37 61 59.37H31C28.2386 59.37 26 57.1314 26 54.37Z" fill="#8C92A5"></path>
<path d="M46 47.7L26.68 36.39C26.2325 37.1579 25.9978 38.0312 26 38.92V54.37C26 57.1314 28.2386 59.37 31 59.37H61C63.7614 59.37 66 57.1314 66 54.37V38.9C66.0022 38.0112 65.7675 37.1379 65.32 36.37L46 47.7Z" fill="#CDCDD8"></path>
<path d="M27.8999 58.27C28.7796 58.9758 29.8721 59.3634 30.9999 59.37H60.9999C63.7613 59.37 65.9999 57.1314 65.9999 54.37V38.9C65.9992 38.0287 65.768 37.1731 65.3299 36.42L27.8999 58.27Z" fill="#E5E5F0"></path>
</svg>
<flux:text>{{ __('No email history') }}</flux:text>
</div>
</div>
</div>
@endif
</div>
</div>
</div>
</div>
</div>
</div>
</div> <!-- Top Div -->
</span>

View File

@@ -0,0 +1,24 @@
@section('title'){{ __('Subscribe now to access this page') }}@endsection
<span>
<div class="flex-1 overflow-hidden rounded-xl border border-neutral-200 dark:border-neutral-700 dark:bg-white/[0.03] sm:px-8 sm:py-8 lg:p-12">
<div class="text-center p-2">
<!-- Message -->
<div class="w-full flex justify-center mb-6">
{{-- <img class="w-20" src="{{ asset('images/logo.webp') }}" alt="logo" />--}}
<flux:icon.lock-keyhole class="size-20" />
</div>
<h2 class="text-xl sm:text-2xl font-semibold text-gray-800 dark:text-white">
You don't have permission to access this page
</h2>
<p class="text-sm sm:text-base py-2 text-gray-600 dark:text-gray-300">
To continue, you need to subscribe to a plan that grants access.
</p>
<!-- Call to Action Button -->
<a href="{{ route('dashboard') }}"
class="inline-block mt-6 mb-2 px-6 py-3 inbox-btn text-white font-medium text-sm sm:text-base rounded-lg shadow-md transition-colors duration-200">
Subscribe now
</a>
</div>
</div>
</span>

View File

@@ -0,0 +1,46 @@
<div class="mx-auto max-w-3xl px-4 py-8 sm:px-6 sm:py-12 lg:px-8 ">
<div class="w-full mb-8 items-center flex justify-center">
<h1 class="text-center text-3xl text-gray-900 dark:text-gray-200">Purchase Subscription</h1>
</div>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 sm:items-center md:gap-8">
@if(isset($plans))
@foreach($plans as $plan)
<div class="rounded-2xl border dark:border-white/[0.1] border-black/[0.3] p-6 shadow-xs ring-1 ring-white/[0.5] sm:px-8 lg:p-12">
<div class="text-center">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-400">{{ $plan->name }} @if(!$plan->monthly_billing)
<flux:badge variant="solid" size="sm" color="emerald">2 Months Free</flux:badge>
@endif</h2>
<p class="mt-2 sm:mt-4">
<strong class="text-3xl font-bold text-gray-900 dark:text-gray-200 sm:text-4xl">${{ $plan->price }}</strong>
<span class="text-sm font-medium text-gray-700 dark:text-gray-400">/{{ $plan->monthly_billing ? 'month' : 'year' }}</span>
</p>
</div>
<ul class="mt-6 space-y-2">
@if($plan->details)
@forelse ($plan->details as $key => $value)
@if ($value)
<li class="flex items-center gap-1">
@if($value == "true")<flux:icon.check-circle />
@else <flux:icon.circle-x />
@endif
<span class="text-gray-700 dark:text-gray-400 "> {{ $key }} </span>
</li>
@endif
@empty
@endforelse
@endif
</ul>
<flux:button variant="primary" class="w-full mt-6 cursor-pointer" wire:click="choosePlan('{{ $plan->pricing_id }}')">
Choose Plan
</flux:button>
</div>
@endforeach
@endif
</div>
</div>

View File

@@ -2,7 +2,9 @@
@if($action == "random") @if($action == "random")
<flux:button wire:click="random()" class="cursor-pointer w-full btn-primary" type="submit" variant="filled">{{ __('Random') }} Email</flux:button> <flux:button wire:click="random()" class="cursor-pointer w-full btn-primary" type="submit" variant="filled">{{ __('Random') }} Email</flux:button>
@elseif($action == "gmail") @elseif($action == "gmail")
<flux:button wire:click="gmail()" class="cursor-pointer w-full ml-2 btn-warning" type="submit" variant="filled">Disposable Gmail</flux:button> <flux:button wire:click="gmail()" class="cursor-pointer w-full ml-2 btn-gmail" type="submit" variant="filled">Disposable Gmail</flux:button>
@elseif($action == "outlook")
<flux:button wire:click="outlook()" class="cursor-pointer w-full btn-outlook" type="submit" variant="filled">Disposable Outlook</flux:button>
@elseif($action == "delete") @elseif($action == "delete")
<flux:button wire:click="deleteEmail()" class="cursor-pointer" type="submit" variant="danger">{{ __('Delete') }}</flux:button> <flux:button wire:click="deleteEmail()" class="cursor-pointer" type="submit" variant="danger">{{ __('Delete') }}</flux:button>
@elseif($action == "customEmail") @elseif($action == "customEmail")
@@ -32,7 +34,8 @@
<div class="flex my-6"> <div class="flex my-6">
<div class="flex w-full"> <div class="flex w-full">
<flux:button x-on:click="$dispatch('closeModal')" class="w-1/2 cursor-pointer" variant="outline">{{ __('Cancel') }}</flux:button> {{-- <flux:button x-on:click="$dispatch('closeModal')" class="w-1/2 cursor-pointer" variant="outline">{{ __('Cancel') }}</flux:button>--}}
<flux:button wire:click="random()" class="cursor-pointer w-1/2 btn-warning" variant="filled">{{ __('Random') }} Email</flux:button>
<flux:button class="ml-2 w-1/2 btn-success cursor-pointer" type="submit" variant="primary">{{ __('Create') }}</flux:button> <flux:button class="ml-2 w-1/2 btn-success cursor-pointer" type="submit" variant="primary">{{ __('Create') }}</flux:button>
</div> </div>
</div> </div>

View File

@@ -87,7 +87,7 @@
<div class="flex justify-center items-center"> <div class="flex justify-center items-center">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 p-1"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 p-1">
@foreach(config('app.blogs') as $blog) @foreach(collect(config('app.blogs'))->take(6) as $blog)
<a href="{{ route('blog', $blog->slug) }}"> <a href="{{ route('blog', $blog->slug) }}">
<div class="flex items-center"> <div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] ease-in-out hover:bg-gradient-to-r hover:from-zinc-600 hover:via-zinc-800 hover:to-zinc-700"> <div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] ease-in-out hover:bg-gradient-to-r hover:from-zinc-600 hover:via-zinc-800 hover:to-zinc-700">

View File

@@ -6,7 +6,7 @@
<div class="flex justify-center items-center"> <div class="flex justify-center items-center">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 p-1"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 p-1">
@foreach(config('app.blogs') as $i => $blog) @foreach(collect(config('app.blogs'))->reverse() as $i => $blog)
<a href="{{ route('blog', $blog->slug) }}"> <a href="{{ route('blog', $blog->slug) }}">
<div class="flex items-center"> <div class="flex items-center">
<div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] ease-in-out hover:bg-gradient-to-r hover:from-zinc-600 hover:via-zinc-800 hover:to-zinc-700"> <div class="group relative mx-auto w-96 overflow-hidden rounded-[16px] dark:bg-zinc-800 bg-zinc-200 p-[1px] ease-in-out hover:bg-gradient-to-r hover:from-zinc-600 hover:via-zinc-800 hover:to-zinc-700">

View File

@@ -0,0 +1,11 @@
<section class="w-full">
@include('partials.settings-heading')
<x-settings.layout :heading="__('Appearance')" :subheading=" __('Update the appearance settings for your account')">
<flux:radio.group x-data variant="segmented" x-model="$flux.appearance">
<flux:radio value="light" icon="sun">{{ __('Light') }}</flux:radio>
<flux:radio value="dark" icon="moon">{{ __('Dark') }}</flux:radio>
<flux:radio value="system" icon="computer-desktop">{{ __('System') }}</flux:radio>
</flux:radio.group>
</x-settings.layout>
</section>

View File

@@ -0,0 +1,34 @@
<section class="mt-10 space-y-6">
<div class="relative mb-5">
<flux:heading>{{ __('Delete account') }}</flux:heading>
<flux:subheading>{{ __('Delete your account and all of its resources') }}</flux:subheading>
</div>
<flux:modal.trigger name="confirm-user-deletion">
<flux:button variant="danger" x-data="" x-on:click.prevent="$dispatch('open-modal', 'confirm-user-deletion')">
{{ __('Delete account') }}
</flux:button>
</flux:modal.trigger>
<flux:modal name="confirm-user-deletion" :show="$errors->isNotEmpty()" focusable class="max-w-lg">
<form wire:submit="deleteUser" class="space-y-6">
<div>
<flux:heading size="lg">{{ __('Are you sure you want to delete your account?') }}</flux:heading>
<flux:subheading>
{{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.') }}
</flux:subheading>
</div>
<flux:input wire:model="password" :label="__('Password')" type="password" />
<div class="flex justify-end space-x-2 rtl:space-x-reverse">
<flux:modal.close>
<flux:button variant="filled">{{ __('Cancel') }}</flux:button>
</flux:modal.close>
<flux:button variant="danger" type="submit">{{ __('Delete account') }}</flux:button>
</div>
</form>
</flux:modal>
</section>

Some files were not shown because too many files have changed in this diff Show More