Ecommerce Template
Next.js·React 등으로 만든 최신 전자상거래 템플릿
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
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.