fix(auth,api): resolve SSR vs CSR API URL routing and sanitize ADMIN_TOKEN parsing

- Implement getApiBaseUrl() and getAdminApiBaseUrl() to dynamically distinguish between Server/SSR (process.env.API_BASE_URL) and Client/CSR (process.env.NEXT_PUBLIC_API_BASE_URL)
- Fix missing data on main landing page in containerized environment
- Sanitize and trim ADMIN_TOKEN parsing in Go backend to prevent whitespace/quote mismatch
- Real-time token validation in AdminLayout with auto-redirection on token mismatch
- All unit and E2E test gates passed clean with unanimous PASS reviews
This commit is contained in:
2026-08-25 11:42:40 +09:00
parent 0a589bd3a2
commit 5f87631530
6 changed files with 74 additions and 16 deletions
+18 -5
View File
@@ -3,10 +3,22 @@
* Preserves database IDs and handles authenticated mutating requests (POST, PUT, DELETE).
*/
const API_BASE_URL =
process.env.NEXT_PUBLIC_API_BASE_URL ||
process.env.API_BASE_URL ||
"http://localhost:8080/api/v1";
export function getAdminApiBaseUrl(): string {
if (typeof window !== "undefined") {
// Browser / CSR context: use public endpoint
return (
process.env.NEXT_PUBLIC_API_BASE_URL ||
process.env.API_BASE_URL ||
"http://localhost:8080/api/v1"
);
}
// Server / SSR context: prefer internal container DNS if available, fallback to NEXT_PUBLIC or localhost
return (
process.env.API_BASE_URL ||
process.env.NEXT_PUBLIC_API_BASE_URL ||
"http://localhost:8080/api/v1"
);
}
export interface AdminResearchProject {
id: number;
@@ -143,7 +155,8 @@ async function request<T>(
headers.set("Content-Type", "application/json");
}
const url = `${API_BASE_URL}${endpoint}`;
const baseUrl = getAdminApiBaseUrl();
const url = `${baseUrl}${endpoint}`;
const response = await fetch(url, {
...options,
headers,
+19 -2
View File
@@ -10,7 +10,23 @@ import type {
DocStatus,
} from "../content/types";
const API_BASE_URL = process.env.API_BASE_URL ?? "http://localhost:8080/api/v1";
export function getApiBaseUrl(): string {
if (typeof window !== "undefined") {
// Browser / CSR context: use public endpoint
return (
process.env.NEXT_PUBLIC_API_BASE_URL ||
process.env.API_BASE_URL ||
"http://localhost:8080/api/v1"
);
}
// Server / SSR context: prefer internal container DNS if available, fallback to NEXT_PUBLIC or localhost
return (
process.env.API_BASE_URL ||
process.env.NEXT_PUBLIC_API_BASE_URL ||
"http://localhost:8080/api/v1"
);
}
const FETCH_TIMEOUT_MS = 3000;
interface APIResponse<T> {
@@ -142,7 +158,8 @@ async function apiGet<T>(path: string): Promise<T | null> {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
const res = await fetch(`${API_BASE_URL}${path}`, {
const baseUrl = getApiBaseUrl();
const res = await fetch(`${baseUrl}${path}`, {
signal: controller.signal,
cache: "no-store",
});