Spaces:
Sleeping
Sleeping
| import os | |
| import base64 | |
| import streamlit as st | |
| from app.db import supabase | |
| from urllib.parse import parse_qsl | |
| def auth_view(): | |
| """Render Supabase authentication with Login, Register, and Forgot Password tabs.""" | |
| # Wrapper (centered) for all auth content | |
| left, center, right = st.columns([1, 2, 1]) | |
| with center: | |
| # Header: PNP logo + title | |
| logo_path = os.path.join("assets", "pnp-logo.png") | |
| def get_base64_image(path): | |
| with open(path, "rb") as f: | |
| return base64.b64encode(f.read()).decode() | |
| encoded_logo = get_base64_image(logo_path) | |
| st.markdown( | |
| f""" | |
| <div style="text-align:center;"> | |
| <img src="data:image/png;base64,{encoded_logo}" width="100"> | |
| <h1 style="margin-top:0.25rem;">PNP bot</h1> | |
| </div> | |
| """, | |
| unsafe_allow_html=True | |
| ) | |
| # --- FIX: Auto convert hash (#) to query (?) using streamlit-url-fragment --- | |
| try: | |
| from streamlit_url_fragment import get_fragment | |
| fragment = get_fragment() | |
| if fragment and not st.session_state.get("hash_migrated"): | |
| # Parse fragment parameters | |
| params = dict(parse_qsl(fragment)) | |
| if params.get("type") == "recovery" and params.get("access_token"): | |
| # Set query params so existing recovery flow works | |
| if hasattr(st, "query_params"): | |
| for key, value in params.items(): | |
| st.query_params[key] = value | |
| else: | |
| st.experimental_set_query_params(**params) | |
| st.session_state["hash_migrated"] = True | |
| st.rerun() | |
| except ImportError: | |
| # Fallback to JS if library not available | |
| st.markdown( | |
| """ | |
| <script> | |
| (function() { | |
| const hash = window.location.hash; | |
| if (hash && hash.length > 1 && !sessionStorage.getItem("hash_migrated")) { | |
| const query = hash.substring(1); | |
| const newUrl = window.location.pathname + "?" + query; | |
| sessionStorage.setItem("hash_migrated", "true"); | |
| window.history.replaceState(null, "", newUrl); | |
| window.location.reload(); | |
| } | |
| })(); | |
| </script> | |
| """, | |
| unsafe_allow_html=True | |
| ) | |
| # --- Recovery flow --- | |
| if hasattr(st, "query_params"): | |
| qp = st.query_params | |
| get_q = lambda k: qp.get(k) | |
| else: | |
| qp = st.experimental_get_query_params() | |
| get_q = lambda k: (qp.get(k, [None])[0] if isinstance(qp.get(k, None), list) else qp.get(k)) | |
| q_type = get_q("type") | |
| if q_type == "recovery": | |
| st.info("Reset password: silakan masukkan password baru Anda.") | |
| access_token = get_q("access_token") | |
| refresh_token = get_q("refresh_token") | |
| with st.form("reset_password_form"): | |
| npw = st.text_input("Password Baru", type="password") | |
| npw2 = st.text_input("Konfirmasi Password Baru", type="password") | |
| submit_reset = st.form_submit_button("Set Password Baru") | |
| if submit_reset: | |
| if not npw or len(npw) < 6: | |
| st.error("Password minimal 6 karakter.") | |
| elif npw != npw2: | |
| st.error("Konfirmasi password tidak sama.") | |
| elif not access_token or not refresh_token: | |
| st.error("Token pemulihan tidak ditemukan. Coba klik ulang tautan dari email.") | |
| else: | |
| try: | |
| supabase.auth.set_session(access_token, refresh_token) | |
| supabase.auth.update_user({"password": npw}) | |
| st.success("Password berhasil diubah. Anda akan diarahkan ke login...") | |
| # bersihkan query params setelah reset | |
| st.experimental_set_query_params() | |
| st.session_state.clear() | |
| st.rerun() | |
| except Exception as e: | |
| st.error(f"Gagal mengubah password: {e}") | |
| return | |
| # --- Auth tabs --- | |
| tab_login, tab_register, tab_forgot = st.tabs(["Login", "Register", "Forgot Password"]) | |
| # LOGIN | |
| with tab_login: | |
| with st.form("login_form"): | |
| email = st.text_input("Email") | |
| password = st.text_input("Password", type="password") | |
| submitted = st.form_submit_button("Login") | |
| if submitted: | |
| shared_pw = os.getenv("APP_DEMO_PASSWORD") | |
| if shared_pw and password == shared_pw: | |
| st.session_state["user"] = {"id": "demo-user", "email": email or "[email protected]"} | |
| st.success("Login demo berhasil") | |
| st.rerun() | |
| try: | |
| auth_res = supabase.auth.sign_in_with_password({ | |
| "email": email, | |
| "password": password, | |
| }) | |
| user = getattr(auth_res, "user", None) | |
| if user: | |
| st.session_state["user"] = {"id": user.id, "email": getattr(user, "email", email)} | |
| session_obj = getattr(auth_res, "session", None) | |
| if session_obj: | |
| st.session_state["sb_session"] = { | |
| "access_token": getattr(session_obj, "access_token", None), | |
| "refresh_token": getattr(session_obj, "refresh_token", None), | |
| } | |
| st.success("Login berhasil") | |
| st.rerun() | |
| else: | |
| st.error("Email atau password salah.") | |
| except Exception as e: | |
| st.error(f"Gagal login: {e}") | |
| # REGISTER | |
| with tab_register: | |
| st.caption("Buat akun baru. Anda akan menerima email konfirmasi.") | |
| with st.form("register_form"): | |
| r_email = st.text_input("Email", key="reg_email") | |
| r_password = st.text_input("Password", type="password", key="reg_password") | |
| r_password2 = st.text_input("Konfirmasi Password", type="password", key="reg_password2") | |
| submitted_r = st.form_submit_button("Register") | |
| if submitted_r: | |
| if r_password != r_password2: | |
| st.error("Password tidak sama.") | |
| else: | |
| try: | |
| redirect_url = os.getenv( | |
| "SUPABASE_EMAIL_REDIRECT", | |
| os.getenv("NEXT_PUBLIC_SITE_URL", "https://yozora721-pnp-chatbot-v1.hf.space"), | |
| ) | |
| supabase.auth.sign_up({ | |
| "email": r_email, | |
| "password": r_password, | |
| "options": {"email_redirect_to": redirect_url} | |
| }) | |
| st.success("Registrasi berhasil. Silakan cek email untuk konfirmasi.") | |
| except Exception as e: | |
| st.error(f"Gagal registrasi: {e}") | |
| # FORGOT PASSWORD | |
| with tab_forgot: | |
| st.caption("Kirim tautan reset password ke email Anda.") | |
| with st.form("forgot_form"): | |
| f_email = st.text_input("Email", key="forgot_email") | |
| submitted_f = st.form_submit_button("Kirim Link Reset") | |
| if submitted_f: | |
| try: | |
| redirect_url = os.getenv( | |
| "SUPABASE_EMAIL_REDIRECT", | |
| os.getenv("NEXT_PUBLIC_SITE_URL", "https://yozora721-pnp-chatbot-v1.hf.space"), | |
| ) | |
| supabase.auth.reset_password_for_email(f_email, {"redirect_to": redirect_url}) | |
| st.success("Email reset password telah dikirim. Periksa kotak masuk Anda.") | |
| except Exception as e: | |
| st.error(f"Gagal mengirim email reset password: {e}") | |