From 29ec1be68c9c00a0b6dfb1c58c5b6818c94db255 Mon Sep 17 00:00:00 2001 From: NotII <46204250+NotII@users.noreply.github.com> Date: Mon, 1 Sep 2025 15:35:10 +0100 Subject: [PATCH] Refactor API URLs and add environment config example Replaces hardcoded production API URLs with localhost defaults for local development in both server and client code. Updates Dockerfile to require API URLs via deployment environment variables. Improves ChatTable to use a batch endpoint for chats and unread counts, with backward compatibility. Adds an env.example file to document required environment variables. Updates next.config.mjs to use environment variables for backend API rewrites and image domains. --- Dockerfile | 5 +++- app/api/auth/check/route.ts | 2 +- components/dashboard/ChatDetail.tsx | 4 ++- components/dashboard/ChatTable.tsx | 43 +++++++++++++++++++---------- env.example | 23 +++++++++++++++ lib/api-client.ts | 13 +++++---- lib/server-api.ts | 2 +- next.config.mjs | 11 ++++---- 8 files changed, 74 insertions(+), 29 deletions(-) create mode 100644 env.example diff --git a/Dockerfile b/Dockerfile index bd36d74..895cf27 100644 --- a/Dockerfile +++ b/Dockerfile @@ -39,7 +39,10 @@ EXPOSE 3000 ENV NODE_ENV=production ENV NEXT_PUBLIC_API_URL=/api -ENV SERVER_API_URL=https://internal-api.inboxi.ng/api +# Backend API URL should be set via deployment environment +# ENV SERVER_API_URL=set_via_deployment +# ENV API_BASE_URL=set_via_deployment +# ENV API_HOSTNAME=set_via_deployment # Set GIT_COMMIT_SHA environment variable in the final image by reading the file ENV GIT_COMMIT_SHA="$(cat /app/git_commit_sha)" diff --git a/app/api/auth/check/route.ts b/app/api/auth/check/route.ts index 68ec3fa..867154a 100644 --- a/app/api/auth/check/route.ts +++ b/app/api/auth/check/route.ts @@ -30,7 +30,7 @@ export async function GET(req: NextRequest) { console.log('Auth check: Token found -', token.substring(0, 15) + '...'); - const apiUrl = process.env.SERVER_API_URL || 'https://internal-api.inboxi.ng/api'; + const apiUrl = process.env.SERVER_API_URL || 'http://localhost:3001/api'; console.log(`Auth check: Calling external API: ${apiUrl}/auth/me`); try { diff --git a/components/dashboard/ChatDetail.tsx b/components/dashboard/ChatDetail.tsx index 6484324..cf99e6c 100644 --- a/components/dashboard/ChatDetail.tsx +++ b/components/dashboard/ChatDetail.tsx @@ -213,7 +213,9 @@ export default function ChatDetail({ chatId }: { chatId: string }) { try { setLoading(true); - // Use clientFetch instead of direct axios calls + // Use clientFetch to load chat data + // For now, we're only loading chat data, but this could be extended + // to load additional data in parallel (user profiles, order details, etc.) const response = await clientFetch(`/chats/${chatId}`); setChatData(response); diff --git a/components/dashboard/ChatTable.tsx b/components/dashboard/ChatTable.tsx index ec2327b..0d7b16e 100644 --- a/components/dashboard/ChatTable.tsx +++ b/components/dashboard/ChatTable.tsx @@ -176,24 +176,39 @@ export default function ChatTable() { // Get the vendor ID from the auth token const { vendorId } = getVendorIdFromToken(); - // Now fetch chats for this vendor using clientFetch with pagination - const response = await clientFetch(`/chats/vendor/${vendorId}?page=${page}&limit=${limit}`); + // Use the optimized batch endpoint that fetches chats and unread counts together + const batchResponse = await clientFetch(`/chats/vendor/${vendorId}/batch?page=${page}&limit=${limit}`); - // Check if the response is the old format (array) or new paginated format - if (Array.isArray(response)) { - // Handle old API response format (backward compatibility) - setChats(response); + // Handle batch response (contains both chats and unread counts) + if (Array.isArray(batchResponse)) { + // Fallback to old API response format (backward compatibility) + setChats(batchResponse); setTotalPages(1); - setTotalChats(response.length); + setTotalChats(batchResponse.length); + // Try to fetch unread counts separately if using old endpoint + try { + const unreadResponse = await clientFetch(`/chats/vendor/${vendorId}/unread`); + setUnreadCounts(unreadResponse); + } catch (error) { + console.warn("Failed to fetch unread counts:", error); + } } else { - // Handle new paginated response format - setChats(response.chats || []); - setTotalPages(response.totalPages || 1); - setCurrentPage(response.page || 1); - setTotalChats(response.totalChats || 0); + // Handle new batch response format + setChats(batchResponse.chats || []); + setTotalPages(batchResponse.totalPages || 1); + setCurrentPage(batchResponse.page || 1); + setTotalChats(batchResponse.totalChats || 0); + + // Handle unread counts from batch response + const newUnreadCounts = batchResponse.unreadCounts || { totalUnread: 0, chatCounts: {} }; + + // Play sound if there are new messages + if (newUnreadCounts.totalUnread > unreadCounts.totalUnread) { + //playNotificationSound(); + } + + setUnreadCounts(newUnreadCounts); } - - await fetchUnreadCounts(); } catch (error) { console.error("Failed to fetch chats:", error); toast.error("Failed to load chat conversations"); diff --git a/env.example b/env.example new file mode 100644 index 0000000..f141545 --- /dev/null +++ b/env.example @@ -0,0 +1,23 @@ +# Frontend Environment Configuration +# Copy this file to .env.local for development or .env for production + +# =========================================== +# FRONTEND CONFIGURATION (Safe for browser) +# =========================================== +NEXT_PUBLIC_API_URL=/api + +# =========================================== +# BACKEND CONFIGURATION (Server-side only) +# =========================================== +# Replace with your actual backend domain +SERVER_API_URL=https://internal-api.inboxi.ng/api +API_BASE_URL=https://internal-api.inboxi.ng +API_HOSTNAME=internal-api.inboxi.ng + +# =========================================== +# DEVELOPMENT OVERRIDES +# =========================================== +# For local development, uncomment these: +# SERVER_API_URL=http://localhost:3001/api +# API_BASE_URL=http://localhost:3001 +# API_HOSTNAME=localhost diff --git a/lib/api-client.ts b/lib/api-client.ts index 7f37802..93a837f 100644 --- a/lib/api-client.ts +++ b/lib/api-client.ts @@ -249,17 +249,18 @@ export async function fetchClient( // 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 + // Handle API endpoint construction without exposing internal domains let url; - if (apiUrl.includes('internal-api.inboxi.ng')) { - // Special case for internal-api.inboxi.ng + // Check if this is a proxied API call (relative URLs starting with /api) + if (apiUrl === '/api' || apiUrl.startsWith('/api')) { + // For proxied requests, use relative URLs if (normalizedEndpoint.startsWith('/api/')) { - url = `${apiUrl}${normalizedEndpoint.substring(4)}`; // Remove the /api part + url = normalizedEndpoint; // Use as-is for proxied requests } else { - url = `${apiUrl}${normalizedEndpoint}`; + url = `/api${normalizedEndpoint}`; } } else { - // Normal case for other environments + // For direct API calls, construct full URL url = `${apiUrl}${normalizedEndpoint}`; } diff --git a/lib/server-api.ts b/lib/server-api.ts index 6732e49..1a36bb9 100644 --- a/lib/server-api.ts +++ b/lib/server-api.ts @@ -21,7 +21,7 @@ try { * @returns A complete URL to the backend API */ function getServerApiUrl(endpoint: string): string { - const apiUrl = process.env.SERVER_API_URL || 'https://internal-api.inboxi.ng/api'; + const apiUrl = process.env.SERVER_API_URL || 'http://localhost:3001/api'; const cleanEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint; return apiUrl.endsWith('/') diff --git a/next.config.mjs b/next.config.mjs index e1ad15f..a1cc7db 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -16,22 +16,23 @@ const baseConfig = { protocol: "https", hostname: "telegram.org", }, - { + // Backend API hostname configured via environment variable + ...(process.env.API_HOSTNAME ? [{ protocol: "https", - hostname: "internal-api.inboxi.ng", - }, + hostname: process.env.API_HOSTNAME, + }] : []), ], }, async rewrites() { return [ { source: '/api/:path*', - destination: 'https://internal-api.inboxi.ng/api/:path*', + destination: `${process.env.API_BASE_URL || 'http://localhost:3001'}/api/:path*`, }, ]; }, experimental: { - serverExternalPackages: [], + // serverExternalPackages has been deprecated in Next.js 15 }, onDemandEntries: { maxInactiveAge: 15 * 1000,