From b8d4a92ed8d4dad81b9f62bf3a69e1980bae83a6 Mon Sep 17 00:00:00 2001
From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com>
Date: Tue, 21 Oct 2025 22:27:58 +0000
Subject: [PATCH] fix: use production URL in email links instead of localhost
 fallback
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- Update request-access API route to use request origin when NEXTAUTH_URL is not set
- Remove localhost fallback in access-requests-service.ts
- Add proper error handling for missing NEXTAUTH_URL
- Update documentation to clarify NEXTAUTH_URL must be set to production URL
- Add missing environment variable documentation for email notifications

Fixes #8

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Seth Webster <sethwebster@users.noreply.github.com>
---
 README.md                                | 10 +++++++++-
 README_STORE.md                          |  2 +-
 docs/store/STORE_MANAGEMENT.md           |  2 +-
 src/app/api/request-access/route.ts      |  4 +++-
 src/lib/admin/access-requests-service.ts | 11 ++++++++---
 5 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/README.md b/README.md
index 6707103..74a3633 100644
--- a/README.md
+++ b/README.md
@@ -135,11 +135,19 @@ Copy `.env.example` to `.env` and provide:
 - `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET` - OAuth App credentials
 - `GITHUB_TOKEN` - Personal access token with `read:user` and `public_repo` scopes
 - `NEXTAUTH_SECRET` - Generate with `openssl rand -base64 32`
-- `NEXTAUTH_URL` - http://localhost:3000
+- `NEXTAUTH_URL` - http://localhost:3000 (for development) or your production URL (e.g., https://yourdomain.com)
 
 **OpenAI (optional - for AI image generation):**
 - `OPENAI_API_KEY` - OpenAI API key
 
+**Email Notifications (required for access request emails):**
+- `RESEND_API_KEY` - Resend API key for sending emails
+- `RESEND_FROM_DOMAIN` - Domain for sending emails (e.g., yourdomain.com)
+- `ADMIN_EMAIL` - Email address to receive access request notifications
+
+**Redis (required for access control):**
+- `REDIS_URL` - Redis connection URL (e.g., redis://localhost:6379)
+
 ## Key Features
 
 ### Unified Foundation + Store
diff --git a/README_STORE.md b/README_STORE.md
index e67797e..d667049 100644
--- a/README_STORE.md
+++ b/README_STORE.md
@@ -49,7 +49,7 @@ Copy `.env.example` to `.env` and provide:
 - `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET` - OAuth App credentials
 - `GITHUB_TOKEN` - Personal access token with `read:user` and `public_repo` scopes
 - `NEXTAUTH_SECRET` - Generate with `openssl rand -base64 32`
-- `NEXTAUTH_URL` - http://localhost:3000
+- `NEXTAUTH_URL` - http://localhost:3000 (for development) or your production URL (e.g., https://yourdomain.com)
 
 **OpenAI (optional - for AI image generation):**
 - `OPENAI_API_KEY` - OpenAI API key
diff --git a/docs/store/STORE_MANAGEMENT.md b/docs/store/STORE_MANAGEMENT.md
index 93f3fc6..393258a 100644
--- a/docs/store/STORE_MANAGEMENT.md
+++ b/docs/store/STORE_MANAGEMENT.md
@@ -65,7 +65,7 @@ GITHUB_CLIENT_ID=xxxxx
 GITHUB_CLIENT_SECRET=xxxxx
 GITHUB_TOKEN=ghp_xxxxx
 NEXTAUTH_SECRET=xxxxx
-NEXTAUTH_URL=http://localhost:3000
+NEXTAUTH_URL=http://localhost:3000  # Use production URL (e.g., https://yourdomain.com) when deployed
 ```
 
 ### 2. API Tokens
diff --git a/src/app/api/request-access/route.ts b/src/app/api/request-access/route.ts
index e873d0e..3d782d0 100644
--- a/src/app/api/request-access/route.ts
+++ b/src/app/api/request-access/route.ts
@@ -75,7 +75,9 @@ export async function POST(request: NextRequest) {
     const approveToken = AccessRequestsService.generateActionToken(accessRequest.id, 'approve');
     const denyToken = AccessRequestsService.generateActionToken(accessRequest.id, 'deny');
 
-    const baseUrl = process.env.NEXTAUTH_URL || 'http://localhost:3000';
+    // Get base URL from request or environment variable
+    const requestUrl = new URL(request.url);
+    const baseUrl = process.env.NEXTAUTH_URL || `${requestUrl.protocol}//${requestUrl.host}`;
     const approveUrl = `${baseUrl}/api/admin/request-action?token=${approveToken}`;
     const denyUrl = `${baseUrl}/api/admin/request-action?token=${denyToken}`;
     const reviewUrl = `${baseUrl}/admin/requests?id=${accessRequest.id}`;
diff --git a/src/lib/admin/access-requests-service.ts b/src/lib/admin/access-requests-service.ts
index 810553e..bf8d177 100644
--- a/src/lib/admin/access-requests-service.ts
+++ b/src/lib/admin/access-requests-service.ts
@@ -195,7 +195,12 @@ export class AccessRequestsService {
       }
 
       const fromDomain = process.env.RESEND_FROM_DOMAIN || 'yourdomain.com';
-      const baseUrl = process.env.NEXTAUTH_URL || 'http://localhost:3000';
+      const baseUrl = process.env.NEXTAUTH_URL;
+
+      if (!baseUrl) {
+        console.error('❌ NEXTAUTH_URL not configured - email will not include links');
+        // Still send email, but without the link
+      }
 
       console.log(`   From: noreply@${fromDomain}`);
       console.log(`   To: ${email}`);
@@ -212,9 +217,9 @@ export class AccessRequestsService {
                 <h1 style="color: #10b981; font-size: 32px;">🎉 Welcome!</h1>
                 <p style="font-size: 18px; color: #fff;">Your access request has been approved.</p>
                 <p style="color: #aaa; margin: 20px 0;">You can now sign in and access the React Foundation.</p>
-                <a href="${baseUrl}" style="display: inline-block; margin-top: 20px; padding: 14px 32px; background: #06b6d4; color: #000; text-decoration: none; border-radius: 8px; font-weight: bold;">
+                ${baseUrl ? `<a href="${baseUrl}" style="display: inline-block; margin-top: 20px; padding: 14px 32px; background: #06b6d4; color: #000; text-decoration: none; border-radius: 8px; font-weight: bold;">
                   Sign In Now
-                </a>
+                </a>` : '<p style="color: #666; margin-top: 20px;">Please visit the React Foundation to sign in.</p>'}
               </div>
             </body>
           </html>
