Let the extension update itself, and host the dashboard
Chrome has no update channel for an unpacked extension, so every pull left one manual step: click reload on its card. The dashboard now publishes the version its own checkout ships at /api/autobuyer/status, and the extension reloads itself when it is behind — chrome.runtime.reload() re-reads the folder from disk, which the update check has already pulled into. It only ever runs code that is on disk, so a reload that changes nothing is reported and waited out rather than repeated; a dashboard that is behind is ignored. Also adds the deploy stack for running the dashboard on the droplet, next to the autotrader one: it joins that nginx's network and renders a server block into its conf.d rather than standing up a second ingress. AUTOFIRMER_DB moves the SQLite file onto a volume, since the working directory in a container is replaced on every deploy. And waitFor gained `hidden`: an absent gate matched on DOM presence only, so a modal that closes by going display:none could never satisfy it. The extension grew a visibility probe for this — locate cannot answer it, because absent and hidden are both errors there and a wait gate has to tell them apart. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
1f4459985a
commit
d1e8668440
@@ -0,0 +1,81 @@
|
||||
# Hosting the dashboard on the droplet
|
||||
|
||||
The dashboard is the only piece that can live on a server. The clicker drives a
|
||||
real mouse and keyboard and the extension runs inside your Chrome, so both stay
|
||||
on the Windows machine — they just point at the droplet instead of at
|
||||
`localhost`.
|
||||
|
||||
```
|
||||
Windows box 159.223.129.19
|
||||
clicker (runner.py) ──────┐
|
||||
Chrome + extension ───────┴──HTTPS──> nginx ──> autofirmer:3000 ──> /data/autotrader.sqlite
|
||||
```
|
||||
|
||||
This stack deliberately owns none of the ingress. The droplet already runs one
|
||||
nginx on 80/443 for `master.juicerroom.com` and `git.juicerroom.com`, from
|
||||
`/opt/autotrader/deploy`; this joins that network, and `deploy.sh` renders a
|
||||
server block into that stack's `nginx/conf.d`. Two consequences worth knowing:
|
||||
|
||||
- The rendered `firmer.conf` is untracked in the autotrader repo. It survives
|
||||
that stack's pulls and deploys. It does **not** survive its
|
||||
`bootstrap-tls.sh`, which clears `conf.d` — re-run `./deploy.sh` here if that
|
||||
ever happens.
|
||||
- `deploy.sh` reloads the shared nginx, so a mistake in the template would be a
|
||||
mistake in front of the autotrader dashboard too. It runs `nginx -t` first and
|
||||
refuses to reload on a bad config, and it will not install the TLS server
|
||||
block until the certificate actually exists on disk.
|
||||
|
||||
## First run
|
||||
|
||||
```bash
|
||||
cp .env.example .env # set FIRMER_HOST
|
||||
./bootstrap-tls.sh # DNS check, certificate, then deploy.sh
|
||||
```
|
||||
|
||||
`bootstrap-tls.sh` uses the autotrader stack's certbot container and Let's
|
||||
Encrypt account, and reads `ACME_EMAIL` from its `.env` rather than keeping a
|
||||
second copy. The A record has to point at the droplet first, or the challenge
|
||||
cannot be answered.
|
||||
|
||||
## Shipping a change
|
||||
|
||||
```bash
|
||||
git pull && ./deploy/deploy.sh
|
||||
```
|
||||
|
||||
The build runs before anything is swapped, so a broken commit leaves the running
|
||||
container serving. The database is on the `autofirmer-data` volume and is never
|
||||
touched by a deploy.
|
||||
|
||||
## Pointing the desktop at it
|
||||
|
||||
- **Extension** — open its popup and set the dashboard URL to
|
||||
`https://<FIRMER_HOST>`.
|
||||
- **Clicker** — `python runner.py --api https://<FIRMER_HOST>`, or edit the
|
||||
argument in the PM2 config that starts it.
|
||||
|
||||
Both keep working against `http://localhost:3000` instead; nothing here forces
|
||||
the hosted one.
|
||||
|
||||
## No login
|
||||
|
||||
There is none, at the owner's request, and basic auth could not be bolted on
|
||||
without breaking the two clients: the extension and the clicker poll
|
||||
`/api/autobuyer/*` unattended with nowhere to put a password. Anyone who reaches
|
||||
the host can queue runs, read the settings table — the 2captcha key included —
|
||||
and spend money. If that changes, the options are an IP allowlist in the nginx
|
||||
template, or a shared secret that both clients learn to send.
|
||||
|
||||
## Updating the extension
|
||||
|
||||
The extension can no longer be updated by this stack alone, and it is worth
|
||||
being clear about why. Chrome has no update channel for an unpacked extension:
|
||||
it reads the folder off disk once and never looks again. What the dashboard
|
||||
does is *publish* the version its own checkout ships, at
|
||||
`/api/autobuyer/status`. The extension compares that against its own manifest
|
||||
and calls `chrome.runtime.reload()`, which re-reads the folder from disk.
|
||||
|
||||
So the files still arrive the way they always did — `scripts/update-check.mjs`
|
||||
pulling on the Windows box every five minutes. If the droplet is ahead of that
|
||||
pull, the extension reloads once, notices nothing changed, and waits instead of
|
||||
looping; the popup says as much.
|
||||
Reference in New Issue
Block a user