github-actions[bot] commited on
Commit
dde0c6d
·
1 Parent(s): 627c009

Sync from GitHub 907b7448edad59db074f2417e42629ba5c3f1cc7

Browse files
Files changed (2) hide show
  1. README.md +1 -2
  2. frontend/app.py +96 -20
README.md CHANGED
@@ -273,8 +273,7 @@ This repo now includes:
273
  4. For HF OAuth mode, also set:
274
  - `HF_OAUTH_CLIENT_ID`
275
  - `HF_OAUTH_CLIENT_SECRET`
276
- - `HF_OAUTH_REDIRECT_URI` (backend callback URL, e.g. `https://<space>.hf.space/proxy/8000/auth/callback`)
277
  - `AUTH_SUCCESS_REDIRECT_URL` (your Space URL)
278
- - `BACKEND_PUBLIC_URL=https://<space>.hf.space/proxy/8000` (used for browser OAuth links)
279
 
280
  The container exposes Streamlit on port `7860` and points the frontend to the internal backend via `BACKEND_URL=http://127.0.0.1:8000`.
 
273
  4. For HF OAuth mode, also set:
274
  - `HF_OAUTH_CLIENT_ID`
275
  - `HF_OAUTH_CLIENT_SECRET`
276
+ - `HF_OAUTH_REDIRECT_URI` (Space URL registered in your HF Connected App, e.g. `https://<space>.hf.space/`)
277
  - `AUTH_SUCCESS_REDIRECT_URL` (your Space URL)
 
278
 
279
  The container exposes Streamlit on port `7860` and points the frontend to the internal backend via `BACKEND_URL=http://127.0.0.1:8000`.
frontend/app.py CHANGED
@@ -9,7 +9,6 @@ import streamlit as st
9
  st.set_page_config(page_title="NotebookLM Clone", page_icon="📚", layout="wide")
10
 
11
  BACKEND_URL = os.getenv("BACKEND_URL", "http://127.0.0.1:8000")
12
- BACKEND_PUBLIC_URL = os.getenv("BACKEND_PUBLIC_URL", "").strip()
13
  REQUEST_TIMEOUT_SECONDS = 30
14
 
