@rackbops/plugin-tracker 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -6
- package/dist/plugin.js +874 -176
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,8 +7,9 @@ on a rackbops-discord-bot instance. Plan of record: Rackbops/Tooling
|
|
|
7
7
|
command, delivered by DM, answered by button, with history -- the first slice of the web area
|
|
8
8
|
(#80): sign-in by one-time link, my tasks, a task's history, and settings -- renewals and
|
|
9
9
|
the price tracker (#81, plan E6): two more types on the notify side, no model, nothing sent to
|
|
10
|
-
city-hall --
|
|
11
|
-
your own tasks from the web, under the commands' own rules
|
|
10
|
+
city-hall -- the web area's task editor (#80, slice 2): make, edit, pause, resume and delete
|
|
11
|
+
your own tasks from the web, under the commands' own rules -- and the admin view and forget-me (#80,
|
|
12
|
+
slice 3).
|
|
12
13
|
|
|
13
14
|
## Commands
|
|
14
15
|
|
|
@@ -65,6 +66,11 @@ served at a hashed path, cached for a year).
|
|
|
65
66
|
| `/tasks/<id>/edit` | The owner's edit form (GET) and saving it (POST). |
|
|
66
67
|
| `/tasks/<id>/pause`, `/resume`, `/delete` | POST only. Delete answers a confirmation first; only a second post carrying `confirm=yes` deletes. |
|
|
67
68
|
| `/settings` | Preferred hour and time zone, checked as `/register` checks them. |
|
|
69
|
+
| `/forget` | Forget me (below): what it deletes (GET); a POST asks for the confirmation; a POST with `confirm=yes` and the word `forget` erases. Linked from Settings. |
|
|
70
|
+
| `/admin` | Admins only: everyone on the list, the decline blocks in force (each with Lift), and a form to allow a person by Discord id. |
|
|
71
|
+
| `/admin/tasks` | Admins only: every task in the store, with its owner, status and how many receive it, each linking to its page. |
|
|
72
|
+
| `/admin/people/<id>` | Admins only: one person -- Discord id, registration, zone and hour, delivery, task counts, their tasks -- with Make admin or Revoke admin, Resume delivery (when paused), and Remove from the tracker. |
|
|
73
|
+
| `/admin/allow`, `/admin/people/<id>/grant`, `/revoke`, `/resume-delivery`, `/forget`, `/admin/blocks/<id>/lift` | Admins only, POST only: the acts below. |
|
|
68
74
|
| `/signin` | Where a request that is not signed in is sent: says to run `/web`. |
|
|
69
75
|
| `/login?t=...` | The link `/web` gives. |
|
|
70
76
|
|
|
@@ -130,7 +136,86 @@ while the last confirmation is under 24 hours old, and are then signed out and t
|
|
|
130
136
|
again. Concurrent requests share one lookup per person, and after a failed one that person is not
|
|
131
137
|
looked up again for a minute, so an outage or rate limit does not pile up calls. A confirmation
|
|
132
138
|
time in the future (a clock set back) counts as stale. Every lookup asks Discord (`force: true`),
|
|
133
|
-
never discord.js's member cache, which with only the Guilds intent never learns that someone left.
|
|
139
|
+
never discord.js's member cache, which with only the Guilds intent never learns that someone left.
|
|
140
|
+
|
|
141
|
+
**The admin view** (slice 3, plan 5.8, 5.10). An admin is a person whose row in the tracker's store
|
|
142
|
+
has the admin flag -- the one admin definition; `/allow` checks the same flag. `TRACKER_ADMIN_DISCORD_IDS`
|
|
143
|
+
grants it at start; admins grant and revoke it here. The flag is read from the store on every
|
|
144
|
+
request, and again inside the write queue before any admin act, so an admin whose flag is revoked is
|
|
145
|
+
refused on their very next request. To a signed-in person who is not an admin, every `/admin` path --
|
|
146
|
+
any method -- answers the same 404, byte for byte, as an unknown page; a visitor who is not signed in
|
|
147
|
+
is sent to sign in, as on every other signed-in path. An admin sees every person, every task
|
|
148
|
+
and every task's page and history, read-only: an admin never edits, pauses or deletes another
|
|
149
|
+
person's task (the owner's acts answer them 404, as in slice 2). What an admin can do, each through
|
|
150
|
+
the function the command path runs:
|
|
151
|
+
|
|
152
|
+
- **Allow** a person by Discord id: `/allow`'s own `allowPerson`, with the same server-membership
|
|
153
|
+
check when `TRACKER_GUILD_ID` is set (one bounded lookup; no Discord client yet since a restart
|
|
154
|
+
refuses, as an unknown answer does). A form cannot tell a bot's id from a person's; a bot's row can
|
|
155
|
+
never `/register`.
|
|
156
|
+
- **Make admin / Revoke admin** (plan 5.8). The last admin can be neither revoked nor removed. An
|
|
157
|
+
admin who revokes themselves lands on My tasks. Each change is logged (`u1 made u2 an admin`).
|
|
158
|
+
An admin named in `TRACKER_ADMIN_DISCORD_IDS` is made an admin again at every start, so their page
|
|
159
|
+
offers neither Revoke nor Remove, and both are refused: take them out of the configuration first.
|
|
160
|
+
Such an admin may still forget themselves (if another admin is left); the pages say that the next
|
|
161
|
+
start makes them again, as a new person, unless the configuration no longer names them.
|
|
162
|
+
- **Resume delivery** of a person paused after failed DMs: `DeliveryHealth.resume`, what the
|
|
163
|
+
person's own next command does -- the count clears and the tasks nothing else holds go back on.
|
|
164
|
+
- **Lift** a decline block in force: docket's `liftBlock`, recorded as that admin's lift.
|
|
165
|
+
- **Remove from the tracker**: forget-me for that person (below), with the same confirmation.
|
|
166
|
+
|
|
167
|
+
Not here yet, though plan 5.10 lists them: grants, budgets and retry (they belong to the execute
|
|
168
|
+
lane, which is not built) and an admin pause of someone else's task.
|
|
169
|
+
|
|
170
|
+
**Forget me** (plan 5.8). A signed-in person erases themselves from `/forget` (web only: the plan
|
|
171
|
+
asks for no Discord command, so without `TRACKER_WEB_URL` the only path is an admin with the web
|
|
172
|
+
area). Two posts: the first asks, the second must carry `confirm=yes` and the word `forget`. Then,
|
|
173
|
+
in one SQLite transaction in the write queue, **deleted, not archived**:
|
|
174
|
+
|
|
175
|
+
- every task they own, archived ones too, with everything under it: runs, run events, replies
|
|
176
|
+
(anyone's), history, series, recipients, delivery claims and pauses;
|
|
177
|
+
- on everyone else's tasks: their recipient rows, their replies and answers, the history rows they
|
|
178
|
+
made or that name them (an invitation of them, a pause for them), the run events and delivery
|
|
179
|
+
claims of DMs to them, and their pause rows;
|
|
180
|
+
- every decline block they are either side of;
|
|
181
|
+
- their admission, delivery health, web sessions and unused sign-in links, and their person row.
|
|
182
|
+
|
|
183
|
+
What stays, and why: another person's task that they received stays that owner's; one paused only
|
|
184
|
+
because DMs to them failed goes back on. Another person's row keeps its own audit fact but not their
|
|
185
|
+
id -- `admitted_by` of someone they admitted, and `lifted_by` of a block they lifted, become
|
|
186
|
+
`forgotten` ("an admin since forgotten"; null already means "the configuration"). Text is matched
|
|
187
|
+
only in the forms the code writes a person's id into -- docket's consent rows (`u5`, `u5 24h`), the
|
|
188
|
+
tracker's `u5: ...` removals and `delivery to {u5} paused` pauses, docket's `u5 <message>` delivery
|
|
189
|
+
events, and the notifier's error phrases (`recipient u5 cannot be messaged`, ...) -- never as any
|
|
190
|
+
id-like word, so an address with `/u5/` in someone else's text is left alone. A history row or a
|
|
191
|
+
delivery event of theirs is deleted; the error of someone else's run keeps its row and has the id
|
|
192
|
+
replaced with `(forgotten)`. A pause for a recipient now names them by id and shows their current
|
|
193
|
+
name when read; a pause row written before 0.6.0 names the recipient's display name of the time,
|
|
194
|
+
and is deleted when it is on a task they were a recipient of, names their current name, and no
|
|
195
|
+
other recipient of that task has that name. A row written under an older name of theirs, or shared
|
|
196
|
+
with a same-named recipient, is kept: the one known gap. A reply other people wrote on the erased
|
|
197
|
+
person's own tasks is deleted with those tasks. Outside the store, nothing is touched: the DMs the
|
|
198
|
+
bot sent stay in the person's Discord DMs, and the host's log may hold their tracker id.
|
|
199
|
+
|
|
200
|
+
Bytes: the database runs with `secure_delete` on, so a deleted row is overwritten, and the first
|
|
201
|
+
start of 0.6.0 on an existing database runs one `VACUUM`, so pages freed before that are rewritten
|
|
202
|
+
too. That one-time VACUUM is recorded in a small `tracker_meta` table, not by a schema bump: the
|
|
203
|
+
schema stays at 3, so rolling back to 0.5.0 still opens the database. If the VACUUM cannot run (a
|
|
204
|
+
reader holds the file), the start goes on, a warning is logged, and the next start tries again.
|
|
205
|
+
Not a VACUUM per erasure: it rewrites the whole file under an
|
|
206
|
+
exclusive lock, and `secure_delete` already covers every later delete. After an erasure the
|
|
207
|
+
write-ahead log is checkpointed without waiting; when a reader keeps it busy, that is logged and
|
|
208
|
+
SQLite's next checkpoint copies it back.
|
|
209
|
+
|
|
210
|
+
Timing: forget-me first waits, outside the write queue, until no notify or poll tick is running (at
|
|
211
|
+
most 20 seconds; then it answers 503, try again, and deletes nothing), so a reminder DM to them
|
|
212
|
+
already in flight cannot be recorded after they are gone -- and no one else's command waits behind
|
|
213
|
+
it. Inside the queue it only checks that no tick started meanwhile (else the same 503). A DM sent
|
|
214
|
+
outside any tick -- an invitation's -- that fails after they are gone writes nothing: the failure
|
|
215
|
+
count is not kept for a person no longer in the store, and the withdrawn invitation is not
|
|
216
|
+
recorded. A `/price` whose page is read while its person is forgotten makes nothing. Their sessions
|
|
217
|
+
are among the rows, so they are signed out everywhere at once, and the browser's cookie is cleared. They can come back only as someone new: an admin
|
|
218
|
+
`/allow`s them again, and the tracker never reuses an id.
|
|
134
219
|
|
|
135
220
|
**Choose the web origin's domain with care.** A host under the same parent domain as
|
|
136
221
|
`TRACKER_WEB_URL` that you do not control can set a `__Secure-` cookie on the parent domain
|
|
@@ -151,6 +236,7 @@ The `__Host-` prefix, which would stop that, requires `Path=/` and so cannot be
|
|
|
151
236
|
| People | `src/people.ts`, `src/admissions.ts` | Discord id, time zone, preferred hour, admin flag -- in the tracker's store, never usr; who admitted each person and when they registered. `TRACKER_ADMIN_DISCORD_IDS` only ever grants admin. |
|
|
152
237
|
| Commands | `src/discord.ts`, `src/interactions.ts`, `src/discord-common.ts` (discord.js); `src/actions.ts`, `src/press.ts`, `src/history.ts`, `src/access.ts` | The discord.js files read options and render; the rest is Discord-free over the injected store, clock and notifier. Store writes are handled one at a time; Discord lookups and DMs run outside that queue. |
|
|
153
238
|
| Health | `src/health.ts` | `GET /tracker/healthz` (through the bot's HTTP router): `200` `ok`/`starting`, `503` `inactive`/`stale`/`blocked`. |
|
|
239
|
+
| Admin, forget-me | `src/admin.ts`, `src/roster.ts`; `src/web/admin.ts`, `src/web/admin-pages.ts` | The admin acts and forget-me, Discord-free; the SQL docket's Store has no method for (listing people and blocks, erasing a person); the routes and pages. |
|
|
154
240
|
| Web area | `src/web/` | `app.ts` authenticates and dispatches (pure over the injected store and clock), `routes.ts` names the paths; `pages.ts`, `editor-pages.ts` and `html.ts` render; `editor.ts` reads the editor's forms and calls the shared rules; `signin-link.ts` and `sessions.ts` hold the sign-in; `theme.ts` the stylesheet; `command.ts` is `/web`. |
|
|
155
241
|
| Task rules | `src/reminders.ts`, `src/tracked.ts`, `src/price.ts`, `src/edit.ts`, `src/manage.ts`, `src/limits.ts` | What makes, edits, pauses, resumes and deletes a task, and the limits on it, Discord-free, for the commands and the web editor alike. |
|
|
156
242
|
|
|
@@ -166,9 +252,9 @@ The `__Host-` prefix, which would stop that, requires `Path=/` and so cannot be
|
|
|
166
252
|
|
|
167
253
|
## Not yet
|
|
168
254
|
|
|
169
|
-
- The rest of the web area (E5): the
|
|
170
|
-
|
|
171
|
-
|
|
255
|
+
- The rest of the web area (E5): sharing a task from the web, the JSON task API, and the admin
|
|
256
|
+
view's grants, budgets and retry (with the execute lane). Discord OAuth2 as a second sign-in
|
|
257
|
+
method, if chosen (plan item 41).
|
|
172
258
|
- The city-hall Executor adapter (the execute lane).
|
|
173
259
|
- Editing in Discord (the editor is on the web only), and changing a price tracker's page or `near` after it is made (make a new one); a free-form pattern for `price` (`near` is the safe subset: a user's regular expression run on a large page could hang the bot).
|
|
174
260
|
- An optional Discord-role gate (plan 5.5), and showing unconfirmed deliveries to admins anywhere but the log.
|