where('is_system', false); if ($request->filled('domain')) { $query->whereHas('domain', fn($q) => $q->where('domain', $request->domain)); } if ($request->filled('active')) { $query->where('is_active', filter_var($request->active, FILTER_VALIDATE_BOOLEAN)); } $mailboxes = $query->orderBy('email')->paginate(100); return response()->json([ 'data' => $mailboxes->map(fn($m) => $this->format($m)), 'meta' => ['total' => $mailboxes->total(), 'per_page' => $mailboxes->perPage(), 'current_page' => $mailboxes->currentPage()], ]); } public function show(int $id): JsonResponse { $mailbox = MailUser::with('domain')->where('is_system', false)->findOrFail($id); return response()->json(['data' => $this->format($mailbox)]); } public function store(Request $request): JsonResponse { $request->tokenCan('mailboxes:write') || abort(403, 'Scope mailboxes:write required.'); if ($request->isSandbox ?? false) { return response()->json(['data' => array_merge(['id' => 9999], $request->only('email')), 'sandbox' => true], 201); } $data = $request->validate([ 'email' => 'required|email|unique:mail_users,email', 'password' => 'required|string|min:8', 'display_name'=> 'nullable|string|max:120', 'quota_mb' => 'nullable|integer|min:0', 'is_active' => 'nullable|boolean', ]); [$local, $domainName] = explode('@', $data['email']); $domain = Domain::where('domain', $domainName)->firstOrFail(); $mailbox = MailUser::create([ 'domain_id' => $domain->id, 'localpart' => $local, 'email' => $data['email'], 'display_name' => $data['display_name'] ?? null, 'password_hash'=> '{ARGON2I}' . base64_encode(password_hash($data['password'], PASSWORD_ARGON2I)), 'quota_mb' => $data['quota_mb'] ?? $domain->default_quota_mb ?? 1024, 'is_active' => $data['is_active'] ?? true, ]); if (!($request->isSandbox ?? false)) { app(WebhookService::class)->dispatch('mailbox.created', $this->format($mailbox->load('domain'))); } return response()->json(['data' => $this->format($mailbox->load('domain'))], 201); } public function update(Request $request, int $id): JsonResponse { $request->tokenCan('mailboxes:write') || abort(403, 'Scope mailboxes:write required.'); $mailbox = MailUser::where('is_system', false)->findOrFail($id); if ($request->isSandbox ?? false) { return response()->json(['data' => $this->format($mailbox), 'sandbox' => true]); } $data = $request->validate([ 'display_name' => 'nullable|string|max:120', 'quota_mb' => 'nullable|integer|min:0', 'is_active' => 'nullable|boolean', 'can_login' => 'nullable|boolean', ]); $mailbox->update(array_filter($data, fn($v) => !is_null($v))); if (!($request->isSandbox ?? false)) { app(WebhookService::class)->dispatch('mailbox.updated', $this->format($mailbox->load('domain'))); } return response()->json(['data' => $this->format($mailbox->load('domain'))]); } public function destroy(Request $request, int $id): JsonResponse { $request->tokenCan('mailboxes:write') || abort(403, 'Scope mailboxes:write required.'); $mailbox = MailUser::where('is_system', false)->findOrFail($id); if ($request->isSandbox ?? false) { return response()->json(['sandbox' => true], 204); } $formatted = $this->format($mailbox->load('domain')); $mailbox->delete(); app(WebhookService::class)->dispatch('mailbox.deleted', $formatted); return response()->json(null, 204); } private function format(MailUser $m): array { return [ 'id' => $m->id, 'email' => $m->email, 'display_name' => $m->display_name, 'domain' => $m->domain?->domain, 'quota_mb' => $m->quota_mb, 'is_active' => $m->is_active, 'can_login' => $m->can_login, 'last_login_at'=> $m->last_login_at?->toIso8601String(), 'created_at' => $m->created_at->toIso8601String(), ]; } }