4.8 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Commands
# Development (starts PHP server + queue + logs + Vite concurrently)
composer dev
# Build assets for production
npm run build
# Vite dev server only
npm run dev
# Run all tests
composer test
php artisan test
# Run a single test file
php artisan test tests/Feature/ExampleTest.php
# Run a single test by name
php artisan test --filter=TestName
# Migrations
php artisan migrate
Architecture
Design
- Tailwind CSS v4
- Modals sind immer vom Livewire Modals
- Kein Inline-Style nur wenn wirklich notwendig.
- Immer prüfen das das Style nicht kaputt ist und wenn Icons in Buttons mit Text sind, sind diese immer nebeneinadner Nie übereinander.
What this is
Mailwolt is a self-hosted mail server administration panel (German UI). It manages domains, mailboxes, aliases, DNS records (DKIM/DMARC/SPF/TLSA), TLS, Fail2ban, IMAP, and email quarantine/queues. It also has a mail sandbox for testing Postfix transports.
Layout & Routing
resources/views/layouts/dvx.blade.phpis the actual app layout (notapp.blade.php). Livewire full-page components use#[Layout('layouts.dvx')].- Routes are in
routes/web.php— each page maps directly to a Livewire full-page component via->name(). - Navigation structure is driven by
config/ui-menu.php. - Für den Style wird Tailwind CSS v4
Livewire component structure
app/Livewire/Ui/
├── Nx/ — Current production UI (Dashboard, DomainList, MailboxList, AliasList, etc.)
├── Domain/ — Domain modals and DKIM/DNS views
├── Mail/ — Mailbox/alias modals, queue, quarantine
├── Security/ — Fail2ban, SSL, RSpamd, TLS, audit logs
├── System/ — Settings, users, API keys, webhooks, sandbox, backups
├── Search/ — Global search palette modal
└── Webmail/ — Webmail-specific components
Full-page components live in Ui/Nx/ and Ui/System/, Ui/Security/, etc. Modals are always in a Modal/ subfolder and extend LivewireUI\Modal\ModalComponent.
Modals (wire-elements/modal v2)
- Open from outside a Livewire component:
onclick="Livewire.dispatch('openModal', {component:'ui.system.modal.my-modal', arguments:{key:value}})" - Open from inside a Livewire component:
$this->dispatch('openModal', component: '...', arguments: [...]) - Never use
wire:click="$dispatch('openModal',...)"outside a Livewire component context — it won't work. - Modal argument keys must match the
mount(int $keyName)parameter names exactly. - To prevent closing on backdrop/Escape, override in the modal class:
public static function closeModalOnClickAway(): bool { return false; } public static function closeModalOnEscape(): bool { return false; } public static function closeModalOnEscapeIsForceful(): bool { return false; } - To force-close the entire modal stack:
$this->forceClose()->closeModal()
Livewire dependency injection
- Never use constructor injection in Livewire components — Livewire calls
new Component()with no args. - Use
boot(MyService $service)instead: this is called on every request and supports DI.
CSS design system
The app uses a custom mw-* variable and class system defined in resources/css/app.css (Tailwind CSS v4, no tailwind.config.js):
CSS variables:
--mw-bg,--mw-bg3,--mw-bg4— background layers--mw-b1,--mw-b2,--mw-b3— border shades--mw-t1through--mw-t5— text shades (t1 = primary, t4/t5 = muted)--mw-v,--mw-v2,--mw-vbg— purple accent (primary brand color)--mw-gr— green (success)
Reusable component classes: .mw-btn-primary, .mw-btn-secondary, .mw-btn-cancel, .mw-btn-save, .mw-btn-del, .mbx-act-btn, .mbx-act-danger, .mw-modal-frame, .mw-modal-head, .mw-modal-body, .mw-modal-foot, .mw-modal-label, .mw-modal-input, .mw-modal-error, .mbx-badge-mute, .mbx-badge-ok, .mbx-badge-warn.
Services
app/Services/ contains: DkimService, DnsRecordService, ImapService, MailStorage, SandboxMailParser, SandboxService, TlsaService, TotpService, WebhookService.
API
REST API under /api/v1/ uses Laravel Sanctum. Token abilities map to scopes like mailboxes:read, domains:write, etc. Defined in routes/api.php.
Sandbox mail system
The mail sandbox intercepts Postfix mail via a pipe transport (php artisan sandbox:receive). SandboxRoute model controls which domains/addresses are intercepted. SandboxService::syncTransportFile() writes /etc/postfix/transport.sandbox and runs postmap.
Queue & real-time
- Queue driver: database (configurable). Jobs in
app/Jobs/. - Real-time updates use Laravel Reverb + Pusher.js. Livewire polls (
wire:poll.5s) are used as fallback on some pages.