gmao/app_new/api/routes.py

161 lines
5.6 KiB
Python

"""
API REST v1 - GMAO Collège
Endpoints publics avec authentification par clé API.
"""
from flask import Blueprint, request, jsonify, current_app
from flask_login import login_required, current_user
from functools import wraps
from werkzeug.security import check_password_hash
from app_new.extensions import db
api_bp = Blueprint('api_v1', __name__, url_prefix='/api/v1')
def api_key_required(f):
"""Authentification par clé API (header X-API-Key) ou session login."""
@wraps(f)
def decorated(*args, **kwargs):
# Accepter soit un header X-API-Key, soit une session Flask-Login
api_key = request.headers.get('X-API-Key')
if api_key:
from app_new.core.models.settings import AppSettings
api_key_hash = AppSettings.get('api_key_hash')
if api_key_hash and check_password_hash(api_key_hash, api_key):
return f(*args, **kwargs)
return jsonify({'error': 'Clé API invalide'}), 401
# Sinon, fallback sur session login
if current_user.is_authenticated:
return f(*args, **kwargs)
return jsonify({'error': 'Authentification requise (X-API-Key ou login)'}), 401
return decorated
@api_bp.route('/status')
@api_key_required
def status():
"""État global du système."""
from app_new.core.models.maintenance import Intervention
from app_new.core.models.equipment import Equipment
from app_new.core.models.college import Room, Building
return jsonify({
'interventions_total': Intervention.query.count(),
'interventions_en_cours': Intervention.query.filter_by(status='en_cours').count(),
'interventions_en_attente': Intervention.query.filter_by(status='en_attente').count(),
'equipments_total': Equipment.query.count(),
'equipments_panne': Equipment.query.filter(Equipment.status.in_(['en_panne', 'hors_service', 'hs'])).count(),
'rooms_total': Room.query.count(),
'buildings_total': Building.query.count(),
})
@api_bp.route('/interventions')
@api_key_required
def list_interventions():
"""Liste des interventions avec pagination."""
from app_new.core.models.maintenance import Intervention
page = max(request.args.get('page', 1, type=int) or 1, 1)
per_page = min(max(request.args.get('per_page', 20, type=int) or 20, 1), 100)
status = request.args.get('status')
query = Intervention.query
if status:
query = query.filter_by(status=status)
total = query.count()
items = query.order_by(Intervention.created_at.desc())\
.offset((page - 1) * per_page)\
.limit(per_page).all()
return jsonify({
'total': total,
'page': page,
'per_page': per_page,
'items': [{
'id': i.id,
'title': i.title,
'description': (i.description or '')[:200],
'type': i.type,
'status': i.status,
'priority': i.priority,
'created_at': i.created_at.isoformat() if i.created_at else None,
'equipment_id': i.equipment_id,
'room_id': i.room_id,
} for i in items]
})
@api_bp.route('/interventions/<int:intervention_id>')
@api_key_required
def get_intervention(intervention_id):
"""Détail d'une intervention."""
from app_new.core.models.maintenance import Intervention
intervention = Intervention.query.get_or_404(intervention_id)
return jsonify({
'id': intervention.id,
'title': intervention.title,
'description': intervention.description,
'type': intervention.type,
'status': intervention.status,
'priority': intervention.priority,
'created_at': intervention.created_at.isoformat() if intervention.created_at else None,
'started_at': intervention.started_at.isoformat() if intervention.started_at else None,
'completed_at': intervention.completed_at.isoformat() if intervention.completed_at else None,
'equipment_id': intervention.equipment_id,
'room_id': intervention.room_id,
'company_id': intervention.company_id,
'assigned_to_id': intervention.assigned_to_id,
})
@api_bp.route('/equipments')
@api_key_required
def list_equipments():
"""Liste des équipements avec pagination."""
from app_new.core.models.equipment import Equipment
page = max(request.args.get('page', 1, type=int) or 1, 1)
per_page = min(max(request.args.get('per_page', 20, type=int) or 20, 1), 100)
status = request.args.get('status')
query = Equipment.query
if status:
query = query.filter_by(status=status)
total = query.count()
items = query.order_by(Equipment.name)\
.offset((page - 1) * per_page)\
.limit(per_page).all()
return jsonify({
'total': total,
'page': page,
'per_page': per_page,
'items': [{
'id': e.id,
'name': e.name,
'status': e.status,
'category_id': e.category_id,
'room_id': e.room_id,
} for e in items]
})
@api_bp.route('/equipments/<int:equipment_id>')
@api_key_required
def get_equipment(equipment_id):
"""Détail d'un équipement."""
from app_new.core.models.equipment import Equipment
equipment = Equipment.query.get_or_404(equipment_id)
return jsonify({
'id': equipment.id,
'name': equipment.name,
'status': equipment.status,
'serial_number': getattr(equipment, 'serial_number', None),
'category_id': equipment.category_id,
'room_id': equipment.room_id,
'description': getattr(equipment, 'description', None),
})