# Changelog ## 1.2.0 — 2026-08-13 - **React SPA frontend + JSON API.** Added `vm_api.py` (FastAPI JSON API on :8098) and a React frontend (`/home/jp/Work/voicemail-ui`, served as static files with an SPA fallback by Apache). The portal UI is now a JavaScript SPA (react-router-dom + TanStack Query + Vite), not the zero-JS server-rendered app. `vm_web.py` (:8099) remains to serve `/audio/` playback and `/healthz`. - **Apache vhost updated** to proxy `/api/` → :8098, `/audio/` → :8099, serve the React `dist`, and SPA-fallback to `index.html`. CSP relaxed to `script-src 'self' 'unsafe-inline'` (was `script-src 'none'`) because the UI is now JavaScript. - **Per-message / per-contact dropdown menus** in the UI (⋮): **Call back** opens a `tel:` link from the sanitised caller digits; **Lookup number** opens a who-called.co.uk reverse-lookup in a new tab, using the **0-prefixed** UK number (swap `+44` → `0` before lookup, search by sanitised digits only). Applied to both the Messages and Contacts pages. - **Contacts backfill on write.** Creating/updating a contact via the API now updates matching `messages.contact_name`/`contact_email` (spaces/quotes stripped before the LIKE; matches both E.164 and 0-prefix variants). - Documentation (`README.md`, `docs/*`) brought in line with the MySQL + React + `vm_api.py` architecture. ## 1.1.0 — 2026-08-13 - **Storage migrated to MySQL**: `vm_store` now defaults to MySQL (PyMySQL) when `/opt/vm-transcribe/db_secret` is present, with a root-owned least-priv `asterisk` user. SQLite remains as a fallback when `VM_STORE=sqlite` is set or no secret file exists. - **Asterisk CDR → MySQL**: CDRs now write to MySQL via `cdr_adaptive_odbc` + `res_odbc` + the MariaDB ODBC driver. Backfill script `vm_backfill_cdr.py` imports `Master.csv` history into MySQL (7,591 rows). - **Contacts email support**: `vm_contacts` now returns `(name, email)`, preferring `TYPE=WORK` addresses from vCard. `messages.contact_email` column backfilled. - **Full E.164 matching**: contacts matcher uses the full digit string (up to 15 digits) instead of last-9; added a guard so emoji/placeholder contacts cannot shadow real names. - **Migrations**: `vm_migrate_sqlite_to_mysql.py` and `vm_backfill_cdr.py` added under `src/` for first-run and CDR backfill. - **Contacts backend consolidated to MySQL only.** The `file` / `google` / `carddav` resolver backends were removed; `vm_contacts` now resolves exclusively from the MySQL `contacts` table. ## 1.0.0 — 2026-08-13 Initial build on mail.txt3.net (Debian 12, Asterisk 20, Apache 2.4, Postfix). - **Transcription**: per-voicemail `mailcmd` (`vm_mailcmd.py`) transcribes the attached recording with faster-whisper `base.en` (CPU, int8, VAD-filtered). - **Summarisation**: local extractive summariser with intent tags and spoken- digit callback-number extraction. No LLM, by choice. - **Email**: rebuilt `multipart/alternative` notification (plain + styled HTML) with the recording attached; relays the original Asterisk message unchanged on any error (fail-safe). - **Telegram** (optional): per-mailbox routed voice-note DM with summary caption and transcript follow-up; degrades sendVoice → sendDocument → sendMessage. - **Web portal**: FastAPI app at `https://vm.txt3.net` — PIN login (mailbox + voicemail PIN from `voicemail.conf`), list, play, download, delete, per-user settings. DB-backed sessions. Brute-force lockout per (mailbox, IP). - **Storage**: content-addressed audio (decoupled from Asterisk's renumbering). - **Backfill**: `vm_import.py` imports and transcribes existing spool recordings; idempotent on `(mailbox, origtime, callerid)`. Backfilled 157 historical messages on first run (3m40s, 0 failures). - **Packaging**: venv at `/opt/vm-transcribe`, systemd units, Apache vhost + certbot TLS, `install.sh`.