Ecommerce Template
Next.js·React 등으로 만든 최신 전자상거래 템플릿
분산 포털 관리 없이 통합 가격·실시간 재고·초단위 사양 검색·즉시 BOM 견적 제공, 상업·산업용 태양·배터리 설계 지원
Stop juggling separate distributor portals. Blended pricing and live stock across distributors, sub-second spec search, and instant BOM quoting — built for commercial and industrial solar/battery inst
커머스TypeScript · PythonGitHub @Jay244521온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Stop juggling separate distributor portals. Blended pricing and live stock across distributors, sub-second spec search, and instant BOM quoting — built for commercial and industrial solar/battery installers and EPC firms running multiple projects at once.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Phase 1 scaffold: B2B procurement search, BOM quoting, and order routing for
solar/battery/EPC contractors, per the platform spec (v2).
Run the migration against a Postgres 15+ instance (Supabase or otherwise):
0001_init.sql creates distributors, contractors, products (JSONB
specs column with a GIN index for sub-second attribute filtering),
boms/bom_lines, orders/order_lines, and purchase_orders (one row per
distributor per order, supporting split fulfillment across a single BOM).README 전문 보기 ↗Phase 1 scaffold: B2B procurement search, BOM quoting, and order routing for solar/battery/EPC contractors, per the platform spec (v2).
db/migrations/ Postgres schema (Supabase-flavored)
ingestion/ Python pipeline that normalizes distributor feeds into products
web/ Next.js app: spec search, BOM upload, checkout, Stripe webhook
Run the migration against a Postgres 15+ instance (Supabase or otherwise):
psql "$DATABASE_URL" -f db/migrations/0001_init.sql
psql "$DATABASE_URL" -f db/migrations/0002_auth_rls.sql
0001_init.sql creates distributors, contractors, products (JSONB
specs column with a GIN index for sub-second attribute filtering),
boms/bom_lines, orders/order_lines, and purchase_orders (one row per
distributor per order, supporting split fulfillment across a single BOM).
0002_auth_rls.sql links contractor_users to Supabase Auth
(auth_user_id) and turns on Row Level Security everywhere: a contractor can
only ever see its own boms/orders/purchase_orders, and — deliberately —
no policy grants contractors read access to distributors at all, so a
direct query can't defeat the blended-pricing display and reveal which
distributor fulfilled what (see Anti-Disintermediation Mechanics). Today all
of this is defense-in-depth: the web app's own routes already enforce
tenant scoping in application code (see below) and talk to Postgres with the
service-role key for anything that legitimately spans contractors, so
nothing currently relies on RLS alone — but it's what stops a leaked anon
key or a future direct-from-browser Supabase call from crossing a tenant
boundary.
Seed at least one distributor before ingesting a feed — the ingestion script
looks distributors up by slug:
insert into distributors (name, slug, is_pilot_partner)
values ('Acme Solar Supply', 'acme-solar-supply', true);
cd ingestion
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python ingest.py --distributor acme-solar-supply --file sample_feeds/acme-solar-supply_2026-09-13.csv \
--database-url "$DATABASE_URL"
Each distributor gets a column-alias mapping in feed_config.py (their CSV/
Excel headers vary; we normalize to our schema rather than asking distributors
to conform). Add a new block there for each pilot partner onboarded in Phase 2.
Products are upserted on (distributor_id, distributor_sku), so re-running
ingestion on a refreshed daily feed just updates price/stock in place.
cd web
cp .env.example .env.local # fill in Supabase + Stripe keys
npm install
npm run dev
Pages/routes:
/login — email/password sign-in (Supabase Auth). There is no public
sign-up: Phase 3 onboarding is invite-only (see below)./search — spec search (/api/search), blended across distributors by
canonical_key — the UI never shows a per-distributor bidding view.
Requires auth (search access is a paid SaaS-tier feature, not public)./bom — BOM upload (/api/bom-upload), CSV/XLSX, heuristic SKU matching
against manufacturer/model name; low-confidence lines come back unmatched
for manual resolution rather than guessing. Requires auth./product/[id] — product detail, spec table, CAD/schematic/datasheet links.
Requires auth./api/checkout — requires auth; contractor_id is derived from the
session (never trusted from the request body — otherwise any caller could
place orders, or read order history, under an arbitrary contractor). Prices
the order entirely from server-side catalog data and opens a Stripe
PaymentIntent for subtotal + platform fee. GridSpec is merchant of record;
distributors are not paid via Stripe Connect transfer at charge time./api/webhooks/stripe — on payment_intent.succeeded, marks the order paid
and performs Automated PO Dispatch: splits the order into one
purchase_order per distributor with a blind packing slip, on Net-30 payout
terms. PO generation never happens before payment actually clears./ops/purchase-orders — ops-only list of purchase orders with links to the
two generated documents per PO: /ops/purchase-orders/[id]/po (names the
distributor, states what's owed to them — sent to their fulfillment desk)
and /ops/purchase-orders/[id]/packing-slip (GridSpec-branded, no
distributor identity or pricing — this is what actually ships to the job
site, replacing the distributor's own packing slip, which is what makes
drop-shipping "blind"). There's no distributor portal yet, so an operator
currently has to view/print these and forward the PO to the distributor
manually. Gated by OPS_ADMIN_EMAILS (see below) — never contractor-reachable.cd web
node --env-file=.env.local scripts/invite-contractor.mjs \
"Acme Solar Installers" [email protected] "Jordan Lee"
This creates the contractors row, sends a Supabase invite email (the
person sets their own password via the link), and links the two via
contractor_users.auth_user_id.src/proxy.ts (Next.js 16 renamed middleware.ts to proxy.ts) only
refreshes the session cookie on every request — it does not gate access.
Every route that touches contractor-owned data calls
getAuthedContractor() (src/lib/auth.ts) itself and derives
contractor_id from the session, per the Next.js proxy docs' own warning
not to rely on proxy/middleware alone for authorization.lib/supabase-server.ts /
lib/supabase-browser.ts use the anon key and a user's session (subject to
RLS); lib/supabase.ts uses the service role key (bypasses RLS) and is
only for the ingestion pipeline and the Stripe webhook, which have no user
session to act as.OPS_ADMIN_EMAILS (comma-separated) gates /ops/* — there's no real
platform-admin role yet, so lib/ops-auth.ts just checks the signed-in
Supabase Auth user's email against this env var. Set it to whichever
account(s) should be able to see distributor identity and payout amounts;
this must never be a contractor's own account./ops/purchase-orders),
but there's no distributor portal or automated email dispatch yet — an
operator has to manually forward the PO to the distributor's fulfillment
desk today.purchase_orders at their
payout_due_at — Net-30/60 payout is modeled in the schema but not executed.