cleanup
Build and Push Docker Image / build (push) Failing after 17s

This commit is contained in:
2026-06-25 22:39:23 -05:00
parent c0a44e7c04
commit 9b18984e3f
3 changed files with 123 additions and 164 deletions
-32
View File
@@ -1,32 +0,0 @@
# UI Stock Tracker — Design Notes & Development History
Supplementary notes that go beyond the [README](./README.md): the reasoning behind the trickier parts of the scraper and the notable fixes made during development. For setup, architecture, API, and schema, see the README; for deployment see [UNRAID.md](./UNRAID.md).
## Stock Detection Rationale
The scraper classifies each product page as `in_stock`, `sold_out`, or `unknown` using case- and whitespace-insensitive substring matching against the page's buttons. Two non-obvious details drove the matching rules:
**Auth-state difference.** The scraper runs logged out. On a sold-out item, Ubiquiti shows `"Login for Notifications"` to logged-out visitors instead of the logged-in `"Notify me when available"`. Both strings must be in the sold-out list, or logged-out checks would fall through to `unknown`.
**The `label` attribute exists only on the in-stock button.** The production DOM carries `label="Add to Cart"` on the primary Add-to-Cart button (confirmed in DevTools), but the sold-out "Login for Notifications" button has no `label` attribute. So in-stock has two redundant signals (the attribute *and* the button text) while sold-out relies on text alone — this asymmetry is by design, not an oversight.
When neither signal matches, the scraper logs a `[Scraper] UNKNOWN debug` payload (page title, button labels, button texts) so a misclassification can be diagnosed from container logs without reproducing locally.
To avoid reading the page before React finishes rendering, the scraper waits 2.5s plus an active wait for a `<button>` to appear (up to 8s) before evaluating.
## Fixes Applied During Development
### 1. `npm ci` → `npm install` (Dockerfile)
`npm ci` requires a committed `package-lock.json`. Lockfiles were never committed, so the build failed immediately. Both the frontend and backend build stages use `npm install` instead. (This is why no lockfiles are present in the repo.)
### 2. TypeScript DOM lib missing (backend)
`scraper.ts` uses `page.evaluate()`, whose callback runs in the browser context. TypeScript flagged `document`, `navigator`, and `HTMLMetaElement` as unknown because the backend `tsconfig.json` only included `"lib": ["ES2020"]`. Fixed by adding the DOM libs:
```json
"lib": ["ES2020", "DOM", "DOM.Iterable"]
```
`DOM.Iterable` was specifically required to allow `for...of` iteration over `NodeListOf<HTMLSpanElement>`.
### 3. Two containers → one container
The original design used separate `frontend` and `backend` Docker services in docker-compose. These were consolidated into a single container running nginx and Node side by side under **supervisord**, with nginx proxying `/api/` to `localhost:3001`. Removed `backend/Dockerfile`, `frontend/Dockerfile`, and `frontend/nginx.conf`; the root-level `Dockerfile`, `nginx.conf`, and `supervisord.conf` replaced them.
+123 -4
View File
@@ -140,7 +140,7 @@ docker compose up -d # serves on http://localhost:8080
The image builds in three stages: Vite builds the frontend, `tsc` compiles the backend (then `npm prune --production`), and the runtime stage assembles `node:20-slim` + nginx + Chromium + supervisor. The image builds in three stages: Vite builds the frontend, `tsc` compiles the backend (then `npm prune --production`), and the runtime stage assembles `node:20-slim` + nginx + Chromium + supervisor.
Full Unraid install instructions are in [UNRAID.md](./UNRAID.md). Full Unraid install instructions are in [Deploy on Unraid](#deploy-on-unraid) below.
## CI/CD ## CI/CD
@@ -167,7 +167,126 @@ Configure via **Settings** in the UI, then use **Test Alert** to verify.
> ⚠️ These are live credentials committed to the repo. Rotate the bot token (via @BotFather) if this repository is ever made public or shared. > ⚠️ These are live credentials committed to the repo. Rotate the bot token (via @BotFather) if this repository is ever made public or shared.
## Further Docs ## Deploy on Unraid
- [PROJECT.md](./PROJECT.md) — deeper design notes and development history. ### 1. Clone and build the image (CLI)
- [UNRAID.md](./UNRAID.md) — step-by-step Unraid install and troubleshooting.
SSH into your Unraid server and run:
```bash
mkdir -p /mnt/user/appdata/ui-tracker
cd /mnt/user/appdata/ui-tracker
git clone https://git.alwisp.com/jason/ui-tracker.git .
docker build -t ui-tracker .
```
The first build takes a few minutes — it compiles the frontend, compiles the backend, and installs Chromium inside the image. The result is available locally as `ui-tracker`.
### 2. Add the container (Unraid GUI)
In the Unraid web UI go to **Docker → Add Container** and fill in each section:
**Basic**
| Field | Value |
|---|---|
| Name | `ui-tracker` |
| Repository | `ui-tracker` |
| Icon URL | *(leave blank)* |
**Network**
| Field | Value |
|---|---|
| Network Type | `br0` |
| Fixed IP Address | Your chosen static LAN IP — e.g. `192.168.1.50` |
> With `br0`, the container gets its own IP on your LAN, so you reach the UI directly at `http://<fixed-ip>:8080` with no port conflict against the Unraid host.
**Port***Add another Path, Port, Variable, Label or Device → Port*
| Field | Value |
|---|---|
| Name | `Web UI` |
| Container Port | `8080` |
| Host Port | `8080` |
| Protocol | `TCP` |
**Path (persistent data)***Add another Path, Port, Variable, Label or Device → Path*
| Field | Value |
|---|---|
| Name | `Data` |
| Container Path | `/app/data` |
| Host Path | `/mnt/user/appdata/ui-tracker/data` |
| Access Mode | `Read/Write` |
> This is where the SQLite database lives — tracked items and Telegram settings persist here across restarts and rebuilds.
**Variables** — none required; all configuration is baked into the image.
Click **Apply** to create and start the container.
### 3. First-time setup
Open the UI at `http://<fixed-ip>:8080` and:
1. Click **Settings** (top right).
2. Enter your **Bot Token** and **Chat ID** (see [Telegram Configuration](#telegram-configuration)).
3. Click **Test Alert** — a Telegram message should arrive within seconds.
4. Click **Save**.
5. Click **Add Item**, paste a `store.ui.com` product URL, set the check interval, and click **Start Tracking**.
### Rebuilding after an update
```bash
cd /mnt/user/appdata/ui-tracker
git pull
docker stop ui-tracker
docker rm ui-tracker
docker build -t ui-tracker .
```
Then re-add the container from **Docker → Add Container** with the same settings, or start it from the Docker tab if Unraid retained the template.
### Troubleshooting
**UI not loading**
- Confirm the container is running in the Docker tab.
- Check logs: click the container icon → **Logs**.
- Make sure the fixed IP isn't already in use on the network.
**Telegram test fails**
- Verify the bot token and chat ID in Settings.
- Send `/start` to your bot in Telegram at least once to open the conversation.
- Confirm Unraid has outbound HTTPS (port 443) access.
**Items stuck on "Unknown" status**
- Open container logs and look for Puppeteer errors.
- Restart the container — Chromium occasionally needs a clean start.
## Design Notes & Development History
### Stock detection rationale
The scraper classifies each product page using case- and whitespace-insensitive substring matching against the page's buttons. Two non-obvious details drove the matching rules:
**Auth-state difference.** The scraper runs logged out. On a sold-out item, Ubiquiti shows `"Login for Notifications"` to logged-out visitors instead of the logged-in `"Notify me when available"`. Both strings must be in the sold-out list, or logged-out checks would fall through to `unknown`.
**The `label` attribute exists only on the in-stock button.** The production DOM carries `label="Add to Cart"` on the primary Add-to-Cart button (confirmed in DevTools), but the sold-out "Login for Notifications" button has no `label` attribute. So in-stock has two redundant signals (the attribute *and* the button text) while sold-out relies on text alone — this asymmetry is by design.
When neither signal matches, the scraper logs a `[Scraper] UNKNOWN debug` payload (page title, button labels, button texts) so a misclassification can be diagnosed from container logs without reproducing locally. To avoid reading the page before React finishes rendering, the scraper waits 2.5s plus an active wait for a `<button>` to appear (up to 8s) before evaluating.
### Fixes applied during development
**`npm ci``npm install` (Dockerfile).** `npm ci` requires a committed `package-lock.json`. Lockfiles were never committed, so the build failed immediately. Both build stages use `npm install` instead — which is why no lockfiles are present in the repo.
**TypeScript DOM lib missing (backend).** `scraper.ts` uses `page.evaluate()`, whose callback runs in the browser context. TypeScript flagged `document`, `navigator`, and `HTMLMetaElement` as unknown because the backend `tsconfig.json` only included `"lib": ["ES2020"]`. Fixed by adding the DOM libs:
```json
"lib": ["ES2020", "DOM", "DOM.Iterable"]
```
`DOM.Iterable` was specifically required to allow `for...of` iteration over `NodeListOf<HTMLSpanElement>`.
**Two containers → one container.** The original design used separate `frontend` and `backend` Docker services in docker-compose. These were consolidated into a single container running nginx and Node side by side under supervisord, with nginx proxying `/api/` to `localhost:3001`. Removed `backend/Dockerfile`, `frontend/Dockerfile`, and `frontend/nginx.conf`; the root-level `Dockerfile`, `nginx.conf`, and `supervisord.conf` replaced them.
-128
View File
@@ -1,128 +0,0 @@
# Unraid Installation Guide — UI Stock Tracker
---
## Step 1 — CLI: Clone and Build the Image
SSH into your Unraid server and run:
```bash
mkdir -p /mnt/user/appdata/ui-tracker
cd /mnt/user/appdata/ui-tracker
git clone https://github.com/<your-username>/ui-tracker.git .
docker build -t ui-tracker .
```
The first build takes a few minutes — it compiles the React frontend, compiles the Node.js backend, and installs Chromium inside the image. Once complete, the image is available locally as `ui-tracker`.
---
## Step 2 — GUI: Add the Container in Unraid
In the Unraid web UI go to **Docker → Add Container**.
Fill in each section as follows:
---
### Basic
| Field | Value |
|---|---|
| Name | `ui-tracker` |
| Repository | `ui-tracker` |
| Icon URL | *(leave blank)* |
---
### Network
| Field | Value |
|---|---|
| Network Type | `br0` |
| Fixed IP Address | Your chosen static LAN IP — e.g. `192.168.1.50` |
> With br0, the container gets its own IP on your LAN. You will access the UI directly at `http://<fixed-ip>:8080` — no port conflict with the Unraid host.
---
### Port
Click **Add another Path, Port, Variable, Label or Device** → select **Port**.
| Field | Value |
|---|---|
| Name | `Web UI` |
| Container Port | `8080` |
| Host Port | `8080` |
| Protocol | `TCP` |
---
### Path (Persistent Data)
Click **Add another Path, Port, Variable, Label or Device** → select **Path**.
| Field | Value |
|---|---|
| Name | `Data` |
| Container Path | `/app/data` |
| Host Path | `/mnt/user/appdata/ui-tracker/data` |
| Access Mode | `Read/Write` |
> This is where the SQLite database lives. All tracked items and Telegram settings persist here across restarts and rebuilds.
---
### Variables
No environment variables are required — all configuration is baked into the image.
---
Click **Apply** to create and start the container.
---
## Step 3 — First-Time Setup
Open the UI at `http://<fixed-ip>:8080` and:
1. Click **Settings** (top right)
2. Enter **Bot Token**: `8769097441:AAFBqPlSTcTIi3I-F5ZIN9EEpwbNDzHg8hM`
3. Enter **Chat ID**: `8435449432`
4. Click **Test Alert** — a Telegram message should arrive within seconds
5. Click **Save**
6. Click **Add Item**, paste a `store.ui.com` product URL, set your check interval, click **Start Tracking**
---
## Rebuilding After an Update
```bash
cd /mnt/user/appdata/ui-tracker
git pull
docker stop ui-tracker
docker rm ui-tracker
docker build -t ui-tracker .
```
Then go back to **Docker → Add Container** and re-add it with the same settings, or start it from the Docker tab if Unraid retained the template.
---
## Troubleshooting
**UI not loading**
- Confirm the container is running in the Docker tab
- Check logs: click the container icon → **Logs**
- Make sure your fixed IP isn't already in use on the network
**Telegram test fails**
- Verify the bot token and chat ID in Settings
- Send `/start` to your bot in Telegram at least once to open the conversation
- Confirm Unraid has outbound HTTPS (port 443) access
**Items stuck on "Unknown" status**
- Open container logs and look for Puppeteer errors
- Restart the container from the Docker tab — Chromium occasionally needs a clean start