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 | |
) | |
# --- Auto convert hash (#) to query (?) using streamlit-url-fragment --- | |
fragment_detected = False | |
try: | |
from streamlit_url_fragment import get_fragment | |
fragment = get_fragment() | |
if fragment and not st.session_state.get("hash_migrated"): | |
# Remove leading # if present and parse fragment parameters | |
clean_fragment = fragment.lstrip('#') | |
params = dict(parse_qsl(clean_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 | |
fragment_detected = True | |
st.rerun() | |
except Exception: | |
pass | |
# Always run JS fallback for reliability | |
if not fragment_detected: | |
st.markdown( | |
""" | |
<script> | |
(function() { | |
try { | |
const hash = window.location.hash; | |
console.log('DEBUG: Hash found:', 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(); | |
} | |
} catch (e) { | |
console.error('Hash migration error:', e); | |
} | |
})(); | |
</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: | |
# Set session temporarily to update password | |
supabase.auth.set_session(access_token, refresh_token) | |
supabase.auth.update_user({"password": npw}) | |
# Sign out after password update | |
supabase.auth.sign_out() | |
st.success("Password berhasil diubah. Silakan login dengan password baru.") | |
# Clear session and query params | |
if hasattr(st, "query_params"): | |
st.query_params.clear() | |
else: | |
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 "demo@example.com"} | |
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}") | |