Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: improve initial setup and configuration #64

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 26 additions & 21 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,37 +1,42 @@
# -----------------------------------------------------------------------------
# App - Don't add "/" in the end of the url (same in production)
# Required Environment Variables
# -----------------------------------------------------------------------------
NEXT_PUBLIC_APP_URL=http://localhost:3000

# -----------------------------------------------------------------------------
# Authentication (NextAuth.js)
# -----------------------------------------------------------------------------
AUTH_SECRET=
# App - Don't add "/" in the end of the url (same in production)
NEXT_PUBLIC_APP_URL="http://localhost:3000"

GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
# Database (PostgreSQL - Neon DB)
DATABASE_URL='postgres://[user]:[password]@[neon_hostname]/[dbname]?sslmode=require'

GITHUB_OAUTH_TOKEN=
# Auth secret for token encryption. Generate one using `npx auth secret` or `openssl rand -base64 33`.
AUTH_SECRET=""

# -----------------------------------------------------------------------------
# Database (MySQL - Neon DB)
# Optional Authentication (NextAuth.js)
# -----------------------------------------------------------------------------
DATABASE_URL='postgres://[user]:[password]@[neon_hostname]/[dbname]?sslmode=require'
# Required only if you want authentication
# GOOGLE_CLIENT_ID=""
# GOOGLE_CLIENT_SECRET=""
# GITHUB_OAUTH_TOKEN=""


# -----------------------------------------------------------------------------
# Email (Resend)
# Optional Email (Resend)
# -----------------------------------------------------------------------------
RESEND_API_KEY=
EMAIL_FROM="SaaS Starter App <[email protected]>"
# Required only if you want email features
# RESEND_API_KEY=""
# EMAIL_FROM="SaaS Starter App <[email protected]>"


# -----------------------------------------------------------------------------
# Subscriptions (Stripe)
# Optional Subscriptions (Stripe)
# -----------------------------------------------------------------------------
STRIPE_API_KEY=
STRIPE_WEBHOOK_SECRET=
# Required only if you want payment features
# STRIPE_API_KEY=""
# STRIPE_WEBHOOK_SECRET=""

NEXT_PUBLIC_STRIPE_PRO_MONTHLY_PLAN_ID=
NEXT_PUBLIC_STRIPE_PRO_YEARLY_PLAN_ID=
# NEXT_PUBLIC_STRIPE_PRO_MONTHLY_PLAN_ID=""
# NEXT_PUBLIC_STRIPE_PRO_YEARLY_PLAN_ID=""