15
  STATUS_LABELS = {
@@ -115,21 +114,58 @@ def api_post_multipart(
115
  return api_request("POST", path, data=data, files=files)
116
 
117
 
118
- def _join_url(base_url: str, path: str) -> str:
119
- base = base_url.rstrip("/")
120
- normalized_path = path if path.startswith("/") else f"/{path}"
121
- return f"{base}{normalized_path}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
 
124
- def build_oauth_login_url(auth_payload: dict[str, Any]) -> str:
125
- login_url = str(auth_payload.get("login_url") or "/auth/login").strip()
126
- if login_url.startswith(("http://", "https://")):
127
- return login_url
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
- browser_backend_root = (BACKEND_PUBLIC_URL or st.session_state.get("backend_url", BACKEND_URL)).strip()
130
- if not browser_backend_root:
131
- browser_backend_root = BACKEND_URL
132
- return _join_url(browser_backend_root, login_url)
 
 
133
 
134
 
135
  def fetch_notebooks() -> tuple[bool, list[dict] | str]:
@@ -718,6 +754,10 @@ if "bridge_error" not in st.session_state:
718
  st.session_state["bridge_error"] = None
719
  if "processed_bridge_token" not in st.session_state:
720
  st.session_state["processed_bridge_token"] = None
 
 
 
 
721
 
722
  bridge_token = get_query_param("auth_bridge")
723
  if bridge_token and bridge_token != st.session_state["processed_bridge_token"]:
@@ -730,6 +770,34 @@ if bridge_token and bridge_token != st.session_state["processed_bridge_token"]:
730
  st.session_state["bridge_error"] = str(bridge_result)
731
  st.rerun()
732
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
733
  with st.sidebar:
734
  st.markdown("### Workspace")
735
  st.caption("Connected client settings")
@@ -763,6 +831,7 @@ with st.sidebar:
763
  st.session_state.pop("selected_notebook_id", None)
764
  st.session_state.pop("selected_notebook_title", None)
765
  st.session_state.pop("selected_thread_id", None)
 
766
  st.rerun()
767
  elif auth_mode == "dev":
768
  with st.form("dev_login_form"):
@@ -781,14 +850,21 @@ with st.sidebar:
781
  st.rerun()
782
  st.error(f"Login failed: {login_result}")
783
  elif auth_mode == "hf_oauth":
784
- login_url = build_oauth_login_url(auth_data)
785
- st.markdown(f"[Sign in with Hugging Face]({login_url})")
 
 
 
 
 
 
 
 
 
 
 
 
786
  st.caption("After provider login, you will be redirected back and signed in automatically.")
787
- if not BACKEND_PUBLIC_URL and ("127.0.0.1" in login_url or "localhost" in login_url):
788
- st.info(
789
- "If this app is hosted remotely, set BACKEND_PUBLIC_URL "
790
- "(example: https://<space>.hf.space/proxy/8000)."
791
- )
792
  if st.button("Refresh auth"):
793
  st.rerun()
794
  else:
 
9
  st.set_page_config(page_title="NotebookLM Clone", page_icon="📚", layout="wide")
10
 
11
  BACKEND_URL = os.getenv("BACKEND_URL", "http://127.0.0.1:8000")
 
12
  REQUEST_TIMEOUT_SECONDS = 30
13
 
14
  STATUS_LABELS = {
 
114
  return api_request("POST", path, data=data, files=files)
115
 
116
 
117
+ def request_backend_get_raw(
118
+ path: str,
119
+ *,
120
+ params: dict | None = None,
121
+ allow_redirects: bool = True,
122
+ ) -> tuple[bool, requests.Response | str]:
123
+ base_url = st.session_state.get("backend_url", BACKEND_URL).rstrip("/")
124
+ url = f"{base_url}{path}"
125
+ try:
126
+ response = get_http_session().request(
127
+ method="GET",
128
+ url=url,
129
+ params=params,
130
+ timeout=REQUEST_TIMEOUT_SECONDS,
131
+ allow_redirects=allow_redirects,
132
+ )
133
+ return True, response
134
+ except requests.RequestException as exc:
135
+ return False, str(exc)
136
 
137
 
138
+ def begin_hf_oauth() -> tuple[bool, str]:
139
+ ok, response_or_error = request_backend_get_raw("/auth/login", allow_redirects=False)
140
+ if not ok:
141
+ return False, str(response_or_error)
142
+
143
+ response = response_or_error
144
+ if not isinstance(response, requests.Response):
145
+ return False, "Unexpected OAuth start response."
146
+ if response.status_code not in {301, 302, 303, 307, 308}:
147
+ return False, f"OAuth start failed with HTTP {response.status_code}: {response.text}"
148
+ location = response.headers.get("location", "").strip()
149
+ if not location:
150
+ return False, "OAuth start failed: missing redirect location."
151
+ return True, location
152
+
153
+
154
+ def complete_hf_oauth(code: str, state: str) -> tuple[bool, str | None]:
155
+ ok, response_or_error = request_backend_get_raw(
156
+ "/auth/callback",
157
+ params={"code": code, "state": state},
158
+ allow_redirects=False,
159
+ )
160
+ if not ok:
161
+ return False, str(response_or_error)
162
 
163
+ response = response_or_error
164
+ if not isinstance(response, requests.Response):
165
+ return False, "Unexpected OAuth callback response."
166
+ if response.status_code in {301, 302, 303, 307, 308}:
167
+ return True, None
168
+ return False, f"OAuth callback failed with HTTP {response.status_code}: {response.text}"
169
 
170
 
171
  def fetch_notebooks() -> tuple[bool, list[dict] | str]:
 
754
  st.session_state["bridge_error"] = None
755
  if "processed_bridge_token" not in st.session_state:
756
  st.session_state["processed_bridge_token"] = None
757
+ if "processed_oauth_callback" not in st.session_state:
758
+ st.session_state["processed_oauth_callback"] = None
759
+ if "oauth_login_url" not in st.session_state:
760
+ st.session_state["oauth_login_url"] = None
761
 
762
  bridge_token = get_query_param("auth_bridge")
763
  if bridge_token and bridge_token != st.session_state["processed_bridge_token"]:
 
770
  st.session_state["bridge_error"] = str(bridge_result)
771
  st.rerun()
772
 
773
+ oauth_error = get_query_param("error")
774
+ oauth_error_description = get_query_param("error_description")
775
+ oauth_code = get_query_param("code")
776
+ oauth_state = get_query_param("state")
777
+ if oauth_error:
778
+ st.session_state["bridge_error"] = (
779
+ f"OAuth provider error: {oauth_error} ({oauth_error_description})"
780
+ if oauth_error_description
781
+ else f"OAuth provider error: {oauth_error}"
782
+ )
783
+ for key in ("error", "error_description", "code", "state"):
784
+ remove_query_param(key)
785
+ st.rerun()
786
+
787
+ if oauth_code and oauth_state:
788
+ callback_key = f"{oauth_code}:{oauth_state}"
789
+ if callback_key != st.session_state["processed_oauth_callback"]:
790
+ st.session_state["processed_oauth_callback"] = callback_key
791
+ callback_ok, callback_error = complete_hf_oauth(code=oauth_code, state=oauth_state)
792
+ for key in ("code", "state", "auth_bridge"):
793
+ remove_query_param(key)
794
+ if callback_ok:
795
+ st.session_state["bridge_error"] = None
796
+ st.session_state["oauth_login_url"] = None
797
+ st.rerun()
798
+ st.session_state["bridge_error"] = str(callback_error)
799
+ st.rerun()
800
+
801
  with st.sidebar:
802
  st.markdown("### Workspace")
803
  st.caption("Connected client settings")
 
831
  st.session_state.pop("selected_notebook_id", None)
832
  st.session_state.pop("selected_notebook_title", None)
833
  st.session_state.pop("selected_thread_id", None)
834
+ st.session_state["oauth_login_url"] = None
835
  st.rerun()
836
  elif auth_mode == "dev":
837
  with st.form("dev_login_form"):
 
850
  st.rerun()
851
  st.error(f"Login failed: {login_result}")
852
  elif auth_mode == "hf_oauth":
853
+ if not st.session_state.get("oauth_login_url"):
854
+ oauth_ok, oauth_result = begin_hf_oauth()
855
+ if oauth_ok:
856
+ st.session_state["oauth_login_url"] = oauth_result
857
+ else:
858
+ st.session_state["bridge_error"] = str(oauth_result)
859
+ login_url = st.session_state.get("oauth_login_url")
860
+ if login_url:
861
+ st.markdown(f"[Sign in with Hugging Face]({login_url})")
862
+ else:
863
+ st.warning("Unable to start OAuth. Check backend auth settings and retry.")
864
+ if st.button("Refresh login link"):
865
+ st.session_state["oauth_login_url"] = None
866
+ st.rerun()
867
  st.caption("After provider login, you will be redirected back and signed in automatically.")
 
 
 
 
 
868
  if st.button("Refresh auth"):
869
  st.rerun()
870
  else: