Skip to content

Commit 1d703a5

Browse files
Create admin.py
1 parent f62f6e0 commit 1d703a5

File tree

1 file changed

+122
-0
lines changed

1 file changed

+122
-0
lines changed

app/admin.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
from flask import Blueprint, render_template, request, jsonify, flash, redirect, url_for
2+
from flask_login import login_required, current_user
3+
from functools import wraps
4+
from app.models import db, User
5+
from werkzeug.security import generate_password_hash
6+
import secrets
7+
8+
admin_bp = Blueprint('admin', __name__, url_prefix='/admin')
9+
10+
def admin_required(f):
11+
@wraps(f)
12+
@login_required
13+
def decorated_function(*args, **kwargs):
14+
if not current_user.is_admin:
15+
flash('You need administrator privileges to access this page.', 'error')
16+
return redirect(url_for('main.dashboard'))
17+
return f(*args, **kwargs)
18+
return decorated_function
19+
20+
@admin_bp.route('/users')
21+
@admin_required
22+
def users():
23+
return render_template('admin/users.html')
24+
25+
@admin_bp.route('/api/users')
26+
@admin_required
27+
def get_users():
28+
users = User.query.all()
29+
return jsonify([user.to_dict() for user in users])
30+
31+
@admin_bp.route('/api/users', methods=['POST'])
32+
@admin_required
33+
def create_user():
34+
data = request.json
35+
36+
# Validate input
37+
if not data.get('username') or not data.get('password'):
38+
return jsonify({'error': 'Username and password are required'}), 400
39+
40+
# Check if username exists
41+
if User.query.filter_by(username=data['username']).first():
42+
return jsonify({'error': 'Username already exists'}), 400
43+
44+
# Create new user
45+
user = User(
46+
username=data['username'],
47+
is_admin=data.get('is_admin', False)
48+
)
49+
user.set_password(data['password'])
50+
51+
db.session.add(user)
52+
db.session.commit()
53+
54+
return jsonify({
55+
'success': True,
56+
'user': user.to_dict()
57+
})
58+
59+
@admin_bp.route('/api/users/<int:user_id>', methods=['PUT'])
60+
@admin_required
61+
def update_user(user_id):
62+
user = User.query.get_or_404(user_id)
63+
data = request.json
64+
65+
# Prevent removing last admin
66+
if user.is_admin and not data.get('is_admin', False):
67+
admin_count = User.query.filter_by(is_admin=True).count()
68+
if admin_count <= 1:
69+
return jsonify({'error': 'Cannot remove last administrator'}), 400
70+
71+
# Update username if provided and different
72+
if data.get('username') and data['username'] != user.username:
73+
if User.query.filter_by(username=data['username']).first():
74+
return jsonify({'error': 'Username already exists'}), 400
75+
user.username = data['username']
76+
77+
# Update password if provided
78+
if data.get('password'):
79+
user.set_password(data['password'])
80+
81+
# Update admin status
82+
if 'is_admin' in data:
83+
user.is_admin = data['is_admin']
84+
85+
# Reset 2FA if requested
86+
if data.get('reset_2fa'):
87+
user.totp_enabled = False
88+
user.totp_secret = None
89+
90+
db.session.commit()
91+
92+
return jsonify({
93+
'success': True,
94+
'user': user.to_dict()
95+
})
96+
97+
@admin_bp.route('/api/users/<int:user_id>', methods=['DELETE'])
98+
@admin_required
99+
def delete_user(user_id):
100+
user = User.query.get_or_404(user_id)
101+
102+
# Prevent self-deletion
103+
if user.id == current_user.id:
104+
return jsonify({'error': 'Cannot delete your own account'}), 400
105+
106+
# Prevent removing last admin
107+
if user.is_admin:
108+
admin_count = User.query.filter_by(is_admin=True).count()
109+
if admin_count <= 1:
110+
return jsonify({'error': 'Cannot delete last administrator'}), 400
111+
112+
db.session.delete(user)
113+
db.session.commit()
114+
115+
return jsonify({'success': True})
116+
117+
@admin_bp.route('/api/users/<int:user_id>/generate-password')
118+
@admin_required
119+
def generate_password(user_id):
120+
# Generate a secure random password
121+
password = secrets.token_urlsafe(12)
122+
return jsonify({'password': password})

0 commit comments

Comments
 (0)