{"repo":"justnode/better-saas","free":true,"listed":false,"github":"https://github.com/justnode/better-saas","clone":"git clone https://github.com/justnode/better-saas.git","description":"Full-stack SaaS application built with Next.js","language":"MDX","stars":306,"topics":["nextjs","saas-boilerplate"],"license":"MIT","category":"saas_boilerplate","readme_excerpt":"# Better SaaS\n\n[中文版 README](README_zh.md) | English\n\nA modern, full-stack SaaS application built with Next.js 15, featuring authentication, payments, file management, and internationalization with comprehensive testing suite.\n\n## 🚀 Features\n\n- **🔐 Authentication**: Email/password and social login (GitHub, Google) with Better Auth\n- **💳 Payments**: Stripe integration with subscription management and billing dashboard\n- **🪙 Credits System**: Flexible quota management for API usage and storage with automated billing\n- **📁 File Management**: Upload, preview, and manage files with AWS S3/R2 storage\n- **🌍 Internationalization**: Multi-language support (English/Chinese) with next-intl\n- **📱 Responsive Design**: Modern UI with Radix UI and Tailwind CSS v4\n- **📖 Documentation**: Built-in documentation system with Fumadocs\n- **🔒 Protected Routes**: Role-based access control and route protection\n- **👥 Admin Dashboard**: User management and system administration\n- **⚡ Performance**: Optimized with Next.js 15 App Router and Turbo mode\n- **🧪 Testing**: Comprehensive testing suite with Jest, Playwright, and integration tests\n\n## 🛠️ Tech Stack\n\n### Frontend\n\n- **Framework**: Next.js 15 (App Router)\n- **UI Components**: Radix UI + Tailwind CSS\n- **State Management**: Zustand\n- **Data Fetching**: SWR\n- **Styling**: Tailwind CSS v4\n- **Icons**: Lucide React\n\n### Backend\n\n- **Runtime**: Node.js\n- **Database**: PostgreSQL with Drizzle ORM\n- **Authentication**: Better Auth\n- **Payments**: Stripe\n- **File Storage**: AWS S3/Cloudflare R2\n- **Validation**: Zod\n\n### Development & Testing\n\n- **Language**: TypeScript\n- **Package Manager**: pnpm\n- **Code Quality**: Biome (formatting, linting)\n- **Environment**: @t3-oss/env-nextjs\n- **Unit Testing**: Jest with React Testing Library\n- **Integration Testing**: Jest with database integration\n- **E2E Testing**: Playwright with multi-browser support\n- **Test Coverage**: Comprehensive coverage reporting\n\n## 📖 Documentation\n\nComplete project documentation has been migrated to our online documentation site:\n\n**🌐 [https://www.better-saas.org/docs](https://www.better-saas.org/docs)**\n\nThe online documentation includes:\n\n- 🏗️ Architecture design and system overview\n- 📦 Detailed installation and configuration guide\n- 🔧 API documentation with examples\n- 🎨 UI component library usage guide\n- 🧪 Testing strategies and best practices\n- 🚀 Deployment guide and production setup\n- 🌍 Internationalization implementation\n- 💳 Payment integration and subscription management\n- 🪙 Credits system configuration and usage patterns\n\n> 💡 **Tip**: The online documentation is continuously updated. We recommend bookmarking it for the latest information.\n\n## 🚀 Quick Start\n\n### Prerequisites\n\n- Node.js 18+\n- pnpm\n- PostgreSQL database\n- Stripe account (for payments)\n- AWS S3 or Cloudflare R2 (for file storage)\n\n### Installation\n\n1. **Clone the repository**\n\n   ```bash\n   git clone <repository-url>\n   cd better-saas\n   ```\n\n2. **Install dependencies**\n\n   ```bash\n   pnpm install\n   ```\n\n3. **Set up environment variables**\n\n   ```bash\n   cp env.example .env\n   ```\n\n   Fill in the required environment variables:\n\n   - `DATABASE_URL`: PostgreSQL connection string\n   - `BETTER_AUTH_SECRET`: Random secret for authentication\n   - `GITHUB_CLIENT_ID` & `GITHUB_CLIENT_SECRET`: GitHub OAuth app credentials\n   - `GOOGLE_CLIENT_ID` & `GOOGLE_CLIENT_SECRET`: Google OAuth app credentials\n   - `STRIPE_SECRET_KEY` & `STRIPE_WEBHOOK_SECRET`: Stripe API keys\n   - `NEXT_PUBLIC_STRIPE_PRICE_PRO_MONTHLY` & `NEXT_PUBLIC_STRIPE_PRICE_PRO_YEARLY`: Stripe price IDs\n   - `R2_*`: Cloudflare R2 or AWS S3 configuration\n   - `NEXT_PUBLIC_APP_URL`: Your app's URL\n   - `ADMIN_EMAILS`: Comma-separated list of admin email addresses\n\n4. **Set up the database**\n\n   ```bash\n   pnpm db:push\n   ```\n\n5. **Configure credits system (optional)**\n\n   Customize credits allocation and pricing in:\n   - `/src/config/credits.config.ts` - API costs and quotas\n   - `/src/config/payment.config.ts` - Subscription plan credits\n\n6. **Start the development server**\n\n   ```bash\n   pnpm dev\n   ```\n\n   Open [http://localhost:3000](http://localhost:3000) in your browser.\n\n\n\n\nThe application supports multiple languages:\n\n- English (default)\n- Chinese (Simplified)\n\nLanguage files are located in `src/i18n/messages/`.\n\n\n\n## 🤝 Contributing\n\n1. Fork the repository\n2. Create a feature branch\n3. Make your changes\n4. Write tests for new features\n5. Run tests and quality checks:\n   ```bash\n   pnpm test:all\n   pnpm check\n   pnpm typecheck\n   ```\n6. Ensure all tests pass and coverage thresholds are met\n7. Submit a pull request\n\n\n## 📄 License\n\nThis project is licensed under the MIT License.\n\n\n## Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=justnode/better-saas&type=Date)](https://www.star-history.com/#justnode/better-saas&Date)\n","default_branch":"main","files":286,"tree":[".gitignore","LICENSE","README.md","README_zh.md","biome.jsonc","components.json","drizzle.config.ts","drizzle/0000_dark_sentry.sql","drizzle/0001_sudden_human_robot.sql","drizzle/0002_strong_shinko_yamashiro.sql","drizzle/0003_calm_punisher.sql","drizzle/0004_far_titania.sql","drizzle/0005_drop_user_quota_usage.sql","drizzle/0006_marvelous_nomad.sql","drizzle/meta/0000_snapshot.json","drizzle/meta/0001_snapshot.json","drizzle/meta/0002_snapshot.json","drizzle/meta/0003_snapshot.json","drizzle/meta/0004_snapshot.json","drizzle/meta/0005_snapshot.json","drizzle/meta/0006_snapshot.json","drizzle/meta/_journal.json","env.example","next.config.ts","open-next.config.ts","package.json","pnpm-lock.yaml","postcss.config.js","public/_headers","public/avatar/1.png","public/avatar/2.png","public/avatar/3.png","public/avatar/4.png","public/avatar/5.png","public/favicon.ico","public/icons/android-chrome-192x192.png","public/icons/android-chrome-512x512.png","public/icons/apple-touch-icon.png","public/icons/favicon-16x16.png","public/icons/favicon-32x32.png","public/images/wechat.png","public/site.webmanifest","scripts/check-users-credits-status.ts","scripts/create-theme.sh","scripts/grant-existing-users-credits.ts","scripts/grant-signup-credits.ts","scripts/setup-admin.ts","scripts/verify-monthly-credits-execution.ts","source.config.ts","src/app/[locale]/(home)/blog/[slug]/page.tsx","src/app/[locale]/(home)/blog/layout.tsx","src/app/[locale]/(home)/blog/page.tsx","src/app/[locale]/(home)/layout.tsx","src/app/[locale]/(home)/page.tsx","src/app/[locale]/(protected)/credits/api-keys/page.tsx","src/app/[locale]/(protected)/credits/balance/page.tsx","src/app/[locale]/(protected)/credits/history/page.tsx","src/app/[locale]/(protected)/credits/layout.tsx","src/app/[locale]/(protected)/dashboard/blog/[id]/edit/page.tsx","src/app/[locale]/(protected)/dashboard/blog/categories/page.tsx","src/app/[locale]/(protected)/dashboard/blog/new/page.tsx","src/app/[locale]/(protected)/dashboard/blog/page.tsx","src/app/[locale]/(protected)/dashboard/files/page.tsx","src/app/[locale]/(protected)/dashboard/layout.tsx","src/app/[locale]/(protected)/dashboard/users/page.tsx","src/app/[locale]/(protected)/settings/billing/page.tsx","src/app/[locale]/(protected)/settings/layout.tsx","src/app/[locale]/(protected)/settings/profile/page.tsx","src/app/[locale]/(protected)/settings/security/page.tsx","src/app/[locale]/docs/[[...slug]]/page.tsx","src/app/[locale]/docs/layout.tsx","src/app/[locale]/layout.tsx","src/app/[locale]/login/page.tsx","src/app/[locale]/privacy/page.tsx","src/app/[locale]/signup/page.tsx","src/app/[locale]/terms/page.tsx","src/app/api/api-keys/[keyId]/route.ts","src/app/api/api-keys/route.ts","src/app/api/auth/[...all]/route.ts","src/app/api/blog/[id]/archive/route.ts","src/app/api/blog/[id]/publish/route.ts","src/app/api/blog/[id]/route.ts","src/app/api/blog/categories/[id]/route.ts","src/app/api/blog/categories/route.ts","src/app/api/blog/route.ts","src/app/api/blog/upload/route.ts","src/app/api/credits/initialize/route.ts","src/app/api/health/route.ts","src/app/api/webhooks/stripe/route.ts","src/app/layout.tsx","src/app/not-found.tsx","src/components/auth/permission-provider.tsx","src/components/auth/permission-wrapper.tsx","src/components/blog/blog-editor-form.tsx","src/components/blog/blog-editor-toolbar.tsx","src/components/blog/blog-editor.tsx","src/components/blog/blog-post-list.tsx","src/components/loading-skeleton.tsx","src/components/mdx-components.tsx","src/components/providers/theme-provider.tsx","src/components/route-guard.tsx","src/components/ui/accordion.tsx","src/components/ui/alert-dialog.tsx","src/components/ui/avatar.tsx","src/components/ui/badge.tsx","src/components/ui/button.tsx","src/components/ui/card.tsx","src/components/ui/checkbox.tsx","src/components/ui/dialog.tsx","src/components/ui/dropdown-menu.tsx","src/components/ui/index.ts","src/components/ui/input.tsx","src/components/ui/label.tsx","src/components/ui/navigation-menu.tsx","src/components/ui/pagination.tsx","src/components/ui/progress.tsx","src/components/ui/scroll-area.tsx","src/components/ui/select.tsx","src/components/ui/separator.tsx","src/components/ui/sheet.tsx","src/components/ui/skeleton.tsx","src/components/ui/sonner.tsx","src/components/ui/switch.tsx","src/components/ui/table.tsx","src/components/ui/toggle.tsx","src/components/ui/tooltip.tsx","src/config/app.config.ts","src/config/appearance.config.ts","src/config/credits.config.ts","src/config/features.config.ts","src/config/i18n.config.ts","src/config/index.ts","src/config/navbar.config.ts","src/config/payment.config.ts","src/content/blog/en/getting-started-with-nextjs.mdx","src/content/blog/en/meta.json","src/content/blog/en/tailwind-css-best-practices.mdx","src/content/blog/en/typescript-advanced-patterns.mdx","src/content/blog/zh/getting-started-with-nextjs.mdx","src/content/blog/zh/meta.json","src/content/blog/zh/tailwind-css-best-practices.mdx","src/content/blog/zh/typescript-advanced-patterns.mdx","src/content/docs/en/architecture.mdx","src/content/docs/en/customization/meta.json","src/content/docs/en/customization/themes.mdx","src/content/docs/en/deployment/ci-cd.mdx","src/content/docs/en/deployment/cloudflare.mdx","src/content/docs/en/deployment/docker.mdx","src/content/docs/en/deployment/meta.json","src/content/docs/en/deployment/production-setup.mdx","src/content/docs/en/deployment/vercel.mdx","src/content/docs/en/development/api-key.mdx","src/content/docs/en/development/configuration.mdx","src/content/docs/en/development/credit.mdx","src/content/docs/en/development/database-development.mdx","src/content/docs/en/development/meta.json","src/content/docs/en/development/project-structure.mdx","src/content/docs/en/features/authentication.mdx","src/content/docs/en/features/file-management.mdx","src/content/docs/en/features/internationalization.mdx","src/content/docs/en/features/meta.json","src/content/docs/en/features/payment-billing.mdx","src/content/docs/en/index.mdx","src/content/docs/en/meta.json","src/content/docs/en/quickstart.mdx","src/content/docs/zh/architecture.mdx","src/content/docs/zh/customization/meta.json","src/content/docs/zh/customization/themes.mdx","src/content/docs/zh/deployment/ci-cd.mdx","src/content/docs/zh/deployment/cloudflare.mdx","src/content/docs/zh/deployment/docker.mdx","src/content/docs/zh/deployment/meta.json","src/content/docs/zh/deployment/production-setup.mdx","src/content/docs/zh/deployment/vercel.mdx","src/content/docs/zh/development/api-key.mdx","src/content/docs/zh/development/configuration.mdx","src/content/docs/zh/development/credit.mdx","src/content/docs/zh/development/database-development.mdx","src/content/docs/zh/development/meta.json","src/content/docs/zh/development/project-structure.mdx","src/content/docs/zh/features/authentication.mdx","src/content/docs/zh/features/file-management.mdx","src/content/docs/zh/features/meta.json","src/content/docs/zh/features/payment-billing.mdx","src/content/docs/zh/index.mdx","src/content/docs/zh/meta.json","src/content/docs/zh/quickstart.mdx","src/env.ts","src/hooks/use-billing.ts","src/hooks/use-config.ts","src/hooks/use-credits.ts","src/hooks/use-debounce.ts","src/hooks/use-files.ts","src/hooks/use-login.ts","src/hooks/use-navbar.ts","src/hooks/use-profile.ts","src/hooks/use-toast-messages.ts","src/hooks/use-users.ts","src/i18n/messages/en.json","src/i18n/messages/zh.json","src/i18n/navigation.ts","src/i18n/request.ts","src/i18n/routing.ts","src/lib/auth/auth-client.ts","src/lib/auth/auth.ts","src/lib/auth/permissions.ts","src/lib/auth/server-session.ts","src/lib/auth/use-auth.ts","src/lib/credits/credit-service.ts","src/lib/credits/index.ts","src/lib/files/file-service.ts","src/lib/files/image-processor.ts","src/lib/files/r2-client.ts","src/lib/fumadocs/blog.ts","src/lib/fumadocs/docs.ts","src/lib/icons.ts","src/lib/utils.ts","src/payment/stripe/client.ts","src/payment/stripe/provider.ts","src/payment/types.ts","src/proxy.ts","src/server/actions/auth-actions.ts","src/server/actions/credit-actions.ts","src/server/actions/error-messages.ts","src/server/actions/file-actions.ts","src/server/actions/payment/cancel-subscription.ts","src/server/actions/payment/create-subscription.ts","src/server/actions/payment/get-billing-info.ts","src/server/actions/payment/sync-subscription-periods.ts","src/server/actions/upload-avatar.ts","src/server/actions/user-actions.ts","src/server/db/index.ts","src/server/db/repositories/base-repository.ts","src/server/db/repositories/blog-repository.ts","src/server/db/repositories/category-repository.ts","src/server/db/repositories/file-repository.ts","src/server/db/repositories/index.ts","src/server/db/repositories/payment-repository.ts","src/server/db/schema.ts","src/server/db/services/index.ts","src/server/db/types.ts","src/styles/globals.css","src/themes/client-loader.tsx","src/themes/default/blocks/credit-balance.tsx","src/themes/default/blocks/credit-history.tsx","src/themes/default/blocks/credits-skeleton.tsx","src/themes/default/blocks/dashboard-header.tsx","src/themes/default/blocks/faq.tsx","src/themes/default/blocks/features.tsx","src/themes/default/blocks/file-table.tsx","src/themes/default/blocks/file-upload.tsx","src/themes/default/blocks/footer.tsx","src/themes/default/blocks/hero.tsx","src/themes/default/blocks/image-preview-modal.tsx","src/themes/default/blocks/language-switcher.tsx","src/themes/default/blocks/login.tsx","src/themes/default/blocks/navbar.tsx","src/themes/default/blocks/pricing.tsx","src/themes/default/blocks/purchase-confirmation-dialog.tsx","src/themes/default/blocks/signup.tsx","src/themes/default/blocks/subscription-card.tsx","src/themes/default/blocks/tech-stack.tsx","src/themes/default/blocks/theme-toggle.tsx","src/themes/default/blocks/user-avatar-menu.tsx","src/themes/default/blocks/user-list.tsx","src/themes/default/layouts/landing.tsx","src/themes/default/layouts/protected-container.tsx","src/themes/default/layouts/protected-sidebar.tsx","src/themes/default/layouts/protected.tsx","src/themes/default/pages/api-key-manager.tsx","src/themes/default/pages/billing.tsx","src/themes/default/pages/credit-history.tsx","src/themes/default/pages/credits.tsx","src/themes/default/pages/file-manager.tsx","src/themes/default/pages/home.tsx","src/themes/default/pages/profile.tsx","src/themes/default/pages/security.tsx","src/themes/index.ts","src/types/index.d.ts","src/types/login.ts","src/types/navbar.ts","src/types/profile.ts","src/types/stripe-extended.ts","tsconfig.json","vercel.json","wrangler.jsonc"],"storefront":"/r/justnode","claimed":false,"request_supported":{"post":"https://gitbuyer.com/r/justnode/better-saas/request-supported","requests":0},"note":"indexed from public GitHub; nothing is for sale on this page. Clone it from GitHub. Paid listings live at /search."}