Update all documentation to current MySQL + React SPA + vm_api.py architecture
README, docs/{ARCHITECTURE,CONFIGURATION,INSTALL,OPERATIONS,SECURITY,TESTING}
and CHANGELOG were stale: they described the original SQLite + CardDAV +
zero-JS vm_web portal. Brought them in line with the actual code:
- MySQL (PyMySQL) as the default store; SQLite fallback only
- vm_contacts resolves from the MySQL contacts table (file/google/carddav removed)
- React SPA frontend + vm_api.py JSON API (:8098); vm_web.py now serves /audio/
- Apache vhost proxies /api/ -> :8098, /audio/ -> :8099, serves the SPA
- CSP relaxed to script-src 'self' 'unsafe-inline' (UI is now JavaScript)
- Added CHANGELOG 1.2.0 entry for the React/vm_api frontend work
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
# Testing
|
||||
|
||||
How to verify each part of the system. Do the mailcmd tests **as the `asterisk`
|
||||
user**, not as yourself — that's where permission bugs hide.
|
||||
How to verify each part of the system. Do the mailcmd tests **as the `asterisk`**
|
||||
user, not as yourself — that's where permission bugs hide.
|
||||
|
||||
---
|
||||
|
||||
@ -10,7 +10,8 @@ user**, not as yourself — that's where permission bugs hide.
|
||||
```bash
|
||||
df -h /var # must not be full (Postfix 452)
|
||||
grep '^mailcmd' /etc/asterisk/voicemail.conf
|
||||
sudo systemctl is-active vm-portal
|
||||
sudo systemctl is-active vm-api vm-portal
|
||||
curl -s http://127.0.0.1:8098/api/healthz
|
||||
curl -s http://127.0.0.1:8099/healthz
|
||||
```
|
||||
|
||||
@ -29,7 +30,7 @@ group id negative; unmapped → default; `enabled=no` → no route; caption clip
|
||||
to 1017 chars on a word boundary when over 1024; a 374 KB wav → 64 KB `OggS`
|
||||
opus.
|
||||
|
||||
`test_contacts.py` asserts: `+447700900123`, `07700900123`, `447700900123` all
|
||||
`test_contacts.py` asserts: `+447****0123`, `07700900123`, `447700900123` all
|
||||
match one contact (last-9-digit key); multi-TEL cards; Google CSV `:::` split.
|
||||
|
||||
---
|
||||
@ -64,8 +65,9 @@ sudo -u asterisk /opt/vm-transcribe/venv/bin/python3 \
|
||||
|
||||
### Fail-safe: a thrown error relays the original
|
||||
|
||||
Temporarily point `VM_DB` at an unreadable path; the script should fall back to
|
||||
relaying the original Asterisk message and log `relaying original, mailcmd error`.
|
||||
Temporarily point `VM_DB` at an unreadable path (or MySQL offline); the script
|
||||
should fall back to relaying the original Asterisk message and log
|
||||
`relaying original, mailcmd error`.
|
||||
|
||||
---
|
||||
|
||||
@ -83,47 +85,49 @@ need a token to verify the config parser.
|
||||
|
||||
---
|
||||
|
||||
## 4. Contacts
|
||||
## 4. Contacts (MySQL)
|
||||
|
||||
```bash
|
||||
# resolve a caller ID against the MySQL contacts table
|
||||
sudo -u asterisk /opt/vm-transcribe/venv/bin/python3 \
|
||||
/opt/vm-transcribe/vm_contacts.py '<07700900123>'
|
||||
# resolves against the configured backend; prints name or the raw caller id
|
||||
# prints (name, email) or the raw caller id
|
||||
|
||||
# load an export
|
||||
sudo -u asterisk /opt/vm-transcribe/venv/bin/python3 \
|
||||
/opt/vm-transcribe/vm_import_contacts.py /path/to/contacts.vcf
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Portal — auth, authz, playback, delete
|
||||
## 5. JSON API — auth, authz, playback, delete
|
||||
|
||||
Run the app locally (or against the live service) and exercise it with curl.
|
||||
Run the API locally (or against the live service) and exercise it with curl.
|
||||
Use `VM_INSECURE_COOKIE=1` only for plain-HTTP local tests so the `Secure`
|
||||
cookie can be set.
|
||||
|
||||
```bash
|
||||
B=http://127.0.0.1:8098
|
||||
|
||||
# login ok
|
||||
curl -s -c /tmp/j -o /dev/null -w '%{redirect_url}\n' \
|
||||
-d 'mailbox=7940&pin=5159' http://127.0.0.1:8099/login
|
||||
curl -s -c /tmp/j -o /dev/null -w '%{redirect_url}\n' -d 'mailbox=7940&pin=5159' $B/api/login
|
||||
|
||||
# wrong pin rejected
|
||||
curl -s -o /dev/null -w '%{http_code}\n' -d 'mailbox=7940&pin=1111' http://127.0.0.1:8099/login
|
||||
curl -s -o /dev/null -w '%{http_code}\n' -d 'mailbox=7940&pin=1111' $B/api/login
|
||||
|
||||
# authenticated list
|
||||
curl -s -b /tmp/j http://127.0.0.1:8099/ | grep -c 'class="card'
|
||||
# authenticated message list
|
||||
curl -s -b /tmp/j $B/api/messages | head -c 400
|
||||
|
||||
# audio streams
|
||||
curl -s -b /tmp/j -o /tmp/x.wav http://127.0.0.1:8099/audio/1
|
||||
file /tmp/x.wav # RIFF WAVE, 8000 Hz mono
|
||||
|
||||
# delete
|
||||
curl -s -b /tmp/j -X POST -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8099/delete/1
|
||||
# contacts CRUD
|
||||
curl -s -b /tmp/j $B/api/contacts | head -c 400
|
||||
```
|
||||
|
||||
### Authorization (IDOR) — must 404
|
||||
|
||||
A session for mailbox A must never reach mailbox B's data. With a cookie for
|
||||
mailbox 7940, hitting `/audio/<id-owned-by-1001>` and `/delete/<id-owned-by-1001>`
|
||||
must both return **404/303**, never serve or delete the other mailbox's message.
|
||||
Add this assertion whenever you change `vm_store` or `vm_web`.
|
||||
mailbox 7940, hitting `/api/messages/{id-owned-by-1001}` (or its audio/delete
|
||||
variants) must return **404**, never serve or delete the other mailbox's message.
|
||||
Add this assertion whenever you change `vm_store` or `vm_api`.
|
||||
|
||||
---
|
||||
|
||||
@ -131,13 +135,20 @@ Add this assertion whenever you change `vm_store` or `vm_web`.
|
||||
|
||||
```bash
|
||||
R="--resolve vm.txt3.net:443:ORIGIN.IP"
|
||||
curl -s $R https://vm.txt3.net/healthz
|
||||
curl -s $R https://vm.txt3.net/api/healthz
|
||||
curl -s $R -o /dev/null -w '%{http_code}\n' https://vm.txt3.net/login
|
||||
curl -s $R -D- -o /dev/null https://vm.txt3.net/login | grep -iE 'strict-transport|content-security|x-frame'
|
||||
# http -> https redirect
|
||||
curl -s $R -o /dev/null -w '%{redirect_url}\n' http://vm.txt3.net/
|
||||
```
|
||||
|
||||
The React SPA is served as static files; all data flows through `/api/`. Confirm
|
||||
the SPA fallback returns `index.html` for arbitrary client-side routes:
|
||||
|
||||
```bash
|
||||
curl -s $R -o /dev/null -w '%{http_code}\n' https://vm.txt3.net/some/client/route
|
||||
```
|
||||
|
||||
Behind a CDN, `--resolve` to the **origin** IP; otherwise you are testing the CDN,
|
||||
not your server.
|
||||
|
||||
@ -155,5 +166,11 @@ sudo -u asterisk /opt/vm-transcribe/venv/bin/python3 \
|
||||
# real: 3 imported, others skipped; re-running is a no-op (idempotent)
|
||||
```
|
||||
|
||||
Verify they appear in the portal and are playable (§5). Expect a `no_speech`
|
||||
count — 44-byte WAVs are hung-up calls, not failures.
|
||||
Verify they appear via the API/messages endpoint and are playable (§5). Expect a
|
||||
`no_speech` count — 44-byte WAVs are hung-up calls, not failures.
|
||||
|
||||
CDR history import:
|
||||
|
||||
```bash
|
||||
sudo -u asterisk /opt/vm-transcribe/venv/bin/python3 /opt/vm-transcribe/vm_backfill_cdr.py
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user