尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證AI travel assistant that creates personalized day-by-day itineraries, ticket plans, and budgets. Customize trips by duration, group size, and style, with credit-based payments built in.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗A modern, full-stack SaaS application for planning travel itineraries using AI technology. Built with Next.js, TypeScript, Tailwind CSS, Shadcn UI, and Framer Motion.
src/
├── app/ # Next.js App Router
│ ├── api/ # API routes
│ │ ├── generate-itinerary/ # AI itinerary generation
│ │ ├── credits/ # Credit management
│ │ └── save-itinerary/ # Save/retrieve itineraries
│ ├── auth/ # Authentication pages
│ ├── dashboard/ # Main app with sidebar
│ ├── layout.tsx # Root layout
│ └── page.tsx # Landing page
├── components/ # Reusable components
│ ├── ui/ # Shadcn UI components
│ └── landing/ # Landing page sections
│ ├── hero-section.tsx
│ ├── features-section.tsx
│ ├── testimonials-section.tsx
│ ├── pricing-section.tsx
│ ├── cta-section.tsx
│ └── footer.tsx
├── lib/ # Utility functions
└── styles/ # Global styles
/ - Landing page with hero, features, testimonials, pricing/auth - Authentication (login/signup)/dashboard - Trip planner/dashboard/itinerary/[id] - Itinerary, budget, bookings, stays, essentials, checklist/dashboard/book - Booking hub/trip/[id]?t=… - Public, read-only shared itinerary (signed link)/api/generate-itinerary - POST endpoint for AI itinerary generation/api/user/credits - GET the signed-in user's credit balance/api/checkout - POST { planId } to start a Dodo Payments checkout/api/webhooks/dodo - Dodo Payments webhook (adds or removes credits)/api/payments - GET the signed-in user's purchases/api/save-itinerary - GET/POST endpoints for saving itinerariesDefined once in src/lib/plans.ts and used across the site. 1 credit = 1 itinerary; credits never expire.
git clone <repository-url>
cd goroam
npm install
npm run dev
Besides DATABASE_URL and DIRECT_URL (see Database schema), NEXTAUTH_SECRET (also signs share links), NEXTAUTH_URL, the Google OAuth keys and OPENAI_API_KEY, these optional affiliate IDs are appended to booking links when set:
NEXT_PUBLIC_BOOKING_AID - Booking.com affiliate idNEXT_PUBLIC_GYG_PARTNER_ID - GetYourGuide partner idNEXT_PUBLIC_SKYSCANNER_ASSOCIATE - Skyscanner associate idSet NEXT_PUBLIC_GOOGLE_MAPS_API_KEY to turn on the live, dark, zoomable Google map in each day of an itinerary, and Google Places photos for every stop. In Google Cloud, enable Maps JavaScript API and Places API (New), turn on billing, and restrict the key to your domains (HTTP referrers). The server sends your site's address as the referrer, so the same key works for photo lookups. GOOGLE_PLACES_API_KEY (a server-only key) is used instead if you'd rather keep them separate. Optional: NEXT_PUBLIC_GOOGLE_MAPS_MAP_ID for your own cloud-styled map.
Without a key, days show the illustrated route and photos come from Wikipedia, Wikimedia Commons and Openverse. Found photos are saved on each trip, so every later view is a plain database read.
Set NEXT_PUBLIC_POSTHOG_KEY (project API key) to record page views, page leaves, clicks and GoRoam events (trip_generated, itinerary_viewed, package_impression, package_viewed, package_customized, booking_partner_clicked, directions_clicked, share_link_created, pdf_downloaded, calendar_downloaded, checkout_started). Signed-in travellers are identified by their GoRoam id only. Events go through /ingest on your own domain; set NEXT_PUBLIC_POSTHOG_HOST (e.g. https://eu.i.posthog.com) for an EU project. Without a key, PostHog never loads.
The ready-made trips in Dashboard → Travel packages are defined in src/lib/packages.ts and stored in src/data/packages/. To add or rebuild one (uses OPENAI_API_KEY):
npx tsx --env-file=.env.local scripts/generate-packages.ts # build any missing packages
npx tsx --env-file=.env.local scripts/generate-packages.ts rome-5-days
npx tsx --env-file=.env.local scripts/generate-packages.ts --photos # re-find photos only
Credit packs are sold through Dodo Payments hosted checkout. Credits are added only by the webhook, never by the redirect back to the site.
src/lib/plans.ts: Starter $9.99, Explorer $24.99, Adventurer $69.99.https://<your-domain>/api/webhooks/dodo subscribed to payment.succeeded, refund.succeeded and dispute.lost, and copy its signing secret.DODO_PAYMENTS_API_KEY - API keyDODO_PAYMENTS_WEBHOOK_KEY - webhook signing secretDODO_PAYMENTS_ENVIRONMENT - test_mode (default) or live_modeDODO_PRODUCT_STARTER, DODO_PRODUCT_EXPLORER, DODO_PRODUCT_ADVENTURER - the product ids (pdt_…)The Payment table is created automatically on the next deploy — see Database schema below.
Until the API key and all three product ids are set, the buy buttons show "Online checkout isn't available yet." When going live, switch to live-mode keys, live product ids and a live webhook secret together.
There's no migrations folder: the build script itself runs prisma db push ("vercel-build" in package.json), so every deploy syncs the live database to whatever is in schema.prisma. Editing that file is enough — you don't need to run anything by hand.
The one exception is a change that would drop or truncate a column with data in it (e.g. deleting a field, changing its type). db push refuses those instead of applying them silently, so the build fails loudly with a "data loss" error rather than deploying and quietly deleting something. If that happens, resolve it locally (DIRECT_URL="<prod-session-url>" npx prisma db push, which shows the same warning and lets you decide) before pushing again.
Two database URLs. Vercel runs every function as its own short-lived instance, so the app connects through Supabase's pooler in transaction mode; session mode caps out at 15 clients and fails with EMAXCONNSESSION under load. Set both in Vercel (and in .env / .env.local locally):
DATABASE_URL - the Transaction pooler string (port 6543) with ?pgbouncer=true&connection_limit=1. Used by the app at runtime.DIRECT_URL - the Session pooler string (port 5432). Used only by prisma db push during the build, which can't run through the transaction pooler.Both are under Supabase → Project Settings → Database → Connection string. If DIRECT_URL is missing, the build fails at prisma db push.
Public, indexable pages (statically generated, with JSON-LD):
/itineraries and /itineraries/[slug]: the trip gallery. Data lives in src/lib/packages.ts (trip facts), src/lib/packages-seo.ts (search title, intro, FAQs) and src/data/packages/<slug>.json (the full plan). Hand-written trips are generated by python3 scripts/content/itineraries.py./destinations and /destinations/[slug]: destination guides in src/lib/destination-guides.ts./sitemap.xml and /robots.txt are generated from the same data. Bump UPDATED in src/app/sitemap.ts when content changes.Google Search Console
https://goroam.world, choose HTML tag verification and copy the content value.NEXT_PUBLIC_GOOGLE_SITE_VERIFICATION=<that value> in Vercel and redeploy, then click Verify. (Or verify the whole domain with a DNS TXT record instead.)sitemap.xml. Use URL inspection → Request indexing for the most important pages.Email (Resend): RESEND_API_KEY and EMAIL_FROM="GoRoam <[email protected]>". Verify goroam.world in Resend (it gives DNS records to add). Used for email sign-in links (the "or with email" option appears on /auth only once these are set), the welcome email on sign-up, and emailing an itinerary. For local testing, EMAIL_DEV_LOG=1 prints emails to the dev-server console instead of sending.
Guest first trip: signed-out visitors who type a trip in the landing hero get it planned without an account (/api/guest-itinerary, the GuestTrip model), see Day 1 at /try/[id], and sign in to claim the rest. Limits: one per device and three per IP a day, plus GUEST_DAILY_LIMIT (default 300) across everyone.
npm run dev - Start development servernpm run build - Build for productionnpm run start - Start production servernpm run lint - Run ESLintThis project uses Shadcn UI. To add new components:
npx shadcn@latest add [component-name]
#f5f1ea), ink (#15130f) and the brand orange from the logo (#d95501) — defined as tokens in src/app/globals.css (bg-paper, text-ink, text-brand, …)prefers-reduced-motionsrc/components/scenes/ renders every destination as a layered, animated SVG scene — monuments in monuments.ts, compositions in scenes.tsx. sceneForDestination() in src/lib/destinations.ts picks a poster for any tripPOST /api/generate-itinerary
{
"destination": "Paris, France",
"duration": "5 days",
"preferences": ["culture", "food", "museums"]
}
GET /api/user/credits // → { credits }
POST /api/checkout // { planId: "starter" | "explorer" | "adventurer" } → { url }
POST /api/webhooks/dodo // signed by Dodo; payment.succeeded / refund.succeeded / dispute.lost
GET /api/payments // → purchase history
POST /api/save-itinerary
{
"userId": "user_123",
"itineraryData": {...}
}
git checkout -b feature/amazing-feature)git commit -m 'Add some amazing feature')git push origin feature/amazing-feature)This project is licensed under the MIT License - see the LICENSE file for details.
Built with ❤️ for travelers worldwide. Start planning your next adventure with GoRoam!