176 lines
5.4 KiB
Python
176 lines
5.4 KiB
Python
# 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'})
|