Add System Settings. Enable/Disable User registration.

This commit is contained in:
Jens Luedicke
2025-06-28 11:30:34 +02:00
parent ff80964956
commit e7593dc840
6 changed files with 201 additions and 7 deletions

49
app.py
View File

@@ -1,5 +1,5 @@
from flask import Flask, render_template, request, redirect, url_for, jsonify, flash, session, g from flask import Flask, render_template, request, redirect, url_for, jsonify, flash, session, g
from models import db, TimeEntry, WorkConfig, User from models import db, TimeEntry, WorkConfig, User, SystemSettings
import logging import logging
from datetime import datetime, time, timedelta from datetime import datetime, time, timedelta
import os import os
@@ -42,6 +42,23 @@ mail = Mail(app)
# Initialize the database with the app # Initialize the database with the app
db.init_app(app) db.init_app(app)
# Add this function to initialize system settings
def init_system_settings():
# Check if registration_enabled setting exists, if not create it
if not SystemSettings.query.filter_by(key='registration_enabled').first():
registration_setting = SystemSettings(
key='registration_enabled',
value='true',
description='Controls whether new user registration is allowed'
)
db.session.add(registration_setting)
db.session.commit()
# Call this function during app initialization (add it where you initialize the app)
@app.before_first_request
def initialize_app():
init_system_settings()
# Authentication decorator # Authentication decorator
def login_required(f): def login_required(f):
@wraps(f) @wraps(f)
@@ -123,6 +140,14 @@ def logout():
@app.route('/register', methods=['GET', 'POST']) @app.route('/register', methods=['GET', 'POST'])
def register(): def register():
# Check if registration is enabled
reg_setting = SystemSettings.query.filter_by(key='registration_enabled').first()
registration_enabled = reg_setting and reg_setting.value == 'true'
if not registration_enabled:
flash('Registration is currently disabled by the administrator.', 'error')
return redirect(url_for('login'))
if request.method == 'POST': if request.method == 'POST':
username = request.form.get('username') username = request.form.get('username')
email = request.form.get('email') email = request.form.get('email')
@@ -681,5 +706,27 @@ def toggle_user_status(user_id):
return redirect(url_for('admin_users')) return redirect(url_for('admin_users'))
# Add this route to manage system settings
@app.route('/admin/settings', methods=['GET', 'POST'])
@admin_required
def admin_settings():
if request.method == 'POST':
# Update registration setting
registration_enabled = 'registration_enabled' in request.form
reg_setting = SystemSettings.query.filter_by(key='registration_enabled').first()
if reg_setting:
reg_setting.value = 'true' if registration_enabled else 'false'
db.session.commit()
flash('System settings updated successfully!', 'success')
# Get current settings
settings = {}
for setting in SystemSettings.query.all():
if setting.key == 'registration_enabled':
settings['registration_enabled'] = setting.value == 'true'
return render_template('admin_settings.html', title='System Settings', settings=settings)
if __name__ == '__main__': if __name__ == '__main__':
app.run(debug=True) app.run(debug=True)

View File