NEXT_PUBLIC_STRIPE_BUSINESS_MONTHLY_PLAN_ID=
NEXT_PUBLIC_STRIPE_BUSINESS_YEARLY_PLAN_ID=
# NEXT_PUBLIC_STRIPE_BUSINESS_MONTHLY_PLAN_ID=""
# NEXT_PUBLIC_STRIPE_BUSINESS_YEARLY_PLAN_ID=""
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,48 @@ pnpm install
```sh
cp .env.example .env.local
```
>
> #### Where to get each of these environment variables:
>
> 1. **AUTH_SECRET**
> - Generate a random string using `openssl rand -base64 32` in terminal
> - Or use any secure random string generator
>
> 2. **Google OAuth Credentials (GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET)**
> - Go to [Google Cloud Console](https://console.cloud.google.com/)
> - Create a new project
> - Enable Google OAuth API
> - Create OAuth 2.0 credentials
> - Set authorized redirect URI to `http://localhost:3000/api/auth/callback/google`
>
> 3. **GITHUB_OAUTH_TOKEN**
> - Go to [GitHub Settings > Developer Settings](https://github.com/settings/tokens)
> - Generate new Personal Access Token
> - Select required scopes
>
> 4. **DATABASE_URL**
> - Sign up at [Neon](https://neon.tech)
> - Create a new project
> - Get connection string from dashboard
> - Replace `[user]`, `[password]`, `[neon_hostname]`, and `[dbname]` with your values
>
> 5. **RESEND_API_KEY**
> - Sign up at [Resend](https://resend.com)
> - Get API key from dashboard
> - Update EMAIL_FROM with your verified domain (or use the default for testing)
>
> 6. **Stripe Variables**
> - Sign up for [Stripe](https://stripe.com)
> - Get API key from dashboard (STRIPE_API_KEY)
> - Set up webhook in Stripe dashboard to get STRIPE_WEBHOOK_SECRET
> - Create products/prices in Stripe dashboard to get plan IDs:
> - NEXT_PUBLIC_STRIPE_PRO_MONTHLY_PLAN_ID
> - NEXT_PUBLIC_STRIPE_PRO_YEARLY_PLAN_ID
> - NEXT_PUBLIC_STRIPE_BUSINESS_MONTHLY_PLAN_ID
> - NEXT_PUBLIC_STRIPE_BUSINESS_YEARLY_PLAN_ID
>
> Remember to use test keys during development and switch to production keys when deploying.
>

3. Start the development server:

Expand Down
4 changes: 4 additions & 0 deletions actions/generate-user-stripe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export type responseAction = {
const billingUrl = absoluteUrl("/pricing")

export async function generateUserStripe(priceId: string): Promise<responseAction> {
if (!stripe) {
throw new Error("Stripe is not configured");
}

let redirectUrl: string = "";

try {
Expand Down
4 changes: 4 additions & 0 deletions app/api/webhooks/stripe/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import { prisma } from "@/lib/db";
import { stripe } from "@/lib/stripe";

export async function POST(req: Request) {
if (!stripe || !env.STRIPE_WEBHOOK_SECRET) {
return new Response("Stripe is not configured", { status: 400 });
}

const body = await req.text();
const signature = headers().get("Stripe-Signature") as string;

Expand Down
28 changes: 18 additions & 10 deletions auth.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,26 @@ import Google from "next-auth/providers/google";
import Resend from "next-auth/providers/resend";

import { env } from "@/env.mjs";
import { sendVerificationRequest } from "@/lib/email";

export default {
providers: [
Google({
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
}),
Resend({
apiKey: env.RESEND_API_KEY,
from: env.EMAIL_FROM,
// sendVerificationRequest,
}),
// Only add Google provider if credentials are present
...(env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET
? [
Google({
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
}),
]
: []),
// Only add Resend provider if credentials are present
...(env.RESEND_API_KEY && env.EMAIL_FROM
? [
Resend({
apiKey: env.RESEND_API_KEY,
from: env.EMAIL_FROM,
}),
]
: []),
],
} satisfies NextAuthConfig;
22 changes: 11 additions & 11 deletions env.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@ export const env = createEnv({
// See https://next-auth.js.org/deployment.
NEXTAUTH_URL: z.string().url().optional(),
AUTH_SECRET: z.string().min(1),
GOOGLE_CLIENT_ID: z.string().min(1),
GOOGLE_CLIENT_SECRET: z.string().min(1),
GITHUB_OAUTH_TOKEN: z.string().min(1),
GOOGLE_CLIENT_ID: z.string().optional(),
GOOGLE_CLIENT_SECRET: z.string().optional(),
GITHUB_OAUTH_TOKEN: z.string().optional(),
DATABASE_URL: z.string().min(1),
RESEND_API_KEY: z.string().min(1),
EMAIL_FROM: z.string().min(1),
STRIPE_API_KEY: z.string().min(1),
STRIPE_WEBHOOK_SECRET: z.string().min(1),
RESEND_API_KEY: z.string().optional(),
EMAIL_FROM: z.string().optional(),
STRIPE_API_KEY: z.string().optional(),
STRIPE_WEBHOOK_SECRET: z.string().optional(),
},
client: {
NEXT_PUBLIC_APP_URL: z.string().min(1),
NEXT_PUBLIC_STRIPE_PRO_MONTHLY_PLAN_ID: z.string().min(1),
NEXT_PUBLIC_STRIPE_PRO_YEARLY_PLAN_ID: z.string().min(1),
NEXT_PUBLIC_STRIPE_BUSINESS_MONTHLY_PLAN_ID: z.string().min(1),
NEXT_PUBLIC_STRIPE_BUSINESS_YEARLY_PLAN_ID: z.string().min(1),
NEXT_PUBLIC_STRIPE_PRO_MONTHLY_PLAN_ID: z.string().optional(),
NEXT_PUBLIC_STRIPE_PRO_YEARLY_PLAN_ID: z.string().optional(),
NEXT_PUBLIC_STRIPE_BUSINESS_MONTHLY_PLAN_ID: z.string().optional(),
NEXT_PUBLIC_STRIPE_BUSINESS_YEARLY_PLAN_ID: z.string().optional(),
},
runtimeEnv: {
NEXTAUTH_URL: process.env.NEXTAUTH_URL,
Expand Down
13 changes: 8 additions & 5 deletions lib/stripe.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import Stripe from "stripe"

import { env } from "@/env.mjs"

export const stripe = new Stripe(env.STRIPE_API_KEY, {
apiVersion: "2024-04-10",
typescript: true,
})
export let stripe: Stripe | null = null;

if (env.STRIPE_API_KEY) {
stripe = new Stripe(env.STRIPE_API_KEY, {
apiVersion: "2024-04-10",
typescript: true,
});
}