import json from abc import ABC, abstractmethod import streamlit as st import streamlit_authenticator as stauth import yaml # 策略介面 class DataHandlerStrategy(ABC): """資料處理策略介面""" @abstractmethod def read_data(self, path): """讀取資料""" pass @abstractmethod def write_data(self, path, data): """寫入資料""" pass # YAML 策略 class YamlHandler(DataHandlerStrategy): """YAML 資料處理策略""" def read_data(self, path): """讀取 YAML 資料""" with open(path, "r") as f: return yaml.safe_load(f) def write_data(self, path, data): """寫入 YAML 資料""" with open(path, "w") as f: yaml.safe_dump(data, f) # JSON 策略 class JsonHandler(DataHandlerStrategy): """JSON 資料處理策略""" def read_data(self, path): """讀取 JSON 資料""" with open(path, "r") as f: return json.load(f) def write_data(self, path, data): """寫入 JSON 資料""" with open(path, "w") as f: json.dump(data, f, ensure_ascii=False, indent=4) class AuthMixin: """驗證混合類別""" def __init__(self, config_path="./config.yaml", handler: DataHandlerStrategy = YamlHandler()): self.config_path = config_path self.handler = handler self.config = self.load_config(config_path) self.authenticator = self.create_authenticator() def load_config(self, config_path): """載入配置檔案""" try: return self.handler.read_data(config_path) except FileNotFoundError: st.error(f"The configuration file {config_path} was not found.") st.stop() def create_authenticator(self): """建立驗證器""" config = self.config return stauth.Authenticate( config["credentials"], config["cookie"]["name"], config["cookie"]["key"], config["cookie"]["expiry_days"], config["preauthorized"], ) def login(self): """登入""" fields = { "Form name": "Login", "Username": "Username", "Password": "Password", "Login": "Login", } name, authentication_status, username = self.authenticator.login(location="main", fields=fields) print(f"Name: {name}, Auth Status: {authentication_status}, Username: {username}") return name, authentication_status, username def logout(self): """登出""" self.authenticator.logout("Logout", "main", key="unique_key") def reset_password(self, username): """重設密碼""" fields = { "Form name": "Reset password", "Username": "Username", "Password": "New password", "Repeat password": "Repeat password", "Submit": "Submit", } try: if self.authenticator.reset_password(username, fields=fields): st.success("Password modified successfully") self.update_config() # 在成功修改密碼後更新配置文件 except Exception as e: st.error(e) def update_config(self): """更新配置檔案""" try: self.handler.write_data(self.config_path, self.config) except FileNotFoundError: st.error(f"The configuration file {self.config_path} was not found for writing.") def is_authenticated(self): """檢查是否已驗證""" _, authentication_status, _ = self.login() return authentication_status is True class MyApp(AuthMixin): """應用程式主類別""" def __init__(self, config_path=None, handler=None): if config_path and handler: AuthMixin.__init__(self, config_path, handler) self.run() def run(self): """執行應用程式""" if hasattr(self, "authenticator"): self.handle_authenticated() else: self.display_content() def handle_authenticated(self): """處理已驗證使用者""" name, authentication_status, username = self.login() if authentication_status: # 如果驗證狀態為真 self.logout() # 顯示 "Logout" 按鈕,連接到 "main",並使用特定的 key st.write(f"Welcome *{name}*") # 顯示歡迎訊息,使用 st.session_state 中的 name 屬性 self.display_content() self.reset_password(username) # 嘗試重設密碼 elif authentication_status is False: # 如果驗證狀態為假 st.error("Username/password is incorrect") # 顯示錯誤訊息,提示使用者帳號或密碼不正確 elif authentication_status is None: # 如果驗證狀態為空 st.warning("Please enter your username and password") # 顯示警告訊息,提示使用者輸入帳號和密碼 def display_content(self): """顯示內容""" st.title("Some content") # 顯示標題為 "Some content" if __name__ == "__main__": # 使用 AuthMixin 進行驗證 # MyApp(config_path="./config.yaml", handler=YamlHandler()) # 使用 JSON 處理器進行驗證 # MyApp(config_path="./config.json", handler=JsonHandler()) # 不使用 AuthMixin 進行驗證 MyApp()