Spaces:
Running
Running
| // Supabase Auth helper (ES module). The frontend signs in with Google and | |
| // sends the access token to the FastAPI backend as a Bearer token. | |
| import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; | |
| export const API_BASE = | |
| window.location.protocol === "file:" | |
| ? "http://localhost:8000" | |
| : window.location.origin; | |
| let _client = null; | |
| let _config = null; | |
| async function getConfig() { | |
| if (_config) return _config; | |
| const resp = await fetch(`${API_BASE}/api/public-config`); | |
| _config = await resp.json(); | |
| return _config; | |
| } | |
| export async function getClient() { | |
| if (_client) return _client; | |
| const cfg = await getConfig(); | |
| if (!cfg.supabase_url || !cfg.supabase_anon_key) { | |
| throw new Error( | |
| "Supabase chưa được cấu hình (thiếu SUPABASE_ANON_KEY trong .env)." | |
| ); | |
| } | |
| _client = createClient(cfg.supabase_url, cfg.supabase_anon_key, { | |
| auth: { persistSession: true, autoRefreshToken: true, detectSessionInUrl: true }, | |
| }); | |
| return _client; | |
| } | |
| export async function signInWithGoogle(redirectTo) { | |
| const supabase = await getClient(); | |
| return supabase.auth.signInWithOAuth({ | |
| provider: "google", | |
| options: { redirectTo: redirectTo || window.location.href }, | |
| }); | |
| } | |
| export async function getSession() { | |
| const supabase = await getClient(); | |
| const { data } = await supabase.auth.getSession(); | |
| return data.session; | |
| } | |
| export async function getAccessToken() { | |
| const session = await getSession(); | |
| return session ? session.access_token : null; | |
| } | |
| export async function signOut() { | |
| const supabase = await getClient(); | |
| await supabase.auth.signOut(); | |
| } | |
| export async function onAuthStateChange(cb) { | |
| const supabase = await getClient(); | |
| return supabase.auth.onAuthStateChange(cb); | |
| } | |
| // fetch() wrapper that attaches the Bearer token to backend calls. | |
| export async function authFetch(path, opts = {}) { | |
| const token = await getAccessToken(); | |
| const headers = { ...(opts.headers || {}) }; | |
| if (token) headers["Authorization"] = `Bearer ${token}`; | |
| const url = path.startsWith("http") ? path : `${API_BASE}${path}`; | |
| return fetch(url, { ...opts, headers }); | |
| } | |
| // Convenience: who am I (or null if not signed in / token rejected). | |
| export async function getMe() { | |
| const token = await getAccessToken(); | |
| if (!token) return null; | |
| const resp = await authFetch("/api/me"); | |
| if (!resp.ok) return null; | |
| return resp.json(); | |
| } | |