contact messages: standalone /_m admin inbox, site-name reply label
The inbox moves out of the analytics tab to its own page at /_m (own pens-row icon, own entry and socket lifecycle; the admin-gated /_api/ws/analytics socket keeps serving both, with typed messaging frames ignored by AnalyticsView). Admin replies are labeled with the currently configured site name in the wire payload (the SSO name stays stored for audit), and the thread view is capped to roughly the visitor panel's width.
This commit is contained in:
1 parent
f4d1f0ac58
commit
4ad4e3bdff
11 files changed
+535
-209
No files matched your search
+19
-16
@@ -2,24 +2,24 @@
|
||||
|
||||
Visitor→admin messaging built into the site: a `{contact: Label}` directive
|
||||
in article Markdown renders a button that opens a chat-style panel; messages
|
||||
are stored in the kanta database and answered from the admin inbox on `/_a`.
|
||||
are stored in the kanta database and answered from the admin inbox on `/_m`.
|
||||
Visitors are anonymous — a localStorage session token identifies their
|
||||
thread — and everything realtime rides the two existing WebSockets; there is
|
||||
no REST polling anywhere.
|
||||
|
||||
- `pagerite/messages.py` — the feature's core: the strict message renderer,
|
||||
thread store ops, wire payloads, both socket protocols and the
|
||||
`/_contact/files` upload route.
|
||||
thread store ops, wire payloads, both socket protocols, the `/_m` inbox
|
||||
page route and the `/_contact/files` upload route.
|
||||
- `pagerite/data.py` — `Data.contact` (`ContactThread`/`ContactMessage`).
|
||||
- `pagerite/tracking.py` — the sockets the protocol rides (see below).
|
||||
- `pagerite/views.py` — the `{contact:}` directive (`_contact_tag`) and the
|
||||
on-demand bundle's asset config.
|
||||
- `pagerite/views.py` — the `{contact:}` directive (`_contact_tag`), the
|
||||
`/_m` page render and the on-demand bundles' asset config.
|
||||
- `frontend/src/contact-main.js` + `ContactPanel.vue` — the visitor panel,
|
||||
imported on demand by `pagerite.js` on trigger clicks.
|
||||
- `frontend/src/Composer.vue` + `richtext.js` — the shared composer and its
|
||||
DOM→Markdown serializer.
|
||||
- `frontend/src/MessagesView.vue` — the admin inbox (the messages tab of
|
||||
`AnalyticsView.vue` on `/_a`).
|
||||
- `frontend/src/messages-main.js` + `MessagesView.vue` — the admin inbox
|
||||
page at `/_m` (own pens-row icon, own socket lifecycle).
|
||||
|
||||
## Trigger
|
||||
|
||||
@@ -78,16 +78,18 @@ solves and retries once. Challenges are a per-connection local in the
|
||||
|
||||
## Admin side
|
||||
|
||||
Admins watch the inbox as the **messages tab on `/_a`**, served by the
|
||||
existing `/_api/ws/analytics` socket (admin-gated like all of `/_api`).
|
||||
Analytics payloads keep their type-less snapshot shape; messaging frames
|
||||
always carry a `type` field, and that is how the client tells them apart.
|
||||
On connect the server sends `{type: "threads", threads: [...]}` (100 most
|
||||
Admins watch the inbox on its own page, **`/_m`** (✉️ icon in the pens
|
||||
row), served by the existing `/_api/ws/analytics` socket (admin-gated like
|
||||
all of `/_api`). Analytics payloads keep their type-less snapshot shape;
|
||||
messaging frames always carry a `type` field, and that is how each client
|
||||
picks out its own frames (the analytics viewer ignores them). On connect
|
||||
the server sends `{type: "threads", threads: [...]}` (100 most
|
||||
recently active); new messages and replies push single-thread updates.
|
||||
Admin→server frames: `{type: "reply", id, text}` and `{type: "read", id}`.
|
||||
Replies are authored with the SSO `remote-user` name and mark the thread
|
||||
read; `unread` on a thread means its last message is the visitor's and
|
||||
newer than the admin's last read.
|
||||
Replies store the SSO `remote-user` name for audit but are labeled with
|
||||
the currently configured **site name** in the wire payload (substituted at
|
||||
read time) and mark the thread read; `unread` on a thread means its last
|
||||
message is the visitor's and newer than the admin's last read.
|
||||
|
||||
## Message format and the XSS model
|
||||
|
||||
@@ -151,7 +153,8 @@ message, fixed afterwards) and
|
||||
`messages: dict[str, ContactMessage]` keyed by a sortable timestamp-random
|
||||
id (dict-keyed, so a save's database diff touches only the appended
|
||||
message). Each message stores `t`, the Markdown source `md`, its strict
|
||||
rendering `html`, `admin` ("" = visitor, else the SSO name) and `page`.
|
||||
rendering `html`, `admin` ("" = visitor, else the SSO name — the wire
|
||||
payload shows the current site name instead) and `page`.
|
||||
Appends are single transactions (`contact` / `contact:reply` /
|
||||
`contact:read`).
|
||||
|
||||
|
||||
Reference in new issue
Block a user