Ecommerce Template
Modern ecommerce template built with Next.js 16, React 19, Drizzle, Better Auth, Supabase, and Stripe.
A free remnant board for machine shops. List leftover bar, plate, tube, and drops so a nearby shop can buy them instead of the scrap bin.
CommerceTypeScript · CSSGitHub @jmwilson92OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
sell scrap baby
Translation pending
Taken from the repository README
Loading the full README.
Read the full README ↗Skrappily is a free remnant-metal board for machine shops. A shop lists leftover plate, bar, tube, angle, or mixed scrap. Nearby shops search by metal type (6061, 7075, 5052, mild steel, 304, 316, copper, brass, bronze, titanium, and others), form, thickness, length, price, distance from a ZIP code, and whether the seller can deliver. They message the seller in the app. A map shows each shop as a pin. Nothing is charged on the site. A delivery fee or a material-cert note is information for the buyer. Sellers choose buyer pickup or that they can deliver. A future $1 Verified Seller badge is shown from a shop flag and is not for sale yet. Posting and browsing stay free.
Example listings around San Diego are seeded so the board is usable on first run. They are marked Example and are not live inventory.
Requires Node.js 22 or newer.
npm install
npm run dev
Open http://127.0.0.1:3847.
No environment variables are required. Listings are stored in data/skrappily.db (SQLite via libSQL). Uploaded photos go in data/uploads/ and are served from /uploads/....
npm test
npm run lint
npm run build
Delete data/skrappily.db and restart to load the example listings again. Set SEED_EXAMPLE_DATA=false before the first boot if you want an empty board.
The codespace only has the files that are on GitHub. Open the scrapyard repo and confirm you see package.json, app/, and this README. An empty repo, or one that only has GitHub’s starter README, needs this project pushed to main first.
On the repo page, click Code, then the Codespaces tab, then Create codespace on main.
When the editor finishes loading, open the terminal and run:
npm install
npm run dev
Open the Ports tab. Port 3847 shows up once Next.js is ready. Click the globe on that row to open the app.
No environment variables are required. Sign in as [email protected] with password skrappily-demo, or create your own shop.
Stop the codespace from github.com/codespaces when you are done. The free plan counts the hours it stays running.
Every seeded shop uses the password skrappily-demo.
| Shop | ZIP | |
|---|---|---|
| Kearny Mesa Tool & Die | [email protected] | 92111 |
| Mira Mesa Aero Cuts | [email protected] | 92126 |
| Harbor Stainless | [email protected] | 91910 |
| National City Marine Fab | [email protected] | 91950 |
| El Cajon Job Shop | [email protected] | 92020 |
| Santee Weldments | [email protected] | 92071 |
| North County Titanium | [email protected] | 92054 |
| Carlsbad Screw Machine | [email protected] | 92008 |
| Escondido Metals | [email protected] | 92025 |
| Sorrento Plastics | [email protected] | 92121 |
| Lemon Grove Chassis | [email protected] | 91945 |
| Vista Sheet Metal | [email protected] | 92083 |
| Barrio Logan Drops | [email protected] | 92113 |
| Old Town Precision | [email protected] | 92110 |
Sign in as one of those shops to mark an example listing sold or to read a message you sent it. Create your own account for real listings.
Copy .env.example to .env.local only when you need something other than the defaults.
| Variable | Required | Purpose |
|---|---|---|
LIBSQL_URL | On Vercel | libSQL URL. Turso URLs look like libsql://your-db.turso.io. TURSO_DATABASE_URL and DATABASE_URL are aliases. DATABASE_URL must be libsql:// or file:, not Postgres. |
LIBSQL_AUTH_TOKEN | With a remote URL | Database token. TURSO_AUTH_TOKEN is an alias. |
SEED_EXAMPLE_DATA | No | Set to false before the first request to skip the San Diego examples. |
STORAGE_PROVIDER | No | local (default) or blob. |
BLOB_READ_WRITE_TOKEN | For blob photos | Vercel Blob token. If this is set and STORAGE_PROVIDER is empty, photos use Blob. |
When LIBSQL_URL is unset, the app uses file:./data/skrappily.db. A file: URL is also accepted for a different local path.
The app is a Next.js App Router project and stays on the Node.js runtime. The Hobby (free) plan is enough. You still need a remote database because Vercel’s filesystem is ephemeral, so the local SQLite file will not keep listings.
Push this project to a Git host and import it in Vercel.
Create a free Turso database:
turso db create skrappily
turso db show skrappily --url
turso db tokens create skrappily
In the Vercel project, set:
LIBSQL_URL to the libsql:// URLLIBSQL_AUTH_TOKEN to the tokenOptional photos that persist: create a Blob store in the Vercel dashboard (included on the free tier) and set STORAGE_PROVIDER=blob plus BLOB_READ_WRITE_TOKEN. Without Blob, listings still work, but photo upload is refused on Vercel so files are not written to a disk that disappears.
Optional: set SEED_EXAMPLE_DATA=false if the production board should start empty. If you leave it unset, the first request seeds the example San Diego listings into the empty Turso database.
Deploy. npm run build is the build command and Next.js is the framework preset.
Distance search does not call a geocoding API. Centroids for US ZIP codes ship in data/zip-centroids.csv. The map uses Leaflet with OpenStreetMap tiles and does not need an API key. Shop pins sit on the shop ZIP. A pin opens the shop name and active listing count, and links to that shop's page.
Accounts are email plus password. Passwords are hashed with bcrypt. The session cookie is HTTP-only and only the hash of the session token is stored. No magic-link email is sent, so the app does not need an email provider.
“Message seller” writes into the in-app inbox. Reports are stored on the listing. There is no admin console and no payment flow.
data/zip-centroids.csv comes from the GeoNames postal code dataset and is used under CC BY 4.0. See data/ZIP_SOURCE.txt.
Payments are limited to RFQs for now: once an RFQ owner accepts a quote, they
see Pay for this quote with a breakdown (quote price, card processing fee,
total). Checkout uses a Stripe Checkout Session as a destination charge to
the supplier's Connect account (Express dashboard; the platform controls fees
and losses), with application_fee_amount. Suppliers turn on payouts from
Account → Get paid. Orders live at /orders; suppliers mark them shipped
or completed. Webhooks land at /api/stripe/webhook (raw-body signature check,
idempotent via stripe_events).
Fee economics. Stripe charges about 2.9% + 30¢ per card payment, and on a
destination charge that cost comes out of the platform's balance. A small
platform fee alone would lose money on every sale, so by default
(STRIPE_PASS_THROUGH_CARD_FEES=true) the buyer's total is grossed up:
total = ceil((price + 30¢) / (1 − 2.9%)). The supplier receives
price − 1%, and the platform keeps the 1% after Stripe's cut. Example:
a $1,000 quote → buyer pays $1,030.18 ($30.18 card fee), supplier receives
$990.00, Skrappily nets ≈ $10. Invoices and net terms come later.