Snapshot of the working tree exactly as it stood, no edits. This is the predecessor
PRICEGOD was rewritten from ("kept intact, untouched" per pricegod/README.md), and it
is still the only place the DYMO scale is actually implemented —
rfid-daemon/index.js:1068-1265: HID discovery, parseScaleReport, one-shot read, and a
streaming /weight + /scale/start|stop session whose JSON shape PRICEGOD's daemon.js
already speaks.
Preserved verbatim on purpose (hence -og), including the known bug: DYMO_PIDS at
index.js:1082 is [0x8003, 0x8004], so it cannot see the bench M25 (0x8009). Fix that
in whatever daemon inherits the scale, not here.
node_modules stays ignored (22M of the 24M tree). No credentials in the import: the
two PEM markers in utils.js/sheets.js only strip headers off a key read from settings,
and mrpadmin / johnking are an SSH and a Postgres username, both key/trust auth.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5.9 KiB
Inventory SKU Lookup — Design Spec
Date: 2026-04-12
Overview
When the popup opens on a Discogs release page and the feature is enabled, it SSHes (via Tailscale) to the inventory MariaDB, looks up SKUs for the current release_id, and auto-fills the SKU custom collection field(s) on the Discogs page. If there is a row/box count mismatch, a picker UI lets the user manually assign each SKU to the correct collection box.
Database Schema
Table: wp_rmp_disc_inventory (on MariaDB at 100.123.123.64)
| Column | Type | Notes |
|---|---|---|
sku |
varchar(255) PK | Timestamp-format integer e.g. 20250515100402 — lower = older |
release_id |
int(11) INDEX | Discogs release ID |
media_condition |
text nullable | |
sleeve_condition |
text nullable | |
price |
decimal(10,2) | May drift vs Discogs — display only, never written to Discogs |
Query:
SELECT sku, price, media_condition, sleeve_condition
FROM wp_rmp_disc_inventory
WHERE release_id = ?
ORDER BY sku ASC
Architecture
Discogs page (content.js)
↕ sendMessage
Popup (popup.js)
↕ fetch localhost:7790
rfid-daemon (index.js) ──ssh2──▶ mrpadmin@100.123.123.64
──mysql2─▶ MariaDB 127.0.0.1:3306 (via tunnel)
The existing updateDiscogsSku message (chrome.tabs.sendMessage) already handles writing a SKU value to a specific collection box index — no changes needed to content.js.
state.collectionBoxCount and state.collectionBoxSkus are already tracked in popup.js and populated when the popup loads on a Discogs release page.
Distinct from existing MONSTERWIKI inventory import (handleImportInventory / INV button): that feature talks to the Python server on 100.91.239.7:5002 (PostgreSQL, different dataset) and is triggered manually. This new feature is auto-triggered on popup open, talks to the WordPress MariaDB at 100.123.123.64, and only writes SKU (not price).
1. Settings UI (Connect section, popup.html)
Add a new collapsible sub-section "Inventory SKU Lookup" inside the existing Connect settings block.
Fields:
| Field | Element ID | Default |
|---|---|---|
| Enable toggle | inventorySkuEnabled (checkbox) |
unchecked |
| DB Host | inventory-db-host |
100.123.123.64 |
| DB Name | inventory-db-name |
wp_rmp_disc_ |
| DB User | inventory-db-user |
(empty) |
| DB Password | inventory-db-pass (type=password) |
(empty) |
| SSH Key Path | inventory-ssh-key |
~/.ssh/id_rsa |
Saved and loaded by the existing "Save Connect Settings" button alongside Discogs token, Google settings, etc. Values stored in chrome.storage.local under key inventorySettings.
2. rfid-daemon: /inventory-lookup endpoint
New npm dependencies
ssh2— SSH tunnelmysql2— MariaDB client (promise API)
Endpoint
GET /inventory-lookup?release_id=&dbHost=&dbName=&dbUser=&dbPass=&sshKeyPath=
Behaviour
- Read SSH private key from
sshKeyPathon disk (resolved from~toos.homedir()) - Open SSH connection to
mrpadmin@dbHostusing the private key - Forward a random local port →
127.0.0.1:3306on the remote host viassh2forwardOut - Connect
mysql2to127.0.0.1:<localPort>withdbUser,dbPass,dbName - Execute query with
release_idas the bound parameter - Close tunnel and DB connection
- Return:
{ "rows": [ { "sku": "20250515100402", "price": "12.00", "media_condition": "VG+", "sleeve_condition": "VG" } ] }
Errors return { "error": "<message>" } with HTTP 500.
The tunnel is opened fresh per request (no persistent connection) — requests are infrequent (one per popup open).
3. Popup logic (popup.js)
Trigger
Runs automatically when the popup finishes loading on a Discogs release page (state.discogsTabId is set and state.releaseData.id exists) and inventorySettings.enabled is true.
Flow
load inventorySettings from chrome.storage.local
if not enabled → stop
GET /inventory-lookup?release_id=<id>&...creds...
if error → show error message in popup, stop
count rows = result.rows.length
count boxes = number of collection boxes on page (state.collectionBoxCount or equivalent)
if rows == 0 → silent, stop
if rows == 1 → writeSkuToBox(rows[0].sku, state.collectionBoxCount - 1) [silent, last box]
if rows == boxes → for each i: writeSkuToBox(rows[i].sku, i) [silent, earliest SKU → first box]
else → show mismatch picker UI
writeSkuToBox(sku, boxIndex)
Calls chrome.tabs.sendMessage(state.discogsTabId, { action: 'updateDiscogsSku', sku, boxIndex }) — existing content.js handler, no changes needed.
4. Mismatch Picker UI
Rendered inside the popup when rows ≠ boxes (and not the 1-row case).
Layout:
⚠ SKU mismatch: 2 DB rows, 3 collection boxes
[ 20250515100402 · $12.00 · VG+ · VG ] ← clickable card
[ 20260101090000 · $18.50 · NM · NM ] ← clickable card
Write selected to box: [1] [2] [3]
Interaction:
- Clicking a card selects it (highlighted border/background), deselects others
- The box number buttons are always visible (one per collection box, labelled 1-based)
- Clicking a box number button calls
writeSkuToBox(selectedSku, boxIndex)for the selected card - After writing, that card gets a small checkmark indicator; user can continue selecting and writing other rows
- No card selected + box button click → no-op (button visually disabled until a card is selected)
5. Error / Status Display
A small status line is added to the Discogs tab in the popup (below existing content) to show:
- Nothing (default)
"SKU mismatch: N rows, M boxes"— triggers picker"Inventory lookup failed: <reason>"— SSH/DB error"SKUs filled"— after successful auto-fill (fades after 3s)
Out of Scope
- Writing price to Discogs (price may drift — display only in picker)
- Caching DB results between popup opens
- Handling multiple Discogs tabs simultaneously