spotnotes.app/blueprints/admin.py

182 lines
6.9 KiB
Python
Raw Normal View History

2026-05-12 23:58:18 +00:00
from flask import Blueprint, jsonify, session
from models.user import User
from models.notepad import Notepad
from models.note import Note
from models.banned_ip import BannedIP
from extensions import db
admin_bp = Blueprint('admin', __name__, url_prefix='/admin')
def check_admin():
user_id = session.get('user_id')
if not user_id:
return False
user = User.query.get(user_id)
return user and user.is_admin
@admin_bp.before_request
def before_request():
if not check_admin():
return jsonify({'error': 'Unauthorized'}), 403
@admin_bp.route('/stats', methods=['GET'])
def get_stats():
return jsonify({
'user_count': User.query.count(),
2026-10-06 19:32:24 +00:00
'banned_user_count': User.query.filter_by(is_banned=True).count(),
2026-05-12 23:58:18 +00:00
'notepad_count': Notepad.query.count(),
2026-10-06 19:32:24 +00:00
'public_notepad_count': Notepad.query.filter_by(is_public=True).count(),
'private_notepad_count': Notepad.query.filter_by(is_public=False).count(),
'note_count': Note.query.count(),
'public_note_count': Note.query.filter_by(is_encrypted=False).count(),
'encrypted_note_count': Note.query.filter_by(is_encrypted=True).count(),
'reported_note_count': Note.query.filter_by(is_hidden=True).count()
2026-05-12 23:58:18 +00:00
})
@admin_bp.route('/users', methods=['GET'])
def get_users():
users = User.query.all()
user_list = [user.to_dict() for user in users]
# Add "Unowned" virtual user for managing public/unowned pads
unowned_count = Notepad.query.filter_by(user_id=None).count()
if unowned_count > 0:
user_list.insert(0, {
'id': 0,
'email': 'Unowned / Public Notepads',
'first_name': 'System',
'last_name': '',
'last_ip': None,
'is_admin': False,
'is_banned': False
})
return jsonify(user_list)
@admin_bp.route('/users/<int:user_id>/notepads', methods=['GET'])
def get_user_notepads(user_id):
# Handle virtual user ID 0 for unowned notepads
query_id = None if user_id == 0 else user_id
notepads = Notepad.query.filter_by(user_id=query_id).all()
return jsonify([notepad.to_dict() for notepad in notepads])
@admin_bp.route('/notepads/<int:notepad_id>/notes', methods=['GET'])
def get_notepad_notes(notepad_id):
notepad = Notepad.query.get_or_404(notepad_id)
notes = notepad.notes.all()
return jsonify([note.to_dict() for note in notes])
@admin_bp.route('/notes/<int:note_id>', methods=['DELETE'])
def delete_note(note_id):
note = Note.query.get_or_404(note_id)
db.session.delete(note)
db.session.commit()
return jsonify({'message': 'Note deleted'}), 200
@admin_bp.route('/notepads/<int:notepad_id>', methods=['DELETE'])
def delete_notepad(notepad_id):
notepad = Notepad.query.get_or_404(notepad_id)
# Clean up notes first to prevent FK errors if cascade is missing
for note in notepad.notes:
db.session.delete(note)
from models.favorite import Favorite
from models.notepad_key import NotepadKey
from models.collaborator import Collaborator
2026-05-12 23:58:18 +00:00
Favorite.query.filter_by(notepad_id=notepad.id).delete()
NotepadKey.query.filter_by(notepad_id=notepad.id).delete()
Collaborator.query.filter_by(notepad_id=notepad.id).delete()
2026-05-12 23:58:18 +00:00
db.session.delete(notepad)
db.session.commit()
return jsonify({'message': 'Notepad deleted'}), 200
@admin_bp.route('/users/<int:user_id>', methods=['DELETE'])
def delete_user(user_id):
user = User.query.get_or_404(user_id)
# Clean up user's notepads and notes
notepads = Notepad.query.filter_by(user_id=user.id).all()
from models.favorite import Favorite
from models.notepad_key import NotepadKey
from models.collaborator import Collaborator
2026-05-12 23:58:18 +00:00
for notepad in notepads:
for note in notepad.notes:
db.session.delete(note)
Favorite.query.filter_by(notepad_id=notepad.id).delete()
NotepadKey.query.filter_by(notepad_id=notepad.id).delete()
Collaborator.query.filter_by(notepad_id=notepad.id).delete()
2026-05-12 23:58:18 +00:00
db.session.delete(notepad)
Favorite.query.filter_by(user_id=user.id).delete()
NotepadKey.query.filter_by(user_id=user.id).delete()
Collaborator.query.filter_by(user_id=user.id).delete()
2026-05-12 23:58:18 +00:00
db.session.delete(user)
db.session.commit()
return jsonify({'message': 'User deleted'}), 200
@admin_bp.route('/users/<int:user_id>/ban', methods=['POST'])
def ban_user(user_id):
user = User.query.get_or_404(user_id)
user.is_banned = True
db.session.commit()
return jsonify({'message': 'User banned'}), 200
@admin_bp.route('/users/<int:user_id>/unban', methods=['POST'])
def unban_user(user_id):
user = User.query.get_or_404(user_id)
user.is_banned = False
db.session.commit()
return jsonify({'message': 'User unbanned'}), 200
@admin_bp.route('/users/<int:user_id>/ban_ip', methods=['POST'])
def ban_user_ip(user_id):
user = User.query.get_or_404(user_id)
if not user.last_ip:
return jsonify({'error': 'User has no recorded IP'}), 400
if not BannedIP.query.filter_by(ip_address=user.last_ip).first():
banned = BannedIP(ip_address=user.last_ip)
db.session.add(banned)
db.session.commit()
2026-10-06 19:32:24 +00:00
return jsonify({'message': f'IP {user.last_ip} banned'}), 200
@admin_bp.route('/users/<int:user_id>/toggle_admin', methods=['POST'])
def toggle_admin(user_id):
curr_user_id = session.get('user_id')
if curr_user_id == user_id:
return jsonify({'error': 'Cannot change your own admin status'}), 400
user = User.query.get_or_404(user_id)
user.is_admin = not user.is_admin
db.session.commit()
return jsonify({'message': f"Admin status set to {user.is_admin}", 'is_admin': user.is_admin}), 200
@admin_bp.route('/reported_notes', methods=['GET'])
def get_reported_notes():
reported = Note.query.filter_by(is_hidden=True).all()
results = []
for note in reported:
n_dict = note.to_dict()
n_dict['notepad_name'] = note.notepad.name if note.notepad else 'Unknown Notepad'
n_dict['author_email'] = note.author.email if note.author else 'Anonymous / Preseeded'
results.append(n_dict)
return jsonify(results)
@admin_bp.route('/notes/<int:note_id>/restore', methods=['POST'])
def restore_note(note_id):
note = Note.query.get_or_404(note_id)
note.is_hidden = False
db.session.commit()
return jsonify({'message': 'Note restored successfully'}), 200
@admin_bp.route('/banned_ips', methods=['GET'])
def get_banned_ips():
ips = BannedIP.query.order_by(BannedIP.created_at.desc()).all()
return jsonify([{'id': ip.id, 'ip_address': ip.ip_address, 'created_at': ip.created_at.isoformat()} for ip in ips])
@admin_bp.route('/banned_ips/<int:ip_id>', methods=['DELETE'])
def delete_banned_ip(ip_id):
banned = BannedIP.query.get_or_404(ip_id)
db.session.delete(banned)
db.session.commit()
return jsonify({'message': 'IP unbanned successfully'}), 200