From cab2de09ff93c6efe7eaa6ed2399609e826ea651 Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 5 May 2026 16:42:06 +0300 Subject: [PATCH] Feature : ajout IMAP gmail pour teste --- .../Controllers/Api/WebmailController.php | 30 ++ .../app/Services/WebmailService.php | 95 ++++- thanasoft-back/routes/api.php | 1 + .../Emails/EmailSettingsPresentation.vue | 234 +++++++++++ .../emails/MailboxSettingsFormPanel.vue | 377 ++++++++++++++++++ .../src/services/mailboxSettings.ts | 115 ++++++ .../src/stores/mailboxSettingsStore.ts | 117 ++++++ .../src/views/pages/Parametrage/Emails.vue | 214 +++++++++- 8 files changed, 1166 insertions(+), 17 deletions(-) create mode 100644 thanasoft-front/src/components/Organism/Parametrage/Emails/EmailSettingsPresentation.vue create mode 100644 thanasoft-front/src/components/molecules/parametrage/emails/MailboxSettingsFormPanel.vue create mode 100644 thanasoft-front/src/services/mailboxSettings.ts create mode 100644 thanasoft-front/src/stores/mailboxSettingsStore.ts diff --git a/thanasoft-back/app/Http/Controllers/Api/WebmailController.php b/thanasoft-back/app/Http/Controllers/Api/WebmailController.php index af48ab1..f9a378b 100644 --- a/thanasoft-back/app/Http/Controllers/Api/WebmailController.php +++ b/thanasoft-back/app/Http/Controllers/Api/WebmailController.php @@ -264,6 +264,36 @@ class WebmailController extends Controller } } + public function testSmtp(): JsonResponse + { + try { + $user = Auth::user(); + + if (! $user) { + return response()->json([ + 'message' => 'Utilisateur non authentifie.', + ], 401); + } + + $result = $this->webmailService->testSmtp($user->loadMissing('mailboxSetting')); + + return response()->json([ + 'data' => $result, + 'message' => 'Test SMTP envoye avec succes.', + ]); + } catch (\Exception $e) { + Log::error('Error testing mailbox SMTP: ' . $e->getMessage(), [ + 'exception' => $e, + 'user_id' => Auth::id(), + ]); + + return response()->json([ + 'message' => 'Une erreur est survenue lors du test SMTP.', + 'error' => config('app.debug') ? $e->getMessage() : null, + ], 500); + } + } + public function mailboxSettings(): JsonResponse { $user = Auth::user(); diff --git a/thanasoft-back/app/Services/WebmailService.php b/thanasoft-back/app/Services/WebmailService.php index 57c5c83..f94fb78 100644 --- a/thanasoft-back/app/Services/WebmailService.php +++ b/thanasoft-back/app/Services/WebmailService.php @@ -37,7 +37,7 @@ class WebmailService { $fromEmail = $user->email ?: config('mail.from.address'); $fromName = $user->name ?: config('mail.from.name'); - $mailboxSetting = $user->mailboxSetting; + $mailboxSetting = $this->resolveMailboxSetting($user); $message = $this->webmailRepository->create([ 'user_id' => $user->id, @@ -60,6 +60,8 @@ class WebmailService try { if ($mailboxSetting instanceof UserMailboxSetting && $mailboxSetting->hasSmtpConfiguration()) { $this->sendUsingUserSmtp($payload, $mailboxSetting, $fromEmail, $fromName); + } elseif ($mailboxSetting instanceof UserMailboxSetting) { + throw new \RuntimeException('La configuration SMTP utilisateur est incomplete. Renseignez smtp_host, smtp_port, smtp_username et smtp_password dans le parametrage emails.'); } else { $mailable = new WebmailMessageMail([ 'subject' => $payload['subject'] ?? '', @@ -116,16 +118,51 @@ class WebmailService */ public function syncMailbox(User $user, int $limit = 30): array { - $mailboxSetting = $user->mailboxSetting; + $mailboxSetting = $this->resolveMailboxSetting($user); if ($mailboxSetting instanceof UserMailboxSetting && $mailboxSetting->hasImapConfiguration()) { return $this->syncImapInbox($user, $mailboxSetting, $limit); } - $result = $this->syncMailtrapInbox($user, $limit); - $result['source'] = 'mailtrap'; + if ($mailboxSetting instanceof UserMailboxSetting) { + throw new \RuntimeException('La configuration IMAP utilisateur est incomplete. Renseignez imap_host, imap_port, imap_username et imap_password dans le parametrage emails.'); + } - return $result; + throw new \RuntimeException('Aucune configuration mailbox utilisateur n\'est enregistree. Configurez votre boite mail dans le parametrage emails.'); + } + + /** + * @return array{recipient:string, source:string} + */ + public function testSmtp(User $user): array + { + $mailboxSetting = $this->resolveMailboxSetting($user); + + if (! $mailboxSetting instanceof UserMailboxSetting) { + throw new \RuntimeException('Aucune configuration mailbox utilisateur n\'est enregistree. Configurez votre boite mail dans le parametrage emails.'); + } + + if (! $mailboxSetting->hasSmtpConfiguration()) { + throw new \RuntimeException('La configuration SMTP utilisateur est incomplete. Renseignez smtp_host, smtp_port, smtp_username et smtp_password dans le parametrage emails.'); + } + + $recipient = $this->resolveSmtpTestRecipient($user, $mailboxSetting); + $fromEmail = $user->email ?: config('mail.from.address'); + $fromName = $user->name ?: config('mail.from.name'); + $timestamp = now()->format('d/m/Y H:i:s'); + + $this->sendUsingUserSmtp([ + 'to' => [$recipient], + 'cc' => [], + 'bcc' => [], + 'subject' => 'Test SMTP Thanasoft', + 'body' => "Ceci est un email de test SMTP envoye depuis Thanasoft le {$timestamp}.", + ], $mailboxSetting, $fromEmail, $fromName); + + return [ + 'recipient' => $recipient, + 'source' => 'user_smtp', + ]; } /** @@ -245,6 +282,38 @@ class WebmailService return Str::limit(trim(strip_tags($body)), 160, '...'); } + private function resolveMailboxSetting(User $user): ?UserMailboxSetting + { + if ($user->relationLoaded('mailboxSetting')) { + $mailboxSetting = $user->mailboxSetting; + + return $mailboxSetting instanceof UserMailboxSetting ? $mailboxSetting : null; + } + + $mailboxSetting = $user->mailboxSetting()->first(); + + return $mailboxSetting instanceof UserMailboxSetting ? $mailboxSetting : null; + } + + private function resolveSmtpTestRecipient(User $user, UserMailboxSetting $mailboxSetting): string + { + $candidates = [ + $mailboxSetting->smtp_from_address, + $user->email, + $mailboxSetting->smtp_username, + ]; + + foreach ($candidates as $candidate) { + $email = is_string($candidate) ? trim($candidate) : ''; + + if ($email !== '') { + return $email; + } + } + + throw new \RuntimeException('Impossible de determiner l\'adresse de destination du test SMTP. Renseignez smtp_from_address ou l\'email utilisateur.'); + } + private function sendUsingUserSmtp( array $payload, UserMailboxSetting $mailboxSetting, @@ -289,6 +358,7 @@ class WebmailService { $imported = 0; $skipped = 0; + $syncedSince = $mailboxSetting->last_synced_at?->copy()->subDay(); try { $clientManager = new ClientManager([ @@ -313,18 +383,21 @@ class WebmailService $folder = $client->getFolder($mailboxSetting->imap_folder ?: 'INBOX'); $query = $folder->query() - ->leaveUnread() + ->all() ->setFetchOrderDesc() ->limit(max(1, $limit)); - if ($mailboxSetting->last_synced_at) { - $query->whereSince($mailboxSetting->last_synced_at->copy()->subDay()); - } - /** @var iterable $messages */ $messages = $query->get(); foreach ($messages as $imapMessage) { + $messageDate = $imapMessage->getDate()?->toDate(); + + if ($syncedSince && $messageDate && $messageDate->lt($syncedSince)) { + $skipped++; + continue; + } + $messageUid = 'imap-' . (string) $imapMessage->getUid(); $alreadyExists = WebmailMessage::query() @@ -358,7 +431,7 @@ class WebmailService 'body' => $body, 'status' => 'received', 'folder' => 'inbox', - 'received_at' => $imapMessage->getDate()->toDate()->toDateTimeString(), + 'received_at' => ($messageDate ?? now())->toDateTimeString(), 'attachments' => [], 'metadata' => [ 'provider' => 'imap', diff --git a/thanasoft-back/routes/api.php b/thanasoft-back/routes/api.php index f3bfcfd..e2ef0c2 100644 --- a/thanasoft-back/routes/api.php +++ b/thanasoft-back/routes/api.php @@ -70,6 +70,7 @@ Route::middleware('auth:sanctum')->group(function () { Route::prefix('webmail')->group(function () { Route::get('settings', [WebmailController::class, 'mailboxSettings']); Route::put('settings', [WebmailController::class, 'upsertMailboxSettings']); + Route::post('settings/test-smtp', [WebmailController::class, 'testSmtp']); Route::get('messages/stats', [WebmailController::class, 'stats']); Route::get('messages', [WebmailController::class, 'index']); Route::post('messages/send', [WebmailController::class, 'send']); diff --git a/thanasoft-front/src/components/Organism/Parametrage/Emails/EmailSettingsPresentation.vue b/thanasoft-front/src/components/Organism/Parametrage/Emails/EmailSettingsPresentation.vue new file mode 100644 index 0000000..72bc9ba --- /dev/null +++ b/thanasoft-front/src/components/Organism/Parametrage/Emails/EmailSettingsPresentation.vue @@ -0,0 +1,234 @@ + + + + + diff --git a/thanasoft-front/src/components/molecules/parametrage/emails/MailboxSettingsFormPanel.vue b/thanasoft-front/src/components/molecules/parametrage/emails/MailboxSettingsFormPanel.vue new file mode 100644 index 0000000..a2102d0 --- /dev/null +++ b/thanasoft-front/src/components/molecules/parametrage/emails/MailboxSettingsFormPanel.vue @@ -0,0 +1,377 @@ + + + + + diff --git a/thanasoft-front/src/services/mailboxSettings.ts b/thanasoft-front/src/services/mailboxSettings.ts new file mode 100644 index 0000000..08b75a4 --- /dev/null +++ b/thanasoft-front/src/services/mailboxSettings.ts @@ -0,0 +1,115 @@ +import { request } from "./http"; + +export interface MailboxSettings { + id: number; + user_id: number; + imap_host: string | null; + imap_port: number | null; + imap_encryption: string | null; + imap_validate_cert: boolean; + imap_username: string | null; + imap_folder: string | null; + imap_password_configured: boolean; + smtp_host: string | null; + smtp_port: number | null; + smtp_encryption: string | null; + smtp_validate_cert: boolean; + smtp_username: string | null; + smtp_from_address: string | null; + smtp_from_name: string | null; + smtp_password_configured: boolean; + has_imap_configuration: boolean; + has_smtp_configuration: boolean; + last_synced_at: string | null; + last_sync_error: string | null; +} + +export interface MailboxSettingsResponse { + data: MailboxSettings | null; + message: string; +} + +export interface MailboxSyncResult { + imported: number; + skipped: number; + source: string; +} + +export interface MailboxSyncResponse { + data: MailboxSyncResult; + message: string; +} + +export interface MailboxSmtpTestResult { + recipient: string; + source: string; +} + +export interface MailboxSmtpTestResponse { + data: MailboxSmtpTestResult; + message: string; +} + +export interface UpsertMailboxSettingsPayload { + imap_host?: string | null; + imap_port?: number | null; + imap_encryption?: string | null; + imap_validate_cert?: boolean; + imap_username?: string | null; + imap_password?: string | null; + imap_folder?: string | null; + smtp_host?: string | null; + smtp_port?: number | null; + smtp_encryption?: string | null; + smtp_validate_cert?: boolean; + smtp_username?: string | null; + smtp_password?: string | null; + smtp_from_address?: string | null; + smtp_from_name?: string | null; + clear_imap_password?: boolean; + clear_smtp_password?: boolean; +} + +export const MailboxSettingsService = { + async getMailboxSettings(): Promise { + const response = await request({ + url: "/api/webmail/settings", + method: "get", + }); + + return response.data; + }, + + async updateMailboxSettings( + payload: UpsertMailboxSettingsPayload + ): Promise { + const response = await request({ + url: "/api/webmail/settings", + method: "put", + data: payload, + }); + + return response.data; + }, + + async syncMailbox(limit = 30): Promise { + const response = await request({ + url: "/api/webmail/messages/sync", + method: "post", + params: { limit }, + }); + + return response.data; + }, + + async testSmtp(): Promise { + const response = await request({ + url: "/api/webmail/settings/test-smtp", + method: "post", + }); + + return response.data; + }, +}; + +export default MailboxSettingsService; diff --git a/thanasoft-front/src/stores/mailboxSettingsStore.ts b/thanasoft-front/src/stores/mailboxSettingsStore.ts new file mode 100644 index 0000000..9acc5d9 --- /dev/null +++ b/thanasoft-front/src/stores/mailboxSettingsStore.ts @@ -0,0 +1,117 @@ +import { defineStore } from "pinia"; +import { computed, ref } from "vue"; +import MailboxSettingsService from "@/services/mailboxSettings"; +import type { + MailboxSettings, + MailboxSmtpTestResult, + MailboxSyncResult, + UpsertMailboxSettingsPayload, +} from "@/services/mailboxSettings"; + +export const useMailboxSettingsStore = defineStore("mailboxSettings", () => { + const loading = ref(false); + const saving = ref(false); + const syncing = ref(false); + const smtpTesting = ref(false); + const error = ref(null); + const settings = ref(null); + + const isLoading = computed(() => loading.value); + const isSaving = computed(() => saving.value); + const isSyncing = computed(() => syncing.value); + const isSmtpTesting = computed(() => smtpTesting.value); + const currentSettings = computed(() => settings.value); + + const fetchMailboxSettings = async () => { + loading.value = true; + error.value = null; + + try { + settings.value = await MailboxSettingsService.getMailboxSettings(); + return settings.value; + } catch (err: any) { + error.value = + err.response?.data?.message || + err.message || + "Failed to fetch mailbox settings"; + throw err; + } finally { + loading.value = false; + } + }; + + const saveMailboxSettings = async (payload: UpsertMailboxSettingsPayload) => { + saving.value = true; + error.value = null; + + try { + settings.value = await MailboxSettingsService.updateMailboxSettings( + payload + ); + return settings.value; + } catch (err: any) { + error.value = + err.response?.data?.message || + err.message || + "Failed to save mailbox settings"; + throw err; + } finally { + saving.value = false; + } + }; + + const syncMailbox = async (limit = 30): Promise => { + syncing.value = true; + error.value = null; + + try { + const result = await MailboxSettingsService.syncMailbox(limit); + await fetchMailboxSettings(); + return result; + } catch (err: any) { + error.value = + err.response?.data?.message || err.message || "Failed to sync mailbox"; + throw err; + } finally { + syncing.value = false; + } + }; + + const testSmtp = async (): Promise => { + smtpTesting.value = true; + error.value = null; + + try { + return await MailboxSettingsService.testSmtp(); + } catch (err: any) { + error.value = + err.response?.data?.error || + err.response?.data?.message || + err.message || + "Failed to test SMTP"; + throw err; + } finally { + smtpTesting.value = false; + } + }; + + return { + loading, + saving, + syncing, + smtpTesting, + error, + settings, + isLoading, + isSaving, + isSyncing, + isSmtpTesting, + currentSettings, + fetchMailboxSettings, + saveMailboxSettings, + syncMailbox, + testSmtp, + }; +}); + +export default useMailboxSettingsStore; diff --git a/thanasoft-front/src/views/pages/Parametrage/Emails.vue b/thanasoft-front/src/views/pages/Parametrage/Emails.vue index 466ccf7..fcc8783 100644 --- a/thanasoft-front/src/views/pages/Parametrage/Emails.vue +++ b/thanasoft-front/src/views/pages/Parametrage/Emails.vue @@ -1,11 +1,213 @@ -