diff --git a/src/EzLanManager.py b/src/EzLanManager.py index 3ac34e5..bfca6df 100644 --- a/src/EzLanManager.py +++ b/src/EzLanManager.py @@ -6,7 +6,7 @@ from rio import App, Theme, Color, Font, Page, Session from from_root import from_root from src.ez_lan_manager import pages, init_services -from src.ez_lan_manager.helpers.LoggedInGuard import logged_in_guard +from src.ez_lan_manager.helpers.LoggedInGuard import logged_in_guard, not_logged_in_guard from src.ez_lan_manager.types.SessionStorage import SessionStorage logger = logging.getLogger(__name__.split(".")[-1]) @@ -99,12 +99,14 @@ if __name__ == "__main__": Page( name="Register", page_url="register", - build=lambda: pages.PlaceholderPage(placeholder_name="Registrierung"), + build=pages.RegisterPage, + guard=not_logged_in_guard ), Page( name="ForgotPassword", page_url="forgot-password", build=pages.ForgotPasswordPage, + guard=not_logged_in_guard ), Page( name="EditProfile", diff --git a/src/ez_lan_manager/helpers/LoggedInGuard.py b/src/ez_lan_manager/helpers/LoggedInGuard.py index 0002984..04aa8ca 100644 --- a/src/ez_lan_manager/helpers/LoggedInGuard.py +++ b/src/ez_lan_manager/helpers/LoggedInGuard.py @@ -5,6 +5,12 @@ from rio import Session, URL from src.ez_lan_manager.types.SessionStorage import SessionStorage +# Guards pages against access from users that are NOT logged in def logged_in_guard(session: Session, _) -> Optional[URL]: if session[SessionStorage].user_id is None: return URL("./") + +# Guards pages against access from users that ARE logged in +def not_logged_in_guard(session: Session, _) -> Optional[URL]: + if session[SessionStorage].user_id is not None: + return URL("./") diff --git a/src/ez_lan_manager/pages/RegisterPage.py b/src/ez_lan_manager/pages/RegisterPage.py new file mode 100644 index 0000000..7ed1f40 --- /dev/null +++ b/src/ez_lan_manager/pages/RegisterPage.py @@ -0,0 +1,205 @@ +import logging +from asyncio import sleep +from typing import Optional + +from email_validator import validate_email, EmailNotValidError +from rio import Column, Component, event, Text, TextStyle, TextInput, TextInputChangeEvent, Button + +from src.ez_lan_manager import ConfigurationService, UserService, MailingService +from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox +from src.ez_lan_manager.pages import BasePage + +MINIMUM_PASSWORD_LENGTH = 6 + +logger = logging.getLogger(__name__.split(".")[-1]) + +class RegisterPage(Component): + def on_pw_change(self, _: TextInputChangeEvent) -> None: + if not (self.pw_1.text == self.pw_2.text) or len(self.pw_1.text) < MINIMUM_PASSWORD_LENGTH: + self.pw_1.is_valid = False + self.pw_2.is_valid = False + return + self.pw_1.is_valid = True + self.pw_2.is_valid = True + + + def on_email_changed(self, change_event: TextInputChangeEvent) -> None: + try: + validate_email(change_event.text, check_deliverability=False) + self.email_input.is_valid = True + except EmailNotValidError: + self.email_input.is_valid = False + + async def on_submit_button_pressed(self) -> None: + self.submit_button.is_loading = True + await self.submit_button.force_refresh() + + if len(self.user_name_input.text) < 1: + await self.display_save_result_animation(False, optional_text="Nutzername darf nicht leer sein!") + self.submit_button.is_loading = False + return + + if not (self.pw_1.text == self.pw_2.text): + await self.display_save_result_animation(False, optional_text="Passwörter stimmen nicht überein!") + self.submit_button.is_loading = False + return + + if len(self.pw_1.text) < MINIMUM_PASSWORD_LENGTH: + await self.display_save_result_animation(False, optional_text=f"Passwort muss mindestens {MINIMUM_PASSWORD_LENGTH} Zeichen lang sein!") + self.submit_button.is_loading = False + return + + if not self.email_input.is_valid or len(self.email_input.text) < 3: + await self.display_save_result_animation(False, optional_text="E-Mail Adresse ungültig!") + self.submit_button.is_loading = False + return + + user_service = self.session[UserService] + mailing_service = self.session[MailingService] + lan_info = self.session[ConfigurationService].get_lan_info() + + if user_service.get_user(self.email_input.text) is not None or user_service.get_user(self.user_name_input.text) is not None: + await self.display_save_result_animation(False, optional_text="Benutzername oder E-Mail bereits regestriert!") + self.submit_button.is_loading = False + return + + try: + new_user = user_service.create_user(self.user_name_input.text, self.email_input.text, self.pw_1.text) + if not new_user: + raise RuntimeError("User could not be created") + except Exception as e: + logger.error(f"Unknown error during new user registration: {e}") + await self.display_save_result_animation(False, optional_text="Es ist ein unbekannter Fehler aufgetreten :(") + self.submit_button.is_loading = False + return + + await mailing_service.send_email( + subject="Erfolgreiche Registrierung", + body=f"Hallo {self.user_name_input.text},\n\n" + f"Du hast dich erfolgreich beim EZ-LAN Manager für {lan_info.name} {lan_info.iteration} registriert.\n\n" + f"Wenn du dich nicht registriert hast, kontaktiere bitte unser Team über unsere Homepage.\n\n" + f"Liebe Grüße\nDein {lan_info.name} - Team", + receiver=self.email_input.text + ) + + self.submit_button.is_loading = False + await self.display_save_result_animation(True, optional_text="Erfolgreich registriert!") + + async def display_save_result_animation(self, success: bool, optional_text: Optional[str] = None) -> None: + self.info_text.text = "" + if success: + self.info_text.style = TextStyle( + fill=self.session.theme.success_color, + font_size=1 + ) + t = "Gespeichert!" if not optional_text else optional_text + for c in t: + self.info_text.text = self.info_text.text + c + await self.info_text.force_refresh() + await sleep(0.04) + else: + self.info_text.style = TextStyle( + fill=self.session.theme.danger_color, + font_size=1 + ) + t = "Fehler!" if not optional_text else optional_text + for c in t: + self.info_text.text = self.info_text.text + c + await self.info_text.force_refresh() + await sleep(0.04) + + @event.on_populate + async def on_populate(self) -> None: + await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Registrieren") + + def build(self) -> Component: + self.user_name_input = TextInput( + label="Benutzername", + text="", + margin_left=1, + margin_right=1, + margin_bottom=1, + grow_x=True + ) + self.email_input = TextInput( + label="E-Mail Adresse", + text="", + margin_left=1, + margin_right=1, + margin_bottom=1, + grow_x=True, + on_change=self.on_email_changed + ) + self.pw_1 = TextInput( + label="Passwort", + text="", + margin_left=1, + margin_right=1, + margin_bottom=1, + grow_x=True, + is_secret=True, + on_change=self.on_pw_change + ) + self.pw_2 = TextInput( + label="Passwort wiederholen", + text="", + margin_left=1, + margin_right=1, + margin_bottom=1, + grow_x=True, + is_secret=True, + on_change=self.on_pw_change + ) + self.submit_button = Button( + content=Text( + "Registrieren", + style=TextStyle(fill=self.session.theme.background_color, font_size=0.9), + align_x=0.5 + ), + grow_x=True, + margin_top=2, + margin_left=1, + margin_right=1, + margin_bottom=1, + shape="rectangle", + style="minor", + color=self.session.theme.secondary_color, + on_press=self.on_submit_button_pressed + ) + self.info_text = Text( + text="", + style=TextStyle( + fill=self.session.theme.background_color, + font_size=1 + ), + margin_top=2, + margin_left=1, + margin_right=1, + margin_bottom=2, + wrap=True + ) + return BasePage( + content=Column( + MainViewContentBox( + content=Column( + Text( + "Neues Konto anlegen", + style=TextStyle( + fill=self.session.theme.background_color, + font_size=1.2 + ), + margin_top=2, + margin_bottom=2, + align_x=0.5 + ), + self.user_name_input, + self.email_input, + self.pw_1, + self.pw_2, + self.submit_button, + self.info_text + ) + ), + align_y=0, + ) + ) diff --git a/src/ez_lan_manager/pages/__init__.py b/src/ez_lan_manager/pages/__init__.py index e11b3bb..8fa8873 100644 --- a/src/ez_lan_manager/pages/__init__.py +++ b/src/ez_lan_manager/pages/__init__.py @@ -5,3 +5,4 @@ from .Logout import LogoutPage from .Account import AccountPage from .EditProfile import EditProfilePage from .ForgotPassword import ForgotPasswordPage +from .RegisterPage import RegisterPage