This commit is contained in:
MarkLo 2025-12-13 06:13:15 +08:00
parent 5d3751602e
commit bcfa6dab06
3 changed files with 40 additions and 13 deletions

View File

@ -0,0 +1,11 @@
/**
* API route to expose public configuration
* This allows environment variables to be loaded at runtime instead of build time
*/
import { NextResponse } from "next/server";
export async function GET() {
return NextResponse.json({
googleClientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || "",
});
}

View File

@ -53,7 +53,11 @@ export function LoginButton() {
return ( return (
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" className="gap-2"> <Button
variant="ghost"
size="sm"
className="gap-2 bg-white/20 hover:bg-white/30 text-white border-white/30 border"
>
{user.avatar_url ? ( {user.avatar_url ? (
<img <img
src={user.avatar_url} src={user.avatar_url}
@ -66,7 +70,7 @@ export function LoginButton() {
<span className="hidden sm:inline max-w-[100px] truncate"> <span className="hidden sm:inline max-w-[100px] truncate">
{user.name || user.email} {user.name || user.email}
</span> </span>
<Cloud className="w-3 h-3 text-green-500" /> <Cloud className="w-3 h-3 text-green-300" />
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56"> <DropdownMenuContent align="end" className="w-56">
@ -90,10 +94,15 @@ export function LoginButton() {
} }
return ( return (
<Button variant="outline" size="sm" onClick={login} className="gap-2"> <Button
variant="ghost"
size="sm"
onClick={login}
className="gap-2 bg-white/20 hover:bg-white/30 text-white border-white/30 border"
>
<GoogleIcon /> <GoogleIcon />
<span className="hidden sm:inline"></span> <span className="hidden sm:inline"></span>
<CloudOff className="w-3 h-3 text-gray-400 sm:hidden" /> <CloudOff className="w-3 h-3 text-white/60 sm:hidden" />
</Button> </Button>
); );
} }

View File

@ -31,12 +31,6 @@ const AuthContext = createContext<AuthContextType | undefined>(undefined);
// Token storage key // Token storage key
const TOKEN_KEY = "tradingagents_auth_token"; const TOKEN_KEY = "tradingagents_auth_token";
// Google OAuth client ID from environment
const GOOGLE_CLIENT_ID = process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || "";
// Backend URL
const BACKEND_URL = process.env.NEXT_PUBLIC_API_URL || "";
/** /**
* Auth Provider Component * Auth Provider Component
*/ */
@ -44,6 +38,19 @@ export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null); const [user, setUser] = useState<User | null>(null);
const [token, setToken] = useState<string | null>(null); const [token, setToken] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [googleClientId, setGoogleClientId] = useState<string>("");
// Fetch Google Client ID from API (runtime)
useEffect(() => {
fetch("/api/config")
.then(res => res.json())
.then(data => {
setGoogleClientId(data.googleClientId || "");
})
.catch(err => {
console.error("Failed to fetch config:", err);
});
}, []);
// Parse JWT token to get user info // Parse JWT token to get user info
const parseToken = useCallback((token: string): User | null => { const parseToken = useCallback((token: string): User | null => {
@ -102,7 +109,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
// Login - redirect to Google OAuth // Login - redirect to Google OAuth
const login = useCallback(() => { const login = useCallback(() => {
if (!GOOGLE_CLIENT_ID) { if (!googleClientId) {
console.error("Google Client ID not configured"); console.error("Google Client ID not configured");
alert("Google 登入尚未設定。請聯繫管理員。"); alert("Google 登入尚未設定。請聯繫管理員。");
return; return;
@ -113,7 +120,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
const scope = "openid email profile"; const scope = "openid email profile";
const params = new URLSearchParams({ const params = new URLSearchParams({
client_id: GOOGLE_CLIENT_ID, client_id: googleClientId,
redirect_uri: redirectUri, redirect_uri: redirectUri,
response_type: "code", response_type: "code",
scope: scope, scope: scope,
@ -122,7 +129,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
}); });
window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`; window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
}, []); }, [googleClientId]);
// Logout // Logout
const logout = useCallback(() => { const logout = useCallback(() => {