From 2c2eb2af7d732bc0ad1f745c8ed49097b0e021de Mon Sep 17 00:00:00 2001 From: Pyronewbic Date: Tue, 26 May 2026 22:18:49 +0530 Subject: [PATCH 1/2] deps: bump helmet, ioredis, playwright, resend, firestore; fix protobufjs and qs vulns --- package-lock.json | 179 +++++++++++++++++++++++----------------------- 1 file changed, 90 insertions(+), 89 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1ece3f4..07f29ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "casecomp", - "version": "1.2.0", + "version": "1.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "casecomp", - "version": "1.2.0", + "version": "1.5.0", "dependencies": { - "@google-cloud/firestore": "^8.5.0", + "@google-cloud/firestore": "^8.6.0", "@google-cloud/storage": "^7.19.0", - "axios": "^1.7.9", + "axios": "^1.16.1", "axios-cookiejar-support": "^7.0.0", "cheerio": "^1.0.0", "compression": "^1.8.1", @@ -78,9 +78,9 @@ } }, "node_modules/@google-cloud/firestore": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-8.5.0.tgz", - "integrity": "sha512-1sbtj7JQfsfekrxwHR0s2CAz8+hkpBdiuB7+20VEgI4EWvW3+GhEUqYOQPRoZ2spHyITLN4gaOPmBN17J539yQ==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-8.6.0.tgz", + "integrity": "sha512-TdvZHfwQj5B5CSDEgDqyrhdVqtOSupmBXDQPasMAJiC64tjsGvyMooNiC43fdk1TsUHeklyoZ6/vQ1TjWKVMbg==", "dependencies": { "@opentelemetry/api": "^1.9.0", "fast-deep-equal": "^3.1.3", @@ -792,9 +792,9 @@ } }, "node_modules/@ioredis/commands": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.5.1.tgz", - "integrity": "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==" + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.10.0.tgz", + "integrity": "sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==" }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -938,17 +938,16 @@ "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==" }, "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==" }, "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" + "@protobufjs/aspromise": "^1.1.1" } }, "node_modules/@protobufjs/float": { @@ -957,9 +956,9 @@ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" }, "node_modules/@protobufjs/inquire": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.1.tgz", - "integrity": "sha512-mnzgDV26ueAvk7rsbt9L7bE0SuAoqyuys/sMMrmVcN5x9VsxpcG3rqAUSgDyLp0UZlmNfIbQ4fHfCtreVBk8Ew==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz", + "integrity": "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==" }, "node_modules/@protobufjs/path": { "version": "1.1.2", @@ -985,8 +984,7 @@ "node_modules/@stablelib/base64": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", - "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", - "license": "MIT" + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==" }, "node_modules/@tootallnate/once": { "version": "2.0.1", @@ -1198,12 +1196,13 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.2.tgz", - "integrity": "sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", + "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", "dependencies": { - "follow-redirects": "^1.15.11", + "follow-redirects": "^1.16.0", "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, @@ -1226,6 +1225,29 @@ "tough-cookie": ">=4.0.0" } }, + "node_modules/axios/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/axios/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1526,9 +1548,9 @@ } }, "node_modules/cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz", + "integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==", "engines": { "node": ">=0.10.0" } @@ -2140,8 +2162,7 @@ "node_modules/fast-sha256": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", - "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", - "license": "Unlicense" + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==" }, "node_modules/fast-xml-builder": { "version": "1.2.0", @@ -2631,11 +2652,14 @@ } }, "node_modules/helmet": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz", - "integrity": "sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.2.0.tgz", + "integrity": "sha512-DRgTIUgnWcJ62KyarxxziuqYxKGnR6Rgg19BlbucN/dpmJbl1XOit6qvoOX0ZT+HhWe5OUVhU/a1zpGyc1xA0Q==", "engines": { "node": ">=18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/EvanHahn" } }, "node_modules/html-entities": { @@ -2799,19 +2823,17 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ioredis": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.10.1.tgz", - "integrity": "sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.11.0.tgz", + "integrity": "sha512-EZBErytyVovD8f6pDfG3Kb37N6Y3lmDA9NNj+4+IP13CzzHGeX+OyeRM2Um13khRzoBSzzL+5lVnCX8V2RLeMg==", "dependencies": { - "@ioredis/commands": "1.5.1", - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.4", - "denque": "^2.1.0", - "lodash.defaults": "^4.2.0", - "lodash.isarguments": "^3.1.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" + "@ioredis/commands": "1.10.0", + "cluster-key-slot": "1.1.1", + "debug": "4.4.3", + "denque": "2.1.0", + "redis-errors": "1.2.0", + "redis-parser": "3.0.0", + "standard-as-callback": "2.1.0" }, "engines": { "node": ">=12.22.0" @@ -3073,16 +3095,6 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" - }, - "node_modules/lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" - }, "node_modules/long": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", @@ -3523,11 +3535,11 @@ } }, "node_modules/playwright": { - "version": "1.59.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", - "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz", + "integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==", "dependencies": { - "playwright-core": "1.59.1" + "playwright-core": "1.60.0" }, "bin": { "playwright": "cli.js" @@ -3540,9 +3552,9 @@ } }, "node_modules/playwright-core": { - "version": "1.59.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", - "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz", + "integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==", "bin": { "playwright-core": "cli.js" }, @@ -3568,23 +3580,23 @@ } }, "node_modules/protobufjs": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.6.tgz", - "integrity": "sha512-M71sTMB146U3u0di3yup8iM+zv8yPRNQVr1KK4tyBitl3qFvEGucq/rGDRShD2rsJhtN02RJaJ7j5X5hmy8SJg==", + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.1.tgz", + "integrity": "sha512-4K0myLaWL5EteuSAro91EGFgcfVgxb64Jx+7oDAY6GOkXD4M69yuSEljNcInGVCA5sOPxmZ/EqDLj2x0Q0+Ygg==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.5", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.1", + "@protobufjs/inquire": "^1.1.2", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", - "long": "^5.0.0" + "long": "^5.3.2" }, "engines": { "node": ">=12.0.0" @@ -3611,9 +3623,9 @@ } }, "node_modules/qs": { - "version": "6.15.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", - "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "dependencies": { "side-channel": "^1.1.0" }, @@ -3723,13 +3735,12 @@ } }, "node_modules/resend": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/resend/-/resend-6.12.3.tgz", - "integrity": "sha512-FkEi6YPnVL96/LvH8+QP7NaeaBy5brYXwlRqUCqZZeNL0/iyKij18IPmyPXYauT/2ODn1JG04qKz+qlJfzqzTw==", - "license": "MIT", + "version": "6.12.4", + "resolved": "https://registry.npmjs.org/resend/-/resend-6.12.4.tgz", + "integrity": "sha512-lRpJ2Hxd+ht+JPDm97juRcUp9HOMuZyxaRFRFmc9Tx8iNWiei94Dx9v6SWufgKk2667C/uCeKKspMotOHSpCSg==", "dependencies": { "postal-mime": "2.7.4", - "svix": "1.92.2" + "standardwebhooks": "1.0.0" }, "engines": { "node": ">=20" @@ -4101,7 +4112,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz", "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==", - "license": "MIT", "dependencies": { "@stablelib/base64": "^1.0.0", "fast-sha256": "^1.3.0" @@ -4253,15 +4263,6 @@ "node": ">=8" } }, - "node_modules/svix": { - "version": "1.92.2", - "resolved": "https://registry.npmjs.org/svix/-/svix-1.92.2.tgz", - "integrity": "sha512-ZmuA3UVvlnF9EgxlzmPtF7CKjQb64Z6OFlyfdDfU0sdcC7dJa+3aOYX5B9mA+RS6ch1AxBa4UP/l6KmqfGtWBQ==", - "license": "MIT", - "dependencies": { - "standardwebhooks": "1.0.0" - } - }, "node_modules/swagger-ui-dist": { "version": "5.32.5", "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.32.5.tgz", From fb1539d4eaaf1f0cfbbf32424dc707d512647cda Mon Sep 17 00:00:00 2001 From: Pyronewbic Date: Tue, 26 May 2026 22:19:21 +0530 Subject: [PATCH 2/2] docs: v1.5.0 release notes, set count, lang filter, PokeWallet, test counts --- CHANGELOG.md | 13 +++++++++++++ README.md | 21 ++++++++++++--------- docs/env-vars.md | 1 + docs/internals.md | 18 +++++++++++------- package.json | 2 +- terraform/README.md | 4 ++-- 6 files changed, 40 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aedf3f6..d6187c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.5.0 (2026-05-25) + +- Admin panel: tabbed layout (Keys / Analytics / Errors / Funnel) +- Analytics dashboard: daily volume chart, by-tier/by-path bars, top queries, avg latency +- Error monitoring: type filter dropdown, 30-day TTL on error-logs, process-level handlers (unhandledRejection, uncaughtException) +- User funnel tracking: signup/firstSearch/firstGrade/firstPortfolioAdd milestones, GET /api/funnel +- Set browser: language filter (?lang=en|jp), 353 sets (up from 238) +- Card database: on-demand sync (POST /api/card-database/sync), no more TTL-based refresh on cold start +- PokeWallet integration: JP card rarity enrichment (SAR/IR/UR/HR/SR/RR/AR/ACE/CR) +- Card ID normalization: deduplicate EN/JA zero-padded IDs for older sets (neo, base) +- Cold start fix: card DB + set metadata fully loaded before accepting connections +- 488 tests (313 unit, 104 API, 71 smoke) + ## 1.4.0 (2026-05-20) - SBOM attestation: Syft SPDX generated from built container image, cosign-attested to digest diff --git a/README.md b/README.md index a8d6c3f..4bd27a3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Casecomp logo Casecomp -[![Version](https://img.shields.io/badge/version-1.4.0-d9b676)](CHANGELOG.md) +[![Version](https://img.shields.io/badge/version-1.5.0-d9b676)](CHANGELOG.md) [![CI](https://github.com/Pyronewbic/casecomp/actions/workflows/ci.yml/badge.svg)](https://github.com/Pyronewbic/casecomp/actions/workflows/ci.yml) [![Deploy](https://github.com/Pyronewbic/casecomp/actions/workflows/deploy.yml/badge.svg)](https://github.com/Pyronewbic/casecomp/actions/workflows/deploy.yml) [![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE) @@ -26,8 +26,8 @@ Search any Pokemon card across four marketplaces in one query. Get live prices, - **PSA grading signals** - population data, difficulty, gem 10%, recommended submission tier - **Slab comparison** - compare PSA 10 / BGS 9.5 / TAG 10 prices across sources - **Portfolio** - track cards, ROI, value over time, grading opportunities -- **Admin** - API key management, stats KPIs, error log at `/admin` -- **Set browser** - browse 238 sets with logos, rarity filters, collection tracking (owned/missing) +- **Admin** - API key management, analytics dashboard, error monitoring with type filters, user funnel tracking at `/admin` +- **Set browser** - browse 353 sets with logos, rarity filters, language filter (EN/JP), collection tracking (owned/missing) - **REST API** - authenticated endpoints with rate limiting, per-key caching, OpenAPI spec - **Claude Code skill** - `/casecomp` for plain-English card search - **Chrome extension** - queue auto-join for Pokemon Center, Walmart, Costco, Target drops @@ -84,7 +84,8 @@ Full reference: [api.casecomp.xyz/docs](https://api.casecomp.xyz/docs) ```bash # ── Public (no key) ────────────────────────────────────────── -curl "https://api.casecomp.xyz/api/sets" # browse 238 sets +curl "https://api.casecomp.xyz/api/sets" # browse 353 sets +curl "https://api.casecomp.xyz/api/sets?lang=en" # EN sets only curl "https://api.casecomp.xyz/api/sets/sv06" # cards in a set curl "https://api.casecomp.xyz/api/autocomplete?q=umbreon" # card search (29K cards) curl "https://api.casecomp.xyz/api/health" # service status @@ -117,7 +118,7 @@ curl -X POST -H "Authorization: Bearer $CASECOMP_KEY" \ ### Public endpoints (no key) -`GET /api/health` | `GET /api/demo` | `GET /api/sitemap` | `GET /api/autocomplete` | `GET /api/sets` | `GET /api/sets/:setCode` | `GET /docs` | `GET /docs/spec.json` | `?demo=true` (sample data) on search/sold/arbitrage/price-history +`GET /api/health` | `GET /api/demo` | `GET /api/sitemap` | `GET /api/autocomplete` | `GET /api/sets` | `GET /api/sets/:setCode` | `GET /api/funnel` | `GET /docs` | `GET /docs/spec.json` | `?demo=true` (sample data) on search/sold/arbitrage/price-history ## Security @@ -199,7 +200,9 @@ All caches use Firestore (shared across Cloud Run instances, persists across dep | `cache-ebay-sold` | 24 hours | eBay sold comp results | | `price-history` | permanent | Sold comp prices over time | | `api-keys` | permanent | Developer API keys (hashed) | -| `error-logs` | permanent | API errors with request IDs | +| `error-logs` | 30 days (TTL) | API errors with request IDs | +| `card-database-cache` | permanent (on-demand sync) | TCGdex card index (~29K cards) | +| `user-milestones` | permanent | User funnel milestones (signup, first search, etc.) | ## Infrastructure @@ -213,12 +216,12 @@ Load unpacked from `extension/` in `chrome://extensions`. ## Tests -486 tests across three layers. CI required checks: unit + codeql. Smoke is non-blocking. +488 tests across three layers. CI required checks: unit + codeql. Smoke is non-blocking. | Suite | Count | Command | Covers | |-------|------:|---------|--------| -| **Unit** | 312 | `yarn test:unit` | Filters, grading, query builder, card identity, condition detection, image preprocessing, email alerts, portfolio ROI, CSV export, autocomplete, JWT auth, price trends, RASP detection | -| **API** | 103 | `yarn test:api` | Search, sold, PSA, grade, auth, admin keys, arbitrage, price history, alerts, share pages, portfolio CRUD, card view, upload-url, analytics, collection tracking | +| **Unit** | 313 | `yarn test:unit` | Filters, grading, query builder, card identity, condition detection, image preprocessing, email alerts, portfolio ROI, CSV export, autocomplete, JWT auth, price trends, RASP detection | +| **API** | 104 | `yarn test:api` | Search, sold, PSA, grade, auth, admin keys, arbitrage, price history, alerts, share pages, portfolio CRUD, card view, upload-url, analytics, collection tracking, lang filter, card-database sync | | **Smoke** | 71 | `yarn test:smoke` | API root page, detail panel, tabs, PSA stats, arbitrage, mobile viewport, portfolio, autocomplete, search filters | ## Contributing diff --git a/docs/env-vars.md b/docs/env-vars.md index 34d0639..93625e8 100644 --- a/docs/env-vars.md +++ b/docs/env-vars.md @@ -56,6 +56,7 @@ Copy **`.env.example`** to **`.env`** and fill in the required values. | `GOOGLE_OAUTH_CLIENT_ID` | *(none)* | Google OAuth client ID for sign-in (popup flow) | | `TOGETHER_API_KEY` | *(none)* | Together AI key for card detection (GLM-4.6V, falls back to Claude Sonnet) | | `RASP_MODE` | `monitor` | RASP enforcement mode: `monitor` (log only) or `block` (reject malicious requests) | +| `POKEWALLET_API_KEY` | *(none)* | PokeWallet API key for JP card rarity data (free tier: 100 req/hr, 1K/day) | ## Email notifications diff --git a/docs/internals.md b/docs/internals.md index 011c404..5d19dac 100644 --- a/docs/internals.md +++ b/docs/internals.md @@ -23,7 +23,7 @@ lib/ auth.js Google OAuth token verification, JWT (HS256) api-keys.js Developer key management cards/ - card-database.js TCGdex card DB (29K EN+JP cards), set browser, rarity + card-database.js TCGdex card DB (29K EN+JP cards), set browser, rarity, PokeWallet JP enrichment card-identity.js Canonical IDs, set resolution, SET_TOTAL_MAP demo.js Sample data (3 multi-source cards) grading-dataset.js ML slab image collection from sold listings (eBay, magi, search) @@ -50,8 +50,8 @@ public/admin/ Admin panel (keys, stats, errors) extension/ Chrome extension: queue auto-join, drop intel terraform/ GCP infra (Cloud Run, Firestore, LB, CDN, Scheduler) test/ - unit-test.js 312 unit tests - api-test.js 103 API integration tests + unit-test.js 313 unit tests + api-test.js 104 API integration tests smoke-test.js 71 Playwright UI smoke tests ``` @@ -66,7 +66,7 @@ test/ - **Dashboard**: static files from `public/` served at `/` and `/admin`. - **Docs**: Swagger UI at `/docs`, spec at `/docs/spec.json`. -On startup: eBay OAuth token pre-fetched, TCGdex card database loaded from Firestore cache (24h TTL), set names + logos loaded in parallel. +On startup: eBay OAuth token pre-fetched, TCGdex card database loaded from Firestore cache (no TTL, on-demand sync via `POST /api/card-database/sync`), set metadata (names + logos) loaded in parallel. Server waits for card DB and set metadata before accepting connections. ## Multi-region deployment @@ -98,8 +98,10 @@ All caches use Firestore (shared across Cloud Run instances, single region). No | `cache-ebay-sold` | 24 hours | eBay sold comp results | | `price-history` | permanent | Sold comp prices over time | | `api-keys` | permanent | Developer API keys (hashed) | -| `error-logs` | permanent | API errors with request IDs | -| `api-analytics` | 30 days | Request analytics (tier, path, latency) | +| `error-logs` | 30 days (TTL) | API errors with request IDs, type filter | +| `api-analytics` | 30 days | Request analytics (tier, path, latency, daily/byStatus) | +| `card-database-cache` | permanent | TCGdex card index (~29K cards), synced on-demand | +| `user-milestones` | permanent | User funnel milestones (signup → search → grade → portfolio) | | `grading-dataset` | permanent | ML training data: slab images + parsed grades | Stale-while-revalidate on active listings for owner key. File-based cache (`.json` files) still used by the CLI. @@ -152,6 +154,8 @@ Use `--refresh` to delete all cache files before a run. | `grade-logs` | source + createdAt desc | Filter grades by source | | `api-analytics` | userId + ts desc | Per-user analytics | | `price-history` | cardKey + recordedAt desc | Card price history | +| `price-history` | cardId + recordedAt desc | Card price history (by canonical ID) | +| `error-logs` | type + createdAt desc | Error filtering by type | **ML dataset pipeline**: graded slab images (PSA/BGS/CGC/TAG) are passively collected into `grading-dataset` Firestore collection from multiple sources: eBay sold (via `track-prices` and `/api/sold`), magi sold (via `track-prices`), and any search with sold results (`/api/search`). Grade is parsed from listing title or grade label. `GET /api/grading-dataset/stats` monitors collection progress. @@ -163,7 +167,7 @@ Three workflows: `ci.yml` (all checks), `deploy.yml` (build + sign + deploy), `t | Job | What | Required? | |-----|------|-----------| -| unit | 312 unit tests | Yes | +| unit | 313 unit tests | Yes | | smoke | 71 Playwright smoke tests | No (continue-on-error) | | codeql | SAST for JavaScript/TypeScript | Yes | | scan | SBOM (Syft) + Grype vulnerability scan | No | diff --git a/package.json b/package.json index 1501814..28b7288 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "casecomp", - "version": "1.4.0", + "version": "1.5.0", "description": "Pokemon TCG card research — live listings from eBay, magi.camp, Yahoo Auctions & SNKRDUNK with AI pre-grading and PSA signals", "type": "module", "engines": { diff --git a/terraform/README.md b/terraform/README.md index bbe7116..f020502 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -12,11 +12,11 @@ Both Cloud Run services deploy to asia-south1 and us-central1. The global HTTPS |----------|---------| | Cloud Run `casecomp-api` | API server, 2 regions, scales to 20 per region | | Cloud Run `casecomp-site` | Frontend SSR (TanStack Start), 2 regions, scales to 10 | -| Firestore | Grade logs, drops, webhooks, alerts, caches, api-keys, price-history, error-logs (asia-south1 only) | +| Firestore | Grade logs, drops, webhooks, alerts, caches, api-keys, price-history, error-logs (30d TTL), card-database-cache, user-milestones, api-analytics (asia-south1 only) | | HTTPS Load Balancer | Global IP, URL map routes by host, serverless NEGs in both regions | | Cloud CDN | Caches static assets from frontend Cloud Run | | SSL Certificates | GCP managed certs for `api.casecomp.xyz` and `casecomp.xyz` | -| Secret Manager | EBAY_CLIENT_ID/SECRET, ANTHROPIC_API_KEY, PSA_AUTH_TOKEN, CASECOMP_API_KEY, CASECOMP_SANDBOX_KEY, RESEND_API_KEY (auto-replicated) | +| Secret Manager | EBAY_CLIENT_ID/SECRET, ANTHROPIC_API_KEY, TOGETHER_API_KEY, PSA_AUTH_TOKEN, CASECOMP_API_KEY, CASECOMP_SANDBOX_KEY, RESEND_API_KEY, CASECOMP_JWT_SECRET, GOOGLE_OAUTH_CLIENT_ID, CASECOMP_ADMIN_SUB, POKEWALLET_API_KEY (auto-replicated) | | Binary Authorization | ENFORCED policy on both Cloud Run services | | Cloud Monitoring | Log-based error metric, error + uptime alerts | | Cloud Scheduler | track-prices + check-alerts every 6h |