feat: add MailAccountController with CRUD + test + toggle routes

This commit is contained in:
Ghassan Yusuf 2026-05-26 12:25:27 +03:00
parent bbc06738e9
commit 8d336466f4
2 changed files with 129 additions and 3 deletions

View File

@ -0,0 +1,121 @@
<?php
namespace App\Http\Controllers;
use App\Models\MailAccount;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use PromoSeven\AzureMailer\Graph\TokenManager;
class MailAccountController extends Controller
{
public function index(): JsonResponse
{
$accounts = MailAccount::orderBy('name')->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,
];
}
}

View File

@ -26,6 +26,7 @@ use App\Http\Controllers\Sales\DeliveryNoteController;
use App\Http\Controllers\Sales\PaymentReceiptController; use App\Http\Controllers\Sales\PaymentReceiptController;
use App\Http\Controllers\Sales\SalesInvoiceController; use App\Http\Controllers\Sales\SalesInvoiceController;
use App\Http\Controllers\Sales\SalesOrderController; use App\Http\Controllers\Sales\SalesOrderController;
use App\Http\Controllers\MailAccountController;
use App\Http\Controllers\SettingsController; use App\Http\Controllers\SettingsController;
use App\Http\Controllers\Settings\ProjectSettingController; use App\Http\Controllers\Settings\ProjectSettingController;
use App\Models\Settings\Location; 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/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/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/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::get('settings/integrations/mail-accounts', [MailAccountController::class, 'index'])->name('settings.mail-accounts.index');
Route::post('settings/integrations/test-azure-mail', [SettingsController::class, 'testAzureMailConnection'])->name('settings.integrations.test-azure-mail'); Route::post('settings/integrations/mail-accounts', [MailAccountController::class, 'store'])->name('settings.mail-accounts.store');
Route::post('settings/integrations/send-test-email', [SettingsController::class, 'sendTestEmail'])->name('settings.integrations.send-test-email'); 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 // Projects settings
Route::get('settings/projects', [ProjectSettingController::class, 'index'])->name('settings.projects.index'); Route::get('settings/projects', [ProjectSettingController::class, 'index'])->name('settings.projects.index');