This commit is contained in:
NotII
2025-03-10 17:39:37 +00:00
parent c08df8919d
commit 20d5559832
69 changed files with 7676 additions and 78 deletions

BIN
lib/.server-service.ts.swp Normal file

Binary file not shown.

33
lib/auth-helpers.ts Normal file
View File

@@ -0,0 +1,33 @@
'use client';
// Helper function to verify authentication with the local API
export async function verifyAuth(token: string) {
try {
// Use a properly formed URL with origin
const origin = typeof window !== 'undefined' ? window.location.origin : '';
const authEndpoint = new URL('/api/auth/me', origin).toString();
console.log(`Verifying auth with ${authEndpoint} using token: ${token.substring(0, 10)}...`);
const response = await fetch(authEndpoint, {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
// Prevent caching of auth requests
cache: 'no-store'
});
if (!response.ok) {
console.error(`Auth verification failed: ${response.status}`);
return false;
}
return await response.json();
} catch (error) {
console.error("Authentication verification failed:", error);
console.error("Error details:", error instanceof Error ? error.message : 'Unknown error');
return false;
}
}

View File

@@ -21,65 +21,77 @@ function getAuthToken(): string | null {
?.split('=')[1] || null;
}
/**
* Gets the API base URL for client-side fetch calls
*/
function getClientApiBaseUrl(): string {
// For client-side, we can access window.location if in browser
if (typeof window !== 'undefined') {
// Use the same origin (which includes the correct port)
return `${window.location.origin}/api`;
}
// Fallback when window is not available
// For development mode, use port 3001 to match our server
if (process.env.NODE_ENV === 'development') {
return 'http://localhost:3001/api';
}
// Default fallback - relative URL
return '/api';
}
export async function fetchClient<T>(
endpoint: string,
options: FetchOptions = {}
): Promise<T> {
const { method = 'GET', body, headers = {}, ...rest } = options;
// Get the base API URL from environment or fallback
const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
// Ensure the endpoint starts with a slash
const normalizedEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
// For the specific case of internal-api.inboxi.ng - remove duplicate /api
let url;
if (apiUrl.includes('internal-api.inboxi.ng')) {
// Special case for internal-api.inboxi.ng
if (normalizedEndpoint.startsWith('/api/')) {
url = `${apiUrl}${normalizedEndpoint.substring(4)}`; // Remove the /api part
} else {
url = `${apiUrl}${normalizedEndpoint}`;
}
} else {
// Normal case for other environments
url = `${apiUrl}${normalizedEndpoint}`;
}
// Get auth token from cookies
const authToken = getAuthToken();
// Prepare headers with authentication if token exists
const requestHeaders: Record<string, string> = {
'Content-Type': 'application/json',
...(headers as Record<string, string>),
};
if (authToken) {
// Backend expects "Bearer TOKEN" format
requestHeaders['Authorization'] = `Bearer ${authToken}`;
console.log('Authorization header set to:', `Bearer ${authToken.substring(0, 10)}...`);
}
console.log('API Request:', {
url,
method,
hasAuthToken: !!authToken
});
const fetchOptions: RequestInit = {
method,
credentials: 'include',
headers: requestHeaders,
...rest,
};
if (body && method !== 'GET') {
fetchOptions.body = JSON.stringify(body);
}
try {
// Get the base API URL
const baseUrl = getClientApiBaseUrl();
// Ensure endpoint doesn't start with a slash if baseUrl ends with one
const normalizedEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint;
// Ensure baseUrl ends with a slash if it doesn't already
const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
// Combine them for the final URL
const url = `${normalizedBaseUrl}${normalizedEndpoint}`;
// Get auth token from cookies
const authToken = getAuthToken();
// Prepare headers with authentication if token exists
const requestHeaders: Record<string, string> = {
'Content-Type': 'application/json',
...(headers as Record<string, string>),
};
if (authToken) {
// Backend expects "Bearer TOKEN" format
requestHeaders['Authorization'] = `Bearer ${authToken}`;
console.log('Authorization header set to:', `Bearer ${authToken.substring(0, 10)}...`);
}
console.log('API Request:', {
url,
method,
hasAuthToken: !!authToken
});
const fetchOptions: RequestInit = {
method,
credentials: 'include',
headers: requestHeaders,
...rest,
};
if (body && method !== 'GET') {
fetchOptions.body = JSON.stringify(body);
}
const response = await fetch(url, fetchOptions);
if (!response.ok) {
@@ -97,6 +109,7 @@ export async function fetchClient<T>(
return data;
} catch (error) {
console.error('API request failed:', error);
console.error('Error details:', error instanceof Error ? error.message : 'Unknown error');
// Only show toast if this is a client-side error (not during SSR)
if (typeof window !== 'undefined') {

View File

@@ -1,6 +1,36 @@
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';
/**
* Gets the base URL for server API requests with proper fallbacks
*/
function getBaseUrl() {
// For server components, we normally would use environment variables
// But we need to be careful with how they're accessed
// Try to get the API URL from environment variables
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
// We need to get the host from somewhere to construct the URL
// In production, we can rely on the VERCEL_URL or similar
if (process.env.VERCEL_URL) {
return `https://${process.env.VERCEL_URL}/api`;
}
// If we have a configured API URL, use that
if (apiUrl) {
// If it's already an absolute URL, use it
if (apiUrl.startsWith('http')) {
return apiUrl;
}
// Otherwise, it's likely a relative path like /api
return `http://localhost:3000${apiUrl.startsWith('/') ? apiUrl : `/${apiUrl}`}`;
}
// Last resort fallback for development
return 'http://localhost:3000/api';
}
/**
* Server-side fetch wrapper with authentication.
*/
@@ -14,8 +44,20 @@ export async function fetchServer<T = unknown>(
if (!authToken) redirect('/login');
try {
console.log(`${endpoint}`)
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}${endpoint}`, {
const baseUrl = getBaseUrl();
// Ensure endpoint doesn't start with a slash if baseUrl ends with one
const normalizedEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint;
// Ensure baseUrl ends with a slash if it doesn't already
const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
// Combine them to get a complete URL
const url = `${normalizedBaseUrl}${normalizedEndpoint}`;
console.log(`Server fetch to: ${url}`);
const res = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',

32
lib/server-utils.ts Normal file
View File

@@ -0,0 +1,32 @@
/**
* Helper function to construct proper URLs for server components.
* In Next.js server components, relative URLs don't work in fetch() calls.
*/
export function getServerApiUrl(endpoint: string): string {
// Base API URL - use a hardcoded value for server components
// This must be an absolute URL with protocol and domain
const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000/api';
// Ensure endpoint doesn't start with a slash if baseUrl ends with one
const normalizedEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint;
// Ensure the baseUrl ends with a slash if it doesn't already
const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
// Combine them to get a complete URL
return `${normalizedBaseUrl}${normalizedEndpoint}`;
}
/**
* Helper to detect if code is running on server or client
*/
export function isServer(): boolean {
return typeof window === 'undefined';
}
/**
* Helper to detect if code is running in development mode
*/
export function isDevelopment(): boolean {
return process.env.NODE_ENV === 'development';
}