first commit
This commit is contained in:
175
routes/admin_routes.py
Normal file
175
routes/admin_routes.py
Normal file
@@ -0,0 +1,175 @@
|
||||
# routes/admin_routes.py - Admin Dashboard Routes
|
||||
|
||||
from flask import Blueprint, request, jsonify, session, send_from_directory
|
||||
from db import get_db
|
||||
from auth import admin_required
|
||||
|
||||
admin_bp = Blueprint('admin', __name__)
|
||||
|
||||
|
||||
@admin_bp.route('/admin')
|
||||
@admin_required
|
||||
def admin_page():
|
||||
"""Serve admin dashboard page."""
|
||||
return send_from_directory('templates', 'admin.html')
|
||||
|
||||
|
||||
@admin_bp.route('/api/admin/users', methods=['GET'])
|
||||
@admin_required
|
||||
def list_users():
|
||||
"""List all users."""
|
||||
db = get_db()
|
||||
cursor = db.cursor()
|
||||
|
||||
cursor.execute('''
|
||||
SELECT id, username, role, is_active, created_at, last_login
|
||||
FROM users ORDER BY created_at DESC
|
||||
''')
|
||||
|
||||
users = []
|
||||
for row in cursor.fetchall():
|
||||
users.append({
|
||||
'id': row['id'],
|
||||
'username': row['username'],
|
||||
'role': row['role'],
|
||||
'is_active': bool(row['is_active']),
|
||||
'created_at': row['created_at'],
|
||||
'last_login': row['last_login']
|
||||
})
|
||||
|
||||
return jsonify({'users': users})
|
||||
|
||||
|
||||
@admin_bp.route('/api/admin/users', methods=['POST'])
|
||||
@admin_required
|
||||
def create_user():
|
||||
"""Create a new user."""
|
||||
data = request.json
|
||||
username = data.get('username', '').strip()
|
||||
password = data.get('password', '')
|
||||
role = data.get('role', 'user')
|
||||
|
||||
if not username or not password:
|
||||
return jsonify({'error': 'Username and password are required'}), 400
|
||||
|
||||
if len(username) < 3:
|
||||
return jsonify({'error': 'Username must be at least 3 characters'}), 400
|
||||
|
||||
if len(password) < 4:
|
||||
return jsonify({'error': 'Password must be at least 4 characters'}), 400
|
||||
|
||||
if role not in ('user', 'admin'):
|
||||
return jsonify({'error': 'Role must be "user" or "admin"'}), 400
|
||||
|
||||
db = get_db()
|
||||
cursor = db.cursor()
|
||||
|
||||
try:
|
||||
cursor.execute('''
|
||||
INSERT INTO users (username, password, role, is_active)
|
||||
VALUES (?, ?, ?, 1)
|
||||
''', (username, password, role))
|
||||
db.commit()
|
||||
|
||||
print(f"✅ New user created: {username} (role: {role})")
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'user_id': cursor.lastrowid,
|
||||
'message': f'User "{username}" created successfully'
|
||||
})
|
||||
except Exception as e:
|
||||
if 'UNIQUE constraint' in str(e):
|
||||
return jsonify({'error': f'Username "{username}" already exists'}), 400
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
@admin_bp.route('/api/admin/users/<int:user_id>', methods=['PUT'])
|
||||
@admin_required
|
||||
def update_user(user_id):
|
||||
"""Update a user."""
|
||||
data = request.json
|
||||
|
||||
db = get_db()
|
||||
cursor = db.cursor()
|
||||
|
||||
cursor.execute('SELECT id, username FROM users WHERE id = ?', (user_id,))
|
||||
user = cursor.fetchone()
|
||||
|
||||
if not user:
|
||||
return jsonify({'error': 'User not found'}), 404
|
||||
|
||||
# Build update query dynamically
|
||||
updates = []
|
||||
params = []
|
||||
|
||||
if 'username' in data:
|
||||
username = data['username'].strip()
|
||||
if len(username) < 3:
|
||||
return jsonify({'error': 'Username must be at least 3 characters'}), 400
|
||||
updates.append('username = ?')
|
||||
params.append(username)
|
||||
|
||||
if 'password' in data and data['password']:
|
||||
password = data['password']
|
||||
if len(password) < 4:
|
||||
return jsonify({'error': 'Password must be at least 4 characters'}), 400
|
||||
updates.append('password = ?')
|
||||
params.append(password)
|
||||
|
||||
if 'role' in data:
|
||||
role = data['role']
|
||||
if role not in ('user', 'admin'):
|
||||
return jsonify({'error': 'Role must be "user" or "admin"'}), 400
|
||||
# Prevent demoting self
|
||||
if user_id == session.get('user_id') and role != 'admin':
|
||||
return jsonify({'error': 'Cannot change your own role'}), 400
|
||||
updates.append('role = ?')
|
||||
params.append(role)
|
||||
|
||||
if 'is_active' in data:
|
||||
# Prevent deactivating self
|
||||
if user_id == session.get('user_id') and not data['is_active']:
|
||||
return jsonify({'error': 'Cannot deactivate your own account'}), 400
|
||||
updates.append('is_active = ?')
|
||||
params.append(1 if data['is_active'] else 0)
|
||||
|
||||
if not updates:
|
||||
return jsonify({'error': 'No fields to update'}), 400
|
||||
|
||||
params.append(user_id)
|
||||
|
||||
try:
|
||||
cursor.execute(f"UPDATE users SET {', '.join(updates)} WHERE id = ?", params)
|
||||
db.commit()
|
||||
|
||||
return jsonify({'success': True, 'message': 'User updated successfully'})
|
||||
except Exception as e:
|
||||
if 'UNIQUE constraint' in str(e):
|
||||
return jsonify({'error': 'Username already exists'}), 400
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
@admin_bp.route('/api/admin/users/<int:user_id>', methods=['DELETE'])
|
||||
@admin_required
|
||||
def delete_user(user_id):
|
||||
"""Delete a user."""
|
||||
# Prevent deleting self
|
||||
if user_id == session.get('user_id'):
|
||||
return jsonify({'error': 'Cannot delete your own account'}), 400
|
||||
|
||||
db = get_db()
|
||||
cursor = db.cursor()
|
||||
|
||||
cursor.execute('SELECT id, username FROM users WHERE id = ?', (user_id,))
|
||||
user = cursor.fetchone()
|
||||
|
||||
if not user:
|
||||
return jsonify({'error': 'User not found'}), 404
|
||||
|
||||
cursor.execute('DELETE FROM users WHERE id = ?', (user_id,))
|
||||
db.commit()
|
||||
|
||||
print(f"🗑️ User deleted: {user['username']}")
|
||||
|
||||
return jsonify({'success': True, 'message': f'User "{user["username"]}" deleted'})
|
||||
Reference in New Issue
Block a user