Add admin profile
This commit is contained in:
parent
0c60a4b4d8
commit
a993767dc0
@ -24,9 +24,9 @@ from sqlalchemy import text
|
||||
from flask_wtf import FlaskForm
|
||||
from flask_bootstrap import Bootstrap
|
||||
from flask_basicauth import BasicAuth
|
||||
from wtforms import StringField, PasswordField, BooleanField
|
||||
from wtforms import StringField, PasswordField, BooleanField, SubmitField
|
||||
from wtforms import DateField
|
||||
from wtforms.validators import InputRequired, Email, Length
|
||||
from wtforms.validators import InputRequired, Email, Length, EqualTo
|
||||
from forms import motmForm, adminSettingsForm2, goalsAssistsForm, DatabaseSetupForm, PlayerForm, ClubForm, TeamForm, DataImportForm, ClubSelectionForm
|
||||
from db_config import sql_write, sql_write_static, sql_read, sql_read_static
|
||||
from sqlalchemy import text
|
||||
@ -35,9 +35,32 @@ from readSettings import mySettings
|
||||
from fixture_scraper import FixtureScraper, get_next_hkfc_c_fixture, get_opponent_club_name, get_opponent_club_info, match_opponent_to_club
|
||||
from club_scraper import ClubScraper, get_hk_hockey_clubs, expand_club_abbreviation
|
||||
|
||||
app.config['BASIC_AUTH_USERNAME'] = 'admin'
|
||||
app.config['BASIC_AUTH_PASSWORD'] = 'letmein'
|
||||
basic_auth = BasicAuth(app)
|
||||
# Custom authentication class that uses database
|
||||
class DatabaseBasicAuth(BasicAuth):
|
||||
def check_credentials(self, username, password):
|
||||
try:
|
||||
sql = text("SELECT password_hash FROM admin_profiles WHERE username = :username")
|
||||
result = sql_read(sql, {'username': username})
|
||||
if result:
|
||||
stored_hash = result[0]['password_hash']
|
||||
password_hash = hashlib.sha256(password.encode()).hexdigest()
|
||||
return password_hash == stored_hash
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"Authentication error: {e}")
|
||||
return False
|
||||
|
||||
app.config['BASIC_AUTH_USERNAME'] = 'admin' # Fallback for compatibility
|
||||
app.config['BASIC_AUTH_PASSWORD'] = 'letmein' # Fallback for compatibility
|
||||
basic_auth = DatabaseBasicAuth(app)
|
||||
|
||||
# Admin profile form
|
||||
class AdminProfileForm(FlaskForm):
|
||||
current_password = PasswordField('Current Password', validators=[InputRequired()])
|
||||
new_password = PasswordField('New Password', validators=[InputRequired(), Length(min=6)])
|
||||
confirm_password = PasswordField('Confirm New Password', validators=[InputRequired(), EqualTo('new_password', message='Passwords must match')])
|
||||
email = StringField('Email', validators=[Email()])
|
||||
submit = SubmitField('Update Profile')
|
||||
|
||||
|
||||
@app.route('/')
|
||||
@ -53,6 +76,57 @@ def admin_dashboard():
|
||||
return render_template('admin_dashboard.html')
|
||||
|
||||
|
||||
@app.route('/admin/profile', methods=['GET', 'POST'])
|
||||
@basic_auth.required
|
||||
def admin_profile():
|
||||
"""Admin profile page for changing password and settings"""
|
||||
form = AdminProfileForm()
|
||||
|
||||
# Get current admin profile
|
||||
sql = text("SELECT username, email FROM admin_profiles WHERE username = 'admin'")
|
||||
profile = sql_read(sql)
|
||||
|
||||
if profile:
|
||||
current_email = profile[0]['email']
|
||||
else:
|
||||
current_email = ''
|
||||
|
||||
if request.method == 'POST' and form.validate_on_submit():
|
||||
# Verify current password
|
||||
sql_check = text("SELECT password_hash FROM admin_profiles WHERE username = 'admin'")
|
||||
result = sql_read(sql_check)
|
||||
|
||||
if result:
|
||||
stored_hash = result[0]['password_hash']
|
||||
current_password_hash = hashlib.sha256(form.current_password.data.encode()).hexdigest()
|
||||
|
||||
if current_password_hash == stored_hash:
|
||||
# Update password and email
|
||||
new_password_hash = hashlib.sha256(form.new_password.data.encode()).hexdigest()
|
||||
|
||||
sql_update = text("""
|
||||
UPDATE admin_profiles
|
||||
SET password_hash = :password_hash, email = :email, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE username = 'admin'
|
||||
""")
|
||||
sql_write(sql_update, {
|
||||
'password_hash': new_password_hash,
|
||||
'email': form.email.data
|
||||
})
|
||||
|
||||
flash('Profile updated successfully!', 'success')
|
||||
return redirect(url_for('admin_profile'))
|
||||
else:
|
||||
flash('Current password is incorrect!', 'error')
|
||||
else:
|
||||
flash('Admin profile not found!', 'error')
|
||||
|
||||
# Pre-populate email field
|
||||
form.email.data = current_email
|
||||
|
||||
return render_template('admin_profile.html', form=form, current_email=current_email)
|
||||
|
||||
|
||||
# ==================== PUBLIC VOTING SECTION ====================
|
||||
|
||||
@app.route('/motm/<randomUrlSuffix>')
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
|
||||
<div class="mb-3">
|
||||
<a href="/" class="btn btn-default">Back to Main Page</a>
|
||||
<a href="/admin/profile" class="btn btn-outline-secondary">Admin Profile</a>
|
||||
</div>
|
||||
|
||||
<!-- Data Management Section -->
|
||||
|
||||
160
motm_app/templates/admin_profile.html
Normal file
160
motm_app/templates/admin_profile.html
Normal file
@ -0,0 +1,160 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Admin Profile - HKFC Men's C Team MOTM System</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.profile-section {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.375rem;
|
||||
padding: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.security-notice {
|
||||
background-color: #fff3cd;
|
||||
border: 1px solid #ffeaa7;
|
||||
border-radius: 0.375rem;
|
||||
padding: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-4">
|
||||
<div class="row">
|
||||
<div class="col-md-8 mx-auto">
|
||||
<h1>Admin Profile</h1>
|
||||
<p class="lead">Manage your admin account settings and password</p>
|
||||
|
||||
<div class="mb-3">
|
||||
<a href="/admin" class="btn btn-outline-primary">Back to Admin Dashboard</a>
|
||||
</div>
|
||||
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ 'danger' if category == 'error' else 'success' }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<!-- Security Notice -->
|
||||
<div class="security-notice">
|
||||
<h5><i class="bi bi-shield-check"></i> Security Notice</h5>
|
||||
<p class="mb-0">
|
||||
<strong>Important:</strong> Changing your password will immediately affect access to all admin functions.
|
||||
Make sure to remember your new password or store it securely.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Profile Form -->
|
||||
<div class="profile-section">
|
||||
<h3>Change Password & Settings</h3>
|
||||
<form method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
{{ form.current_password.label(class="form-label") }}
|
||||
{{ form.current_password(class="form-control", placeholder="Enter current password") }}
|
||||
{% if form.current_password.errors %}
|
||||
<div class="text-danger">
|
||||
{% for error in form.current_password.errors %}
|
||||
<small>{{ error }}</small>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
{{ form.new_password.label(class="form-label") }}
|
||||
{{ form.new_password(class="form-control", placeholder="Enter new password (min 6 characters)") }}
|
||||
{% if form.new_password.errors %}
|
||||
<div class="text-danger">
|
||||
{% for error in form.new_password.errors %}
|
||||
<small>{{ error }}</small>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
{{ form.confirm_password.label(class="form-label") }}
|
||||
{{ form.confirm_password(class="form-control", placeholder="Confirm new password") }}
|
||||
{% if form.confirm_password.errors %}
|
||||
<div class="text-danger">
|
||||
{% for error in form.confirm_password.errors %}
|
||||
<small>{{ error }}</small>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
{{ form.email.label(class="form-label") }}
|
||||
{{ form.email(class="form-control", placeholder="admin@example.com") }}
|
||||
{% if form.email.errors %}
|
||||
<div class="text-danger">
|
||||
{% for error in form.email.errors %}
|
||||
<small>{{ error }}</small>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<button type="submit" class="btn btn-primary">Update Profile</button>
|
||||
<a href="/admin" class="btn btn-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Current Profile Info -->
|
||||
<div class="profile-section">
|
||||
<h3>Current Profile Information</h3>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p><strong>Username:</strong> admin</p>
|
||||
<p><strong>Email:</strong> {{ current_email or 'Not set' }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p><strong>Account Type:</strong> Administrator</p>
|
||||
<p><strong>Access Level:</strong> Full Admin Rights</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Password Requirements -->
|
||||
<div class="profile-section">
|
||||
<h3>Password Requirements</h3>
|
||||
<ul class="list-unstyled">
|
||||
<li><i class="bi bi-check-circle text-success"></i> Minimum 6 characters</li>
|
||||
<li><i class="bi bi-check-circle text-success"></i> Must match confirmation</li>
|
||||
<li><i class="bi bi-check-circle text-success"></i> Current password must be correct</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user