尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Price comparison for construction materials. Search or browse lumber, electrical, plumbing and tools, filter by real specs (wire gauge, length, lumber size, PEX diameter…), and see what each item costs at Home Depot, Lowe's, Menards, Ace Hardware, Harbor Freight and Amazon. Includes in-store prices at your nearest stores and online prices with shipping, plus unit prices (per ft, per sheet, per 100 ct).
Draft: all prices come from a mock provider that generates believable sample data. See Real price data for the plan to plug in real sources.
@prisma/adapter-pg)Requirements: Node 20+ and a PostgreSQL database. Local Postgres works, or use a free hosted tier (Neon, Supabase, Prisma Postgres).
npm install
cp .env.example .env # then edit DATABASE_URL
npm run db:migrate # create tables
npm run db:seed # load sample catalog + prices
npm run dev # http://localhost:3000
Other scripts:
| Script | What it does |
|---|---|
npm run prices:refresh [-- <retailer-slug>] | Pull fresh quotes from each retailer's provider |
npm run amazon:match [-- --apply] | Find Amazon ASINs for products (needs Amazon credentials) |
npm test | Unit tests (optimizer, Amazon provider) |
npm run db:reset | Drop, re-migrate and re-seed the database |
npm run typecheck / npm run lint | Checks |
The app needs a Node server and Postgres, so it runs on Vercel's free Hobby plan with a free Neon database added through Vercel. Deploys come straight from GitHub.
SailTheUniverse/2by4, and click Deploy. This first deploy fails because there's no
database yet. That's expected.DATABASE_URL, DATABASE_URL_UNPOOLED).CRON_SECRET with a long random value.vercel.json schedules /api/cron/refresh-prices weekly (Mondays). It refreshes prices and trims history older than
120 days, which keeps the database at roughly 130–150 MB, well under Neon's free 0.5 GB. Every push to draft-mvp
redeploys. If the seed data changed (prisma/seed/*), the deploy reseeds the catalog automatically. Project lists are
kept and re-linked to products by slug.
Category (tree) ──< AttributeDef filterable specs per category (ENUM / NUMBER / BOOLEAN)
Product (specs JSON, unitLabel/unitQty) one canonical item, matched across retailers
└─< Listing (product × retailer, url, retailerSku e.g. ASIN)
├─< Offer (current price per location: a Store id or "online"; source = mock | live)
└─< PricePoint (price history)
Retailer ──< Store (zip)
ProjectList ──< ProjectItem (product, quantity)
/c/wire?gauge=12+AWG&length=250&sort=unit), so every view can be shared.
Facet counts are disjunctive: each one ignores its own selection. See src/lib/filters.ts.prisma/seed/categories.ts with its attributes. Product
specs are JSON keyed by attribute key, so no migration is needed.src/lib/location.ts and src/lib/pricing.ts./list, shareable at /lists/[id]): there are no accounts yet, so the list id lives in a cookie.
src/lib/optimizer.ts tries every combination of retailers and pickup vs. online and returns the cheapest plan
overall, with up to 2 stops, and with one stop. Shipping is one fee per online order: free if any item in the order
ships free on its own, otherwise the largest single-item fee.PricePoints. The product page charts each retailer's best price per day for
your location (30/90 days) and flags "Lowest price in 90 days". The seed back-fills 90 days of sample history.prisma/schema.prisma data model
prisma/seed/ sample retailers, stores, categories, products
scripts/refresh-prices.ts price refresh job
scripts/amazon-match.ts ASIN matching for Amazon listings
src/lib/providers/ PriceProvider interface, mock + Amazon providers
src/lib/catalog.ts queries (products, prices, history)
src/lib/filters.ts filter parsing, facets, sorting
src/lib/optimizer.ts cheapest-plan search for project lists
src/app/ pages: / , /c/[slug], /p/[slug], /search, /list, /lists/[id]
src/components/ UI
Each retailer gets a PriceProvider (src/lib/providers/types.ts). Providers are picked per retailer in
src/lib/providers/index.ts, and every offer records its source, so the UI tags live prices with LIVE. Options,
roughly in order of cost:
Product matching across retailers should key on UPC, then brand + model number.
Amazon retired PA-API 5 in spring 2026; the Creators API replaces it, with new credentials.
AMAZON_* variables in .env (see .env.example).npm run amazon:match: searches Amazon for each product and shows proposed ASINs. Matches where the model number
appears in the Amazon title are saved with --apply; review the rest and save them with
npm run amazon:match -- --set <product-slug> <ASIN>.npm run prices:refresh -- amazon fetches live prices (10 ASINs per request, about 1 request/second).Amazon links go straight to the product page (amazon.com/dp/<ASIN>). The verified ASINs live in
prisma/seed/amazon.ts; each was matched by brand, model and pack size. A product only gets an Amazon listing when
it has an ASIN there, so there are no search-page links. Once credentials are set, Amazon listings without an ASIN
show no Amazon price, rather than sample data. The request
format (token endpoints, getItems/searchItems fields, offersV2 response) was taken from published Creators API
client SDKs, because Amazon's docs site wasn't reachable while building this. Unit tests cover it with a mocked API.
Check the first live run against Amazon's current docs, since it hasn't run against real credentials yet.