Added signup form and functionality
This commit is contained in:
@ -3,6 +3,7 @@ from flask_login import login_user, logout_user
|
|||||||
from app.models.users import User
|
from app.models.users import User
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from app.models import db
|
from app.models import db
|
||||||
|
import re
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@ -42,4 +43,41 @@ def login():
|
|||||||
def logout():
|
def logout():
|
||||||
logout_user()
|
logout_user()
|
||||||
flash('You have been logged out.', 'info')
|
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'))
|
return redirect(url_for('main.login_route'))
|
@ -7,4 +7,5 @@ class User(db.Model, UserMixin):
|
|||||||
username = db.Column(db.String(80), unique=True, nullable=False)
|
username = db.Column(db.String(80), unique=True, nullable=False)
|
||||||
email = db.Column(db.String(120), unique=True, nullable=False)
|
email = db.Column(db.String(120), unique=True, nullable=False)
|
||||||
password = db.Column(db.String(120), 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)
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
from flask_login import login_required
|
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
|
from app.controllers.home_controller import home
|
||||||
|
|
||||||
|
|
||||||
main = Blueprint('main', __name__)
|
main = Blueprint('main', __name__)
|
||||||
|
|
||||||
@main.route('/')
|
@main.route('/')
|
||||||
@ -16,4 +17,8 @@ def login_route():
|
|||||||
@main.route('/logout')
|
@main.route('/logout')
|
||||||
@login_required
|
@login_required
|
||||||
def logout_route():
|
def logout_route():
|
||||||
return logout()
|
return logout()
|
||||||
|
|
||||||
|
@main.route('/signup', methods=['POST'])
|
||||||
|
def signup_route():
|
||||||
|
return signup()
|
@ -50,8 +50,8 @@
|
|||||||
border: 1px solid #D9D9D9;
|
border: 1px solid #D9D9D9;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background-color: #D9D9D9;
|
background-color: #D9D9D9;
|
||||||
color: white;
|
color: rgb(0, 0, 0);
|
||||||
font-size: 16px;
|
font-size: 20px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,19 +81,6 @@
|
|||||||
letter-spacing: 3px;
|
letter-spacing: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.login-container {
|
|
||||||
width: 95%;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-message p {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.signup-container {
|
.signup-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -105,6 +92,19 @@
|
|||||||
color: #8f8f8f;
|
color: #8f8f8f;
|
||||||
margin-top: 14px;
|
margin-top: 14px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signup u:hover {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message p {
|
||||||
|
color: red;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -112,30 +112,57 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="spotlight"></div>
|
<div class="spotlight"></div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="login-signup-container">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
<h2>Login</h2>
|
<!-- Login Form -->
|
||||||
<p class="login-description">Enter your credentials below to login to your account</p>
|
<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') }}">
|
<form method="POST" action="{{ url_for('main.login_route') }}" id="login-form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="username">Username</label>
|
<label for="username">Username</label>
|
||||||
<input type="text" id="username" name="username" required>
|
<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>
|
||||||
|
|
||||||
<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>
|
</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() %}
|
{% with messages = get_flashed_messages() %}
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
@ -148,4 +175,61 @@
|
|||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
</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 %}
|
{% endblock %}
|
Reference in New Issue
Block a user