bemade-addons/unifi_integration/models/unifi_api_config.py
2025-03-26 08:46:03 -04:00

232 lines
7.9 KiB
Python

# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
import logging
_logger = logging.getLogger(__name__)
class UnifiApiConfig(models.Model):
"""Configuration API pour le système UniFi
Ce modèle stocke les configurations API pour les différents types d'API UniFi.
Il centralise les paramètres de connexion et les options de sécurité.
"""
_name = 'unifi.api.config'
_description = 'UniFi API Configuration'
_inherit = ['mail.thread', 'mail.activity.mixin']
name = fields.Char(
string='Name',
required=True,
help='Nom de la configuration API'
)
api_type = fields.Selection(
selection=[
('controller', 'UniFi Controller'),
('site_manager', 'UniFi Site Manager'),
],
string='API Type',
required=True,
default='controller',
help='Type d\'API UniFi'
)
controller_type = fields.Selection(
selection=[
('udm', 'UniFi Dream Machine'),
('cloud_key', 'Cloud Key'),
('other', 'Other'),
],
string='Controller Type',
default='udm',
help='Type de contrôleur UniFi (applicable uniquement pour le type d\'API Controller)',
)
base_url = fields.Char(
string='Base URL',
required=True,
help='URL de base pour les appels API (ex: https://unifi.example.com)'
)
api_version = fields.Char(
string='API Version',
default='v1',
help='Version de l\'API à utiliser'
)
username = fields.Char(
string='Username',
help='Nom d\'utilisateur pour l\'authentification API'
)
password = fields.Char(
string='Password',
help='Mot de passe pour l\'authentification API',
)
token = fields.Char(
string='API Token',
help='Jeton d\'authentification pour l\'API (si applicable)',
)
verify_ssl = fields.Boolean(
string='Verify SSL',
default=True,
help='Vérifier les certificats SSL lors des appels API'
)
timeout = fields.Integer(
string='Timeout',
default=30,
help='Délai d\'attente maximum pour les appels API (en secondes)'
)
retry_count = fields.Integer(
string='Retry Count',
default=3,
help='Nombre de tentatives en cas d\'échec d\'un appel API'
)
active = fields.Boolean(
string='Active',
default=True,
help='Indique si cette configuration est active'
)
company_id = fields.Many2one(
comodel_name='res.company',
string='Company',
default=lambda self: self.env.company,
help='Entreprise associée à cette configuration'
)
site_ids = fields.One2many(
comodel_name='unifi.site',
inverse_name='api_config_id',
string='Sites',
help='Sites utilisant cette configuration API',
readonly=True # Lecture seule car le champ n'existe pas encore dans unifi.site
)
log_ids = fields.One2many(
comodel_name='unifi.api.log',
inverse_name='api_config_id',
string='API Logs',
help='Journaux des appels API utilisant cette configuration',
readonly=True # Lecture seule car le champ n'existe pas encore dans unifi.api.log
)
notes = fields.Text(
string='Notes',
help='Notes additionnelles sur cette configuration API'
)
@api.constrains('base_url')
def _check_base_url(self):
"""Vérifie que l'URL de base est valide"""
for record in self:
if not record.base_url:
continue
if not (record.base_url.startswith('http://') or record.base_url.startswith('https://')):
raise ValidationError(_("L'URL de base doit commencer par 'http://' ou 'https://'."))
def test_connection(self):
"""Teste la connexion à l'API UniFi"""
self.ensure_one()
try:
# Logique de test de connexion selon le type d'API
if self.api_type == 'controller':
# Importer la classe UnifiClient
from ..controllers.main import UnifiClient
# Extraire le host et le port de l'URL de base
from urllib.parse import urlparse
parsed_url = urlparse(self.base_url)
host = parsed_url.netloc.split(':')[0] if ':' in parsed_url.netloc else parsed_url.netloc
port = parsed_url.port or 443
# Créer une instance de l'API
api = UnifiClient(
host=host,
port=port,
username=self.username,
password=self.password,
verify_ssl=self.verify_ssl
)
# Tester l'authentification
if not api.login():
raise Exception(_("Échec de l'authentification au contrôleur UniFi"))
# Tester la récupération du statut du système
try:
response = api._make_api_request('GET', api.API_SYSTEM_INFO_ENDPOINT)
if not response.ok:
raise Exception(_("Échec de la récupération du statut du système. Code: %s") % response.status_code)
except Exception as e:
raise Exception(_("Échec de la récupération du statut du système: %s") % str(e))
# Pas besoin de déconnexion explicite avec cette API
elif self.api_type == 'site_manager':
# TODO(dev): Implémenter le test de connexion pour l'API Site Manager
# Cette partie sera implémentée ultérieurement
raise NotImplementedError(_("Le test de connexion pour l'API Site Manager n'est pas encore implémenté"))
# Si on arrive ici, la connexion est réussie
_logger.info("Connexion à l'API réussie pour %s", self.name)
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': _('Succès'),
'message': _('Connexion à l\'API réussie.'),
'sticky': False,
'type': 'success',
}
}
except Exception as e:
# En cas d'erreur, on affiche un message
error_message = str(e)
_logger.error("Échec de la connexion à l'API pour %s: %s", self.name, error_message)
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': _('Erreur'),
'message': _('Échec de la connexion à l\'API: %s') % error_message,
'sticky': True,
'type': 'danger',
}
}
def get_headers(self):
"""Retourne les en-têtes HTTP pour les appels API"""
self.ensure_one()
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
}
# Ajouter le token d'authentification si disponible
if self.token:
if self.api_type == 'controller':
headers['Authorization'] = f'Bearer {self.token}'
elif self.api_type == 'site_manager':
headers['X-Auth-Token'] = self.token
return headers
def encrypt_sensitive_data(self):
"""Chiffre les données sensibles (mot de passe, token)"""
# Cette méthode pourrait être implémentée pour sécuriser davantage les données
# en utilisant des mécanismes de chiffrement d'Odoo
pass