191 lines
6.8 KiB
Python
191 lines
6.8 KiB
Python
from flask import Blueprint, request, jsonify
|
|
from flask_jwt_extended import jwt_required, get_jwt_identity
|
|
from datetime import datetime, timedelta
|
|
from sqlalchemy import func
|
|
from models import db, TodoItem, TodoAuditLog, TodoMailLog, TodoImportJob
|
|
from utils.logger import get_logger
|
|
|
|
admin_bp = Blueprint('admin', __name__)
|
|
logger = get_logger(__name__)
|
|
|
|
# Admin users (in production, this should be in database or config)
|
|
ADMIN_USERS = ['admin', 'administrator']
|
|
|
|
def is_admin(identity):
|
|
"""Check if user is admin"""
|
|
return identity.lower() in ADMIN_USERS
|
|
|
|
@admin_bp.route('/stats', methods=['GET'])
|
|
@jwt_required()
|
|
def get_stats():
|
|
"""Get system statistics"""
|
|
try:
|
|
identity = get_jwt_identity()
|
|
|
|
if not is_admin(identity):
|
|
return jsonify({'error': 'Admin access required'}), 403
|
|
|
|
# Get date range
|
|
days = request.args.get('days', 30, type=int)
|
|
start_date = datetime.utcnow() - timedelta(days=days)
|
|
|
|
# Todo statistics
|
|
todo_stats = db.session.query(
|
|
func.count(TodoItem.id).label('total'),
|
|
func.sum(func.if_(TodoItem.status == 'NEW', 1, 0)).label('new'),
|
|
func.sum(func.if_(TodoItem.status == 'DOING', 1, 0)).label('doing'),
|
|
func.sum(func.if_(TodoItem.status == 'BLOCKED', 1, 0)).label('blocked'),
|
|
func.sum(func.if_(TodoItem.status == 'DONE', 1, 0)).label('done')
|
|
).filter(TodoItem.created_at >= start_date).first()
|
|
|
|
# User activity
|
|
active_users = db.session.query(
|
|
func.count(func.distinct(TodoAuditLog.actor_ad))
|
|
).filter(TodoAuditLog.created_at >= start_date).scalar()
|
|
|
|
# Email statistics
|
|
email_stats = db.session.query(
|
|
func.count(TodoMailLog.id).label('total'),
|
|
func.sum(func.if_(TodoMailLog.status == 'SENT', 1, 0)).label('sent'),
|
|
func.sum(func.if_(TodoMailLog.status == 'FAILED', 1, 0)).label('failed')
|
|
).filter(TodoMailLog.created_at >= start_date).first()
|
|
|
|
# Import statistics
|
|
import_stats = db.session.query(
|
|
func.count(TodoImportJob.id).label('total'),
|
|
func.sum(func.if_(TodoImportJob.status == 'COMPLETED', 1, 0)).label('completed'),
|
|
func.sum(func.if_(TodoImportJob.status == 'FAILED', 1, 0)).label('failed')
|
|
).filter(TodoImportJob.created_at >= start_date).first()
|
|
|
|
return jsonify({
|
|
'period_days': days,
|
|
'todos': {
|
|
'total': todo_stats.total or 0,
|
|
'new': todo_stats.new or 0,
|
|
'doing': todo_stats.doing or 0,
|
|
'blocked': todo_stats.blocked or 0,
|
|
'done': todo_stats.done or 0
|
|
},
|
|
'users': {
|
|
'active': active_users or 0
|
|
},
|
|
'emails': {
|
|
'total': email_stats.total or 0,
|
|
'sent': email_stats.sent or 0,
|
|
'failed': email_stats.failed or 0
|
|
},
|
|
'imports': {
|
|
'total': import_stats.total or 0,
|
|
'completed': import_stats.completed or 0,
|
|
'failed': import_stats.failed or 0
|
|
}
|
|
}), 200
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error fetching stats: {str(e)}")
|
|
return jsonify({'error': 'Failed to fetch statistics'}), 500
|
|
|
|
@admin_bp.route('/audit-logs', methods=['GET'])
|
|
@jwt_required()
|
|
def get_audit_logs():
|
|
"""Get audit logs"""
|
|
try:
|
|
identity = get_jwt_identity()
|
|
|
|
if not is_admin(identity):
|
|
return jsonify({'error': 'Admin access required'}), 403
|
|
|
|
page = request.args.get('page', 1, type=int)
|
|
per_page = request.args.get('per_page', 50, type=int)
|
|
actor = request.args.get('actor')
|
|
action = request.args.get('action')
|
|
todo_id = request.args.get('todo_id')
|
|
|
|
query = TodoAuditLog.query
|
|
|
|
if actor:
|
|
query = query.filter(TodoAuditLog.actor_ad == actor)
|
|
if action:
|
|
query = query.filter(TodoAuditLog.action == action)
|
|
if todo_id:
|
|
query = query.filter(TodoAuditLog.todo_id == todo_id)
|
|
|
|
query = query.order_by(TodoAuditLog.created_at.desc())
|
|
|
|
pagination = query.paginate(page=page, per_page=per_page, error_out=False)
|
|
|
|
logs = []
|
|
for log in pagination.items:
|
|
logs.append({
|
|
'id': log.id,
|
|
'actor_ad': log.actor_ad,
|
|
'todo_id': log.todo_id,
|
|
'action': log.action,
|
|
'detail': log.detail,
|
|
'created_at': log.created_at.isoformat()
|
|
})
|
|
|
|
return jsonify({
|
|
'logs': logs,
|
|
'total': pagination.total,
|
|
'page': page,
|
|
'per_page': per_page,
|
|
'pages': pagination.pages
|
|
}), 200
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error fetching audit logs: {str(e)}")
|
|
return jsonify({'error': 'Failed to fetch audit logs'}), 500
|
|
|
|
@admin_bp.route('/mail-logs', methods=['GET'])
|
|
@jwt_required()
|
|
def get_mail_logs():
|
|
"""Get mail logs"""
|
|
try:
|
|
identity = get_jwt_identity()
|
|
|
|
if not is_admin(identity):
|
|
return jsonify({'error': 'Admin access required'}), 403
|
|
|
|
page = request.args.get('page', 1, type=int)
|
|
per_page = request.args.get('per_page', 50, type=int)
|
|
status = request.args.get('status')
|
|
type_ = request.args.get('type')
|
|
|
|
query = TodoMailLog.query
|
|
|
|
if status:
|
|
query = query.filter(TodoMailLog.status == status)
|
|
if type_:
|
|
query = query.filter(TodoMailLog.type == type_)
|
|
|
|
query = query.order_by(TodoMailLog.created_at.desc())
|
|
|
|
pagination = query.paginate(page=page, per_page=per_page, error_out=False)
|
|
|
|
logs = []
|
|
for log in pagination.items:
|
|
logs.append({
|
|
'id': log.id,
|
|
'todo_id': log.todo_id,
|
|
'type': log.type,
|
|
'triggered_by_ad': log.triggered_by_ad,
|
|
'recipients': log.recipients,
|
|
'subject': log.subject,
|
|
'status': log.status,
|
|
'error_text': log.error_text,
|
|
'created_at': log.created_at.isoformat(),
|
|
'sent_at': log.sent_at.isoformat() if log.sent_at else None
|
|
})
|
|
|
|
return jsonify({
|
|
'logs': logs,
|
|
'total': pagination.total,
|
|
'page': page,
|
|
'per_page': per_page,
|
|
'pages': pagination.pages
|
|
}), 200
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error fetching mail logs: {str(e)}")
|
|
return jsonify({'error': 'Failed to fetch mail logs'}), 500 |