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 (
<DropdownMenu>
<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 ? (
<img
src={user.avatar_url}
@ -66,7 +70,7 @@ export function LoginButton() {
<span className="hidden sm:inline max-w-[100px] truncate">
{user.name || user.email}
</span>
<Cloud className="w-3 h-3 text-green-500" />
<Cloud className="w-3 h-3 text-green-300" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56">
@ -90,10 +94,15 @@ export function LoginButton() {
}
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 />
<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>
);
}

View File

@ -31,12 +31,6 @@ const AuthContext = createContext<AuthContextType | undefined>(undefined);
// Token storage key
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
*/
@ -44,6 +38,19 @@ export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [token, setToken] = useState<string | null>(null);
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
const parseToken = useCallback((token: string): User | null => {
@ -102,7 +109,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
// Login - redirect to Google OAuth
const login = useCallback(() => {
if (!GOOGLE_CLIENT_ID) {
if (!googleClientId) {
console.error("Google Client ID not configured");
alert("Google 登入尚未設定。請聯繫管理員。");
return;
@ -113,7 +120,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
const scope = "openid email profile";
const params = new URLSearchParams({
client_id: GOOGLE_CLIENT_ID,
client_id: googleClientId,
redirect_uri: redirectUri,
response_type: "code",
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()}`;
}, []);
}, [googleClientId]);
// Logout
const logout = useCallback(() => {