Added signup form and functionality

This commit is contained in:
2025-02-17 18:02:07 +01:00
parent 809b9dc638
commit 93bb9335a2
4 changed files with 165 additions and 37 deletions

View File

@ -3,6 +3,7 @@ from flask_login import login_user, logout_user
from app.models.users import User
from datetime import datetime, timedelta
from app.models import db
import re
import sys
@ -42,4 +43,41 @@ def login():
def logout():
logout_user()
flash('You have been logged out.', 'info')
return redirect(url_for('main.login_route'))
def signup():
username = request.form.get('username')
email = request.form.get('email')
password = request.form.get('password')
# Server-side validation
if len(username) < 5:
flash('Username must be at least 5 characters long', 'danger')
return redirect(url_for('main.login_route'))
email_regex = r'^[a-zA-Z][a-zA-Z0-9._-]*@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
if not re.match(email_regex, email):
flash('Please enter a valid email address', 'danger')
return redirect(url_for('main.login_route'))
password_regex = r'^(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{8,}$'
if not re.match(password_regex, password):
flash('Password must be at least 8 characters long and contain a number and a special character', 'danger')
return redirect(url_for('main.login_route'))
# Check if username or email already exists
if User.query.filter_by(username=username).first():
flash('Username already exists', 'danger')
return redirect(url_for('main.login_route'))
if User.query.filter_by(email=email).first():
flash('Email already exists', 'danger')
return redirect(url_for('main.login_route'))
# Create new user
new_user = User(username=username, email=email, password=password)
db.session.add(new_user)
db.session.commit()
flash('Account created successfully! Please login.', 'success')
return redirect(url_for('main.login_route'))

View File

@ -7,4 +7,5 @@ class User(db.Model, UserMixin):
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(120), nullable=False)
failed_login_attempts = db.Column(db.Integer, default=0, nullable=False)
last_failed_login_attempt = db.Column(db.DateTime, default=None, nullable=True)

View File

@ -1,8 +1,9 @@
from flask import Blueprint
from flask_login import login_required
from app.controllers.auth_controller import login, logout
from app.controllers.auth_controller import login, logout, signup
from app.controllers.home_controller import home
main = Blueprint('main', __name__)
@main.route('/')
@ -16,4 +17,8 @@ def login_route():
@main.route('/logout')
@login_required
def logout_route():
return logout()
return logout()
@main.route('/signup', methods=['POST'])
def signup_route():
return signup()

View File

@ -50,8 +50,8 @@
border: 1px solid #D9D9D9;
border-radius: 6px;
background-color: #D9D9D9;
color: white;
font-size: 16px;
color: rgb(0, 0, 0);
font-size: 20px;
box-sizing: border-box;
}
@ -81,19 +81,6 @@
letter-spacing: 3px;
}
@media (max-width: 768px) {
.login-container {
width: 95%;
padding: 20px;
}
}
.error-message p {
color: red;
}
.signup-container {
display: flex;
justify-content: center;
@ -105,6 +92,19 @@
color: #8f8f8f;
margin-top: 14px;
align-items: center;
cursor: pointer;
}
.signup u:hover {
color: #fff;
}
.hidden {
display: none;
}
.error-message p {
color: red;
}
</style>
{% endblock %}
@ -112,30 +112,57 @@
{% block content %}
<div class="spotlight"></div>
<div class="container">
<div class="login-signup-container">
</div>
<div class="login-container">
<h2>Login</h2>
<p class="login-description">Enter your credentials below to login to your account</p>
<!-- Login Form -->
<div id="loginForm">
<h2>Login</h2>
<p class="login-description">Enter your credentials below to login to your account</p>
<form method="POST" action="{{ url_for('main.login_route') }}">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username" required>
<form method="POST" action="{{ url_for('main.login_route') }}" id="login-form">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username" required>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" required class="password-input">
</div>
<button type="submit">Login</button>
</form>
<div class="signup-container">
<p class="signup">Don't have an account? <u onclick="toggleForms()">Sign up</u></p>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" required class="password-input">
</div>
<button type="submit">Login</button>
</form>
<div class="signup-container">
<p class="signup">Dont have an account? <u>Sign up</u></p>
</div>
<!-- Signup Form -->
<div id="signupForm" class="hidden">
<h2>Sign Up</h2>
<p class="login-description">Create your account to get started</p>
<form method="POST" action="{{ url_for('main.signup_route') }}" id="signup-form" onsubmit="return validateForm()">
<div class="form-group">
<label for="signup-username">Username</label>
<input type="text" id="signup-username" name="username" required>
</div>
<div class="form-group">
<label for="signup-email">Email</label>
<input type="email" id="signup-email" name="email" required>
</div>
<div class="form-group">
<label for="signup-password">Password</label>
<input type="password" id="signup-password" name="password" required class="password-input">
</div>
<button type="submit">Sign Up</button>
</form>
<div class="signup-container">
<p class="signup">Already have an account? <u onclick="toggleForms()">Login</u></p>
</div>
</div>
{% with messages = get_flashed_messages() %}
{% if messages %}
@ -148,4 +175,61 @@
{% endwith %}
</div>
</div>
<script>
function toggleForms() {
const loginForm = document.getElementById('loginForm');
const signupForm = document.getElementById('signupForm');
loginForm.classList.toggle('hidden');
signupForm.classList.toggle('hidden');
}
function validateForm() {
const username = document.getElementById('signup-username').value;
const email = document.getElementById('signup-email').value;
const password = document.getElementById('signup-password').value;
// Username validation
if (username.length < 5) {
flash('Username must be at least 5 characters long');
return false;
}
// Email validation
const emailRegex = /^[a-zA-Z][a-zA-Z0-9._-]*@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!emailRegex.test(email)) {
flash('Please enter a valid email address');
return false;
}
// Password validation
const passwordRegex = /^(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{8,}$/;
if (!passwordRegex.test(password)) {
flash('Password must be at least 8 characters long and contain a number and a special character');
return false;
}
return true;
}
function flash(message) {
const errorDiv = document.querySelector('.error-message');
if (!errorDiv) {
const newErrorDiv = document.createElement('div');
newErrorDiv.className = 'error-message';
const p = document.createElement('p');
p.className = 'text-danger';
p.textContent = message;
newErrorDiv.appendChild(p);
document.querySelector('.login-container').appendChild(newErrorDiv);
} else {
const p = errorDiv.querySelector('p') || document.createElement('p');
p.className = 'text-danger';
p.textContent = message;
if (!errorDiv.contains(p)) {
errorDiv.appendChild(p);
}
}
}
</script>
{% endblock %}