Spaces:
Sleeping
Sleeping
import os | |
import base64 | |
import streamlit as st | |
from app.db import supabase | |
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: smaller PNP logo and centered title | |
# Path logo | |
logo_path = os.path.join("assets", "pnp-logo.png") | |
# Convert ke base64 biar bisa di-embed langsung | |
def get_base64_image(path): | |
with open(path, "rb") as f: | |
return base64.b64encode(f.read()).decode() | |
encoded_logo = get_base64_image(logo_path) | |
# Render dalam satu div center | |
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 | |
) | |
# --- Password recovery handler (Supabase redirect) --- | |
# 1) Move hash params to query params on first load, then reload once | |
if not st.session_state.get("_hash_migrated"): | |
st.markdown( | |
""" | |
<script> | |
(function(){ | |
const hash = window.location.hash.startsWith('#') ? window.location.hash.substring(1) : window.location.hash; | |
if (hash && !window.__hashMigrated) { | |
const h = new URLSearchParams(hash); | |
const qp = new URLSearchParams(window.location.search); | |
for (const [k,v] of h.entries()) { qp.set(k, v); } | |
const newUrl = window.location.pathname + '?' + qp.toString(); | |
window.history.replaceState(null, '', newUrl); | |
window.location.hash = ''; | |
window.__hashMigrated = true; | |
window.location.reload(); | |
} | |
})(); | |
</script> | |
""", | |
unsafe_allow_html=True, | |
) | |
st.session_state["_hash_migrated"] = True | |
# 2) Read query params for recovery flow | |
try: | |
qp = st.query_params # Streamlit >= 1.30 | |
get_q = lambda k: qp.get(k, None) | |
except Exception: | |
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, None)) | |
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 current session using tokens from redirect | |
supabase.auth.set_session(access_token, refresh_token) | |
# Update user password | |
supabase.auth.update_user({"password": npw}) | |
st.success("Password berhasil diubah. Silakan login kembali.") | |
# Optional: clear recovery query params | |
try: | |
# Best-effort to clear params | |
st.markdown( | |
""" | |
<script> | |
(function(){ | |
const qp = new URLSearchParams(window.location.search); | |
["type","access_token","refresh_token","expires_in","expires_at","token_type"].forEach(k=>qp.delete(k)); | |
const newUrl = window.location.pathname + (qp.toString()?('?'+qp.toString()):''); | |
window.history.replaceState(null, '', newUrl); | |
})(); | |
</script> | |
""", | |
unsafe_allow_html=True, | |
) | |
except Exception: | |
pass | |
except Exception as e: | |
st.error(f"Gagal mengubah password: {e}") | |
# When in recovery mode, do not render login/register tabs | |
return | |
# Auth tabs inside wrapper | |
tab_login, tab_register, tab_forgot = st.tabs(["Login", "Register", "Forgot Password"]) | |
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: | |
# Demo password fallback | |
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}") | |
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: | |
# Prefer explicit env, then generic site URL, then localhost for dev | |
redirect_url = os.getenv( | |
"SUPABASE_EMAIL_REDIRECT", | |
os.getenv("NEXT_PUBLIC_SITE_URL", "http://localhost:8501"), | |
) | |
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}") | |
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: | |
# Prefer explicit env, then generic site URL, then localhost for dev | |
redirect_url = os.getenv( | |
"SUPABASE_EMAIL_REDIRECT", | |
os.getenv("NEXT_PUBLIC_SITE_URL", "http://localhost:8501"), | |
) | |
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}") | |