UllrAI SaaS Starter Kit Developer Documentation
This documentation provides a comprehensive and in-depth technical reference for developers using the UllrAI SaaS Starter Kit. Whether you want to quickly launch a new project or perform deep customization and secondary development, this documentation will provide you with the necessary guidance.
1. Project Overview
1.1. Project Introduction
UllrAI SaaS Starter Kit is a free, open-source, production-ready full-stack SaaS starter kit. It integrates the most respected technologies and practices in modern web development, designed to help developers launch their next SaaS project at unprecedented speed, allowing you to focus on business logic rather than infrastructure setup.
- Core Features: Provides authentication, payment subscriptions, database management, file uploads, content management, and other core SaaS application features.
- Technology Stack: Based on Next.js 15 App Router, TypeScript, PostgreSQL, Drizzle ORM, and integrates Creem payments, Resend email service, and Cloudflare R2 file storage.
- Use Cases:
- Quickly build full-stack SaaS applications requiring user login and paid subscription features.
- As a practical project for learning modern full-stack web development technologies.
- Provide a stable, scalable initial scaffold for enterprise-level projects.
- Independent developers or small teams quickly validate business ideas.
1.2. Quick Start
1.Clone the project
git clone https://github.com/ullrai/saas-starter.git cd saas-starter
2.Install dependencies
pnpm install
3.Configure environment Copy .env.example to .env and fill in all required environment variables.
cp .env.example .env
4.Sync database Make sure your local PostgreSQL database is running, then execute:
pnpm db:push
5.Run development server
pnpm dev
The application will run at http://localhost:3000.
1.3. Feature List
- Modern Framework: Next.js 15 (App Router, RSC), React 19, TypeScript
- UI: Tailwind CSS v4, shadcn/ui, Lucide Icons, Dark/Light Mode
- Authentication: Better-Auth (Magic Link, OAuth - Google/GitHub/LinkedIn)
- Database: PostgreSQL + Drizzle ORM (Type-safe queries, Migration management)
- Payment Subscriptions: Creem integration (One-time payments, Subscriptions, Customer portal, Webhooks)
- File Upload: Cloudflare R2 integration (Client-side presigned direct upload, Server-side proxy upload, Image compression)
- Content Management: Keystatic (Markdown/MDX blog system)
- Email Service: Resend + React Email (Transactional email templates)
- Form Handling: React Hook Form + Zod (Type-safe form validation)
- Code Quality: ESLint, Prettier
- Admin Dashboard: Generic data management dashboard, easily extensible to manage any database table
- Deployment: One-click Vercel deployment
1.4. Technical Architecture Diagram
](https://mermaid.live/edit#pako:eNqNk1tPwjAUx79K09cRkuVSLqsv-g1MfDBkw8TEB1rOHGMd2zLWQkhCmAlKXP7bFr6F09Ol59d-_Z_Tt8N6DhoCVlSHoA3IqmKCU3-JOHy2hHhLUWOZ-_Ek1pj1Qv-KCWp7vdfJlsIe20g-kXGXGjQpIqQiEpMUpGOGJB9JGhCdhRJJWnNwCuI_gdWCTfZH5pB6Cri_7j6Fz5vF-Ow3SrqLAHaMvBVSLGj0bYG6vWm0LcSgBm0A6ZKjmzJdLlFmQgQZ8VsXuPFd4x4GzWfH6VpXILdvuJBsWKjXIW3ywQoTqPRNWDcnW3UqaEpGOzBW-a6V7N3x0wIgqtd6I3_sxJ6-77jEtJXHBhJ47QvqUpEVX6VCRBKrn2RfqDgvQU-XfgQsG3RFgNpj4Hx2v4DH7ZPQR2X8aRlQdGWKoKGbWXhQZJG3k4LhlDVs6Rf6QFyS6qYWWKGV4jZp4rJPJ1kVWJA6wPdg0r3qWANyf7K5yJT3rn7-Acr4J7o)
graph TD
subgraph "Frontend (Browser)"
A[User] --> B{Next.js App};
end
subgraph "Vercel Platform"
B -- React Server Components --> C["UI (shadcn/ui, Tailwind)"];
B -- API Routes/Server Actions --> D[Backend Logic];
end
subgraph "Core Services"
D -- ORM --> E[Drizzle ORM];
E --> F[(PostgreSQL)];
D -- Auth API --> G[Better-Auth];
D -- Payment API --> H[Creem];
D -- Email API --> I[Resend];
D -- Storage API --> J[Cloudflare R2];
end
subgraph "Content Management (CMS)"
K[Keystatic] -- Reads/Writes --> L[Markdown/JSON in Git];
B -- Reads data --> L;
end
G -- OAuth --> M[Google/GitHub/LinkedIn];
H -- Webhooks --> D;
style A fill:#f9f,stroke:#333,stroke-width:2px;
style F fill:#add,stroke:#333,stroke-width:2px;
style J fill:#f90,stroke:#333,stroke-width:2px;
style H fill:#f66,stroke:#333,stroke-width:2px;
style I fill:#9cf,stroke:#333,stroke-width:2px;
2. In-Depth Technical Analysis
2.1. Directory Structure Breakdown
SaaS-Starter-main/ ├── src/ # All application source code │ ├── app/ # Next.js App Router core directory │ │ ├── (auth)/ # Authentication-related pages (login, signup) │ │ ├── (dashboard)/ # Protected dashboard pages │ │ ├── (pages)/ # Public pages (home, about, blog, etc.) │ │ ├── api/ # API routes │ │ ├── keystatic/ # Keystatic CMS admin interface │ │ ├── layout.tsx # Root layout │ │ └── not-found.tsx # Global 404 page │ ├── components/ # React components │ │ ├── admin/ # Admin dashboard components │ │ ├── auth/ # Authentication flow components │ │ ├── blog/ # Blog-related components │ │ ├── forms/ # Form components │ │ ├── homepage/ # Homepage-specific components │ │ └── ui/ # Generic UI components (based on shadcn/ui) │ ├── database/ # Drizzle ORM related │ │ ├── migrations/ # Database migration files │ │ ├── config.ts # Development migration config │ │ ├── config.prod.ts # Production migration config │ │ ├── index.ts # Drizzle client initialization │ │ └── schema.ts # Database table structure definitions │ ├── emails/ # React Email templates │ ├── hooks/ # Custom React Hooks │ ├── lib/ # Core logic and utility functions │ │ ├── actions/ # Next.js Server Actions │ │ ├── admin/ # Admin dashboard core logic │ │ ├── auth/ # Authentication config and logic (Better-Auth) │ │ ├── billing/ # Payment abstraction layer and providers (Creem) │ │ ├── config/ # Global constants, products, roles, etc. │ │ ├── database/ # Database helper functions │ │ ├── email.tsx # Email sending service │ │ └── r2.ts # Cloudflare R2 file upload service │ ├── schemas/ # Zod validation schemas │ └── types/ # TypeScript type definitions ├── content/ # Keystatic-managed content (Markdown, JSON) ├── public/ # Static assets ├── scripts/ # Helper scripts (like setting up admin) └── styles/ # Global styles and CSS
2.2. Core Module Analysis
2.2.1. Entry Files and Startup Flow
src/app/layout.tsx: The project's root layout that wraps all pages. It handles:- Setting HTML
langattribute and font variables. - Integrating
ThemeProviderfor dark/light mode. - Integrating
NextTopLoaderfor page loading progress. - Integrating
Toasterfor global notifications. - Integrating
CookieConsentfor cookie consent management.
- Setting HTML
middleware.ts: Runs before requests reach pages, core for route protection.- Checks user session cookies.
- Redirects to
/loginif user is not logged in but accessing/dashboard/*. - Redirects to
/dashboardif user is logged in but accessing/loginor/signup.
src/app/dashboard/layout.tsx: Root layout for the dashboard.- Uses
SessionGuardcomponent to protect all child routes.SessionGuardis a client component that verifies session existence, redirects to login if not found, and shows loading animation during verification. - Renders
AppSidebarand main content areaSidebarInset.
- Uses
2.2.2. Configuration System Design
The project's configuration is highly centralized for easy maintenance and extension.
- Environment Variables (
env.ts): Uses@t3-oss/env-nextjsto enforce environment variable validation. All environment variables (like API keys, database URLs) must be defined in the.envfile and accessed throughenv.tsfor type safety. This prevents runtime errors due to missing environment variables. - Application Constants (
src/lib/config/constants.ts): Stores app name, description, contact email, and other hardcoded values that don't change frequently. - Product Plans (
src/lib/config/products.ts): Centrally defines all paid plans. Each plan includes internal ID, name, feature list, and product IDs in different payment providers (like Creem). This structure makes it easy to add new plans or switch payment providers. - User Roles (
src/lib/config/roles.ts): Defines user roles and their hierarchical relationships (user,admin,super_admin). Helper functions likehasRoleprovide unified permission checking logic. - File Upload (
src/lib/config/upload.ts): Centrally manages all file upload rules, including maximum file size, allowed file types, etc. All upload paths (client and server-side) share this configuration, ensuring rule consistency.
2.2.3. Routing Architecture
The project uses Next.js App Router and leverages Route Groups for logical page separation.
(pages): Contains all public pages like home, about, blog, pricing, etc. Usessrc/app/(pages)/layout.tsxto provide unified header and footer.(auth): Contains authentication flow pages like login, signup. Usessrc/app/(auth)/layout.tsxto provide a centered, clean layout.(dashboard): Contains all pages requiring user login. Its layoutsrc/app/dashboard/layout.tsximplements route protection throughSessionGuard.
2.2.4. Build and Packaging Process
next.config.ts: Next.js core configuration file.- Configures
images.remotePatternsto allow loading images from Unsplash and Cloudflare R2. - Integrates
@next/bundle-analyzer. WhenANALYZEenvironment variable is set totrue, runningpnpm analyzegenerates and opens bundle size analysis report after build, helping developers optimize frontend resource size.
- Configures
package.json:dev: Starts development server with Next.js 15's--turbomode for faster local compilation.build: Builds production application.start: Starts production server.
3. Development Guide
3.1. Environment Setup
- Install Tools:
- Node.js v20.x or higher.
- pnpm (
npm install -g pnpm). - PostgreSQL database (recommended using Docker:
docker run --name my-postgres -e POSTGRES_PASSWORD=mysecretpassword -p 5432:5432 -d postgres).
- Clone and Install:
git clone https://github.com/ullrai/saas-starter.git cd saas-starter pnpm install
- Configure Environment Variables:
- Copy
.env.exampleto.env. - Generate a secure
BETTER_AUTH_SECRET:openssl rand -base64 32. - Fill in your PostgreSQL
DATABASE_URL. - Register and obtain API keys for Creem, Resend, Cloudflare R2, and fill them in the
.envfile.
- Copy
- Database Setup:
- Development:
pnpm db:pushsynchronizes changes fromdatabase/schema.tsdirectly to the database, suitable for rapid iteration. - Production: Must use migration files. Process:
pnpm db:generate:prod: Generate production migration SQL files.- Deploy code and migration files to production.
- Run
pnpm db:migrate:prodin production to apply migrations.
- Development:
3.2. Development Workflow
- Start Development Server:
pnpm dev - Modify Database:
- Edit
database/schema.ts. - Run
pnpm db:pushto sync changes.
- Edit
- Create New Pages:
- Create new folders and
page.tsxfiles inapp/(pages)orapp/(dashboard).
- Create new folders and
- Create API Routes:
- Create new folders and
route.tsfiles in theapp/apidirectory.
- Create new folders and
- Create Server Actions:
- Create new files in the
lib/actionsdirectory using the"use server";directive.
- Create new files in the
- Code Checking:
- Run
pnpm lintto check code style. - Run
pnpm prettier:formatto format code.
- Run
3.3. Code Standards
- Naming Conventions:
- Components use PascalCase, e.g.,
FileUploader. - Functions and variables use camelCase.
- Constants use UPPER_SNAKE_CASE.
- Components use PascalCase, e.g.,
- File Organization:
- Page components are placed in their respective
approute folders, usually in_componentssubdirectories. - Reusable components are placed in the
componentsdirectory. - Logic, types, configurations are separated into
lib,types,schemasdirectories.
- Page components are placed in their respective
- Comment Requirements:
- Use JSDoc comments for complex functions or logic blocks.
- Use inline comments for non-intuitive code.
4. Feature Module Details
4.1. Authentication System (Better-Auth)
This starter kit uses the better-auth library to provide a complete authentication solution.
- Core Configuration:
src/lib/auth/server.ts- Configures Drizzle database adapter.
- Dynamically loads social login providers (Google, GitHub, LinkedIn), only enabled when corresponding
CLIENT_IDandSECRETare provided in.env. - Integrates
magicLinkplugin and configures using Resend for email sending.
- API Route:
app/api/auth/[...all]/route.ts- This is a dynamic route that captures all
better-authauthentication requests (like/api/auth/magic-link,/api/auth/google/login, etc.) and hands them toauth.handler.
- This is a dynamic route that captures all
- Client:
src/lib/auth/client.ts- Provides methods for interacting with the authentication system in client components, like
signIn,signOut,useSession, etc.
- Provides methods for interacting with the authentication system in client components, like
- Authentication Flow (Magic Link):
sequenceDiagram participant User participant Client as Frontend (AuthForm) participant Server as Server (API) participant Resend as Email Service User->>Client: Enter email and click login Client->>Server: POST /api/auth/magic-link Server->>Server: Generate time-limited Token Server->>Resend: Request to send email (with Token URL) Resend-->>User: Send magic link email User->>User: Click link in email Client->>Server: GET /api/auth/callback?token=... Server->>Server: Verify Token, create session Server-->>Client: Set session Cookie and redirect to /dashboard
4.2. Database & ORM (Drizzle)
- Schema Definition:
database/schema.tsis the single source of truth for all database tables, defining table structures, relationships, and constraints using Drizzle ORM syntax. - Client Initialization:
database/index.tsinitializes the Drizzle client and applies different connection pool configurations based on environment (Serverless or traditional server) (src/lib/database/connection.ts). - Migration Management:
- The project maintains two separate migration histories for development and production environments, located in
database/migrations/developmentanddatabase/migrations/productionrespectively. pnpm db:generate&pnpm db:generate:prod: Generate SQL migration files based on changes inschema.ts.pnpm db:push: Development only, directly syncs schema to database, loses history.pnpm db:migrate:dev&pnpm db:migrate:prod: Apply migration files to database.
- The project maintains two separate migration histories for development and production environments, located in
4.3. Payment & Subscriptions (Creem)
- Abstraction Layer:
src/lib/billing/index.tsexports a unifiedbillingobject, making it easy to switch to other payment providers (like Stripe) in the future without modifying upper-level business code. - Provider Implementation:
src/lib/billing/creem/provider.tsis the specific implementation for Creem payment provider, encapsulating logic for creating checkout sessions, customer portals, and handling webhooks. - API Interfaces:
/api/billing/checkout: Creates payment sessions. Returns409 Conflictstatus and management link when user tries to purchase existing subscription./api/billing/portal: Creates a URL to Creem customer portal where users can manage their subscriptions./api/billing/webhooks/creem: Receives webhook events from Creem for updating subscription status, recording payments, etc.
- Webhook Handling:
src/lib/billing/creem/webhook.ts- Security: Uses
crypto.timingSafeEqualto verify webhook signatures, preventing forged requests. - Idempotency: Records processed event IDs in
webhook_eventstable to prevent duplicate processing of the same event. - Transactional: All database operations are completed in one transaction, ensuring data consistency.
- Security: Uses
- Payment Flow:
sequenceDiagram participant User participant Client as Frontend (Pricing Page) participant Server as Server participant Creem User->>Client: Click "Get Plan" Client->>Server: POST /api/billing/checkout Server->>Creem: Create Checkout Session Creem-->>Server: checkoutUrl Server-->>Client: Return checkoutUrl Client->>User: Redirect to Creem payment page User->>Creem: Complete payment Creem-->>Server: Webhook (checkout.completed) Server->>Server: Verify signature, record event Server->>Server: (DB Transaction) Update user subscription status User->>Client: Redirect to /payment-status
4.4. File Upload (Cloudflare R2)
The system supports two upload modes, providing optimal choices for different scenarios. All upload rules are centralized in src/lib/config/upload.ts.
4.4.1. Client-side Presigned Upload (UI Recommended)
This is the default method used by the FileUploader component, offering better performance.
Flow Diagram:
sequenceDiagram
participant User
participant FileUploader as Frontend Component
participant Server as Server API
participant R2 as Cloudflare R2
User->>FileUploader: Select/drag files
FileUploader->>FileUploader: Client-side validation (type/size), image compression
FileUploader->>Server: POST /api/upload/presigned-url (request upload URL)
Server->>Server: Verify identity and file metadata
Server->>R2: Request presigned URL
R2-->>Server: Return presigned URL
Server-->>FileUploader: Return presigned URL
FileUploader->>R2: PUT (direct file upload)
R2-->>FileUploader: Upload success
FileUploader->>FileUploader: onUploadComplete callback
4.4.2. Server-side Proxy Upload
This mode allows server-side processing before storage.
Flow Diagram:
sequenceDiagram
participant Client as Client/Script
participant Server as Server API
participant R2 as Cloudflare R2
Client->>Server: POST /api/upload/server-upload (multipart/form-data)
Server->>Server: Verify identity and file
Server->>R2: Stream file
R2-->>Server: Upload success
Server->>Server: Record to database
Server-->>Client: Return upload result
4.5. Blog & Content Management (Keystatic)
- CMS: Uses
Keystaticas Git-based CMS, all content stored in Markdown and JSON files undercontent/directory. - Admin Interface: In development environment, access
/keystaticto enter admin dashboard. For security, this interface is disabled by default in production. - Content Reading:
@keystatic/core/readerused for safely reading content undercontent/directory on server-side.src/app/(pages)/blog/page.tsx: Blog list page, reads all articles.src/app/(pages)/blog/[slug]/page.tsx: Blog detail page, reads single article and uses@markdoc/markdocto parse Markdoc content.
4.6. Admin Dashboard
Provides a powerful, extensible data management system.
- Generic Table Manager:
components/admin/generic-table/generic-table-manager.tsxis a core component that can automatically generate a complete CRUD management interface for any table declared insrc/lib/config/admin-tables.ts. - Configuration-Driven:
- Add Drizzle table object in
src/lib/config/admin-tables.ts. - Add navigation link in
genericTableNavigationinsrc/app/dashboard/_components/app-sidebar.tsx. - Access new management page at
/dashboard/admin/tables/[tableName].
- Add Drizzle table object in
- Server Actions: All CRUD operations completed through type-safe Server Actions in
src/lib/actions/admin-generic.ts, no need to write additional API routes. - Schema Parsing:
src/lib/admin/schema-generator.tsdynamically parses Drizzle schema, automatically generates forms and validation rules (Zod), greatly simplifying backend development.
5. Secondary Development Guide
5.1. Extension Point Identification
- Add New Pages: Create new routes in
app/(pages)orapp/(dashboard). - Add New Admin Management Tables:
- Define new table in
database/schema.ts. - Register the table in
enabledTablesMapinsrc/lib/config/admin-tables.ts. - Add navigation link in
src/app/dashboard/_components/app-sidebar.tsx.
- Define new table in
- Add New Payment Provider:
- Create new provider implementation file under
src/lib/billing/, must followPaymentProviderinterface insrc/lib/billing/provider.ts. - Modify
PAYMENT_PROVIDERlogic insrc/lib/billing/index.tsto switch to new provider.
- Create new provider implementation file under
- Customize Email Templates: Create or modify React Email components in
src/emails/directory. - Customize UI Components: Modify
shadcn/uicomponents or add new ones insrc/components/ui/.
5.2. API Reference
| Route | Method | Description |
|---|---|---|
/api/auth/[...all] | GET, POST | Handle all better-auth authentication requests. |
/api/billing/checkout | POST | Create payment session. |
/api/billing/portal | GET | Get customer portal URL. |
/api/billing/webhooks/creem | POST | Receive Creem webhook events. |
/api/upload/presigned-url | POST | Get presigned URL for client-side direct upload. |
/api/upload/server-upload | POST | Server-side proxy file upload. |
/api/payment-status | GET | Query payment status. |
/api/keystatic/[...params] | GET, POST | Keystatic CMS API interface (development only). |
5.3. Hooks and Events
useSidebar(): Used in dashboard components to control sidebar expand/collapse state.useIsMobile(): Client-side hook to determine if current device is mobile size, safe for responsive components, avoiding SSR hydration errors.useAdminTable(): Core hook for driving admin dashboard table components. Encapsulates data fetching, pagination, search, filtering, and loading state management logic.onUploadComplete: Callback prop forFileUploadercomponent, triggered after successful file upload.
6. Developer Toolchain
6.1. Testing Strategy
- Framework: Uses
JestandReact Testing Library. - Configuration Files:
jest.config.ts,jest.setup.ts. - Examples: Project includes unit test examples for UI components (
logo.test.tsx), layouts (layout.test.tsx), schemas (auth.schema.test.ts), and core logic (database/index.test.ts). - Run Tests:
pnpm test
6.2. Code Quality Assurance
- ESLint: Configured in
.eslintrc.json, followseslint-config-nextbest practices. - Prettier: Integrated with ESLint, uses
prettier-plugin-tailwindcssto auto-sort Tailwind CSS classes. - Run Checks:
pnpm lintandpnpm prettier:check. - Auto Format:
pnpm prettier:format.
6.3. Bundle Size Analysis
- Uses
@next/bundle-analyzerto analyze production build bundle size. - Run
pnpm analyzeto generate client and server analysis reports. - This is crucial for identifying and optimizing large dependencies.
7. Real-world Application Scenarios
7.1. Typical Use Cases
- Enterprise SaaS: As starting point for new projects, integrates user management, role permissions, payments, and audit logs (through webhook events) needed by enterprises.
- AI Applications: Quickly build AI tools requiring user login and usage/subscription-based payments. File upload functionality can be used for processing user data.
- Paid Content Platforms: Blog and content management system combined with payment functionality can easily be extended to paid content platforms.
- Internal Tools: Leverage powerful admin dashboard and data management capabilities to quickly build company internal data management tools or dashboards.
8. Utility Tools
8.1. CLI Commands
| Script | Description |
|---|---|
pnpm dev | Start development server (Turbo mode) |
pnpm build | Build production application |
pnpm start | Start production server |
pnpm lint | Run ESLint checks |
pnpm test | Run Jest unit tests |
pnpm prettier:format | Format all code |
pnpm db:generate | Generate migration files for development |
pnpm db:generate:prod | Generate migration files for production |
pnpm db:migrate:dev | Apply development migrations |
pnpm db:migrate:prod | Apply production migrations |
pnpm db:push | (Development only) Push schema to database |
pnpm analyze | Build and analyze bundle size |
pnpm set:admin | (Local) Promote user to super admin |
pnpm set:admin:prod | (Production) Promote user to super admin |
8.2. Configuration Options
All required and optional environment variables are detailed in the environment configuration section of README.md. Be sure to completely fill out the .env file.
8.3. Utility Functions
src/lib/utils.ts provides some useful utility functions:
cn(...inputs): Safely merge Tailwind CSS class names and resolve conflicts.formatCurrency(amount, currency): Format amounts in cents to currency strings.calculateReadingTime(text): Calculate estimated reading time based on text content.renderMarkdoc(node): Convert Markdoc AST nodes to plain text strings for generating summaries.
9. Version Management & Updates
9.1. Dependency Management
- Package Manager: Project uses
pnpm, ensure you have it installed globally.pnpmleverages content-addressable storage to save disk space and speed up installations. - Version Locking:
pnpm-lock.yamlfile locks exact versions of all dependencies and their sub-dependencies, ensuring consistency across team members and different deployment environments. - Dependency Updates: Recommend using
pnpm up --latestto safely update dependencies, and pay attention to major version change logs.
10. Best Practices
10.1. Performance Optimization
- Code Splitting: Use
next/dynamicfor dynamic imports of large components, like dynamic imports for each settings page insrc/app/dashboard/settings/_components/settings.tsx. - Image Optimization: Prioritize using Next.js
<Image>component, which automatically performs image size optimization, format conversion (like WebP), and lazy loading. - Server Components: Use React Server Components (RSC) as much as possible for data fetching and logic execution, reducing JavaScript code sent to client.
- Database Queries: Avoid executing database queries in loops. Leverage Drizzle ORM's join and batch operation capabilities to reduce database round trips.
10.2. Security Considerations
- Environment Variables: Never commit
.envfile to Git repository. Use secret management tools from platforms like Vercel to store production environment variables. - Route Protection:
middleware.tsis the first line of defense, but must use functions likerequireAuth,requireAdminin Server Actions and API routes for backend permission verification. - SQL Injection: Using Drizzle ORM effectively prevents SQL injection attacks because it automatically parameterizes queries.
- XSS: Next.js and React escape JSX content by default, preventing cross-site scripting attacks. When handling user-generated content, use mature libraries (like
DOMPurify) for sanitization. - Webhook Security: Signature verification in
src/lib/billing/creem/webhook.tsis key to ensuring webhook requests come from trusted sources.
10.3. Deployment Guide
Vercel deployment is recommended.
- Push your code to GitHub/GitLab/Bitbucket repository.
- Import the Git repository in Vercel.
- Vercel will automatically detect Next.js project and configure build settings.
- In Vercel project's
Settings > Environment Variables, add all environment variables defined in your.envfile. - Configure production database migration process in your CI/CD pipeline, ensuring
pnpm db:migrate:prodruns after each successful deployment. - Each push to main branch will automatically build and deploy your application on Vercel.
11. Community & Ecosystem
11.1. Community Resources
- Official Repository: UllrAI SaaS Starter on GitHub
- Issues & Discussions: Use GitHub Issues to submit bug reports and feature requests.
- Main Dependency Documentation:
11.2. Contribution Guidelines
We welcome community contributions!
- Fork this project repository.
- Create a new branch (
git checkout -b feature/your-feature-name). - Make changes and commit (
git commit -m 'feat: Add some feature'). - Push your branch to forked repository (
git push origin feature/your-feature-name). - Create a Pull Request.
12. Troubleshooting
12.1. Common Issues FAQ
Q: Why can't I access the /keystatic admin dashboard?
A: Keystatic admin interface is only enabled in development environment (NODE_ENV=development) by default for security. You need to run pnpm dev locally to access it.
Q: File upload fails with CORS error.
A: This is the most common file upload issue. Make sure you have correctly configured CORS policy in your Cloudflare R2 bucket settings, allowing PUT and GET requests from your deployment domain and http://localhost:3000.
Q: How to set up the first admin account?
A: The system doesn't automatically set up admins. You need to:
- First register an account normally in the app with the email you want to make admin.
- Run
pnpm set:admin --email=your-email@example.com(local) orpnpm set:admin:prod --email=your-email@example.com(production) in your project root directory.
Q: Social login doesn't work, what to do?
A: Please check the following:
- Make sure you correctly filled in the corresponding social platform's
CLIENT_IDandCLIENT_SECRETin the.envfile. - Make sure in the social platform's OAuth app configuration (like Google Cloud Console, GitHub Developer Settings), you've added
http://localhost:3000/api/auth/[provider]/callbackand your production domain's callback URL to the authorized callback URL list.