尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗A website for selling digital wedding invitations. Customers pick one of twenty-three animated designs (royal doors, a sealed envelope, a blush envelope that opens onto a palace arch at sunset (Gulab Mahal), a burgundy velvet envelope with red roses (Laal Gulab), a moon rising over a palace (Mahtab), a jasmine sehra opening onto a rose garden (Chaman), a marigold mehndi night (Mehfil), mehndi curtains, an emerald arch, a crimson book, a silver moon-dial, a black roller shutter, blue Multani tiles, a pure-white jasmine design, a wine-red royal decree, a sandstone lattice window a peacock-feather fan Bazm, a sky-blue cloud design (Aasmani), a champagne-gold gift box (Sunehri) and a saffron diya (Kesar)). Most designs are a set of full-screen chapters: as you scroll, swipe or use the menu, the background picture, the colours and the layout all change; the Royal designs each have their own hand illustrated inside), you create their invitation from the admin panel, and they fill in the rest from their own portal. Every guest gets a personal link (“Dear Mr & Mrs Ali”) to share on WhatsApp, with RSVP, maps, music and a countdown.
The most important rule: the groom and bride names are entered only by you (the admin) and are locked. A customer can never change them, so one purchase cannot be reused for another wedding. Proof it works · Where to read more
| Part | What it is |
|---|---|
| Framework | Next.js 16 (App Router) + TypeScript (strict) |
| Look | Tailwind CSS 4, GSAP + ScrollTrigger (scroll animations), Framer Motion (small interactions) |
| Database | PostgreSQL on Neon, Prisma 6 |
| Login | Auth.js v5, passwords hashed with bcrypt, roles ADMIN / CLIENT |
| Validation | Zod, on every form and every server action |
| Uploads | Cloudinary (and a local fallback while developing) |
| Hosting | Vercel |
Next.js 16 replaced the file called
middleware.tswithsrc/proxy.ts. If you read older tutorials, that is the only big difference you will meet.
You need Node.js 20.9 or newer (node -v) and Git.
DATABASE_URLDIRECT_URLCopy-Item .env.example .env
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
Open .env and fill in DATABASE_URL, DIRECT_URL, AUTH_SECRET (the long text printed above), ADMIN_EMAIL and ADMIN_PASSWORD.npm install
npm run db:deploy
npm run db:seed
npm run dev
npm run dev prints).Logins after seeding
| Who | Login | Password |
|---|---|---|
| You (admin) | your ADMIN_EMAIL | your ADMIN_PASSWORD |
| Demo customer | demo | demo12345 |
The seed also creates the demo invitation Ahmad & Sara at /i/ahmad-weds-sara (personal link: /i/ahmad-weds-sara/demosara).
/designs, watches the live demos and taps Order This Design (opens WhatsApp with the message ready)./login and fills in events, families, photos, music and guests, then sends personal links to each guest on WhatsApp.Where to change things
| What | Where |
|---|---|
| Brand name, WhatsApp number, default expiry days, the guest WhatsApp message, logo, your admin password | Admin → Settings |
| Plan limits (events, guests, photos, RSVP, own music) | src/lib/plans.ts, one file |
| Which music tracks each design offers | src/templates/registry.ts |
| Brand defaults used before you save settings | src/lib/site.ts |
groomName, it is rejected, not ignored.Run the proof yourself:
npm run test:name-lock
It creates a temporary customer and invitation, tries 30+ ways to cheat, prints ✔ or ✖ for each, and deletes everything it made. Expect ALL CHECKS PASSED.
All twenty-three designs are built with CSS and SVG, so they work with no images at all. See docs/ASSETS.md for the list of included photos, AI prompts for better ones, and what music to use.
public/music/ were generated as placeholders. Replace them with royalty-free music (same file names) before launch.Uploads (customer photos, couple photo, own music, your logo).
While developing, files are saved in public/uploads/ (ignored by git). On the live site they go to Cloudinary: create a free account at https://cloudinary.com, open Dashboard → Product Environment → API Keys, and put the three values in .env (and in Vercel): CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY, CLOUDINARY_API_SECRET.
Photos are shrunk in the customer's browser before uploading (so uploads are fast on mobile data), and music goes straight to Cloudinary (so it is not limited by Vercel's 4.5 MB request size).
I could test the local upload path end to end. The Cloudinary path is written and its signing is standard, but it has not been run against a real Cloudinary account. After adding your keys, upload one photo and one MP3 from a test customer portal to confirm (see the launch checklist).
A. Put the code on GitHub
wedcard).git add -A
git commit -m "First version"
git branch -M main
git remote add origin https://github.com/YOUR-NAME/wedcard.git
git push -u origin main
(.env is in .gitignore, so your passwords are not uploaded.)B. Deploy on Vercel
Sign up at https://vercel.com with GitHub → Add New → Project → pick the repository → Import.
Open Environment Variables and add (same values as your .env, except the last two):
| Name | Value |
|---|---|
DATABASE_URL | Neon pooled string |
DIRECT_URL | Neon direct string |
AUTH_SECRET | the long random text (use a new one for the live site) |
NEXT_PUBLIC_SITE_URL | https://your-domain.com (or the …vercel.app address at first) |
CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY, CLOUDINARY_API_SECRET | from Cloudinary |
ADMIN_EMAIL, ADMIN_PASSWORD, ADMIN_NAME | only needed for the seed (step C) |
SEED_DEMO | false |
Click Deploy. Vercel runs npm run vercel-build, which installs, applies the database migrations automatically, and builds.
C. Create your admin account on the live database (once) If the live site uses the same Neon database you already seeded, you are done: log in with the same admin. For a new, empty database, run in PowerShell (use the live strings):
$env:DATABASE_URL="postgresql://...pooled..."
$env:DIRECT_URL="postgresql://...direct..."
$env:ADMIN_EMAIL="[email protected]"; $env:ADMIN_PASSWORD="a-strong-password"; $env:SEED_DEMO="false"
npm run db:deploy
npm run db:seed
(Open a new PowerShell window afterwards so these temporary values disappear.)
D. Connect your own domain
your-domain.com (and www.your-domain.com).NEXT_PUBLIC_SITE_URL in Vercel to https://your-domain.com and Redeploy (Deployments → … → Redeploy). Invitation links and WhatsApp previews use this address.E. After launch: checklist
public/music/.| Command | What it does |
|---|---|
npm run dev | Start the site locally |
npm run build / npm start | Make and run the production version |
npm run typecheck | Check the code for TypeScript errors |
npm run test:name-lock | Prove customers cannot change the locked fields |
npm run db:deploy | Apply database migrations |
npm run db:seed | Create/refresh your admin account (and the demo data unless SEED_DEMO=false) |
npm run db:studio | Browse the database visually |
npm run db:migrate | (Developer) create a migration after editing prisma/schema.prisma |
node scripts/make-farman-corners.cjs | Re-draws the gold corner flourishes of the Farman design (change the colour inside the file) |
node scripts/make-chambeli-sprig.cjs | Re-draws the jasmine artwork of the Chambeli design (change its colours inside the file) |
node scripts/make-thumbnails.cjs | Re-take the design thumbnails (needs npm i --no-save playwright-core and Chrome) |
After changing the database schema, stop
npm run devfirst (Windows locks a Prisma file while the server runs), then runnpm run db:deployandnpx prisma generate, then startnpm run devagain.
prisma/ schema.prisma (tables), migrations/, seed.ts
public/
templates/<design>/ photos + thumbnails for each design
music/ the four MP3s
demo/ demo photos (preview pages only)
scripts/ test-name-lock.ts, make-thumbnails.cjs, make-placeholder-music.cjs
docs/ ASSETS.md, MOBILE-TESTING.md
src/
proxy.ts, auth.ts, auth.config.ts login + route protection
app/
page.tsx, designs/ public marketing pages
preview/[templateKey]/ live demos
i/[slug]/ and i/[slug]/[guestCode]/ the real invitations (+ share-preview images)
login/, dashboard/ login and role redirect
admin/ your panel
portal/ the customer portal
api/ login endpoints, local upload (dev only)
templates/
registry.ts the list of designs and music tracks
shahi-darwaza/ gulzar/ mehndi-rang/ noor/ one folder per design (opening screen + colours)
demo-data.ts, template-host.tsx, types.ts
components/
invite-shared/ sections every design shares (events, RSVP, gallery…)
marketing/ admin/ portal/ ui/ screens and small building blocks
lib/
dal.ts "who is logged in" checks (the real security)
plans.ts, site.ts, settings.ts limits, defaults, saved settings
invitations.ts admin-only rules (create, correct names, expiry…)
portal.ts, portal-ops.ts, portal-schemas.ts what a customer may change (strict)
guests.ts, uploads.ts, storage.ts guests/import, uploads, Cloudinary
rate-limit.ts, audit.ts, og.tsx …
Categories and prices: every design belongs to a category: Regular (PKR 2,599), Advanced (PKR 3,399), Prime (PKR 6,899) or Royal (PKR 7,999). The category of a design is the plan line of its entry in src/templates/registry.ts, and the prices and limits of each category are in src/lib/plans.ts (Regular is the BASIC plan, Advanced is STANDARD, Prime is PREMIUM, Royal is ROYAL). The Royal category was added by the migration 20261023000000_royal_plan: run npm run db:deploy (or deploy on Vercel, which runs it) before selling a Royal invitation. In Admin, choosing a design selects its category and fills in the amount for you. Every invitation carries a small "Made by Dawatnamaa" credit at the top that opens WhatsApp (see WATERMARK_NAME in src/lib/site.ts).
Add another design: copy a folder in src/templates/, change its colours and opening screen, add it to registry.ts and template-host.tsx, and take a thumbnail. Nothing else changes.
src/components/invite-shared/chapters/; each design only supplies a small chapters.tsx with its own pictures, colour schemes and layouts (split, flip, overlay, arch, poster). Each guest's name is free text (for example Mr & Mrs Ali, or Mr Ali and his family) and appears on the cover of every design as well as in the greeting. The first chapter always shows the same generic picture (hands-wedding.webp, no couple); the couple's own photo goes first in their Moments gallery. The pictures are in public/templates/photos/ (replace any file with your own, same name; list in CREDITS.md). The older single-page layout is still in invite-shared/content.tsx: a design uses it when it does not pass loadContent.src/components/invite-shared/use-invite-motion.ts.src/components/marketing/page-motion.tsx.stackCards, horizontalScroll, riseIn, parallax) are in src/lib/motion.ts: use them to add the same effects anywhere.--inv-stack-bg line near the top of every design's CSS file./admin and /portal are guarded twice: quickly in src/proxy.ts and properly on the server in every page and action (src/lib/dal.ts). When you reset a password or a customer changes theirs, they are logged out everywhere immediately.noindex and blocked in robots.txt; only the homepage and /designs are for Google.| You see | Do this |
|---|---|
| “Missing settings in .env” in the terminal | Fill in DATABASE_URL, DIRECT_URL, AUTH_SECRET, then restart npm run dev |
| Login page says “Can't connect to the database” | Check DATABASE_URL; run npm run db:deploy and npm run db:seed |
| “Server configuration” error page on login | AUTH_SECRET is missing in .env |
EPERM … query_engine when running Prisma | Stop npm run dev first (see the note under Commands) |
| Invitation says “not found” | Check the link; the invitation may be a draft or deleted |
| Uploads say “not set up yet” (on the live site) | Add the three Cloudinary values in Vercel and redeploy |
| WhatsApp preview shows the old picture | WhatsApp caches links; add ?v=2 to the end of the link to refresh |