diff --git a/src/tjts5901/authentication/forms.py b/src/tjts5901/authentication/forms.py index b075d8dd1d1f518a9188aeee5fa695d9b91a28d8..3b66682a1e9f9af8b1631d240eca702c03098f84 100644 --- a/src/tjts5901/authentication/forms.py +++ b/src/tjts5901/authentication/forms.py @@ -7,6 +7,7 @@ from wtforms import EmailField, PasswordField, BooleanField, StringField from wtforms.validators import DataRequired, ValidationError, Length from tjts5901.config import MIN_PASSWORD_LENGTH +from tjts5901.user.models import User class LoginForm(FlaskForm): email = EmailField(validators=[DataRequired()]) @@ -19,6 +20,12 @@ class RegisterForm(FlaskForm): password = PasswordField(validators=[DataRequired(), Length(min=MIN_PASSWORD_LENGTH)]) password_confirm = PasswordField(validators=[DataRequired()], label="Confirm password") - def validate_password_confirm(form, field): - if field.data != form.password.data: + def validate_password_confirm(self, field): + if field.data != self.password.data: raise ValidationError("Passwords don't match") + + def validate_email(self, field): + user = User.find_by_email(field.data) + if user and user.password: # This is an existing user! + raise ValidationError("Email already taken!") + diff --git a/src/tjts5901/authentication/routes.py b/src/tjts5901/authentication/routes.py index ff6d0d99feb18a5871104e299942ceaa69a562f7..581553b9ffc03982b1b60db823a899720dac7c32 100644 --- a/src/tjts5901/authentication/routes.py +++ b/src/tjts5901/authentication/routes.py @@ -2,7 +2,7 @@ Routes for authentication """ -from flask import abort, redirect, render_template, request, session, g, flash +from flask import abort, redirect, render_template, request, session, g, flash, url_for from tjts5901.authentication import blueprint from tjts5901.authentication.forms import LoginForm, RegisterForm from tjts5901.authentication.utils import google_login, get_user_from_session, create_token_for_user, is_correct_password, hash_password @@ -13,25 +13,31 @@ from tjts5901.app import logger @blueprint.route("/login", methods=["GET", "POST"]) def login(): + if g.user: + return redirect(url_for('user_blueprint.self_view')) login_form = LoginForm(request.form) if "login" in request.form: if login_form.validate(): user = User.find_by_email(login_form.email.data) if not user or not is_correct_password(login_form.password.data, user.password): + flash("Wrong email or password", "error") logger.info("Failed login attempt") else: jwt_token = create_token_for_user(user) session["token"] = jwt_token - return redirect("/") # TODO profile page + flash("You are now logged in!", "success") + return redirect(url_for('user_blueprint.self_view')) return render_template("authentication/login.html.j2", form=login_form, google_callback_uri=GOOGLE_CALLBACK_URI) @blueprint.route("/register", methods=["GET", "POST"]) def register(): + if g.user: + return redirect(url_for('user_blueprint.self_view')) register_form = RegisterForm(request.form) if "register" in request.form: - if register_form.validate(): # TODO more checking + if register_form.validate(): try: - user = User.find_by_email(register_form.email.data) + user = User.find_by_email(register_form.email.data) if not user: # Completely new user user = User( name=register_form.username.data, @@ -45,9 +51,11 @@ def register(): jwt_token = create_token_for_user(user) session["token"] = jwt_token - return redirect("/") # TODO profile page + flash("Succesfully registered. Also you are now logged in!", "success") + return redirect(url_for('user_blueprint.self_view')) except Exception as e: logger.info("User registration failed because of error: %s", e) + flash("Oops something went wrong! Validate the information or try again later.", "error") return render_template("authentication/register.html.j2", form=register_form) @blueprint.before_app_request @@ -73,7 +81,7 @@ def google_callback(): credentials = request.form['credential'] jwt_token = google_login(credentials) session["token"] = jwt_token - flash("You've been logged in!") + flash("You are now logged in!", "success") return redirect("/") @blueprint.route("/logout") @@ -83,5 +91,5 @@ def logout(): """ session.clear() g.user = None - flash("You've been logged out!") + flash("You are now logged out!", "success") return redirect("/") diff --git a/src/tjts5901/bid/models.py b/src/tjts5901/bid/models.py index 70ea894d4a10448468fe6199fba75d325e0699b6..d2fef749c1a3f873ef3632a16d3712bfb0de4682 100644 --- a/src/tjts5901/bid/models.py +++ b/src/tjts5901/bid/models.py @@ -22,6 +22,9 @@ class Bid(Document): }, { 'fields': ['-created_on'] + }, + { + 'fields': ['-amount', '-created_on'] } ] } diff --git a/src/tjts5901/listing/routes.py b/src/tjts5901/listing/routes.py index 8fbf95a3696d84ee326b6f5622c299aa009c5f25..e181f36cb6d8f34607faafe6c5803d0c68945080 100644 --- a/src/tjts5901/listing/routes.py +++ b/src/tjts5901/listing/routes.py @@ -87,19 +87,23 @@ def create_listing(): create_listing_form = CreateListingForm(request.form) if "create_listing" in request.form: if g.user and create_listing_form.validate(): - image = request.files.get(create_listing_form.image.name, None) + try: + image = request.files.get(create_listing_form.image.name, None) - listing = Listing( - name=create_listing_form.name.data, - description=create_listing_form.description.data, - initial_price=create_listing_form.initial_price.data, - min_increase_euros=create_listing_form.min_increase.data, - user=g.user, - ) - if image: - listing.image.put(image) - - listing.save() - return redirect(f"/listings/{listing.id}") + listing = Listing( + name=create_listing_form.name.data, + description=create_listing_form.description.data, + initial_price=create_listing_form.initial_price.data, + min_increase_euros=create_listing_form.min_increase.data, + user=g.user, + ) + if image: + listing.image.put(image) + + listing.save() + return redirect(f"/listings/{listing.id}") + except Exception as e: + flash("Something went wrong creating a new listing. Validate the form or try again later.", "error") + logger.info("Unknown error when creating a listing. %s", e) return render_template('listings/create.html.j2', form=create_listing_form) diff --git a/src/tjts5901/templates/api/guide.html.j2 b/src/tjts5901/templates/api/guide.html.j2 index 31821579b36336bb227c3167df0632488ca4ce8b..9f95da06b77b51a0a7b2be7ac5ac374208a4d61f 100644 --- a/src/tjts5901/templates/api/guide.html.j2 +++ b/src/tjts5901/templates/api/guide.html.j2 @@ -1,12 +1,24 @@ {% extends "layout.html.j2" %} {% block content %} -<p>You can use our rest API for listings and bids! <small>(Some functionality missing)</small></p> +<strong>{{_("You can use our REST API for listings and bids!")}}</strong> +<small>({{_("Some functionality is missing")}})</small> -<p>For endpoints that require authentication set the Authorization field as follows:</p> -<code> -Authorization: bearer: <Your token here> +<p class="my-2">{{_("Currently available REST endpoints are")}}</p> +<ul class="list-group"> + <li class="list-group-item"> + <a href="{{url_for('api_blueprint.bid_api_blueprint.get_bids')}}">{{url_for('api_blueprint.bid_api_blueprint.get_bids')}}</a> + </li> + <li class="list-group-item"> + <a href="{{url_for('api_blueprint.listing_api_blueprint.get_listings')}}">{{url_for('api_blueprint.listing_api_blueprint.get_listings')}}</a> + </li> +</ul> + +<p class="my-0 py-0 mt-2">{{_("For endpoints that require authentication set the Authorization field as follows")}}:</p> +<code class="my-0 py-0"> +Authorization: bearer: <{{_("Your token here")}}> </code> -<p>Your token is:</p> + +<p class="my-0 py-0 mt-4">{{_("Your token is")}}:</p> <div class="p-2 m-0 bg-primary text-white rounded text-wrap text-break"> <p class="m-0">{{user_token}}</p> </div> diff --git a/src/tjts5901/templates/authentication/login.html.j2 b/src/tjts5901/templates/authentication/login.html.j2 index 2e3892c9417dc776dfbb60ecb53f5a5ac5cc6512..cad5665ab87fff255ef531ab39f0c9b1c91add00 100644 --- a/src/tjts5901/templates/authentication/login.html.j2 +++ b/src/tjts5901/templates/authentication/login.html.j2 @@ -1,6 +1,8 @@ {% extends "layout.html.j2" %} {% block content %} {% import "components/google_button.html.j2" as google%} {% import "components/forms.html.j2" as form_components %} +{% from "components/alerts.html.j2" import alerts_from_flashed %} + <div> <form role="form" method="post"> @@ -10,6 +12,8 @@ {{ form_components.field(form.password) }} {{form_components.submit_button("Login","login")}} </form> + {{alerts_from_flashed()}} + <hr /> <div class="mb-4"> diff --git a/src/tjts5901/templates/authentication/register.html.j2 b/src/tjts5901/templates/authentication/register.html.j2 index 4f98b724d7bba67f53e34d1b7c06635841c7ec4f..776d83efdd2996e15273d22127e72809d2fdfaf0 100644 --- a/src/tjts5901/templates/authentication/register.html.j2 +++ b/src/tjts5901/templates/authentication/register.html.j2 @@ -1,5 +1,6 @@ {% extends "layout.html.j2" %} {% block content %} {% import "components/forms.html.j2" as form_components%} +{% from "components/alerts.html.j2" import alerts_from_flashed %} <div> <form role="form" method="post"> @@ -12,6 +13,7 @@ {{form_components.submit_button("Register","register")}} </form> + {{alerts_from_flashed()}} </div> diff --git a/src/tjts5901/templates/components/alerts.html.j2 b/src/tjts5901/templates/components/alerts.html.j2 new file mode 100644 index 0000000000000000000000000000000000000000..7382c067c7a4576aa5fee42440ef8fefd9687242 --- /dev/null +++ b/src/tjts5901/templates/components/alerts.html.j2 @@ -0,0 +1,15 @@ +{% macro alert(msg, type="danger") %} +<div class="alert alert-{{type}} my-2" role="alert"> + <strong>{{msg}}</strong> +</div> +{%- endmacro -%} + +{% macro alerts_from_flashed(category_filter="error", type="danger") %} +{% with alerts = get_flashed_messages(category_filter=[category_filter]) %} +{% if alerts %} +{% for msg in alerts %} +{{alert(msg, type=type)}} +{% endfor %} +{% endif %} +{% endwith %} +{% endmacro %} \ No newline at end of file diff --git a/src/tjts5901/templates/listings/create.html.j2 b/src/tjts5901/templates/listings/create.html.j2 index 5f35d63ab588d0fbcaf9da9d77dfd5cbaaacfc0c..896129ff9ee2b43a06fadc59b93f7ebcc8cb8e13 100644 --- a/src/tjts5901/templates/listings/create.html.j2 +++ b/src/tjts5901/templates/listings/create.html.j2 @@ -3,6 +3,7 @@ https://jinja.palletsprojects.com/en/3.1.x/ #} {% extends "layout.html.j2" %} {% block content %} {% import "components/forms.html.j2" as form_components %} +{% from "components/alerts.html.j2" import alerts_from_flashed %} <form role="form" class="py-4" method="post" enctype="multipart/form-data"> {{ form.hidden_tag() }} @@ -15,4 +16,6 @@ https://jinja.palletsprojects.com/en/3.1.x/ {{ form_components.submit_button("Create", "create_listing")}} </form> +{{alerts_from_flashed()}} + {% endblock %} \ No newline at end of file diff --git a/src/tjts5901/templates/users/profile_view.html.j2 b/src/tjts5901/templates/users/profile_view.html.j2 index e6d81baed6dafe114256adaedfbbf37b7ed930ea..45b06e0f5fc06b36923cab39d44fd9f20ca177c2 100644 --- a/src/tjts5901/templates/users/profile_view.html.j2 +++ b/src/tjts5901/templates/users/profile_view.html.j2 @@ -1,8 +1,9 @@ {% extends "layout.html.j2" %} {% block content %} {% import "components/user_info.html.j2" as info %} +{% from "components/alerts.html.j2" import alerts_from_flashed %} <h3 class="my-3">User profile</h3> - +{{alerts_from_flashed("success", "success")}} <div class="card mb-3 col-sm-4"> <div class="card-body"> {{ info.infoRow("Name", g.user.name) }}