fix(api): stop routing through a nonexistent /api/gateway rewrite layer
All checks were successful
build-and-release / build (push) Successful in 1m27s

The K8s ingress for this domain routes any /api/* path directly to the
Rust gateway service — there is no rewrite/proxy layer in production that
collapses /api/gateway/* down to /api/*. That rewrite only exists as a
dev-only vite proxy, so lib/api.ts's request()/apiFetch() (used by Save
Changes, Submit for Verification, document upload, wallet, jobs,
marketplace, portfolio, and more) were 404ing on every call in production
with "Route not found in gateway". ProfilePage.tsx and
VerificationStatusPage.tsx each had their own local apiFetch with the same
assumption, doubly broken (paths already included /api/, prefixed again).
All three now hit the already-fully-qualified /api/... path directly,
matching how the working signup/login calls have always done it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Ashwin Kumar Sivakumar 2026-07-15 01:17:44 +05:30
parent 3ec5e60d15
commit 0b38bc6ca5
3 changed files with 17 additions and 12 deletions

View file

@ -29,7 +29,10 @@ import {
type DocField, type DocField,
} from "~/lib/profile-fields-config"; } from "~/lib/profile-fields-config";
const API = "/api"; // The K8s ingress routes /api/* directly to the Rust gateway service in
// production — there is no /api/gateway rewrite layer, so paths below are
// hit as-is (each apiFetch() call already passes a fully-qualified /api/... path).
const API = "";
const PORTFOLIO_PREFIX: Record<string, string> = { const PORTFOLIO_PREFIX: Record<string, string> = {
PHOTOGRAPHER: "photographers", PHOTOGRAPHER: "photographers",
@ -800,7 +803,7 @@ export default function ProfilePage(props: Props) {
: ""; : "";
const fd = new FormData(); const fd = new FormData();
fd.append("file", file); fd.append("file", file);
const res = await fetch(`${API}/profile/photo`, { const res = await fetch(`${API}/api/profile/photo`, {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
headers: token ? { Authorization: `Bearer ${token}` } : {}, headers: token ? { Authorization: `Bearer ${token}` } : {},

View file

@ -9,19 +9,20 @@ import { ShieldCheck, FileText, Activity } from 'lucide-solid';
import { CARD, BTN_ORANGE, BTN_GHOST } from '~/components/DashboardShell'; import { CARD, BTN_ORANGE, BTN_GHOST } from '~/components/DashboardShell';
import { getDocFields } from '~/lib/profile-fields-config'; import { getDocFields } from '~/lib/profile-fields-config';
const API = '/api/gateway';
const NAVY = '#0D0D2A'; const NAVY = '#0D0D2A';
const ORANGE = '#FF5E13'; const ORANGE = '#FF5E13';
type TabKey = 'approval_status' | 'documents' | 'activity'; type TabKey = 'approval_status' | 'documents' | 'activity';
// The K8s ingress routes /api/* directly to the Rust gateway service in
// production — there is no /api/gateway rewrite layer, so paths (already
// fully-qualified /api/... from callers) are hit as-is.
async function apiFetch(path: string, opts?: RequestInit) { async function apiFetch(path: string, opts?: RequestInit) {
const token = const token =
typeof window !== "undefined" typeof window !== "undefined"
? window.sessionStorage.getItem("nxtgauge_access_token") || "" ? window.sessionStorage.getItem("nxtgauge_access_token") || ""
: ""; : "";
const cleanPath = path.startsWith("/api/") ? path.slice(4) : path; return fetch(path, {
return fetch(`${API}${cleanPath}`, {
...opts, ...opts,
credentials: "include", credentials: "include",
headers: { headers: {

View file

@ -1,4 +1,7 @@
const API = '/api/gateway'; // The K8s ingress routes any /api/* path directly to the Rust gateway service —
// there is no /api/gateway indirection or rewrite in production (a dev-only
// vite proxy used to do that rewrite, but it doesn't exist in the deployed
// build). Callers already pass fully-qualified /api/... paths; hit them as-is.
function getAuthHeaders(): Record<string, string> { function getAuthHeaders(): Record<string, string> {
const token = typeof window !== 'undefined' const token = typeof window !== 'undefined'
@ -12,8 +15,7 @@ function getAuthHeaders(): Record<string, string> {
} }
export async function apiFetch(path: string, options?: RequestInit): Promise<any> { export async function apiFetch(path: string, options?: RequestInit): Promise<any> {
const cleanPath = path.startsWith('/api/') ? path.slice(4) : path; const res = await fetch(path, {
const res = await fetch(`${API}${cleanPath}`, {
headers: getAuthHeaders(), headers: getAuthHeaders(),
credentials: 'include', credentials: 'include',
...options, ...options,
@ -47,8 +49,7 @@ export async function request<T = any>(
if (options?.body !== undefined) { if (options?.body !== undefined) {
init.body = JSON.stringify(options.body); init.body = JSON.stringify(options.body);
} }
const cleanPath = path.startsWith('/api/') ? path.slice(4) : path; const res = await fetch(path, init);
const res = await fetch(`${API}${cleanPath}`, init);
const raw = await res.text(); const raw = await res.text();
const data = raw ? JSON.parse(raw) : null; const data = raw ? JSON.parse(raw) : null;
if (!res.ok) { if (!res.ok) {
@ -223,7 +224,7 @@ export async function uploadDocument(rolePrefix: string, file: File, documentTyp
? (sessionStorage.getItem('nxtgauge_access_token') || '') ? (sessionStorage.getItem('nxtgauge_access_token') || '')
: ''; : '';
const res = await fetch(`${API}/api/${rolePrefix}/profile/documents`, { const res = await fetch(`/api/${rolePrefix}/profile/documents`, {
method: 'POST', method: 'POST',
headers: { headers: {
...(token ? { Authorization: `Bearer ${token}` } : {}), ...(token ? { Authorization: `Bearer ${token}` } : {}),
@ -257,7 +258,7 @@ export async function submitCompanyProfileWithDocuments(
// Append a stable filename so the backend can derive document_type from the stem // Append a stable filename so the backend can derive document_type from the stem
fd.append('documents', file, `${documentType}_${file.name}`); fd.append('documents', file, `${documentType}_${file.name}`);
} }
const res = await fetch(`${API}/api/companies/profile/submit-with-documents`, { const res = await fetch(`/api/companies/profile/submit-with-documents`, {
method: 'POST', method: 'POST',
headers: token ? { Authorization: `Bearer ${token}` } : {}, headers: token ? { Authorization: `Bearer ${token}` } : {},
credentials: 'include', credentials: 'include',