Ecommerce Template
Next.js·React 등으로 만든 최신 전자상거래 템플릿
Vokter: 진위 확인 코드·AI 쇼핑 어시스턴트·대기열 독점 드롭
Vokter: cada producto con código de autenticidad verificable, un asistente de compras con IA que solo conoce el catálogo real y drops exclusivos con lista de espera.
커머스JavaScript · PLpgSQLGitHub @Cachureto온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Platarfom E-Commerce based in Vokter - Developed by Villaquiran & Mier & Badillo
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Vokter is a full-stack e-commerce platform for urban tech, accessories and home goods. Instead of
being a generic multi-category store, Vokter is built around three differentiators: verified
product authenticity on every item, an AI shopping assistant grounded strictly in the real
catalog, and drops with waitlists for limited releases.
The browser only ever talks to the store's domain: Next.js proxies /api/* to the API behind the
scenes. See Deploying for why.README 전문 보기 ↗Vokter is a full-stack e-commerce platform for urban tech, accessories and home goods. Instead of being a generic multi-category store, Vokter is built around three differentiators: verified product authenticity on every item, an AI shopping assistant grounded strictly in the real catalog, and drops with waitlists for limited releases.
| Component | Path |
|---|---|
| Store | https://nova-web-vokter.vercel.app |
| API | https://nova-api-kappa.vercel.app/api/health |
| Android app | /download on the store, or the APK link from EAS |
The browser only ever talks to the store's domain: Next.js proxies /api/* to the API behind the
scenes. See Deploying for why.
VKT-…) that customers
can validate on the web or by scanning its QR with the app.Web (apps/web, Next.js)
Backend (apps/backend, Express + PostgreSQL)
src/server.js) and as a Vercel Function (api/index.js)Mobile (apps/mobile, Expo + Expo Router)
| Layer | Technology |
|---|---|
| Web | Next.js 16 (App Router), React 19, Tailwind CSS 4, lucide-react, self-hosted fonts (Fontsource) |
| Backend | Node.js 22, Express 5, JWT, Zod, bcryptjs, qrcode, expo-server-sdk |
| Database | PostgreSQL 16 — Docker locally, Neon (pooled, serverless) in production |
| Mobile | React Native 0.86, Expo SDK 57, Expo Router, EAS Build |
| AI | Google Gemini (@google/genai) with function calling |
| Monorepo | npm workspaces + Turborepo |
| Hosting | Vercel — two projects, one for the web and one for the API |
.
├── apps/
│ ├── backend/
│ │ ├── api/index.js # Entry point on Vercel: exports the Express app
│ │ ├── public/ # Placeholder page; also Vercel's output directory
│ │ ├── vercel.json # framework: null, no build, rewrite /(.*) → /api
│ │ ├── db/
│ │ │ ├── migrations/ # SQL schema and data, applied in order by db:migrate
│ │ │ ├── seeds/ # Demo catalog: 40 products, 6 categories, 3 drops
│ │ │ └── dump-latest.sql # Snapshot of the team's local catalog (local use only)
│ │ ├── scripts/ # migrate.js, seed.js, make-admin.js
│ │ └── src/
│ │ ├── config/ db/ middleware/ utils/
│ │ ├── app.js # The Express app (named + default export)
│ │ ├── server.js # app.listen — local development only
│ │ └── modules/ # auth, products, categories, orders, reviews,
│ │ # drops, authenticity, notifications, ai
│ ├── web/
│ │ ├── app/ # Routes: catalog, products, drops, search, cart, checkout, admin…
│ │ │ └── icon.png # Favicon (the Vokter emblem)
│ │ ├── components/ # ProductCard, FilterBar, AuthenticityValidator, Logo…
│ │ ├── lib/ # API client, auth/cart contexts, formatters
│ │ ├── next.config.mjs # Rewrites /api/* to the API; validates API_ORIGIN
│ │ └── public/
│ │ ├── products/ # Product photos used by the catalog
│ │ ├── vokter-mark.png # Emblem, transparent, used in the navbar
│ │ └── vokter-logo.png # Full lockup
│ └── mobile/
│ ├── app/ # Screens (Expo Router): index, product/[slug], carrito,
│ │ # pedidos, escaner, drops, login, registro, cuenta, ajustes
│ ├── assets/ # App icon, favicon, splash and Android adaptive icon
│ ├── src/lib/ # api, session, auth-context, cart-context, push, authenticity
│ └── eas.json # Build profiles (the APK comes from `preview`)
├── docker-compose.yml # Local PostgreSQL 16 (host port 5433)
├── DEPLOY.md # Step-by-step production deploy
├── package.json # npm workspaces + scripts
└── turbo.json # Task graph, and which env vars reach each build
Requirements
| Node.js | 22 or newer (node -v) |
| npm | 10 or newer (npm -v) |
| Docker Desktop | running, for the database |
| For the mobile app | a physical Android phone with Expo Go, or Android Studio with an emulator |
Every command below is run from the repository root unless it says otherwise.
npm install
One install covers the three apps — it's an npm workspaces monorepo.
npm run db:up
This starts the nova-db container with PostgreSQL 16, exposed on localhost:5433 (user nova,
password nova_dev, database nova). Port 5433 and not 5432, so it doesn't collide with a
PostgreSQL you may already have installed on your machine.
cp apps/backend/.env.example apps/backend/.env
cp apps/web/.env.example apps/web/.env
Neither .env is in git. In apps/backend/.env you must set:
JWT_ACCESS_SECRET — any random string of 32+ characters. Generate one with:
node -e "console.log(require('crypto').randomBytes(48).toString('hex'))"
GEMINI_API_KEY — only for the AI assistant. Free key at
aistudio.google.com/apikey. Everything else works without it.
DATABASE_URL already points to port 5433 in the example; leave it as it is unless you changed
docker-compose.yml.
npm run db:migrate
Applies every pending file in db/migrations/ and records it in the schema_migrations table, so
it is safe to run again: it only applies what's missing.
On a brand-new container Docker already ran those files on first boot, so this just registers them
and finishes. It matters later: run it after every git pull that brings a new migration, and
you never need to delete the database to update the schema.
npm run dev
Turborepo starts both in parallel:
{"status":"ok"})At this point you can register an account on the web and browse the catalog.
Every account created from the website is a customer. Register on the web, then promote it:
npm run db:admin -- [email protected]
Log out and log in again so the session picks up the new role, and the Admin entry appears in the menu. To create an admin from scratch instead (it asks for the password in the terminal):
npm run db:admin -- [email protected] "Your Name"
In development the app talks to the backend on your computer, so the backend and the web have to
be running (npm run dev) and the phone has to be on the same Wi-Fi as the computer. A
released APK talks to production instead — see Build the APK.
This is the step people forget, and the symptom is the app showing "Sin conexión" while the web works fine: Windows blocks incoming connections to Node by default. In PowerShell as administrator, once:
New-NetFirewallRule -DisplayName "Vokter API 4000" -Direction Inbound -Protocol TCP -LocalPort 4000 -Action Allow -Profile Private
New-NetFirewallRule -DisplayName "Vokter Web 3000" -Direction Inbound -Protocol TCP -LocalPort 3000 -Action Allow -Profile Private
Port 4000 is the API; 3000 is needed too, because the product photos are served by Next.js, not by the backend. Without it the catalog loads but the images come out blank.
cd apps/mobile
npx expo start --android --clear
Scan the QR with Expo Go (physical phone) or press a to open it in the emulator.
You don't have to configure the IP. The app reads the address Expo is serving the bundle from and looks for the API on that same host, so it works on an emulator and on a physical phone with the same code. If you ever need to point it elsewhere, the Ajustes screen (Account → Connection) lets you type the URL on the device, and it's remembered.
Push notifications do not work in Expo Go: Expo removed remote notifications from it in SDK 53. Everything else does. Waitlist sign-ups are saved correctly; the alerts only arrive in the APK.
npm install -g eas-cli
cd apps/mobile
eas login
eas build -p android --profile preview
Three things to know before building:
preview profile in eas.json already points
EXPO_PUBLIC_API_URL and EXPO_PUBLIC_WEB_URL at the production store. Both point at the store
domain on purpose: the API is reached through its proxy, and the photos are served by the store
itself, so the app depends on a single host.usesCleartextTraffic is still enabled in app.json via expo-build-properties. It was
needed to reach a local backend over plain HTTP; now that everything is HTTPS it can be removed
if you no longer test against a LAN address.eas credentials.EAS uploads what is committed to git, so commit before building. The app icon, splash and Android adaptive icon also come from the commit, so icon changes need a new build.
When it finishes, take the artifact URL (eas build:view <id> prints it as Application Archive
URL) and set it as NEXT_PUBLIC_APK_URL — in apps/web/.env locally, and in the web project's
environment variables on Vercel. The /download page turns into a download button plus a QR
pointing at the APK.
Three pieces: a Neon PostgreSQL database, a Vercel project for the API (root directory
apps/backend), and a Vercel project for the web (root directory apps/web).
Only the store has a public domain. The browser never talks to the API's domain; it requests
/api/… from the store and Next forwards it (rewrites in next.config.mjs). That is not a
preference: the refresh-token cookie is httpOnly and sameSite=lax, so a browser would refuse to
send it to a different domain and web login would break. The proxy keeps everything same-site,
with no CORS and a single URL to remember.
Environment variables that matter
| Project | Variable | Note |
|---|---|---|
| Web | API_ORIGIN | The API's domain, with https://, no trailing slash |
| Web | NEXT_PUBLIC_SITE_URL | The store's own domain |
| Web | NEXT_PUBLIC_APK_URL | The EAS artifact link |
| Web | NEXT_PUBLIC_API_URL | Leave it undefined. If set, the browser bypasses the proxy and login breaks |
| API | DATABASE_URL | Neon's pooled string (the host contains -pooler) |
| API | CORS_ORIGINS | The store's domain |
| API | JWT_ACCESS_SECRET | 32+ random characters, different from your local one |
Two traps worth knowing about, because both cost hours:
turbo.json. NEXT_PUBLIC_* gets through by framework inference,
but anything else does not. That is why API_ORIGIN is listed under tasks.build.env — without
it the variable exists on Vercel and next build still can't see it. Any new variable that
doesn't start with NEXT_PUBLIC_ has to be added there too.API_ORIGIN is missing while the
build runs, the localhost fallback gets baked in and the deployed site answers 404 on /api/*
while the pages crash when rendering. next.config.mjs now fails the build with a clear message
instead of letting that ship, and logs the resolved origin so you can check it in the build log.The API on Vercel. apps/backend/api/index.js exports the Express app, and vercel.json
rewrites every path to it, so Express does all the routing exactly as it does locally.
src/server.js (the app.listen) is for local development only and isn't used in production. The
vercel.json also sets an empty build command — without it Vercel runs the monorepo's
turbo run build, which produces nothing for the backend and then fails looking for a static output
directory.
Do not point dump-latest.sql at Neon. It is a --clean dump; it would drop every table,
accounts included. Two safe ways:
db/migrations/, applied with npm run db:migrate while
DATABASE_URL points at Neon. This is how 004_conjuntos_y_sneakers.sql added 30 products: a
plain INSERT … ON CONFLICT DO NOTHING, so it only adds and can be re-run safely. Use this for
bulk loads and anything that should also reach a fresh local database.To promote an account to admin on production, either run the script with DATABASE_URL pointing at
Neon, or run this in Neon's SQL Editor after registering on the web:
UPDATE users SET role = 'admin', is_active = TRUE
WHERE email = '[email protected]'
RETURNING id, name, email, role;
The role travels inside the access token, so log out and back in afterwards.
| Script | What it does |
|---|---|
npm run dev | Runs web and backend in parallel (Turborepo) |
npm run build | Production build |
npm run lint | Lints the web app |
npm run db:up / db:down | Starts / stops the PostgreSQL container |
npm run db:migrate | Applies pending migrations. Respects DATABASE_URL from the shell, so it also works against Neon |
npm run db:seed | Loads the demo catalog (idempotent) |
npm run db:restore | Loads db/dump-latest.sql into the local container. Never usable against production |
npm run db:reset | Deletes the volume and recreates the database from scratch. Docker replays the migrations on first boot, so afterwards you only need db:seed |
npm run db:admin -- email | Gives the admin role to an account (or creates it) |
npm run mobile | Shortcut for expo start --android in apps/mobile |
price_cents), never floats, and are
frozen in order_items at purchase time. The order total is always recalculated by the backend
inside a transaction, so a stale price in a client's cart can never be charged.VKT-…) live on each product; the schema also supports per-unit
codes (VKU-…) for physical QR labels. Every validation is logged in authenticity_checks,
including the ones that fail — that's how cloned codes get spotted.expo-secure-store and a 401 transparently refreshes and retries the request.drops.announced_at, so restarting the server or re-marking it live never notifies twice; if the
send fails entirely, the mark is released so it can be retried.schema_migrations records what has been applied, so the schema evolves
without recreating the database — and the same files bring a fresh local database and production
to the same state.src/app.js. Locally server.js
listens on a port; on Vercel api/index.js hands the same app to the platform. No branching on
the environment, so what you test locally is what runs in production.-pooler endpoint). The startup DB check is
non-blocking, so a database hiccup degrades individual requests instead of killing the function.Database
password authentication failed — locally, DATABASE_URL must use port 5433, not 5432.
If you have PostgreSQL installed natively, that's what's answering on 5432. On Neon, check you
copied the pooled string.unable to get image 'postgres:16' — Docker Desktop isn't running, or its engine hasn't
started yet.npm run db:seed (or db:restore).git pull — run npm run db:migrate.must be able to SET ROLE "nova" — the dump was regenerated without --no-owner --no-acl.no schema has been selected to create in (3F000) — the role's default search_path is
empty. db:migrate sets it on first run; re-run it against that database.Web
NEXT_PUBLIC_API_URL doesn't match.Deployment (Vercel)
Falta API_ORIGIN — the variable isn't reaching next build. Check it
exists, is marked for Production, and is listed in turbo.json under tasks.build.env.No Output Directory named "public" found on the API project — Vercel is treating it as a
static site. Root Directory must be apps/backend, and Build Command and Output Directory must be
left empty in Settings so vercel.json governs./api/health works but /api/auth/login 404s — a Vercel 404, not an Express one: the request
never reached the app. Check vercel.json still has the catch-all rewrite to /api.API_ORIGIN and the Runtime Logs.vercel.com/login — Deployment Protection is on. Set it to None in
Settings → Deployment Protection. Standard Protection is not enough while the site is served
from a generated *.vercel.app URL.Mobile
eas is not recognized as a command — the npm global folder isn't in your PATH. Open a new
terminal, or use npx eas-cli@latest … instead.This project was developed for evaluation purposes as part of a technical assessment.