132 lines
4.4 KiB
Python
132 lines
4.4 KiB
Python
"""Developer routes and panel."""
|
|
import os
|
|
import signal
|
|
import random
|
|
import string
|
|
from flask import Blueprint, render_template, jsonify, request, current_app
|
|
from database import get_db
|
|
from decorators import token_required, role_required
|
|
|
|
dev_bp = Blueprint('dev', __name__)
|
|
|
|
|
|
def get_socketio():
|
|
"""Get SocketIO instance from current app context."""
|
|
return current_app.extensions.get('socketio')
|
|
|
|
|
|
def generate_invite_code(custom=None):
|
|
"""Generate a random invite code or use custom one."""
|
|
if custom:
|
|
return custom.upper()
|
|
groups = [''.join(random.choices(string.ascii_uppercase, k=size)) for size in [3, 3, 5]]
|
|
return '-'.join(groups)
|
|
|
|
|
|
@dev_bp.route('/api/dev/system-stats', methods=['POST'])
|
|
@token_required
|
|
@role_required('Dev')
|
|
def dev_system_stats():
|
|
"""Get system statistics (dev only)."""
|
|
db_path = current_app.config['DATABASE']
|
|
size = os.path.getsize(db_path) if os.path.exists(db_path) else 0
|
|
connected_clients = current_app.extensions.get('connected_clients', set())
|
|
return jsonify({
|
|
'db_size_mb': round(size / (1024 * 1024), 2),
|
|
'active_users': len(connected_clients),
|
|
'uptime': 'N/A',
|
|
'message_count': get_db(current_app).execute('SELECT COUNT(*) FROM messages').fetchone()[0]
|
|
})
|
|
|
|
|
|
@dev_bp.route('/api/dev/list-codes', methods=['GET'])
|
|
@token_required
|
|
@role_required('Dev')
|
|
def dev_list_codes():
|
|
"""List unused registration codes (dev only)."""
|
|
return jsonify([{'code': c['code']} for c in get_db(current_app).execute(
|
|
'SELECT code FROM registration_codes WHERE is_used = 0 ORDER BY id DESC'
|
|
).fetchall()])
|
|
|
|
|
|
@dev_bp.route('/api/dev/generate-code', methods=['POST'])
|
|
@token_required
|
|
@role_required('Dev')
|
|
def dev_generate_code():
|
|
"""Generate registration codes (dev only)."""
|
|
data = request.json or {}
|
|
custom = data.get('custom')
|
|
db = get_db(current_app)
|
|
if custom:
|
|
code = generate_invite_code(custom)
|
|
db.execute('INSERT INTO registration_codes (code) VALUES (?)', (code,))
|
|
res = [{'code': code}]
|
|
else:
|
|
res = []
|
|
for _ in range(data.get('num', 10)):
|
|
code = generate_invite_code()
|
|
db.execute('INSERT INTO registration_codes (code) VALUES (?)', (code,))
|
|
res.append({'code': code})
|
|
db.commit()
|
|
return jsonify(res)
|
|
|
|
|
|
@dev_bp.route('/api/dev/global-broadcast', methods=['POST'])
|
|
@token_required
|
|
@role_required('Dev')
|
|
def dev_global_broadcast():
|
|
"""Send a system message to all users and channels (dev only)."""
|
|
msg = request.json.get('message')
|
|
db = get_db(current_app)
|
|
for row in db.execute('SELECT id FROM channels').fetchall():
|
|
db.execute('INSERT INTO messages (sender_id, channel_id, content, is_global) VALUES (-1, ?, ?, 0)',
|
|
(row['id'], msg))
|
|
for row in db.execute('SELECT id FROM users').fetchall():
|
|
db.execute('INSERT INTO messages (sender_id, receiver_id, content, is_global) VALUES (-1, ?, ?, 0)',
|
|
(row['id'], msg))
|
|
db.commit()
|
|
|
|
socketio = get_socketio()
|
|
if socketio:
|
|
socketio.emit('system_notification', {'message': msg, 'type': 'global_broadcast'})
|
|
|
|
return jsonify({'status': 'broadcasted'})
|
|
|
|
|
|
@dev_bp.route('/api/dev/restart', methods=['POST'])
|
|
@token_required
|
|
@role_required('Dev')
|
|
def dev_restart():
|
|
"""Trigger application restart using signal (dev only).
|
|
|
|
This sends SIGHUP to the current process, which should be caught by the
|
|
process manager (e.g., Gunicorn) to gracefully restart the application.
|
|
Requires the application to run under a process manager that respects signals.
|
|
"""
|
|
try:
|
|
# Send SIGHUP signal to current process for graceful restart
|
|
# The process manager (e.g., Gunicorn) should handle this signal
|
|
os.kill(os.getpid(), signal.SIGHUP)
|
|
return jsonify({'status': 'restart signal sent'}), 200
|
|
except (OSError, RuntimeError) as e:
|
|
return jsonify({'error': f'Failed to send restart signal: {str(e)}'}), 500
|
|
|
|
|
|
@dev_bp.route('/api/dev/broadcast', methods=['POST'])
|
|
@token_required
|
|
@role_required('Dev')
|
|
def dev_broadcast():
|
|
"""Send a system notification to all connected clients (dev only)."""
|
|
socketio = get_socketio()
|
|
if socketio:
|
|
socketio.emit('system_notification', {'message': request.json.get('message')})
|
|
return jsonify({'status': 'broadcasted'})
|
|
|
|
|
|
@dev_bp.route('/imdadev')
|
|
@token_required
|
|
@role_required('Dev')
|
|
def imdadev():
|
|
"""Dev panel page."""
|
|
return render_template('dev.html')
|