# מערכת קריאות שירות — Help Desk

Hebrew (RTL) service desk for faults (תקלות) and training/implementation (הדרכה והטמעה),
built as a React single-page app on a PHP 8 + MySQL 8 API, for Ubuntu with Apache.

---

## What it does

**Customer** signs in, opens a ticket of either type, and fills in:
product, serial number and manufacturer (chosen from admin-managed catalogs),
plus unit, contact, location and description as free text, with any number of
photos or videos attached.

**Customer service** receives an email the moment a ticket is opened, sets its
severity, and routes it to a technician or an instructor. A procedure checklist
is copied onto the ticket at that point.

**Technician / instructor** works through the checklist, attaches evidence,
writes a summary, and sends the ticket back.

**Customer service** reviews and closes it. **Admin** sees everything and builds
the catalogs, users, procedure templates and settings.

Status flow:

    new → triage → assigned → in_progress → handler_done → closed
                                   ↕
                            pending_customer

---

## Requirements

- Ubuntu (tested against Apache 2.4, MySQL 8.0, PHP 8.3)
- Node 18+ on the build machine only — the server serves static files

## Installation

```bash
git clone <repo> /var/www/helpdesk
cd /var/www/helpdesk
bash deploy/install.sh          # packages, Apache modules, directories, NTP
```

Then the steps the installer prints:

```bash
# 1. Database
mysql < database/schema.sql
mysql -e "CREATE USER 'helpdesk'@'127.0.0.1' IDENTIFIED BY 'strong-password';
          GRANT ALL ON helpdesk.* TO 'helpdesk'@'127.0.0.1';"

# 2. Application config
cp api/config/config.sample.php api/config/config.php
php bin/genkey.php                    # paste into app_key
nano api/config/config.php

# 3. PHP limits (upload size, timeouts)
cp deploy/99-helpdesk.ini /etc/php/8.3/fpm/conf.d/
systemctl restart php8.3-fpm

# 4. Apache
cp deploy/helpdesk.conf /etc/apache2/sites-available/
a2ensite helpdesk && systemctl reload apache2
certbot --apache -d helpdesk.example.co.il

# 5. Cron (outgoing mail + housekeeping)
cp deploy/helpdesk.cron /etc/cron.d/helpdesk
mkdir -p /var/log/helpdesk && chown www-data:www-data /var/log/helpdesk

# 6. Front end
cd web && npm install && npm run build && cd ..

# 7. First administrator
php bin/create_admin.php "השם שלך" you@example.co.il 'strong-passphrase'

# 8. Confirm two-factor works on this machine
php bin/selftest.php
```

Sign in, open **ניהול**, and create customers, manufacturers, products, serial
numbers and users. Set the customer-service address under **הגדרות** —
that is where new-ticket notifications go.

## Development

```bash
cd web && npm run dev      # Vite on :5173, proxying /api to Apache on :80
```

---

## Layout

```
api/                PHP API — no framework, no composer dependencies
  index.php         front controller and router
  config/           config.php (git-ignored) and its sample
  lib/              DB, Auth, Crypto, Totp, Mailer, Uploads, Tickets, Audit
  routes/           auth, catalogs, tickets, attachments, admin
bin/                CLI: create_admin, genkey, send_queue, purge, selftest
database/schema.sql full schema with Hebrew seed data
deploy/             Apache vhost, PHP ini drop-in, cron, installer
web/                React app (Vite)
  src/pages/        Login, Tickets, NewTicket, TicketDetail, Profile, Stats, Admin
  src/components/   Layout, Enroll2FA, shared UI
```

---

## Design notes worth knowing

**Two-factor.** Plain TOTP (RFC 6238) — Microsoft Authenticator needs no
Microsoft-specific integration. The secret is stored AES-256-GCM encrypted with
`app_key`, and the QR is drawn in the browser from the `otpauth://` URI, so no
image of the secret ever crosses the network. The time step of every accepted
code is recorded, so a code cannot be replayed. Eight one-time recovery codes
are issued at enrollment; an admin can reset a user's enrollment from
**ניהול → משתמשים**.

Because TOTP validates against the UTC clock, a server whose clock drifts more
than ~30 seconds will reject every valid code. `deploy/install.sh` enables NTP;
`php bin/selftest.php` confirms the algorithm against the RFC test vectors.

**Sessions, not tokens.** The SPA and the API are same-origin, so auth is an
HttpOnly `SameSite=Lax` cookie holding a random session id, with a CSRF token
required on every state-changing request. Password verification creates a
half-authenticated session; only a valid OTP promotes it.

**Procedure checklists are snapshotted.** `procedure_templates` is what admin
edits; `ticket_tasks` is a copy taken when the ticket is assigned, including the
label and field type. Editing a template next year cannot rewrite a ticket
closed last year.

**Uploads live outside the document root** (`/var/lib/helpdesk/uploads`) with
random filenames. Every download goes through a PHP permission check and is
then handed to Apache via `X-Sendfile`, so video gets proper HTTP range
requests instead of being streamed through PHP.

Upload size is governed by `upload_max_filesize` and `post_max_size` in
`deploy/99-helpdesk.ini` — `post_max_size` must stay larger, since the form
fields ride along with the file.

**Mail is queued, never sent inline.** Ticket creation inserts a row in
`email_queue`; `bin/send_queue.php` delivers it from cron with retries. A slow
or unreachable SMTP server can never hang a ticket submission. Leave
`smtp_host` empty to use the local sendmail instead.

**Every change is audited.** `ticket_history` records status changes,
assignments, severity changes and attachments, with who and when. Staff see the
trail on the ticket page.

---

## Roles

| Role | Sees | Can do |
|---|---|---|
| לקוח | own organisation's tickets | open tickets, attach files, comment |
| שירות לקוחות | everything | set severity, assign, close, reopen, internal notes |
| טכנאי / מדריך | tickets assigned to them | fill the checklist, attach, finish |
| מנהל מערכת | everything | all of the above plus catalogs, users, templates, settings |

Internal comments and internal attachments are hidden from customers.

---

## What was verified before delivery

- Schema imports cleanly into MySQL 8.0 and the React app builds with no warnings
- Full lifecycle end to end: customer opens → upload → mail queued → assign →
  checklist → handler done → close, with the audit trail written at each step
- TOTP matches the RFC 6238 test vectors; enrollment, login, wrong code, code
  replay, recovery code and recovery-code reuse all behave correctly
- Isolation: a customer from another organisation gets 403 on the ticket and on
  its attachments and sees an empty list; a technician gets 403 on admin routes;
  no session gets 401; a signed-in POST without the CSRF header gets 419
- A customer cannot close a ticket

## Not built yet

SLA timers and escalation, scheduled reports, CSV/Excel export, customer
satisfaction survey on close, and push/SMS notification. The schema has room for
the first two (`due_at`, `first_response_at`).
