Compare commits

...

1 Commits

Author SHA1 Message Date
Willem Jiang 4dc328e460 fix(auth): use getBackendBaseURL() in auth-related fetch calls
Auth pages (login, setup) and components (AuthProvider, account-settings,
  workspace layout) used hardcoded relative paths like /api/v1/auth/...
  instead of the configurable getBackendBaseURL() used by the rest of the
  codebase. This prevented them from reaching the backend when
  NEXT_PUBLIC_BACKEND_BASE_URL was set to a different origin.

  Closes #2859
2026-05-13 15:46:29 +08:00
5 changed files with 42 additions and 30 deletions
+4 -3
View File
@@ -10,6 +10,7 @@ import { FlickeringGrid } from "@/components/ui/flickering-grid";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { useAuth } from "@/core/auth/AuthProvider"; import { useAuth } from "@/core/auth/AuthProvider";
import { parseAuthError } from "@/core/auth/types"; import { parseAuthError } from "@/core/auth/types";
import { getBackendBaseURL } from "@/core/config";
/** /**
* Validate next parameter * Validate next parameter
@@ -71,7 +72,7 @@ export default function LoginPage() {
useEffect(() => { useEffect(() => {
let cancelled = false; let cancelled = false;
void fetch("/api/v1/auth/setup-status") void fetch(`${getBackendBaseURL()}/api/v1/auth/setup-status`)
.then((r) => r.json()) .then((r) => r.json())
.then((data: { needs_setup?: boolean }) => { .then((data: { needs_setup?: boolean }) => {
if (!cancelled && data.needs_setup) { if (!cancelled && data.needs_setup) {
@@ -94,8 +95,8 @@ export default function LoginPage() {
try { try {
const endpoint = isLogin const endpoint = isLogin
? "/api/v1/auth/login/local" ? `${getBackendBaseURL()}/api/v1/auth/login/local`
: "/api/v1/auth/register"; : `${getBackendBaseURL()}/api/v1/auth/register`;
const body = isLogin const body = isLogin
? `username=${encodeURIComponent(email)}&password=${encodeURIComponent(password)}` ? `username=${encodeURIComponent(email)}&password=${encodeURIComponent(password)}`
: JSON.stringify({ email, password }); : JSON.stringify({ email, password });
+18 -14
View File
@@ -10,6 +10,7 @@ import { Input } from "@/components/ui/input";
import { getCsrfHeaders } from "@/core/api/fetcher"; import { getCsrfHeaders } from "@/core/api/fetcher";
import { useAuth } from "@/core/auth/AuthProvider"; import { useAuth } from "@/core/auth/AuthProvider";
import { parseAuthError } from "@/core/auth/types"; import { parseAuthError } from "@/core/auth/types";
import { getBackendBaseURL } from "@/core/config";
type SetupMode = "loading" | "init_admin" | "change_password"; type SetupMode = "loading" | "init_admin" | "change_password";
@@ -36,7 +37,7 @@ export default function SetupPage() {
setMode("change_password"); setMode("change_password");
} else if (!isAuthenticated) { } else if (!isAuthenticated) {
// Check if the system has no users yet // Check if the system has no users yet
void fetch("/api/v1/auth/setup-status") void fetch(`${getBackendBaseURL()}/api/v1/auth/setup-status`)
.then((r) => r.json()) .then((r) => r.json())
.then((data: { needs_setup?: boolean }) => { .then((data: { needs_setup?: boolean }) => {
if (cancelled) return; if (cancelled) return;
@@ -72,7 +73,7 @@ export default function SetupPage() {
setLoading(true); setLoading(true);
try { try {
const res = await fetch("/api/v1/auth/initialize", { const res = await fetch(`${getBackendBaseURL()}/api/v1/auth/initialize`, {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
credentials: "include", credentials: "include",
@@ -113,19 +114,22 @@ export default function SetupPage() {
setLoading(true); setLoading(true);
try { try {
const res = await fetch("/api/v1/auth/change-password", { const res = await fetch(
method: "POST", `${getBackendBaseURL()}/api/v1/auth/change-password`,
headers: { {
"Content-Type": "application/json", method: "POST",
...getCsrfHeaders(), headers: {
"Content-Type": "application/json",
...getCsrfHeaders(),
},
credentials: "include",
body: JSON.stringify({
current_password: currentPassword,
new_password: newPassword,
new_email: email || undefined,
}),
}, },
credentials: "include", );
body: JSON.stringify({
current_password: currentPassword,
new_password: newPassword,
new_email: email || undefined,
}),
});
if (!res.ok) { if (!res.ok) {
const data = await res.json(); const data = await res.json();
+2 -1
View File
@@ -4,6 +4,7 @@ import { redirect } from "next/navigation";
import { AuthProvider } from "@/core/auth/AuthProvider"; import { AuthProvider } from "@/core/auth/AuthProvider";
import { getServerSideUser } from "@/core/auth/server"; import { getServerSideUser } from "@/core/auth/server";
import { assertNever } from "@/core/auth/types"; import { assertNever } from "@/core/auth/types";
import { getBackendBaseURL } from "@/core/config";
import { WorkspaceContent } from "./workspace-content"; import { WorkspaceContent } from "./workspace-content";
@@ -44,7 +45,7 @@ export default async function WorkspaceLayout({
Retry Retry
</Link> </Link>
<Link <Link
href="/api/v1/auth/logout" href={`${getBackendBaseURL()}/api/v1/auth/logout`}
className="text-muted-foreground hover:bg-muted rounded-md border px-4 py-2 text-sm" className="text-muted-foreground hover:bg-muted rounded-md border px-4 py-2 text-sm"
> >
Logout &amp; Reset Logout &amp; Reset
@@ -8,6 +8,7 @@ import { Input } from "@/components/ui/input";
import { fetch, getCsrfHeaders } from "@/core/api/fetcher"; import { fetch, getCsrfHeaders } from "@/core/api/fetcher";
import { useAuth } from "@/core/auth/AuthProvider"; import { useAuth } from "@/core/auth/AuthProvider";
import { parseAuthError } from "@/core/auth/types"; import { parseAuthError } from "@/core/auth/types";
import { getBackendBaseURL } from "@/core/config";
import { useI18n } from "@/core/i18n/hooks"; import { useI18n } from "@/core/i18n/hooks";
import { SettingsSection } from "./settings-section"; import { SettingsSection } from "./settings-section";
@@ -38,17 +39,20 @@ export function AccountSettingsPage() {
setLoading(true); setLoading(true);
try { try {
const res = await fetch("/api/v1/auth/change-password", { const res = await fetch(
method: "POST", `${getBackendBaseURL()}/api/v1/auth/change-password`,
headers: { {
"Content-Type": "application/json", method: "POST",
...getCsrfHeaders(), headers: {
"Content-Type": "application/json",
...getCsrfHeaders(),
},
body: JSON.stringify({
current_password: currentPassword,
new_password: newPassword,
}),
}, },
body: JSON.stringify({ );
current_password: currentPassword,
new_password: newPassword,
}),
});
if (!res.ok) { if (!res.ok) {
const data = await res.json(); const data = await res.json();
+4 -2
View File
@@ -10,6 +10,8 @@ import React, {
type ReactNode, type ReactNode,
} from "react"; } from "react";
import { getBackendBaseURL } from "@/core/config";
import { type User, buildLoginUrl } from "./types"; import { type User, buildLoginUrl } from "./types";
// Re-export for consumers // Re-export for consumers
@@ -56,7 +58,7 @@ export function AuthProvider({ children, initialUser }: AuthProviderProps) {
const refreshUser = useCallback(async () => { const refreshUser = useCallback(async () => {
try { try {
setIsLoading(true); setIsLoading(true);
const res = await fetch("/api/v1/auth/me", { const res = await fetch(`${getBackendBaseURL()}/api/v1/auth/me`, {
credentials: "include", credentials: "include",
}); });
@@ -88,7 +90,7 @@ export function AuthProvider({ children, initialUser }: AuthProviderProps) {
setUser(null); setUser(null);
try { try {
await fetch("/api/v1/auth/logout", { await fetch(`${getBackendBaseURL()}/api/v1/auth/logout`, {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
}); });