ugh
This commit is contained in:
@@ -11,9 +11,13 @@ export async function GET(req: NextRequest) {
|
|||||||
// If not in headers, check cookies
|
// If not in headers, check cookies
|
||||||
if (!token) {
|
if (!token) {
|
||||||
token = req.cookies.get('Authorization')?.value;
|
token = req.cookies.get('Authorization')?.value;
|
||||||
console.log('Auth check: Token from cookies');
|
console.log('Auth check: Token from cookies:', token ? `${token.substring(0, 10)}...` : 'none');
|
||||||
|
|
||||||
|
// Debug: List all cookies
|
||||||
|
const cookiesList = req.cookies.getAll();
|
||||||
|
console.log('Auth check: All cookies:', JSON.stringify(cookiesList.map(c => c.name)));
|
||||||
} else {
|
} else {
|
||||||
console.log('Auth check: Token from headers');
|
console.log('Auth check: Token from headers:', token.substring(0, 10) + '...');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
|
|||||||
59
app/api/auth/set-cookie/route.ts
Normal file
59
app/api/auth/set-cookie/route.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
// Force dynamic execution to ensure cookies are set at runtime
|
||||||
|
export const dynamic = 'force-dynamic';
|
||||||
|
|
||||||
|
export async function POST(req: NextRequest) {
|
||||||
|
try {
|
||||||
|
// Parse the request body to get the auth token
|
||||||
|
const body = await req.json();
|
||||||
|
const { token } = body;
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
console.error('Set-cookie API: No token provided');
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'No token provided' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a response object
|
||||||
|
const response = NextResponse.json(
|
||||||
|
{ success: true },
|
||||||
|
{ status: 200 }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set the token in an HTTP-only cookie that will be sent with requests
|
||||||
|
// The secure flag is conditionally set based on the environment
|
||||||
|
const isLocalhost = req.headers.get('host')?.includes('localhost') ||
|
||||||
|
req.headers.get('host')?.includes('127.0.0.1');
|
||||||
|
|
||||||
|
const cookieOptions = {
|
||||||
|
// HttpOnly for security - prevents JavaScript access
|
||||||
|
httpOnly: true,
|
||||||
|
// Valid for 7 days (same as the JWT)
|
||||||
|
maxAge: 7 * 24 * 60 * 60,
|
||||||
|
// Only send in requests to our domain
|
||||||
|
path: '/',
|
||||||
|
// Strict same-site policy to prevent CSRF
|
||||||
|
sameSite: 'strict' as const,
|
||||||
|
// Secure only in production environments
|
||||||
|
secure: !isLocalhost
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the cookie with the options
|
||||||
|
response.cookies.set('Authorization', token, cookieOptions);
|
||||||
|
|
||||||
|
console.log('Set-cookie API: Cookie set successfully');
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Set-cookie API error:', error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
error: 'Failed to set cookie',
|
||||||
|
details: error instanceof Error ? error.message : String(error)
|
||||||
|
},
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
|
||||||
traefik:
|
|
||||||
image: traefik:v2.5
|
|
||||||
container_name: traefik
|
|
||||||
command:
|
|
||||||
- "--api.insecure=true"
|
|
||||||
- "--log.level=DEBUG"
|
|
||||||
- "--accesslog"
|
|
||||||
- "--providers.docker=true"
|
|
||||||
- "--providers.docker.network=web"
|
|
||||||
- "--entrypoints.web.address=:80"
|
|
||||||
- "--entrypoints.websecure.address=:443"
|
|
||||||
- "--entrypoints.websecure.forwardedHeaders.trustedIPs=212.113.116.6,194.26.229.41,138.124.13.13,77.73.131.156" # Trust IPs for websecure
|
|
||||||
- "--entrypoints.web.forwardedHeaders.trustedIPs=212.113.116.6,194.26.229.41,138.124.13.13,77.73.131.156" # Trust IPs for web
|
|
||||||
ports:
|
|
||||||
- "80:80"
|
|
||||||
- "443:443"
|
|
||||||
- "8080:8080"
|
|
||||||
volumes:
|
|
||||||
- "/var/run/docker.sock:/var/run/docker.sock"
|
|
||||||
networks:
|
|
||||||
- web
|
|
||||||
|
|
||||||
nextjs-app:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
container_name: nextjs-app-container
|
|
||||||
expose:
|
|
||||||
- "3000"
|
|
||||||
labels:
|
|
||||||
- "traefik.enable=true"
|
|
||||||
- "traefik.http.routers.nextjs-app.rule=Host(`dash.embermarket.app`) || Host(`77.73.131.156`) || PathPrefix(`/`)"
|
|
||||||
- "traefik.http.services.nextjs-app.loadbalancer.server.port=3000"
|
|
||||||
- "traefik.http.routers.nextjs-app-https.rule=Host(`dash.embermarket.app`) || Host(`77.73.131.156`) || PathPrefix(`/`)"
|
|
||||||
- "traefik.http.middlewares.redirect-to-http.redirectscheme=http"
|
|
||||||
- "traefik.http.routers.nextjs-app-https.middlewares=redirect-to-http"
|
|
||||||
|
|
||||||
networks:
|
|
||||||
- web
|
|
||||||
environment:
|
|
||||||
- NODE_ENV=production
|
|
||||||
- NEXT_PUBLIC_API_URL=https://internal-api.inboxi.ng/api
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
networks:
|
|
||||||
web:
|
|
||||||
external: true
|
|
||||||
@@ -27,24 +27,16 @@ export async function fetchClient<T>(
|
|||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
const { method = 'GET', body, headers = {}, ...rest } = options;
|
const { method = 'GET', body, headers = {}, ...rest } = options;
|
||||||
|
|
||||||
// Get the base API URL from environment or fallback
|
// Always use the Next.js API proxy by creating a path starting with /api/
|
||||||
const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
|
// This ensures requests go through Next.js rewrites
|
||||||
|
|
||||||
// Ensure the endpoint starts with a slash
|
|
||||||
const normalizedEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
|
const normalizedEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
|
||||||
|
|
||||||
// For the specific case of internal-api.inboxi.ng - remove duplicate /api
|
// Construct the URL to always use the Next.js API routes
|
||||||
let url;
|
let url;
|
||||||
if (apiUrl.includes('internal-api.inboxi.ng')) {
|
|
||||||
// Special case for internal-api.inboxi.ng
|
|
||||||
if (normalizedEndpoint.startsWith('/api/')) {
|
if (normalizedEndpoint.startsWith('/api/')) {
|
||||||
url = `${apiUrl}${normalizedEndpoint.substring(4)}`; // Remove the /api part
|
url = normalizedEndpoint; // Already has /api/ prefix
|
||||||
} else {
|
} else {
|
||||||
url = `${apiUrl}${normalizedEndpoint}`;
|
url = `/api${normalizedEndpoint}`; // Add /api/ prefix
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Normal case for other environments
|
|
||||||
url = `${apiUrl}${normalizedEndpoint}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get auth token from cookies
|
// Get auth token from cookies
|
||||||
|
|||||||
@@ -14,14 +14,16 @@ export async function clientFetch(url: string, options: RequestInit = {}): Promi
|
|||||||
...options.headers,
|
...options.headers,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Ensure the url doesn't start with a slash if it's going to be appended to a URL that ends with one
|
// Always use the Next.js API proxy for consistent routing
|
||||||
const cleanUrl = url.startsWith('/') ? url.substring(1) : url;
|
// Format the URL to ensure it has the /api prefix
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_API_URL || '/api';
|
let fullUrl;
|
||||||
|
if (url.startsWith('/api/')) {
|
||||||
// Ensure there's only one slash between the base URL and endpoint
|
fullUrl = url; // Already has /api/ prefix
|
||||||
const fullUrl = baseUrl.endsWith('/')
|
} else {
|
||||||
? `${baseUrl}${cleanUrl}`
|
// Add /api prefix if not already present
|
||||||
: `${baseUrl}/${cleanUrl}`;
|
const cleanUrl = url.startsWith('/') ? url : `/${url}`;
|
||||||
|
fullUrl = `/api${cleanUrl}`;
|
||||||
|
}
|
||||||
|
|
||||||
const res = await fetch(fullUrl, {
|
const res = await fetch(fullUrl, {
|
||||||
...options,
|
...options,
|
||||||
|
|||||||
@@ -91,7 +91,8 @@ export const updateProductStock = async (
|
|||||||
authToken: string
|
authToken: string
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const url = `${process.env.NEXT_PUBLIC_API_URL}/stock/${productId}`;
|
// Use Next.js API proxy to ensure request goes through rewrites
|
||||||
|
const url = `/api/stock/${productId}`;
|
||||||
return await fetchData(url, {
|
return await fetchData(url, {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -29,10 +29,18 @@ export const apiRequest = async <T = any>(endpoint: string, method: string = "GE
|
|||||||
options.body = JSON.stringify(body);
|
options.body = JSON.stringify(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
const API_URL = process.env.NEXT_PUBLIC_API_URL;
|
// Always use the Next.js API proxy to ensure all requests go through rewrites
|
||||||
if (!API_URL) throw new Error("NEXT_PUBLIC_API_URL is not set in environment variables");
|
// Format the endpoint to ensure it has the /api prefix
|
||||||
|
let url;
|
||||||
|
if (endpoint.startsWith('/api/')) {
|
||||||
|
url = endpoint; // Already has /api/ prefix
|
||||||
|
} else {
|
||||||
|
// Add /api prefix and ensure no duplicate slashes
|
||||||
|
const cleanEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
|
||||||
|
url = `/api${cleanEndpoint}`;
|
||||||
|
}
|
||||||
|
|
||||||
const res = await fetchData(`${API_URL}${endpoint}`, options);
|
const res = await fetchData(url, options);
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
const errorResponse = await res.json().catch(() => null);
|
const errorResponse = await res.json().catch(() => null);
|
||||||
|
|||||||
@@ -2,14 +2,28 @@ import { NextResponse } from "next/server";
|
|||||||
import type { NextRequest } from "next/server";
|
import type { NextRequest } from "next/server";
|
||||||
|
|
||||||
export async function middleware(req: NextRequest) {
|
export async function middleware(req: NextRequest) {
|
||||||
|
// Check for auth token in cookies
|
||||||
const token = req.cookies.get("Authorization")?.value;
|
const token = req.cookies.get("Authorization")?.value;
|
||||||
|
|
||||||
|
// Debug info about all cookies
|
||||||
|
const allCookies = req.cookies.getAll();
|
||||||
|
console.log("Middleware: All cookies:", allCookies.map(c => c.name).join(', '));
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
console.log("Middleware: No token found, redirecting to login...");
|
// Try to get from Authorization header as fallback
|
||||||
|
const authHeader = req.headers.get('Authorization');
|
||||||
|
|
||||||
|
if (authHeader?.startsWith('Bearer ')) {
|
||||||
|
console.log("Middleware: Token found in Authorization header");
|
||||||
|
// Continue with validation using header auth
|
||||||
|
// The authCheckUrl will handle extracting the token from header
|
||||||
|
} else {
|
||||||
|
console.log("Middleware: No token found in cookies or headers, redirecting to login...");
|
||||||
return NextResponse.redirect(new URL("/auth/login", req.url));
|
return NextResponse.redirect(new URL("/auth/login", req.url));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
console.log("Middleware: Token found, validating...");
|
console.log("Middleware: Token found in cookies, validating...");
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get the origin but handle localhost differently to avoid SSL issues
|
// Get the origin but handle localhost differently to avoid SSL issues
|
||||||
@@ -24,13 +38,17 @@ export async function middleware(req: NextRequest) {
|
|||||||
|
|
||||||
console.log(`Using internal auth check URL: ${authCheckUrl}`);
|
console.log(`Using internal auth check URL: ${authCheckUrl}`);
|
||||||
|
|
||||||
|
// Clone headers to avoid modifying the original request
|
||||||
|
const headers = new Headers(req.headers);
|
||||||
|
|
||||||
|
// If token is in cookie, ensure it's also in Authorization header
|
||||||
|
if (token && !headers.has('Authorization')) {
|
||||||
|
headers.set('Authorization', `Bearer ${token}`);
|
||||||
|
}
|
||||||
|
|
||||||
const res = await fetch(authCheckUrl, {
|
const res = await fetch(authCheckUrl, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers,
|
||||||
"Content-Type": "application/json",
|
|
||||||
// Explicitly pass the token in headers as well
|
|
||||||
"Authorization": `Bearer ${token}`
|
|
||||||
},
|
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user