diff --git a/__pycache__/app.cpython-312.pyc b/__pycache__/app.cpython-312.pyc new file mode 100644 index 0000000..8a525d5 Binary files /dev/null and b/__pycache__/app.cpython-312.pyc differ diff --git a/app.py b/app.py index 89ab1c5..32c493b 100644 --- a/app.py +++ b/app.py @@ -1,11 +1,9 @@ from application import app from flask import redirect, url_for -from flask_login import login_required # type: ignore -# home route +# home route, place holder in case we want a home page @app.route("/") -@login_required def index(): return redirect(url_for("dash.index")) diff --git a/application/__init__.py b/application/__init__.py index d4cd08f..67c3339 100644 --- a/application/__init__.py +++ b/application/__init__.py @@ -20,9 +20,6 @@ db.init_app(app) migrate = Migrate(app, db) -# bp import -from application.auth.views import auth_blueprint -from application.dash.views import dash_blueprint # Login manager from application.auth.models import User @@ -32,6 +29,7 @@ login_manager.init_app(app) # type: ignore login_manager.login_view = "auth.login" # type: ignore +# Gets all the user data @login_manager.user_loader # type: ignore def load_user(user_id): # type: ignore return User.query.get(int(user_id)) # type: ignore @@ -39,5 +37,14 @@ def load_user(user_id): # type: ignore # Blueprint magic +# bp import +# Would like to do this at the top of the file, +# but can't easily figure out how to do this. +# I think everything that the views depend on have to be moved +# into a seperate .py and imported. +from application.auth.views import auth_blueprint +from application.dash.views import dash_blueprint + +# Register blueprints app.register_blueprint(dash_blueprint, url_prefix="/dash") app.register_blueprint(auth_blueprint, url_prefix="/auth") diff --git a/application/__pycache__/__init__.cpython-312.pyc b/application/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 295154d..0000000 Binary files a/application/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/application/auth/__pycache__/forms.cpython-312.pyc b/application/auth/__pycache__/forms.cpython-312.pyc deleted file mode 100644 index 4a3f074..0000000 Binary files a/application/auth/__pycache__/forms.cpython-312.pyc and /dev/null differ diff --git a/application/auth/__pycache__/models.cpython-312.pyc b/application/auth/__pycache__/models.cpython-312.pyc deleted file mode 100644 index 4a61f2a..0000000 Binary files a/application/auth/__pycache__/models.cpython-312.pyc and /dev/null differ diff --git a/application/auth/__pycache__/views.cpython-312.pyc b/application/auth/__pycache__/views.cpython-312.pyc deleted file mode 100644 index 5a28b60..0000000 Binary files a/application/auth/__pycache__/views.cpython-312.pyc and /dev/null differ diff --git a/application/auth/forms.py b/application/auth/forms.py index 90c31fb..4db03b1 100644 --- a/application/auth/forms.py +++ b/application/auth/forms.py @@ -3,16 +3,21 @@ from wtforms import StringField, SubmitField, PasswordField, BooleanField from wtforms.validators import DataRequired +# Default Form that inherits from FlaskForm and +# contains a username, password and submit button class defaultForm(FlaskForm): username = StringField("Username", validators=[DataRequired()]) password = PasswordField("Password", validators=[DataRequired()]) submit = SubmitField("Submit") +# LoginForm, contains exactly the same as defaultForm class LoginForm(defaultForm): pass +# RegisterForm that inherits from the default. +# Adds a password confirmation and if the user is an admin or not. class RegisterForm(defaultForm): confirm_password = PasswordField( "Confirm Password", validators=[DataRequired()] @@ -20,6 +25,8 @@ class RegisterForm(defaultForm): is_admin = BooleanField("Admin") +# Form to update password information. +# Needs a confirmation password and the current password class UpdateForm(defaultForm): confirm_password = PasswordField( "Confirm Password", validators=[DataRequired()] diff --git a/application/auth/models.py b/application/auth/models.py index 554a4fc..086f42c 100644 --- a/application/auth/models.py +++ b/application/auth/models.py @@ -2,14 +2,18 @@ from application import db from flask_login import UserMixin # type: ignore +# User model class User(db.Model, UserMixin): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(150), unique=True, nullable=False) password = db.Column(db.String(150), nullable=False) is_admin = db.Column(db.Boolean, default=False) + # Purely a relationship not a column, + # makes all the services accessible through User.services services = db.relationship("Service", backref="user", lazy="joined") + # Initialize user, prevents red stuff def __init__(self, username: str, password: str, is_admin: bool = False): self.username = username self.password = password diff --git a/application/auth/templates/admin.html b/application/auth/templates/register_user.html similarity index 100% rename from application/auth/templates/admin.html rename to application/auth/templates/register_user.html diff --git a/application/auth/views.py b/application/auth/views.py index 7652eea..05c0a8e 100644 --- a/application/auth/views.py +++ b/application/auth/views.py @@ -1,23 +1,22 @@ -from flask import Blueprint, render_template, redirect, url_for +from flask import Blueprint, render_template, redirect, url_for, flash from application import db from application.auth.models import User from application.auth.forms import LoginForm from flask_login import ( # type: ignore - login_required, # type: ignore login_user, # type: ignore logout_user, current_user, ) from werkzeug.security import check_password_hash, generate_password_hash -from application.decorators import admin_required +from application.decorators import admin_required, login_required from application.auth.forms import RegisterForm, UpdateForm auth_blueprint = Blueprint("auth", __name__, template_folder="templates") -# Routes -@auth_blueprint.route("/register", methods=["GET", "POST"]) +# Add user +@auth_blueprint.route("/register_user", methods=["GET", "POST"]) @admin_required def register(): register_form = RegisterForm() @@ -29,14 +28,14 @@ def register(): is_admin = register_form.is_admin.data if confirm_password != password: return render_template( - "admin.html", + "register_user.html", form=register_form, feedback="Passwords don't match, please try again", active_page="register", ) if User.query.filter_by(username=username).first(): return render_template( - "admin.html", + "register_user.html", form=register_form, feedback="Username is already taken", active_page="register", @@ -49,16 +48,17 @@ def register(): db.session.add(new_user) db.session.commit() return render_template( - "admin.html", + "register_user.html", form=RegisterForm(formdata=None), feedback="User succesfully added", active_page="register", ) return render_template( - "admin.html", form=register_form, active_page="register" + "register_user.html", form=register_form, active_page="register" ) +# Update user (specifically password) @auth_blueprint.route("/update_user", methods=["GET", "POST"]) @login_required def update(): @@ -85,10 +85,12 @@ def update(): ) db.session.commit() logout_user() + flash("Password changed succesfully, please log back in") return redirect(url_for("auth.login")) return render_template("update_user.html", form=form, active_page="update") +# Login as user or admin @auth_blueprint.route("/login", methods=["GET", "POST"]) def login(): login_form = LoginForm() @@ -103,6 +105,7 @@ def login(): user.password, password # type: ignore ): login_user(user) # type: ignore + flash("Logged in succesfully") return redirect("/") else: feedback = "Username or password is incorrect" @@ -110,8 +113,10 @@ def login(): return render_template("login.html", form=login_form, feedback=feedback) +# Logout @auth_blueprint.route("/logout") @login_required def logout(): logout_user() + flash("Logged out succesfully") return redirect(url_for("index")) diff --git a/application/dash/forms.py b/application/dash/forms.py index 69bfe06..681d8d4 100644 --- a/application/dash/forms.py +++ b/application/dash/forms.py @@ -4,9 +4,19 @@ from wtforms.validators import DataRequired from flask_wtf.file import FileField, FileAllowed # type: ignore +# Form for service on dashboard, connected to database through ORM class ServiceForm(FlaskForm): - name = StringField("Service name:", validators=[DataRequired()]) - url = URLField("Service URL:", validators=[DataRequired()]) + name = StringField( + "Service name:", + validators=[DataRequired()], + render_kw={"placeholder": "Service Name"}, + ) + url = URLField( + "Service URL:", + validators=[DataRequired()], + render_kw={"placeholder": "https://example.com"}, + ) + # File field that only allows jpg, jpeg or png image = FileField( "Icon:", validators=[ diff --git a/application/dash/models.py b/application/dash/models.py index 588a913..a152b40 100644 --- a/application/dash/models.py +++ b/application/dash/models.py @@ -1,14 +1,17 @@ from application import db +# Service class for dashboard class Service(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String, nullable=False) url = db.Column(db.String, nullable=False) icon = db.Column(db.String, default="google.png") + # Foreign key to connect to User table user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False) + # Initialize the service (prevents ugly red lines) def __init__( self, name: str, url: str, user_id: int, icon: str = "google.png" ): diff --git a/application/dash/templates/dashboard.html b/application/dash/templates/dashboard.html index 238932f..249266e 100644 --- a/application/dash/templates/dashboard.html +++ b/application/dash/templates/dashboard.html @@ -6,43 +6,41 @@