pliceclogs-og/docs/superpowers/specs/2026-04-12-inventory-sku-lookup-design.md
type-two e5b8504bc6 Import pliceclogs as-is — the original Discogs seller extension
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>
2026-07-24 13:47:09 +10:00

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 tunnel
  • mysql2 — MariaDB client (promise API)

Endpoint

GET /inventory-lookup?release_id=&dbHost=&dbName=&dbUser=&dbPass=&sshKeyPath=

Behaviour

  1. Read SSH private key from sshKeyPath on disk (resolved from ~ to os.homedir())
  2. Open SSH connection to mrpadmin@dbHost using the private key
  3. Forward a random local port → 127.0.0.1:3306 on the remote host via ssh2 forwardOut
  4. Connect mysql2 to 127.0.0.1:<localPort> with dbUser, dbPass, dbName
  5. Execute query with release_id as the bound parameter
  6. Close tunnel and DB connection
  7. 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