@@ -1,7 +1,7 @@
from app import app, db from app import app, db
import sqlite3 import sqlite3
import os import os
from models import User, TimeEntry, WorkConfig from models import User, TimeEntry, WorkConfig, SystemSettings
from werkzeug.security import generate_password_hash from werkzeug.security import generate_password_hash
from datetime import datetime from datetime import datetime
@@ -13,6 +13,9 @@ def migrate_database():
print("Database doesn't exist. Creating new database.") print("Database doesn't exist. Creating new database.")
with app.app_context(): with app.app_context():
db.create_all() db.create_all()
# Initialize system settings
init_system_settings()
return return
print("Migrating existing database...") print("Migrating existing database...")
@@ -104,6 +107,20 @@ def migrate_database():
print("Adding is_blocked column to user table...") print("Adding is_blocked column to user table...")
cursor.execute("ALTER TABLE user ADD COLUMN is_blocked BOOLEAN DEFAULT 0") cursor.execute("ALTER TABLE user ADD COLUMN is_blocked BOOLEAN DEFAULT 0")
# Check if the system_settings table exists
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='system_settings'")
if not cursor.fetchone():
print("Creating system_settings table...")
cursor.execute("""
CREATE TABLE system_settings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
key VARCHAR(50) UNIQUE NOT NULL,
value VARCHAR(255) NOT NULL,
description VARCHAR(255),
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
# Commit changes and close connection # Commit changes and close connection
conn.commit() conn.commit()
conn.close() conn.close()
@@ -112,6 +129,9 @@ def migrate_database():
# Create tables if they don't exist # Create tables if they don't exist
db.create_all() db.create_all()
# Initialize system settings
init_system_settings()
# Check if admin user exists # Check if admin user exists
admin = User.query.filter_by(username='admin').first() admin = User.query.filter_by(username='admin').first()
if not admin: if not admin:
@@ -154,6 +174,21 @@ def migrate_database():
print(f"Associated {len(orphan_configs)} existing work configs with admin user") print(f"Associated {len(orphan_configs)} existing work configs with admin user")
print(f"Marked {len(existing_users)} existing users as verified") print(f"Marked {len(existing_users)} existing users as verified")
def init_system_settings():
"""Initialize system settings with default values if they don't exist"""
# Check if registration_enabled setting exists
reg_setting = SystemSettings.query.filter_by(key='registration_enabled').first()
if not reg_setting:
print("Adding registration_enabled system setting...")
reg_setting = SystemSettings(
key='registration_enabled',
value='true', # Default to enabled
description='Controls whether new user registration is allowed'
)
db.session.add(reg_setting)
db.session.commit()
print("Registration setting initialized to enabled")
if __name__ == "__main__": if __name__ == "__main__":
migrate_database() migrate_database()
print("Database migration completed") print("Database migration completed")

View File

@@ -49,6 +49,16 @@ class User(db.Model):
def __repr__(self): def __repr__(self):
return f'<User {self.username}>' return f'<User {self.username}>'
class SystemSettings(db.Model):
id = db.Column(db.Integer, primary_key=True)
key = db.Column(db.String(50), unique=True, nullable=False)
value = db.Column(db.String(255), nullable=False)
description = db.Column(db.String(255))
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
def __repr__(self):
return f'<SystemSettings {self.key}={self.value}>'
class TimeEntry(db.Model): class TimeEntry(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
arrival_time = db.Column(db.DateTime, nullable=False) arrival_time = db.Column(db.DateTime, nullable=False)

View File

@@ -523,3 +523,77 @@ input[type="time"]::-webkit-datetime-edit {
background-color: #f8d7da; background-color: #f8d7da;
color: #721c24; color: #721c24;
} }
.settings-card {
background-color: #f8f9fa;
border-radius: 5px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
}
.setting-description {
color: #6c757d;
font-size: 0.9em;
margin-top: 5px;
}
.checkbox-container {
display: block;
position: relative;
padding-left: 35px;
margin-bottom: 12px;
cursor: pointer;
font-size: 16px;
user-select: none;
}
.checkbox-container input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
.checkmark {
position: absolute;
top: 0;
left: 0;
height: 25px;
width: 25px;
background-color: #eee;
border-radius: 4px;
}
.checkbox-container:hover input ~ .checkmark {
background-color: #ccc;
}
.checkbox-container input:checked ~ .checkmark {
background-color: #2196F3;
}
.checkmark:after {
content: "";
position: absolute;
display: none;
}
.checkbox-container input:checked ~ .checkmark:after {
display: block;
}
.checkbox-container .checkmark:after {
left: 9px;
top: 5px;
width: 5px;
height: 10px;
border: solid white;
border-width: 0 3px 3px 0;
transform: rotate(45deg);
}
.form-actions {
margin-top: 20px;
}

View File

@@ -11,14 +11,11 @@
<a href="{{ url_for('admin_users') }}" class="btn btn-primary">Manage Users</a> <a href="{{ url_for('admin_users') }}" class="btn btn-primary">Manage Users</a>
</div> </div>
<!-- You can add more admin cards here in the future -->
<!-- For example:
<div class="admin-card"> <div class="admin-card">
<h2>System Settings</h2> <h2>System Settings</h2>
<p>Configure application-wide settings.</p> <p>Configure application-wide settings like registration and more.</p>
<a href="#" class="btn btn-primary">Configure</a> <a href="{{ url_for('admin_settings') }}" class="btn btn-primary">Configure</a>
</div> </div>
-->
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -0,0 +1,31 @@
{% extends "layout.html" %}
{% block content %}
<div class="admin-container">
<h1>System Settings</h1>
<form method="POST" action="{{ url_for('admin_settings') }}">
<div class="settings-card">
<h2>Registration Settings</h2>
<div class="form-group">
<label class="checkbox-container">
<input type="checkbox" name="registration_enabled"
{% if settings.registration_enabled %}checked{% endif %}>
<span class="checkmark"></span>
Enable User Registration
</label>
<p class="setting-description">
When enabled, new users can register accounts. When disabled, only administrators can create new accounts.
</p>
</div>
<!-- You can add more settings here in the future -->
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Save Settings</button>
</div>
</form>
</div>
{% endblock %}