diff --git a/app/Http/Controllers/MailAccountController.php b/app/Http/Controllers/MailAccountController.php new file mode 100644 index 0000000..af0490e --- /dev/null +++ b/app/Http/Controllers/MailAccountController.php @@ -0,0 +1,121 @@ +get()->map(fn($a) => $this->accountData($a)); + return response()->json(['accounts' => $accounts]); + } + + public function show(MailAccount $mailAccount): JsonResponse + { + return response()->json([ + 'account' => array_merge($this->accountData($mailAccount), ['config' => $mailAccount->config]), + ]); + } + + public function store(Request $request): JsonResponse + { + $data = $this->validated($request); + $account = MailAccount::create($data); + return response()->json(['success' => true, 'account' => $this->accountData($account)], 201); + } + + public function update(Request $request, MailAccount $mailAccount): JsonResponse + { + $data = $this->validated($request, $mailAccount->id); + $mailAccount->update($data); + return response()->json(['success' => true, 'account' => $this->accountData($mailAccount->fresh())]); + } + + public function destroy(MailAccount $mailAccount): JsonResponse + { + $mailAccount->delete(); + return response()->json(['success' => true]); + } + + public function testConnection(MailAccount $mailAccount): JsonResponse + { + try { + if ($mailAccount->type === 'azure') { + $config = array_merge($mailAccount->config, ['from_address' => $mailAccount->from_address]); + (new TokenManager($config))->getToken(); + } else { + $cfg = $mailAccount->config; + $host = $cfg['host'] ?? ''; + $port = (int) ($cfg['port'] ?? 587); + $socket = @fsockopen($host, $port, $errno, $errstr, 5); + if (! $socket) { + throw new \RuntimeException("Cannot connect to {$host}:{$port} — {$errstr}"); + } + fclose($socket); + } + return response()->json(['success' => true]); + } catch (\Exception $e) { + return response()->json(['success' => false, 'message' => $e->getMessage()]); + } + } + + public function toggleEnabled(MailAccount $mailAccount): JsonResponse + { + $mailAccount->update(['enabled' => ! $mailAccount->enabled]); + return response()->json(['success' => true, 'enabled' => $mailAccount->fresh()->enabled]); + } + + private function validated(Request $request, ?int $ignoreId = null): array + { + $nameUnique = 'unique:mail_accounts,name' . ($ignoreId ? ",{$ignoreId}" : ''); + $rules = [ + 'name' => ['required', 'string', 'max:100', 'regex:/^[a-z0-9\-]+$/', $nameUnique], + 'label' => ['required', 'string', 'max:150'], + 'type' => ['required', 'in:azure,smtp'], + 'from_address' => ['required', 'email', 'max:255'], + 'from_name' => ['nullable', 'string', 'max:150'], + 'enabled' => ['boolean'], + ]; + + if ($request->input('type') === 'azure') { + $rules['config.tenant_id'] = ['required', 'string', 'max:100']; + $rules['config.client_id'] = ['required', 'string', 'max:100']; + $rules['config.client_secret'] = ['required', 'string', 'max:500']; + } else { + $rules['config.host'] = ['required', 'string', 'max:255']; + $rules['config.port'] = ['required', 'integer', 'min:1', 'max:65535']; + $rules['config.encryption'] = ['required', 'in:tls,ssl,none']; + $rules['config.username'] = ['nullable', 'string', 'max:255']; + $rules['config.password'] = ['nullable', 'string', 'max:500']; + } + + $v = $request->validate($rules); + return [ + 'name' => $v['name'], + 'label' => $v['label'], + 'type' => $v['type'], + 'from_address' => $v['from_address'], + 'from_name' => $v['from_name'] ?? null, + 'config' => $v['config'], + 'enabled' => $v['enabled'] ?? true, + ]; + } + + private function accountData(MailAccount $account): array + { + return [ + 'id' => $account->id, + 'name' => $account->name, + 'label' => $account->label, + 'type' => $account->type, + 'from_address' => $account->from_address, + 'from_name' => $account->from_name, + 'enabled' => $account->enabled, + ]; + } +} diff --git a/routes/web.php b/routes/web.php index 483f53c..86566b7 100644 --- a/routes/web.php +++ b/routes/web.php @@ -26,6 +26,7 @@ use App\Http\Controllers\Sales\DeliveryNoteController; use App\Http\Controllers\Sales\PaymentReceiptController; use App\Http\Controllers\Sales\SalesInvoiceController; use App\Http\Controllers\Sales\SalesOrderController; +use App\Http\Controllers\MailAccountController; use App\Http\Controllers\SettingsController; use App\Http\Controllers\Settings\ProjectSettingController; use App\Models\Settings\Location; @@ -123,9 +124,13 @@ Route::middleware(['auth', 'verified'])->group(function () { Route::post('settings/integrations/whatsapp', [SettingsController::class, 'updateWhatsapp'])->name('settings.integrations.whatsapp'); Route::post('settings/integrations/test-whatsapp', [SettingsController::class, 'testWhatsappConnection'])->name('settings.integrations.test-whatsapp'); Route::post('settings/integrations/send-test-message', [SettingsController::class, 'sendTestMessage'])->name('settings.integrations.send-test-message'); - Route::post('settings/integrations/azure-mail', [SettingsController::class, 'updateAzureMail'])->name('settings.integrations.azure-mail'); - Route::post('settings/integrations/test-azure-mail', [SettingsController::class, 'testAzureMailConnection'])->name('settings.integrations.test-azure-mail'); - Route::post('settings/integrations/send-test-email', [SettingsController::class, 'sendTestEmail'])->name('settings.integrations.send-test-email'); + Route::get('settings/integrations/mail-accounts', [MailAccountController::class, 'index'])->name('settings.mail-accounts.index'); + Route::post('settings/integrations/mail-accounts', [MailAccountController::class, 'store'])->name('settings.mail-accounts.store'); + Route::get('settings/integrations/mail-accounts/{mailAccount}', [MailAccountController::class, 'show'])->name('settings.mail-accounts.show'); + Route::put('settings/integrations/mail-accounts/{mailAccount}', [MailAccountController::class, 'update'])->name('settings.mail-accounts.update'); + Route::delete('settings/integrations/mail-accounts/{mailAccount}', [MailAccountController::class, 'destroy'])->name('settings.mail-accounts.destroy'); + Route::post('settings/integrations/mail-accounts/{mailAccount}/test', [MailAccountController::class, 'testConnection'])->name('settings.mail-accounts.test'); + Route::patch('settings/integrations/mail-accounts/{mailAccount}/toggle', [MailAccountController::class, 'toggleEnabled'])->name('settings.mail-accounts.toggle'); // Projects settings Route::get('settings/projects', [ProjectSettingController::class, 'index'])->name('settings.projects.index');