- E-mail intake (IMAP), optional and off by default: clients can create a ticket or reply to an existing one just by sending/replying to an e-mail. Configure any number of mailboxes in the new Admin > Poczta page (SMTP + IMAP together, replacing the old "E-MAIL" tab), each routed to a specific subcategory or a whole category (new tickets.category_id column). Replies are matched to their ticket via the number/checksum already in every notification subject; autoresponders/bounces are detected and rejected; "restrict tickets to LDAP" is enforced for e-mail like the guest web form. Manual "Pobierz teraz" per-mailbox fetch button; dedicated storage/logs/imap-*.log regardless of the app's log level; mail-icon badges on e-mail-originated tickets/messages in the operator queue and ticket view. - Operator queue: "select all" checkbox in the table header for every currently visible ticket under the active filter/tab. - Fixed: scheduled commands (SLA breach check, automation rules, and now IMAP fetch) always sent notifications through .env's default mailer instead of the configured SMTP server, because AppServiceProvider's Settings override used to skip itself for any console command, not just migrate. - Fixed: visiting a ticket that no longer exists (deleted mid-session, or a stale background refresh) showed a raw 404 instead of redirecting back to the operator queue / client dashboard. - Docs: README/ARCHITECTURE/CLAUDE/install/wiki updated for all of the above, including the previously-missing host crontab entry for schedule:run. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
115 lines
6.3 KiB
Markdown
115 lines
6.3 KiB
Markdown
# CLAUDE.md
|
|
|
|
Guidance for AI coding agents working in this repo. See [README.md](README.md)
|
|
for the feature/tech overview, [ARCHITECTURE.md](ARCHITECTURE.md) for how the app
|
|
is put together, and [install.md](install.md) for full deployment instructions.
|
|
|
|
## This is a live production system
|
|
|
|
The only real user today is the owner's own account (client+operator+admin
|
|
roles). Treat the running database as production, not a sandbox:
|
|
|
|
- Never create/modify/delete real DB records (users, tickets, settings, etc.)
|
|
without the user explicitly asking. Don't spin up throwaway test users via
|
|
`php artisan tinker` against this DB.
|
|
- For UI/CSS verification, prefer an offline static-HTML harness reproducing the
|
|
real markup+CSS, or ask for test credentials, rather than poking at prod data.
|
|
- Production is fronted by Traefik with a **private-CA TLS cert** (not publicly
|
|
trusted) — tools like `curl`/Playwright need `-k` / `ignore_https_errors` to
|
|
hit it directly.
|
|
|
|
## Container operations: use `sudo`, never build the image locally
|
|
|
|
All Docker commands against this stack need `sudo` (e.g.
|
|
`sudo docker compose exec servicedesk ...`, `sudo docker exec servicedesk-servicedesk-1 ...`).
|
|
|
|
**Never run `docker build`, `docker compose build`, or `--build`.** The
|
|
`servicedesk` image is built by CI (`.gitea/workflows/build.yml`, triggered on
|
|
`Dockerfile` changes on `main`) and pushed to the Gitea container registry;
|
|
`compose.yaml` only ever `pull`s a tag (`sudo docker compose pull && sudo docker
|
|
compose up -d`, see [install.md](install.md) 1.3/1.3a) — building locally would
|
|
just diverge from what CI produces. The app container
|
|
(`servicedesk-servicedesk-1`) mounts `./src` from the host over NFS
|
|
(`/mnt/rabbit-containers` → NFS export), so plain file edits already take effect
|
|
with no rebuild or restart:
|
|
|
|
- Blade/PHP/route edits: just edit the files — live instantly, no restart needed.
|
|
- `resources/css/app.css` / `resources/js/*` (Vite/Tailwind entrypoints) **do**
|
|
need a rebuild — `public/build/assets/` is static compiled output. Neither the
|
|
host nor the app container has Node installed; rebuild with a throwaway
|
|
container instead of touching the app image:
|
|
```bash
|
|
sudo docker run --rm -v "$(pwd)/src":/app -w /app node:22 npm run build
|
|
```
|
|
(run from the repo root; `node_modules` already exists, no install needed).
|
|
Verify via `public/build/manifest.json` picking up a new hash.
|
|
- After running artisan cache commands for diagnostics (`config:cache`,
|
|
`view:cache`), clear them again afterward (`config:clear`/`view:clear`) — this
|
|
app normally runs uncached so edits apply live; leaving a cache on silently
|
|
breaks that workflow.
|
|
- **`sudo docker exec` runs as `root`, not `www-data`.** Apache's worker
|
|
processes run as `www-data`; anything you run via a plain `docker exec`
|
|
(`php artisan test`, `tinker`, `view:cache`, etc.) runs as `root`. On this
|
|
NFS-backed mount, a file Blade compiles/caches while running as `root`
|
|
(`storage/framework/views/*.php`) can't later be overwritten by `www-data`
|
|
when a real request needs to recompile it (the source changed) — this
|
|
surfaces in production as a 500 with `touch(): Utime failed: Operation not
|
|
permitted`. If you ran `php artisan test`/`tinker`/any artisan command via
|
|
`docker exec` in a session where you also edited Blade files afterward,
|
|
finish with `sudo docker exec servicedesk-servicedesk-1 php artisan
|
|
view:clear` to flush any root-owned compiled views before ending the
|
|
session — don't wait for a report of a broken page to catch it.
|
|
|
|
## Scheduled commands need a host crontab entry
|
|
|
|
The Docker image ships no cron/supervisor of its own (see [install.md](install.md)),
|
|
so `tickets:check-sla-breaches`, `automation:run-rules`, and `emails:fetch-imap`
|
|
(all registered in `routes/console.php` via `Schedule::command(...)`) only ever
|
|
run if something outside the container calls `php artisan schedule:run` on a
|
|
timer. **As of 2026-07-23 this is configured** — root's crontab on the host
|
|
runs, every minute:
|
|
|
|
```cron
|
|
* * * * * cd /mnt/rabbit-containers/servicedesk && docker compose exec -T servicedesk php artisan schedule:run >> /dev/null 2>&1
|
|
```
|
|
|
|
(`sudo crontab -l -u root` to inspect/edit — it previously did not exist at all,
|
|
which meant none of the three scheduled commands above had ever run
|
|
automatically; ask before changing this again, since removing it silently
|
|
breaks SLA checks, automation rules and IMAP fetching, and confusingly not the
|
|
IMAP feature alone if you're only debugging that one.) IMAP-specific activity
|
|
(connect attempts, per-message accept/reject decisions, created/replied ticket
|
|
ids) is logged separately from the app's normal `LOG_LEVEL` to
|
|
`storage/logs/imap-*.log` (see the `imap` channel in `config/logging.php`) —
|
|
check there first when a mailbox isn't behaving as expected, before assuming
|
|
the scheduler itself isn't firing.
|
|
|
|
## Apache `/icons/` alias trap
|
|
|
|
The stock `php:apache` image enables `mods-enabled/alias.conf`, which defines
|
|
`Alias /icons/ "/usr/share/apache2/icons/"`. Any app-level static directory at
|
|
`public/icons/` is silently shadowed by Apache's own stock icon set — requests
|
|
404 before ever reaching Laravel, with no useful log line. This project
|
|
deliberately uses `public/pwa-icons/` for PWA manifest icons for exactly this
|
|
reason — never add a `public/icons/` directory.
|
|
|
|
## Established UI conventions
|
|
|
|
- **Post-login redirect** always lands on `/client` (`User::defaultArea()` in
|
|
`app/Models/User.php`), even for accounts holding operator/admin roles too —
|
|
staff switch areas via the role switcher in the header
|
|
(`resources/views/components/profile-menu.blade.php`). Keep `/client`
|
|
first-priority in `defaultArea()` if you add new roles/areas.
|
|
- **Mobile tables**: don't rely on horizontal scroll alone for wide data tables.
|
|
The established pattern (`.table-cards-mobile` in `resources/css/app.css`,
|
|
`@media (max-width:640px)`) turns each `<tr>` into a card with `data-label`
|
|
attributes on `<td>`s. Currently applied to the operator ticket queue; apply
|
|
the same treatment to any other wide table you add or make mobile-relevant
|
|
(admin panel tables don't have it yet).
|
|
|
|
## Testing & code style
|
|
|
|
See [TESTING.md](TESTING.md) and [CONTRIBUTING.md](CONTRIBUTING.md) — run
|
|
`php artisan test` and `./vendor/bin/pint` before considering a change done.
|
|
There is no CI; local test runs are the only gate.
|