Ecommerce Template
Modern ecommerce template built with Next.js 16, React 19, Drizzle, Better Auth, Supabase, and Stripe.
Private used cars for sale in Alberta.
Translation pending
CommerceTypeScript · PLpgSQLGitHub @maksjoyOnlineOwner 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
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Alberta-Cars is an Alberta-only marketplace for private used-vehicle sales. The production application is a single Next.js 15 + Supabase web app deployed on Vercel. There is no second static production/ frontend and no Telegram runtime in this repository.
TODO: the official public domain and support mailbox are not yet verified. Keep the current deployment URL configuration until an Alberta-Cars domain is configured and tested. Do not publish an unverified support address or hardcode a new canonical host before then.
pending until an administrator approves them.listings.user_id from anon are committed and still waiting to be applied. See ops/READINESS.md./_next/static/* assets with immutable caching.script-src 'unsafe-inline'.npm ci..env.example to .env.local and configure the Supabase URL and publishable key.npm run dev.Useful checks:
npm run typecheck
npm run test:unit
npm run build
npm run test:security-live
npm run test:e2e
npm run test:security-live talks to the live Supabase project. Two tests stay red until the pending migrations in ops/READINESS.md are applied: anonymous listings.user_id reads, and anonymous listing-photo list or /object/authenticated/ downloads. The other security-live tests should pass.
GitHub Actions workflow Test Alberta-Cars Web uses Node 24 and npm ci, typechecks, runs npm run test:unit, builds the Next.js app, runs the public security boundary tests, runs tests/database-security.sql when the SUPABASE_DB_URL repository secret is set, and runs Playwright plus a read-only load smoke. On main it also checks that the production catalog can be served from the Vercel CDN cache.
The CI source-tree guard fails if a legacy production/ directory, a browser Supabase session client, or Telegram runtime references are reintroduced into app, components, lib, or middleware.ts.
Update an existing database only from supabase/migrations/, in filename order. Apply each new file yourself with:
psql "$SUPABASE_DB_URL" -v ON_ERROR_STOP=1 --single-transaction -f path/to/migration.sql
supabase/schema.sql is an outdated sketch for a brand-new empty project. Do not apply it to the live database. It does not match production tables, policies, or grants.
Applied on the live project on 2026-10-23, in this order, and recorded in supabase_migrations.schema_migrations. Do not apply them again.
supabase/migrations/20261023210000_signed_url_only_listing_photos.sql — installed without storage.allow_* helpers; the operation check is inlinedsupabase/migrations/20261023211000_hide_listing_owner_from_anon.sqlsupabase/migrations/20261023212000_pending_only_listing_photo_uploads.sqlsupabase/migrations/20261023213000_protect_profile_trust_columns.sqlDetails and rollback are in those files and in ops/READINESS.md. Do not apply schema.sql.
The /admin route is owner/admin-only. Every admin mutation rechecks membership in the admins table on the server. Admin tools include listing approval/removal, reports, user moderation, aggregate analytics and recent activity.
Never place a Supabase service-role key, Sentry auth token, database URL, or other server credential in browser code or a NEXT_PUBLIC_* variable, except values explicitly designed to be public such as the Supabase publishable key and Sentry DSN.
Sign-in, signup, and password recovery can show a Cloudflare Turnstile widget and send its token to Supabase Auth as captchaToken. Supabase checks that token only after CAPTCHA is enabled in the Supabase dashboard. Password updates from the reset page are rate limited, and they do not show a widget. Sign-out is not limited.
The widget is rendered only when NEXT_PUBLIC_TURNSTILE_SITE_KEY is set. The server reads that value per request, so an empty value is not baked into the client bundle. Rate limiting uses @upstash/ratelimit and @upstash/redis. It runs only when both UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN are set. Otherwise the server logs one warning and allows the attempt. A Redis error also allows the attempt, so an Upstash outage does not take down login.
Limits, per action:
| Action | Per client IP | Per normalized email |
|---|---|---|
| Sign in | 20 / 15 minutes | 10 / 15 minutes |
| Signup | 5 / hour | 3 / hour |
| Password recovery | 5 / hour | 3 / hour |
| Password update | 10 / hour | — |
The client IP comes from Vercel's x-real-ip, then the first address in x-vercel-forwarded-for. x-forwarded-for is ignored. If neither trusted header is present, requests share an unknown bucket. Email is trimmed and lowercased. A value without @ counts against the IP bucket only. A limited request returns 429 with Retry-After and Too many attempts. Please try again later. The response does not say which bucket fired.
Rollout order, so login never breaks:
NEXT_PUBLIC_TURNSTILE_SITE_KEY, UPSTASH_REDIS_REST_URL, and UPSTASH_REDIS_REST_TOKEN in Vercel, then redeploy.CSP already allows https://challenges.cloudflare.com on script-src, frame-src, and connect-src. The widget script uses the per-request nonce.
Vercel builds the repository as Next.js using Node 24 and npm ci. The live site uses the URL configured through NEXT_PUBLIC_SITE_URL. It uses the existing Supabase project. Do not point production at a different project until the pending migrations and a restore drill are done.