""" 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/') @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/') @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), })