Added Create Custom, Random, Gmail Generation
This commit is contained in:
54
app/Http/Controllers/AppController.php
Normal file
54
app/Http/Controllers/AppController.php
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\ZEmail;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class AppController extends Controller
|
||||||
|
{
|
||||||
|
public function mailbox($email = null) {
|
||||||
|
if ($email) {
|
||||||
|
if (json_decode(config('app.settings.configuration_settings'))->enable_create_from_url) {
|
||||||
|
ZEmail::createCustomEmailFull($email);
|
||||||
|
}
|
||||||
|
return redirect()->route('mailbox');
|
||||||
|
}
|
||||||
|
if (!ZEmail::getEmail()) {
|
||||||
|
return redirect()->route('home');
|
||||||
|
}
|
||||||
|
if (json_decode(config('app.settings.configuration_settings'))->disable_mailbox_slug) {
|
||||||
|
return redirect()->route('home');
|
||||||
|
}
|
||||||
|
return $this->app();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function app() {
|
||||||
|
return redirect()->route('home');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getStringBetween($string, $start, $end) {
|
||||||
|
$string = ' ' . $string;
|
||||||
|
$ini = strpos($string, $start);
|
||||||
|
if ($ini == 0) return '';
|
||||||
|
$ini += strlen($start);
|
||||||
|
$len = strpos($string, $end, $ini) - $ini;
|
||||||
|
return substr($string, $ini, $len);
|
||||||
|
}
|
||||||
|
private function setHeaders($page) {
|
||||||
|
$header = $page->header;
|
||||||
|
foreach ($page->meta ? unserialize($page->meta) : [] as $meta) {
|
||||||
|
if ($meta['name'] == 'canonical') {
|
||||||
|
$header .= '<link rel="canonical" href="' . $meta['content'] . '" />';
|
||||||
|
} else if (str_contains($meta['name'], 'og:')) {
|
||||||
|
$header .= '<meta property="' . $meta['name'] . '" content="' . $meta['content'] . '" />';
|
||||||
|
} else {
|
||||||
|
$header .= '<meta name="' . $meta['name'] . '" content="' . $meta['content'] . '" />';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$page->header = $header;
|
||||||
|
return $page;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
13
app/Livewire/Email.php
Normal file
13
app/Livewire/Email.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Email extends Component
|
||||||
|
{
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.email');
|
||||||
|
}
|
||||||
|
}
|
||||||
62
app/Livewire/EmailInbox.php
Normal file
62
app/Livewire/EmailInbox.php
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use App\Models\ZEmail;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Livewire\WithPagination;
|
||||||
|
|
||||||
|
class EmailInbox extends Component
|
||||||
|
{
|
||||||
|
use WithPagination;
|
||||||
|
|
||||||
|
public $currentEmail;
|
||||||
|
public $messages = [];
|
||||||
|
public $selectedMessage = null;
|
||||||
|
public $searchTerm = '';
|
||||||
|
public $refreshInterval = 30; // seconds
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->currentEmail = ZEmail::getEmail();
|
||||||
|
$this->loadMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadMessages()
|
||||||
|
{
|
||||||
|
if ($this->currentEmail) {
|
||||||
|
$this->messages = ZEmail::getMessages($this->currentEmail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function selectMessage($messageId)
|
||||||
|
{
|
||||||
|
$this->selectedMessage = $messageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteMessage($messageId)
|
||||||
|
{
|
||||||
|
ZEmail::deleteMessage($messageId);
|
||||||
|
$this->loadMessages();
|
||||||
|
$this->selectedMessage = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateNewEmail()
|
||||||
|
{
|
||||||
|
$this->currentEmail = ZEmail::generateRandomEmail();
|
||||||
|
$this->loadMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPollingInterval()
|
||||||
|
{
|
||||||
|
return $this->refreshInterval * 1000; // Convert to milliseconds
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.email-inbox', [
|
||||||
|
'messages' => $this->messages,
|
||||||
|
'currentEmail' => $this->currentEmail
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
134
app/Livewire/Frontend/Action.php
Normal file
134
app/Livewire/Frontend/Action.php
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Frontend;
|
||||||
|
|
||||||
|
use App\Models\Log;
|
||||||
|
use App\Models\ZEmail;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Action extends Component
|
||||||
|
{
|
||||||
|
public $username, $email, $emails, $domain, $domains, $action, $initial;
|
||||||
|
|
||||||
|
|
||||||
|
public function mount() {
|
||||||
|
$this->domains = json_decode(config('app.settings.configuration_settings'))->domains ?? [];
|
||||||
|
$this->email = ZEmail::getEmail();
|
||||||
|
$this->emails = ZEmail::getEmails();
|
||||||
|
$this->validateDomainInEmail();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ') . json_decode(config('app.settings.configuration_settings'))->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.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->email = ZEmail::createCustomEmail($this->username, $this->domain);
|
||||||
|
$this->dispatch('updateEmail');
|
||||||
|
$this->dispatch('closeModal');
|
||||||
|
|
||||||
|
}
|
||||||
|
public function random() {
|
||||||
|
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::generateRandomEmail();
|
||||||
|
$this->dispatch('updateEmail');
|
||||||
|
$this->dispatch('closeModal');
|
||||||
|
|
||||||
|
//$this->redirect(route('mailbox'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function gmail() {
|
||||||
|
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::generateRandomGmail();
|
||||||
|
$this->dispatch('updateEmail');
|
||||||
|
$this->dispatch('closeModal');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteEmail() {
|
||||||
|
ZEmail::removeEmail($this->email);
|
||||||
|
// if (count($this->emails) <= 1 && json_decode(config('app.settings.configuration_settings'))->after_last_email_delete == 'redirect_to_homepage') {
|
||||||
|
// return redirect()->route('home');
|
||||||
|
// }
|
||||||
|
$this->email = ZEmail::getEmail(true);
|
||||||
|
$this->emails = ZEmail::getEmails();
|
||||||
|
|
||||||
|
$this->dispatch('updateEmail');
|
||||||
|
$this->dispatch('closeModal');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function showAlert($type, $message): void
|
||||||
|
{
|
||||||
|
$this->dispatch('showAlert', ['type' => $type, 'message' => $message]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkEmailLimit() {
|
||||||
|
$logs = Log::select('ip', 'email')->where('ip', request()->ip())->where('created_at', '>', Carbon::now()->subDay())->groupBy('email')->groupBy('ip')->get();
|
||||||
|
if (count($logs) >= json_decode(config('app.settings.configuration_settings'))->email_limit) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkUsedEmail() {
|
||||||
|
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() {
|
||||||
|
$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
|
||||||
|
{
|
||||||
|
$data = explode('@', $this->email);
|
||||||
|
if (isset($data[1])) {
|
||||||
|
$domain = $data[1];
|
||||||
|
$domains = json_decode(config('app.settings.configuration_settings'))->domains ?? [];
|
||||||
|
if (!in_array($domain, $domains)) {
|
||||||
|
$key = array_search($this->email, $this->emails);
|
||||||
|
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('home');
|
||||||
|
} else {
|
||||||
|
redirect()->route('mailbox');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render() {
|
||||||
|
return view('livewire.frontend.action');
|
||||||
|
}
|
||||||
|
}
|
||||||
222
app/Livewire/Frontend/ActionOld.php
Normal file
222
app/Livewire/Frontend/ActionOld.php
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Frontend;
|
||||||
|
|
||||||
|
use App\Models\Log;
|
||||||
|
use App\Models\ZEmail;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class ActionOld extends Component
|
||||||
|
{
|
||||||
|
public $in_app = false;
|
||||||
|
public $user, $domain, $domains, $email, $emails, $captcha;
|
||||||
|
|
||||||
|
protected $listeners = ['syncEmail', 'checkReCaptcha3'];
|
||||||
|
|
||||||
|
public function mount() {
|
||||||
|
$this->domains = config('app.settings.domains');
|
||||||
|
$this->email = ZEmail::getEmail();
|
||||||
|
$this->emails = ZEmail::getEmails();
|
||||||
|
$this->validateDomainInEmail();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function refreshMessages()
|
||||||
|
{
|
||||||
|
$this->emit('fetchMessages');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadMsg($email) {
|
||||||
|
$this->email = $email;
|
||||||
|
if (count($this->emails) == 0) {
|
||||||
|
$this->emails = [$email];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function syncEmail($email) {
|
||||||
|
$this->email = $email;
|
||||||
|
if (count($this->emails) == 0) {
|
||||||
|
$this->emails = [$email];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDomain($domain) {
|
||||||
|
$this->domain = $domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkReCaptcha3($token, $action) {
|
||||||
|
$response = Http::post('https://www.google.com/recaptcha/api/siteverify?secret=' . config('app.settings.recaptcha3.secret_key') . '&response=' . $token);
|
||||||
|
$data = $response->json();
|
||||||
|
if ($data['success']) {
|
||||||
|
$captcha = $data['score'];
|
||||||
|
if ($captcha > 0.5) {
|
||||||
|
if ($action == 'create') {
|
||||||
|
$this->create();
|
||||||
|
} else {
|
||||||
|
$this->random();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return $this->showAlert('error', __('Captcha Failed! Please try again'));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return $this->showAlert('error', __('Captcha Failed! Error: ') . json_encode($data['error-codes']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function create() {
|
||||||
|
if (!$this->user) {
|
||||||
|
return $this->showAlert('error', __('Please enter Username'));
|
||||||
|
}
|
||||||
|
$this->checkDomainInUsername();
|
||||||
|
if (strlen($this->user) < config('app.settings.custom.min') || strlen($this->user) > config('app.settings.custom.max')) {
|
||||||
|
return $this->showAlert('error', __('Username length cannot be less than') . ' ' . config('app.settings.custom.min') . ' ' . __('and greater than') . ' ' . config('app.settings.custom.max'));
|
||||||
|
}
|
||||||
|
if (!$this->domain) {
|
||||||
|
return $this->showAlert('error', __('Please Select a Domain'));
|
||||||
|
}
|
||||||
|
if (in_array($this->user, config('app.settings.forbidden_ids'))) {
|
||||||
|
return $this->showAlert('error', __('Username not allowed'));
|
||||||
|
}
|
||||||
|
if (!$this->checkEmailLimit()) {
|
||||||
|
return $this->showAlert('error', __('You have reached daily limit of MAX ') . config('app.settings.email_limit', 5) . __(' 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->validateCaptcha()) {
|
||||||
|
return $this->showAlert('error', __('Invalid Captcha. Please try again'));
|
||||||
|
}
|
||||||
|
$this->email = ZEmail::createCustomEmail($this->user, $this->domain);
|
||||||
|
$this->redirect(route('mailbox'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function random() {
|
||||||
|
if (!$this->checkEmailLimit()) {
|
||||||
|
return $this->showAlert('error', __('You have reached daily limit of maximum ') . config('app.settings.email_limit', 5) . __(' temp mail addresses.'));
|
||||||
|
}
|
||||||
|
if (!$this->validateCaptcha()) {
|
||||||
|
return $this->showAlert('error', __('Invalid Captcha. Please try again'));
|
||||||
|
}
|
||||||
|
$this->email = ZEmail::generateRandomEmail();
|
||||||
|
$this->redirect(route('mailbox'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tempgmail() {
|
||||||
|
if (!$this->checkEmailLimit()) {
|
||||||
|
return $this->showAlert('error', __('You have reached daily limit of maximum ') . config('app.settings.email_limit', 5) . __(' temp mail addresses.'));
|
||||||
|
}
|
||||||
|
if (!$this->validateCaptcha()) {
|
||||||
|
return $this->showAlert('error', __('Invalid Captcha. Please try again'));
|
||||||
|
}
|
||||||
|
$this->email = ZEmail::generateRandomGmail();
|
||||||
|
$this->redirect(route('mailbox'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteEmail() {
|
||||||
|
ZEmail::removeEmail($this->email);
|
||||||
|
if (count($this->emails) == 1 && config('app.settings.after_last_email_delete') == 'redirect_to_homepage') {
|
||||||
|
return redirect()->route('home');
|
||||||
|
}
|
||||||
|
$this->email = ZEmail::getEmail(true);
|
||||||
|
$this->emails = ZEmail::getEmails();
|
||||||
|
return redirect()->route('mailbox');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render() {
|
||||||
|
if (count($this->emails) >= intval(config('app.settings.email_limit', 5))) {
|
||||||
|
for ($i = 0; $i < (count($this->emails) - intval(config('app.settings.email_limit', 5))); $i++) {
|
||||||
|
ZEmail::removeEmail($this->emails[$i]);
|
||||||
|
}
|
||||||
|
$this->emails = ZEmail::getEmails();
|
||||||
|
ZEmail::setEmail($this->email);
|
||||||
|
}
|
||||||
|
return view('livewire.frontend.action');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private Functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
private function showAlert($type, $message) {
|
||||||
|
$this->dispatchBrowserEvent('showAlert', ['type' => $type, 'message' => $message]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't allow used email
|
||||||
|
*/
|
||||||
|
private function checkUsedEmail() {
|
||||||
|
if (config('app.settings.disable_used_email', false)) {
|
||||||
|
$check = Log::where('email', $this->user . '@' . $this->domain)->where('ip', '<>', request()->ip())->count();
|
||||||
|
if ($check > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate Captcha
|
||||||
|
*/
|
||||||
|
private function validateCaptcha() {
|
||||||
|
if (config('app.settings.captcha') == 'hcaptcha') {
|
||||||
|
$response = Http::asForm()->post('https://hcaptcha.com/siteverify', [
|
||||||
|
'response' => $this->captcha,
|
||||||
|
'secret' => config('app.settings.hcaptcha.secret_key')
|
||||||
|
])->object();
|
||||||
|
return $response->success;
|
||||||
|
} else if (config('app.settings.captcha') == 'recaptcha2') {
|
||||||
|
$response = Http::asForm()->post('https://www.google.com/recaptcha/api/siteverify', [
|
||||||
|
'response' => $this->captcha,
|
||||||
|
'secret' => config('app.settings.recaptcha2.secret_key')
|
||||||
|
])->object();
|
||||||
|
return $response->success;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the user is crossing email limit
|
||||||
|
*/
|
||||||
|
private function checkEmailLimit() {
|
||||||
|
$logs = Log::select('ip', 'email')->where('ip', request()->ip())->where('created_at', '>', Carbon::now()->subDay())->groupBy('email')->groupBy('ip')->get();
|
||||||
|
if (count($logs) >= config('app.settings.email_limit', 5)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if Username already consist of Domain
|
||||||
|
*/
|
||||||
|
private function checkDomainInUsername() {
|
||||||
|
$parts = explode('@', $this->user);
|
||||||
|
if (isset($parts[1])) {
|
||||||
|
if (in_array($parts[1], $this->domains)) {
|
||||||
|
$this->domain = $parts[1];
|
||||||
|
}
|
||||||
|
$this->user = $parts[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate if Domain in Email Exist
|
||||||
|
*/
|
||||||
|
private function validateDomainInEmail() {
|
||||||
|
$data = explode('@', $this->email);
|
||||||
|
if (isset($data[1])) {
|
||||||
|
$domain = $data[1];
|
||||||
|
$domains = config('app.settings.domains');
|
||||||
|
if (!in_array($domain, $domains)) {
|
||||||
|
$key = array_search($this->email, $this->emails);
|
||||||
|
TMail::removeEmail($this->email);
|
||||||
|
if ($key == 0 && count($this->emails) == 1 && config('app.settings.after_last_email_delete') == 'redirect_to_homepage') {
|
||||||
|
return redirect()->route('home');
|
||||||
|
} else {
|
||||||
|
return redirect()->route('mailbox');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
109
app/Livewire/Frontend/App.php
Normal file
109
app/Livewire/Frontend/App.php
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Frontend;
|
||||||
|
|
||||||
|
use App\Models\Message;
|
||||||
|
use App\Models\ZEmail;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class App extends Component
|
||||||
|
{
|
||||||
|
public $messages = [];
|
||||||
|
public $deleted = [];
|
||||||
|
public $error = '';
|
||||||
|
public $email;
|
||||||
|
public $initial;
|
||||||
|
public $overflow = false;
|
||||||
|
|
||||||
|
protected $listeners = ['fetchMessages' => 'fetch', 'syncEmail'];
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->email = ZEmail::getEmails();
|
||||||
|
$this->initial = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function syncEmail($email) {
|
||||||
|
$this->email = $email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fetch() {
|
||||||
|
try {
|
||||||
|
$count = count($this->messages);
|
||||||
|
$responses = [];
|
||||||
|
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 == false && count($this->messages) == $count) {
|
||||||
|
$this->overflow = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->overflow = false;
|
||||||
|
}
|
||||||
|
foreach ($notifications as $notification) {
|
||||||
|
$this->dispatchBrowserEvent('showNewMailNotification', $notification);
|
||||||
|
}
|
||||||
|
ZEmail::incrementMessagesStats(count($notifications));
|
||||||
|
} 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->dispatchBrowserEvent('stopLoader');
|
||||||
|
$this->dispatchBrowserEvent('loadDownload');
|
||||||
|
$this->initial = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete($messageId) {
|
||||||
|
if (config('app.beta_feature')) {
|
||||||
|
Message::find($messageId)->delete();
|
||||||
|
}
|
||||||
|
$this->deleted[] = $messageId;
|
||||||
|
foreach ($this->messages as $key => $message) {
|
||||||
|
if ($message['id'] == $messageId) {
|
||||||
|
$directory = './tmp/attachments/' . $messageId;
|
||||||
|
$this->rrmdir($directory);
|
||||||
|
unset($this->messages[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.frontend.app');
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
65
app/Livewire/Frontend/Email.php
Normal file
65
app/Livewire/Frontend/Email.php
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Frontend;
|
||||||
|
|
||||||
|
use App\Models\ZEmail;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Email extends Component
|
||||||
|
{
|
||||||
|
public $list = false;
|
||||||
|
public $type, $email, $emails, $initial;
|
||||||
|
|
||||||
|
protected $listeners = ['updateEmail' => 'syncEmail', 'getEmail' => 'generateEmail'];
|
||||||
|
|
||||||
|
public function mount(): void
|
||||||
|
{
|
||||||
|
$this->email = ZEmail::getEmail();
|
||||||
|
$this->emails = ZEmail::getEmails();
|
||||||
|
$this->initial = false;
|
||||||
|
$this->checkMultipleEmails();
|
||||||
|
}
|
||||||
|
|
||||||
|
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): void
|
||||||
|
{
|
||||||
|
ZEmail::setEmail($email);
|
||||||
|
$this->email = $email;
|
||||||
|
$this->dispatch('updateEmail');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function syncEmail(): void
|
||||||
|
{
|
||||||
|
$this->email = ZEmail::getEmail();
|
||||||
|
$this->emails = ZEmail::getEmails();
|
||||||
|
if (count($this->emails) == 0) {
|
||||||
|
$this->dispatch('getEmail');
|
||||||
|
}
|
||||||
|
$this->checkMultipleEmails();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateEmail(): void
|
||||||
|
{
|
||||||
|
if ($this->email == null) {
|
||||||
|
ZEmail::generateRandomEmail();
|
||||||
|
}
|
||||||
|
$this->checkMultipleEmails();
|
||||||
|
$this->dispatch('updateEmail');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.frontend.email')->with(['email' => $this->email, 'emails' => $this->emails, 'initial' => $this->initial, 'type' => $this->type, 'list' => $this->list]);
|
||||||
|
}
|
||||||
|
}
|
||||||
13
app/Livewire/Home.php
Normal file
13
app/Livewire/Home.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Home extends Component
|
||||||
|
{
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.home');
|
||||||
|
}
|
||||||
|
}
|
||||||
13
app/Livewire/Inbox.php
Normal file
13
app/Livewire/Inbox.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Inbox extends Component
|
||||||
|
{
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.inbox');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Ddeboer\Imap\Search\Date\Since;
|
||||||
|
use Ddeboer\Imap\Search\Email\Cc;
|
||||||
|
use Ddeboer\Imap\Search\Email\To;
|
||||||
|
use Ddeboer\Imap\SearchExpression;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@@ -62,7 +67,7 @@ class Message extends Model
|
|||||||
$obj['sender_email'] = $obj['sender_name'];
|
$obj['sender_email'] = $obj['sender_name'];
|
||||||
}
|
}
|
||||||
$obj['timestamp'] = $message->created_at;
|
$obj['timestamp'] = $message->created_at;
|
||||||
$obj['date'] = $message->created_at->format(json_decode(config('app.settings.configuration_settings'))->date_format, 'd M Y h:i A');
|
$obj['date'] = $message->created_at->format(json_decode(config('app.settings.configuration_settings'))->date_format ?? 'd M Y h:i A');
|
||||||
$obj['datediff'] = $message->created_at->diffForHumans();
|
$obj['datediff'] = $message->created_at->diffForHumans();
|
||||||
$obj['id'] = $message->id;
|
$obj['id'] = $message->id;
|
||||||
$obj['content'] = $content;
|
$obj['content'] = $content;
|
||||||
@@ -105,4 +110,239 @@ class Message extends Model
|
|||||||
}
|
}
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function fetchMessages($email, $type = 'to', $deleted = []): array
|
||||||
|
{
|
||||||
|
$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 = ZEmail::connectMailBox();
|
||||||
|
|
||||||
|
$mailbox = $connection->getMailbox('INBOX');
|
||||||
|
$search = new SearchExpression();
|
||||||
|
if ($type == 'cc') {
|
||||||
|
$search->addCondition(new Cc($email));
|
||||||
|
} else {
|
||||||
|
$search->addCondition(new To($email));
|
||||||
|
}
|
||||||
|
$search->addCondition(new Since((new \DateTime('-1 day'))));
|
||||||
|
$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 = '';
|
||||||
|
$html = $message->getBodyHtml();
|
||||||
|
if ($html) {
|
||||||
|
$content = str_replace('<a', '<a target="blank"', $html);
|
||||||
|
} else {
|
||||||
|
$text = $message->getBodyText();
|
||||||
|
$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['content'] = $content;
|
||||||
|
$obj['attachments'] = [];
|
||||||
|
//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');
|
||||||
|
}
|
||||||
|
if ($message->hasAttachments() && !$blocked) {
|
||||||
|
$attachments = $message->getAttachments();
|
||||||
|
$directory = './tmp/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())) {
|
||||||
|
file_put_contents(
|
||||||
|
$directory . $attachment->getFilename(),
|
||||||
|
$attachment->getDecodedContent()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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 fetchMessages($email, $type = 'to', $deleted = []): array
|
||||||
|
// {
|
||||||
|
// $startTime = microtime(true);
|
||||||
|
// $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 = ZEmail::connectMailBox();
|
||||||
|
//
|
||||||
|
// $mailbox = $connection->getMailbox('INBOX');
|
||||||
|
// $search = new SearchExpression();
|
||||||
|
// if ($type == 'cc') {
|
||||||
|
// $search->addCondition(new Cc($email));
|
||||||
|
// } else {
|
||||||
|
// $search->addCondition(new To($email));
|
||||||
|
// }
|
||||||
|
// $search->addCondition(new Since((new \DateTime('-1 day'))));
|
||||||
|
// $stepStart = microtime(true);
|
||||||
|
// $messages = $mailbox->getMessages($search, \SORTDATE, true);
|
||||||
|
// \Log::info("1111 messages took: " . (microtime(true) - $stepStart));
|
||||||
|
//
|
||||||
|
// $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 = '';
|
||||||
|
// $html = $message->getBodyHtml();
|
||||||
|
// if ($html) {
|
||||||
|
// $content = str_replace('<a', '<a target="blank"', $html);
|
||||||
|
// } else {
|
||||||
|
// $text = $message->getBodyText();
|
||||||
|
// $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['content'] = $content;
|
||||||
|
// $obj['attachments'] = [];
|
||||||
|
// //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');
|
||||||
|
// }
|
||||||
|
// if ($message->hasAttachments() && !$blocked) {
|
||||||
|
// $attachments = $message->getAttachments();
|
||||||
|
// $directory = './tmp/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())) {
|
||||||
|
// file_put_contents(
|
||||||
|
// $directory . $attachment->getFilename(),
|
||||||
|
// $attachment->getDecodedContent()
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// 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();
|
||||||
|
// $endTime = microtime(true);
|
||||||
|
// $executionTime = $endTime - $startTime;
|
||||||
|
// \Log::info("getMessages execution time: {$executionTime} seconds");
|
||||||
|
// return $response;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use Ddeboer\Imap\Search\Date\Since;
|
||||||
use Ddeboer\Imap\Search\Email\Cc;
|
use Ddeboer\Imap\Search\Email\Cc;
|
||||||
use Ddeboer\Imap\Search\Email\To;
|
use Ddeboer\Imap\Search\Email\To;
|
||||||
use Ddeboer\Imap\SearchExpression;
|
use Ddeboer\Imap\SearchExpression;
|
||||||
@@ -38,112 +39,7 @@ class ZEmail extends Model
|
|||||||
if (config('app.beta_feature')) {
|
if (config('app.beta_feature')) {
|
||||||
return Message::getMessages($email);
|
return Message::getMessages($email);
|
||||||
}
|
}
|
||||||
$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');
|
return Message::fetchMessages($email, $type, $deleted);
|
||||||
$connection = ZEmail::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 = '';
|
|
||||||
$html = $message->getBodyHtml();
|
|
||||||
if ($html) {
|
|
||||||
$content = str_replace('<a', '<a target="blank"', $html);
|
|
||||||
} else {
|
|
||||||
$text = $message->getBodyText();
|
|
||||||
$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['content'] = $content;
|
|
||||||
$obj['attachments'] = [];
|
|
||||||
//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');
|
|
||||||
}
|
|
||||||
if ($message->hasAttachments() && !$blocked) {
|
|
||||||
$attachments = $message->getAttachments();
|
|
||||||
$directory = './tmp/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())) {
|
|
||||||
file_put_contents(
|
|
||||||
$directory . $attachment->getFilename(),
|
|
||||||
$attachment->getDecodedContent()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
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 deleteMessage($id): void
|
public static function deleteMessage($id): void
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"filament/filament": "3.3",
|
"filament/filament": "3.3",
|
||||||
"laravel/framework": "^12.0",
|
"laravel/framework": "^12.0",
|
||||||
"laravel/tinker": "^2.10.1",
|
"laravel/tinker": "^2.10.1",
|
||||||
|
"livewire/flux": "^2.1",
|
||||||
"livewire/livewire": "^3.6"
|
"livewire/livewire": "^3.6"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
|||||||
65
composer.lock
generated
65
composer.lock
generated
@@ -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": "3c178f04aa87a34364e767ad999d5889",
|
"content-hash": "8a5cd9a0ccf67c460d95ecb968192ef2",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "anourvalar/eloquent-serialize",
|
"name": "anourvalar/eloquent-serialize",
|
||||||
@@ -3143,6 +3143,69 @@
|
|||||||
],
|
],
|
||||||
"time": "2024-12-08T08:18:47+00:00"
|
"time": "2024-12-08T08:18:47+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "livewire/flux",
|
||||||
|
"version": "v2.1.4",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/livewire/flux.git",
|
||||||
|
"reference": "a19709fc94f5a1b795ce24ad42662bd398c19371"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/livewire/flux/zipball/a19709fc94f5a1b795ce24ad42662bd398c19371",
|
||||||
|
"reference": "a19709fc94f5a1b795ce24ad42662bd398c19371",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"illuminate/console": "^10.0|^11.0|^12.0",
|
||||||
|
"illuminate/support": "^10.0|^11.0|^12.0",
|
||||||
|
"illuminate/view": "^10.0|^11.0|^12.0",
|
||||||
|
"laravel/prompts": "^0.1|^0.2|^0.3",
|
||||||
|
"livewire/livewire": "^3.5.19",
|
||||||
|
"php": "^8.1",
|
||||||
|
"symfony/console": "^6.0|^7.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"aliases": {
|
||||||
|
"Flux": "Flux\\Flux"
|
||||||
|
},
|
||||||
|
"providers": [
|
||||||
|
"Flux\\FluxServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Flux\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"proprietary"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Caleb Porzio",
|
||||||
|
"email": "calebporzio@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "The official UI component library for Livewire.",
|
||||||
|
"keywords": [
|
||||||
|
"components",
|
||||||
|
"flux",
|
||||||
|
"laravel",
|
||||||
|
"livewire",
|
||||||
|
"ui"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/livewire/flux/issues",
|
||||||
|
"source": "https://github.com/livewire/flux/tree/v2.1.4"
|
||||||
|
},
|
||||||
|
"time": "2025-04-14T11:59:19+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "livewire/livewire",
|
"name": "livewire/livewire",
|
||||||
"version": "v3.6.3",
|
"version": "v3.6.3",
|
||||||
|
|||||||
BIN
public/images/logo.webp
Normal file
BIN
public/images/logo.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
BIN
public/images/user.webp
Normal file
BIN
public/images/user.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
public/images/zemail-logo-dark.webp
Normal file
BIN
public/images/zemail-logo-dark.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
public/images/zemail-logo-light.webp
Normal file
BIN
public/images/zemail-logo-light.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -1,11 +1,52 @@
|
|||||||
@import 'tailwindcss';
|
@import 'tailwindcss';
|
||||||
|
|
||||||
|
|
||||||
|
@import '../../vendor/livewire/flux/dist/flux.css';
|
||||||
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
|
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
|
||||||
@source '../../storage/framework/views/*.php';
|
@source '../../storage/framework/views/*.php';
|
||||||
@source '../**/*.blade.php';
|
@source '../**/*.blade.php';
|
||||||
@source '../**/*.js';
|
@source '../**/*.js';
|
||||||
|
|
||||||
|
|
||||||
|
@custom-variant dark (&:where(.dark, .dark *));
|
||||||
|
|
||||||
|
/*@theme {*/
|
||||||
|
/* --font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',*/
|
||||||
|
/* 'Segoe UI Symbol', 'Noto Color Emoji';*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
--font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
--font-sans: Inter, sans-serif;
|
||||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Re-assign Flux's gray of choice... */
|
||||||
|
@theme {
|
||||||
|
--color-zinc-50: var(--color-gray-50);
|
||||||
|
--color-zinc-100: var(--color-gray-100);
|
||||||
|
--color-zinc-200: var(--color-gray-200);
|
||||||
|
--color-zinc-300: var(--color-gray-300);
|
||||||
|
--color-zinc-400: var(--color-gray-400);
|
||||||
|
--color-zinc-500: var(--color-gray-500);
|
||||||
|
--color-zinc-600: var(--color-gray-600);
|
||||||
|
--color-zinc-700: var(--color-gray-800);
|
||||||
|
--color-zinc-800: var(--color-gray-800);
|
||||||
|
--color-zinc-900: var(--color-gray-900);
|
||||||
|
--color-zinc-950: var(--color-gray-950);
|
||||||
|
/*--color-white: var(--color-gray-100);*/
|
||||||
|
}
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--color-accent: var(--color-gray-900);
|
||||||
|
--color-accent-content: var(--color-gray-900);
|
||||||
|
--color-accent-foreground: var(--color-white);
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer theme {
|
||||||
|
.dark {
|
||||||
|
--color-accent: var(--color-white);
|
||||||
|
--color-accent-content: var(--color-white);
|
||||||
|
--color-accent-foreground: var(--color-gray-900);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,60 @@
|
|||||||
.art {
|
.inbox-btn {
|
||||||
color: orange;
|
background-color: #F14743;
|
||||||
|
color: white;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inbox-btn:hover {
|
||||||
|
background-color: #f72a25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
color: white;
|
||||||
|
background-color: #4361ee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-warning {
|
||||||
|
color: white;
|
||||||
|
background-color: #e2a03f;
|
||||||
|
}
|
||||||
|
.btn-success {
|
||||||
|
background-color: #00AB55;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.inbox-list {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.inbox-new {
|
||||||
|
background-color: #00AB55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iframe-min-height {
|
||||||
|
min-height: 70vh;
|
||||||
|
}
|
||||||
|
@keyframes slide-in {
|
||||||
|
from {
|
||||||
|
transform: translateY(-1rem);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateY(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes progress {
|
||||||
|
from {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
width: 0%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-slide-in {
|
||||||
|
animation: slide-in 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-progress {
|
||||||
|
animation: progress 4s linear forwards;
|
||||||
}
|
}
|
||||||
|
|||||||
69
resources/js/boil.js
Normal file
69
resources/js/boil.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
if (window.Livewire && typeof window.Livewire.dispatch === 'function') {
|
||||||
|
setTimeout(() => {
|
||||||
|
Livewire.dispatch('getEmail');
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
|
document.addEventListener('closeModal', () => {
|
||||||
|
document.querySelectorAll('dialog[data-modal]').forEach(dialog => {
|
||||||
|
if (typeof dialog.close === 'function') {
|
||||||
|
dialog.close();
|
||||||
|
console.log(`Closed dialog with data-modal="${dialog.getAttribute('data-modal')}"`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.warn('Livewire is not loaded yet.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function showToast({ type = 'success', message = '' }) {
|
||||||
|
const container = document.getElementById('toast-container');
|
||||||
|
|
||||||
|
const colors = {
|
||||||
|
success: {
|
||||||
|
icon: 'text-green-500 bg-green-100 dark:bg-green-800 dark:text-green-200',
|
||||||
|
svg: `<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 8.207-4 4a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L9 10.586l3.293-3.293a1 1 0 0 1 1.414 1.414Z" />`,
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
icon: 'text-red-500 bg-red-100 dark:bg-red-800 dark:text-red-200',
|
||||||
|
svg: `<path d="M10 0a10 10 0 1 0 10 10A10 10 0 0 0 10 0Zm1 14a1 1 0 0 1-2 0v-2a1 1 0 0 1 2 0Zm0-4a1 1 0 0 1-2 0V6a1 1 0 0 1 2 0Z"/>`,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toast = document.createElement('div');
|
||||||
|
toast.className = `flex items-center w-full max-w-xs p-4 text-gray-500 bg-white rounded-lg shadow dark:text-gray-400 dark:bg-gray-800`;
|
||||||
|
toast.setAttribute('role', 'alert');
|
||||||
|
|
||||||
|
toast.innerHTML = `
|
||||||
|
<div class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 ${colors[type].icon} rounded-lg">
|
||||||
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">${colors[type].svg}</svg>
|
||||||
|
</div>
|
||||||
|
<div class="ml-3 text-sm font-normal">${message}</div>
|
||||||
|
<div class="absolute inset-x-0 bottom-0 h-1 bg-${type === 'error' ? 'red' : 'green'}-500 animate-progress rounded-bl-md rounded-br-md"></div>
|
||||||
|
<button type="button" class="ml-auto text-gray-400 hover:text-gray-900 dark:hover:text-white rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||||
|
onclick="this.parentElement.remove()">
|
||||||
|
<svg class="w-3 h-3" fill="none" viewBox="0 0 14 14"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1l6 6m0 0l6 6M7 7l6-6M7 7L1 13"/></svg>
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
container.appendChild(toast);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
toast.remove();
|
||||||
|
}, 4000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDispatches(dispatches) {
|
||||||
|
dispatches.forEach(dispatch => {
|
||||||
|
if (dispatch.name === "showAlert") {
|
||||||
|
const params = dispatch.params[0];
|
||||||
|
showToast(params);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window.addEventListener("showAlert", (event) => {
|
||||||
|
const detail = event.detail[0];
|
||||||
|
showToast(detail);
|
||||||
|
});
|
||||||
129
resources/views/components/layouts/app.blade.php
Normal file
129
resources/views/components/layouts/app.blade.php
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<title>{{ $title ?? 'Page Title' }}</title>
|
||||||
|
@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
|
||||||
|
</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" />
|
||||||
|
<div class="flex items-center px-2 py-2 cursor-pointer" onclick="window.location.href='{{ route('home') }}'">
|
||||||
|
<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"/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<flux:navlist variant="outline">
|
||||||
|
<flux:button class="cursor-pointer inbox-btn" variant="filled" icon="inbox" x-on:click="$dispatch('getEmail')">Inbox</flux:button>
|
||||||
|
<flux:button class="mt-2 cursor-pointer" variant="filled" icon="refresh-cw" x-on:click="Livewire.dispatch('updateEmail')">Refresh</flux:button>
|
||||||
|
</flux:navlist>
|
||||||
|
<img src="https://placehold.co/300x250?font=roboto" alt="sideAds" />
|
||||||
|
<flux:spacer />
|
||||||
|
<flux:navlist variant="outline">
|
||||||
|
<flux:navlist.item href="#">API</flux:navlist.item>
|
||||||
|
<flux:navlist.item href="#">FAQ</flux:navlist.item>
|
||||||
|
<flux:navlist.item href="#">Privacy</flux:navlist.item>
|
||||||
|
<flux:navlist.item href="#">Feedback</flux:navlist.item>
|
||||||
|
<flux:navlist.item href="#">Contacts</flux:navlist.item>
|
||||||
|
<flux:separator variant="subtle" />
|
||||||
|
</flux:navlist>
|
||||||
|
<p>© {{ config('app.settings.app_name') }}</p>
|
||||||
|
</flux:sidebar>
|
||||||
|
<flux:header sticky container class="bg-zinc-50 dark:bg-zinc-900 border-b border-zinc-200 dark:border-zinc-700">
|
||||||
|
<flux:sidebar.toggle class="lg:hidden" icon="bars-3" inset="left" />
|
||||||
|
<flux:separator vertical class="lg:hidden lg:mx-0 mx-3" />
|
||||||
|
<flux:icon.mail variant="mini"/>
|
||||||
|
<livewire:frontend.email type="header"/>
|
||||||
|
<flux:spacer />
|
||||||
|
<flux:navbar class="me-4">
|
||||||
|
<flux:tooltip content="Language">
|
||||||
|
<flux:button x-data x-on:click="" icon="language" variant="subtle" aria-label="Language" />
|
||||||
|
</flux:tooltip>
|
||||||
|
<flux:tooltip content="Switch Theme">
|
||||||
|
<flux:button x-data x-on:click="$flux.dark = ! $flux.dark" icon="moon" variant="subtle" aria-label="Toggle dark mode" />
|
||||||
|
</flux:tooltip>
|
||||||
|
<flux:dropdown position="top" align="start">
|
||||||
|
<flux:button icon="circle-user-round" variant="subtle" aria-label="Account" />
|
||||||
|
<flux:menu>
|
||||||
|
<flux:text class="p-1">You are signed in as:</flux:text>
|
||||||
|
<div wire:ignore>
|
||||||
|
<livewire:frontend.email />
|
||||||
|
</div>
|
||||||
|
<flux:menu.separator />
|
||||||
|
<div class="flex">
|
||||||
|
<flux:modal.trigger name="create-account">
|
||||||
|
<flux:button class="p-1 w-full" variant="outline" icon="mail-plus" style="">Create Account</flux:button>
|
||||||
|
</flux:modal.trigger>
|
||||||
|
<flux:modal.trigger name="delete-account">
|
||||||
|
<flux:tooltip content="Delete Account">
|
||||||
|
<flux:button class="ml-1 p-1" icon="trash" variant="outline" style="color:#F14743;"></flux:button>
|
||||||
|
</flux:tooltip>
|
||||||
|
</flux:modal.trigger>
|
||||||
|
</div>
|
||||||
|
</flux:menu>
|
||||||
|
</flux:dropdown>
|
||||||
|
</flux:navbar>
|
||||||
|
|
||||||
|
</flux:header>
|
||||||
|
|
||||||
|
|
||||||
|
<flux:modal name="create-account" class="md:w-96">
|
||||||
|
<div class="space-y-6">
|
||||||
|
<div>
|
||||||
|
<flux:heading size="lg">Generate Your Temporary Email</flux:heading>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<livewire:frontend.action action="customEmail" />
|
||||||
|
|
||||||
|
<flux:separator text="or" />
|
||||||
|
|
||||||
|
<div class="flex w-full">
|
||||||
|
<div class="w-1/2">
|
||||||
|
<livewire:frontend.action action="random" />
|
||||||
|
</div>
|
||||||
|
<div class="w-1/2">
|
||||||
|
<livewire:frontend.action action="gmail" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</flux:modal>
|
||||||
|
|
||||||
|
<flux:modal name="delete-account" class="min-w-[22rem]">
|
||||||
|
<div class="space-y-6">
|
||||||
|
<div>
|
||||||
|
<flux:heading size="lg">Delete account?</flux:heading>
|
||||||
|
<flux:text class="mt-2">
|
||||||
|
<p>You're about to delete this account.</p>
|
||||||
|
</flux:text>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<flux:spacer />
|
||||||
|
<flux:modal.close>
|
||||||
|
<flux:button variant="ghost">Cancel</flux:button>
|
||||||
|
</flux:modal.close>
|
||||||
|
<livewire:frontend.action action="delete" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</flux:modal>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<!-- Toast Wrapper (top center) -->
|
||||||
|
<div id="toast-container" class="fixed flex flex-col w-[calc(100%-2rem)] sm:w-96 z-[999999] right-4 top-4 left-1/2 -translate-x-1/2 space-y-2 pointer-events-none"></div>
|
||||||
|
|
||||||
|
@fluxScripts
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<x-filament-panels::page>
|
|
||||||
|
|
||||||
</x-filament-panels::page>
|
|
||||||
43
resources/views/flux/icon/circle-user-round.blade.php
Normal file
43
resources/views/flux/icon/circle-user-round.blade.php
Normal 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"
|
||||||
|
>
|
||||||
|
<path d="M18 20a6 6 0 0 0-12 0" />
|
||||||
|
<circle cx="12" cy="10" r="4" />
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
</svg>
|
||||||
43
resources/views/flux/icon/download.blade.php
Normal file
43
resources/views/flux/icon/download.blade.php
Normal 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"
|
||||||
|
>
|
||||||
|
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
||||||
|
<polyline points="7 10 12 15 17 10" />
|
||||||
|
<line x1="12" x2="12" y1="15" y2="3" />
|
||||||
|
</svg>
|
||||||
44
resources/views/flux/icon/file-down.blade.php
Normal file
44
resources/views/flux/icon/file-down.blade.php
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{{-- 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="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" />
|
||||||
|
<path d="M14 2v4a2 2 0 0 0 2 2h4" />
|
||||||
|
<path d="M12 18v-6" />
|
||||||
|
<path d="m9 15 3 3 3-3" />
|
||||||
|
</svg>
|
||||||
42
resources/views/flux/icon/file.blade.php
Normal file
42
resources/views/flux/icon/file.blade.php
Normal 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="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" />
|
||||||
|
<path d="M14 2v4a2 2 0 0 0 2 2h4" />
|
||||||
|
</svg>
|
||||||
44
resources/views/flux/icon/mail-plus.blade.php
Normal file
44
resources/views/flux/icon/mail-plus.blade.php
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{{-- 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="M22 13V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v12c0 1.1.9 2 2 2h8" />
|
||||||
|
<path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" />
|
||||||
|
<path d="M19 16v6" />
|
||||||
|
<path d="M16 19h6" />
|
||||||
|
</svg>
|
||||||
42
resources/views/flux/icon/mail.blade.php
Normal file
42
resources/views/flux/icon/mail.blade.php
Normal 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"
|
||||||
|
>
|
||||||
|
<rect width="20" height="16" x="2" y="4" rx="2" />
|
||||||
|
<path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" />
|
||||||
|
</svg>
|
||||||
44
resources/views/flux/icon/refresh-cw.blade.php
Normal file
44
resources/views/flux/icon/refresh-cw.blade.php
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{{-- 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="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" />
|
||||||
|
<path d="M21 3v5h-5" />
|
||||||
|
<path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" />
|
||||||
|
<path d="M8 16H3v5" />
|
||||||
|
</svg>
|
||||||
41
resources/views/flux/icon/sparkle.blade.php
Normal file
41
resources/views/flux/icon/sparkle.blade.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{{-- 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="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z" />
|
||||||
|
</svg>
|
||||||
51
resources/views/livewire/email-inbox.blade.php
Normal file
51
resources/views/livewire/email-inbox.blade.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<div>
|
||||||
|
<div class="bg-white shadow rounded-lg p-6">
|
||||||
|
<!-- Email Address Display -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<h2 class="text-2xl font-bold mb-2">Your Disposable Email</h2>
|
||||||
|
<div class="flex items-center space-x-4">
|
||||||
|
<input type="text" value="{{ $currentEmail }}" readonly class="flex-1 p-2 border rounded bg-gray-50" />
|
||||||
|
<button wire:click="generateNewEmail" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
|
||||||
|
Generate New
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Messages List -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
@if(count($messages) > 0)
|
||||||
|
@foreach($messages as $message)
|
||||||
|
<div class="border rounded p-4 hover:bg-gray-50 cursor-pointer {{ $selectedMessage === $message->id ? 'bg-blue-50' : '' }}"
|
||||||
|
wire:click="selectMessage({{ $message->id }})">
|
||||||
|
<div class="flex justify-between items-start">
|
||||||
|
<div>
|
||||||
|
<h3 class="font-semibold">{{ $message->subject }}</h3>
|
||||||
|
<p class="text-sm text-gray-600">From: {{ $message->from }}</p>
|
||||||
|
<p class="text-sm text-gray-500">{{ $message->date }}</p>
|
||||||
|
</div>
|
||||||
|
<button wire:click.stop="deleteMessage({{ $message->id }})"
|
||||||
|
class="text-red-500 hover:text-red-700">
|
||||||
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
@else
|
||||||
|
<div class="text-center py-8 text-gray-500">
|
||||||
|
No messages yet. They will appear here automatically.
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Auto-refresh script -->
|
||||||
|
<script>
|
||||||
|
document.addEventListener('livewire:load', function () {
|
||||||
|
setInterval(() => {
|
||||||
|
@this.loadMessages()
|
||||||
|
}, {{ $this->getPollingInterval() }});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
54
resources/views/livewire/email.blade.php
Normal file
54
resources/views/livewire/email.blade.php
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<div class="-mx-2">
|
||||||
|
<nav class="flex items-center">
|
||||||
|
<flux:icon.chevron-left variant="mini"/>
|
||||||
|
<flux:text>Back</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">
|
||||||
|
Resend Verification Email
|
||||||
|
</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">Download</flux:button>
|
||||||
|
<flux:button iconVariant="mini" iconLeading="file">Source</flux:button>
|
||||||
|
<flux:button iconVariant="mini" iconLeading="printer">Print</flux:button>
|
||||||
|
<flux:button iconVariant="mini" iconLeading="trash">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">
|
||||||
|
<img src="{{ asset('images/user.webp') }}" class="size-12" alt="inbox-logo" />
|
||||||
|
</div>
|
||||||
|
<div class="ml-4">
|
||||||
|
<div class="text-gray-700 text-lg font-medium leading-6">
|
||||||
|
Fake-IT
|
||||||
|
<span class="text-gray-700 text-sm font-normal leading-5">register@receivefreesms.co.uk</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:czxcc@oakon.com"
|
||||||
|
class="ml-1">czxcc@oakon.com;</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">
|
||||||
|
16 minutes ago
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="px-4 py-5 sm:px-6">
|
||||||
|
<iframe src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ?si=fAMHSQ_S73NGoCMX" class="w-full iframe-min-height">
|
||||||
|
|
||||||
|
</iframe>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
42
resources/views/livewire/frontend/action.blade.php
Normal file
42
resources/views/livewire/frontend/action.blade.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<span>
|
||||||
|
@if($action == "random")
|
||||||
|
<flux:button wire:click="random()" class="cursor-pointer w-full btn-primary" type="submit" variant="filled">Random Email</flux:button>
|
||||||
|
@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>
|
||||||
|
@elseif($action == "delete")
|
||||||
|
<flux:button wire:click="deleteEmail()" class="cursor-pointer" type="submit" variant="danger">Delete account</flux:button>
|
||||||
|
@elseif($action == "customEmail")
|
||||||
|
<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" 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>Choose a 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 mt-6">
|
||||||
|
<div class="flex w-full">
|
||||||
|
<flux:button x-on:click="$dispatch('closeModal')" class="w-1/2" type="submit" variant="outline">Cancel</flux:button>
|
||||||
|
<flux:button class="ml-2 w-1/2 btn-success" type="submit" variant="primary">Create Account</flux:button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
3
resources/views/livewire/frontend/app.blade.php
Normal file
3
resources/views/livewire/frontend/app.blade.php
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<div>
|
||||||
|
{{-- Success is as dangerous as failure. --}}
|
||||||
|
</div>
|
||||||
13
resources/views/livewire/frontend/email.blade.php
Normal file
13
resources/views/livewire/frontend/email.blade.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<span>
|
||||||
|
@if($type === "header")<p class="px-2 text-sm cursor-pointer truncate">{{ $email ?? __('...') }}</p>
|
||||||
|
@else<flux:text class="p-1" variant="subtle">{{ $email ?? __('...') }}</flux:text>
|
||||||
|
@if($list)
|
||||||
|
@foreach(array_reverse($emails) as $email_list_item)
|
||||||
|
<span>
|
||||||
|
<flux:menu.separator />
|
||||||
|
<span wire:click="switchEmail('{{$email_list_item}}')" 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">{{ $email_list_item }}</span>
|
||||||
|
</span>
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
11
resources/views/livewire/home.blade.php
Normal file
11
resources/views/livewire/home.blade.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<div>
|
||||||
|
<flux:heading class="mb-3" size="xl" level="1">Inbox</flux:heading>
|
||||||
|
<div class="mb-3"></div>
|
||||||
|
@php
|
||||||
|
for ($i=0; $i<=10; $i++) {
|
||||||
|
@endphp
|
||||||
|
<livewire:inbox />
|
||||||
|
@php
|
||||||
|
}
|
||||||
|
@endphp
|
||||||
|
</div>
|
||||||
48
resources/views/livewire/inbox.blade.php
Normal file
48
resources/views/livewire/inbox.blade.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<div class="inbox-list cursor-pointer" x-on:click="">
|
||||||
|
<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="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>
|
||||||
|
</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">
|
||||||
|
Fake-IT
|
||||||
|
</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">register@receivefreesms.co.uk</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="hidden md:block">
|
||||||
|
<div>
|
||||||
|
<div class="dark:text-gray-300 text-gray-900 text-sm leading-5 truncate">
|
||||||
|
Resend Verification Email
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="flex items-center mt-2 text-gray-400 dark:text-gray-400 text-sm leading-5 truncate">
|
||||||
|
Hi czxcc, Welcome on board, to complete your registration process. Please verify
|
||||||
|
your email by visting following link in your…
|
||||||
|
</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>
|
||||||
@@ -1,25 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Models\ZEmail;
|
use App\Http\Controllers\AppController;
|
||||||
|
use App\Livewire\Email;
|
||||||
|
use App\Livewire\EmailInbox;
|
||||||
|
use App\Livewire\Home;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::get('/', function () {
|
Route::get('/', Home::class)->name('home');
|
||||||
return view('welcome');
|
Route::get('/mailbox', Email::class)->name('mailbox');
|
||||||
});
|
Route::get('/inbox', EmailInbox::class)->name('inbox');
|
||||||
|
|
||||||
Route::get('ac', function () {
|
//Route::get('/add/{email?}', [AppController::class, 'mailbox'])->name('mailbox');
|
||||||
return json_decode(config('app.settings.configuration_settings'))->random_username_length_max ?? 0;
|
|
||||||
//return json_decode(config('app.settings.configuration_settings')) ?? [];
|
|
||||||
});
|
|
||||||
|
|
||||||
Route::get('bc', function (){
|
|
||||||
return ZEmail::check();
|
|
||||||
});
|
|
||||||
|
|
||||||
Route::get('zz', function (){
|
|
||||||
try {
|
|
||||||
ZEmail::connectMailBox();
|
|
||||||
} catch (Exception $ex) {
|
|
||||||
return $ex->getMessage();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import tailwindcss from '@tailwindcss/vite';
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
laravel({
|
laravel({
|
||||||
input: ['resources/css/app.css', 'resources/css/boil.css', 'resources/js/app.js'],
|
input: ['resources/css/app.css', 'resources/css/boil.css', 'resources/js/app.js', 'resources/css/boil.js'],
|
||||||
refresh: true,
|
refresh: true,
|
||||||
}),
|
}),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|||||||
Reference in New Issue
Block a user