package repositories import ( "backend/models" "bytes" "database/sql" "encoding/json" "fmt" "log" "net/http" "os" "time" "github.com/jmoiron/sqlx" "github.com/lib/pq" _ "github.com/lib/pq" ) type NodeRepository struct { DB *sqlx.DB } func (r *NodeRepository) UpdateNodeLastSeen(nodeId string) error { query, _ := os.ReadFile("sql/update_service_status.sql") _, err := r.DB.Exec(string(query), nodeId, time.Now().UTC()) return err } func (r *NodeRepository) UpdateServiceStatus(serviceUpdate models.ServiceUpdateRequest) error { query, _ := os.ReadFile("sql/update_service_status.sql") var ( serviceIds []int statuses []int timestamps []time.Time ) for _, s := range serviceUpdate.Services { serviceIds = append(serviceIds, s.ServiceId) statuses = append(statuses, int(s.Status.Status)) timestamps = append(timestamps, s.Status.Timestamp) } _, err := r.DB.Exec(string(query), pq.Array(serviceIds), pq.Array(statuses), pq.Array(timestamps)) return err } func (r *NodeRepository) RegisterNode(node models.NodeInfo) (int, error) { log.Printf("Register node id %v, name %v, address %v", node.Id, node.Name, node.Address) query, err := os.ReadFile("sql/register_node.sql") if err != nil { log.Printf("❌ Erreur de lecture SQL: %v", err) return 0, err } var id int err = r.DB.QueryRow(string(query), node.Name, node.Address, 0, node.Id).Scan(&id) if err != nil { if err == sql.ErrNoRows { return node.Id, nil } return 0, err } return id, nil } func (r *NodeRepository) RegisterService(service models.Service, nodeId int) (int, error) { log.Printf("Register service id %v, name %v, nodeId %v with command %v", service.Id, service.Name, nodeId, service.Command) query, err := os.ReadFile("sql/register_service.sql") if err != nil { log.Printf("❌ Erreur de lecture SQL: %v", err) return 0, err } var id int err = r.DB.QueryRow(string(query), nodeId, service.Name, 0, service.Command, service.Id).Scan(&id) if err != nil { if err == sql.ErrNoRows { return service.Id, nil } return 0, err } return id, nil } func (r *NodeRepository) RetriveNodeList() (map[string]models.FullNodeInfo, error) { query, err := os.ReadFile("sql/retrieve_node_list.sql") rows, err := r.DB.Query(string(query)) if err != nil { return nil, err } defer rows.Close() var nodes = make(map[string]models.FullNodeInfo) for rows.Next() { log.Printf("new node") var node models.FullNodeInfo var servicesData []byte err := rows.Scan( &node.Id, &node.Name, &node.Address, &node.Status, &node.LastSeen, &servicesData, ) if err != nil { log.Printf("error on request") return nil, err } if err := json.Unmarshal(servicesData, &node.Services); err != nil { return nil, fmt.Errorf("error decoding services for node %d: %v", node.Id, err) } nodes[node.Name] = node } return nodes, nil } func deleteServiceFromNode(node *models.NodeInfo, serviceId int) error { apiURL := node.Address + "/services" bodyData := map[string]int{ "service_id": serviceId, } jsonData, err := json.Marshal(bodyData) if err != nil { return err } req, err := http.NewRequest(http.MethodDelete, apiURL, bytes.NewBuffer(jsonData)) req.Header.Set("Content-Type", "application/json") if err != nil { return fmt.Errorf("Erreur lors de la création de la requête DELETE : %v", err) } client := http.Client{ Timeout: 10 * time.Second, } resp, err := client.Do(req) if err != nil { return fmt.Errorf("Erreur lors de l'exécution de la requête DELETE : %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusOK { return fmt.Errorf("Requête DELETE échouée pour %s. Statut : %s", apiURL, resp.Status) } log.Printf("Service %d supprimé avec succès. Réponse : %s", serviceId, resp.Status) return nil } func (r *NodeRepository) DeleteService(node models.NodeInfo, serviceId int) error { tx, err := r.DB.Begin() log.Printf("Delete service id %v, on nodeId %v", serviceId, node.Id) query, err := os.ReadFile("sql/delete_service.sql") if err != nil { log.Printf("❌ Erreur de lecture SQL: %v", err) return err } _, err = tx.Exec(string(query), node.Id, serviceId) if err != nil { tx.Rollback() return err } err = deleteServiceFromNode(&node, serviceId) if err != nil { tx.Rollback() return fmt.Errorf("échec sur le node, annulation du changement en base de données : %w", err) } return tx.Commit() } func (r *NodeRepository) RetriveNode(nodeId int) (models.NodeInfo, error) { query := "SELECT * FROM nodes WHERE id = $1" var node models.SimpleNodeInfo err := r.DB.Get(&node, query, nodeId) if err != nil { return models.NodeInfo{}, err } var result models.NodeInfo result.Address = node.Address result.Id = node.Id result.LastSeen = node.LastSeen.String() result.Name = node.Name result.Status = node.Status return result, nil } func (r *NodeRepository) RetriveApiKeyList() ([]models.ApiKey, error) { query := "SELECT id, key_name, key_value FROM node_api_keys" apiKeys := make([]models.ApiKey, 0) err := r.DB.Select(&apiKeys, query) if err != nil { return nil, err } return apiKeys, nil } func addServiceToNode(node *models.NodeInfo, service *models.Service, nodeApiKey string) error { apiURL := node.Address + "/add" bodyData := map[string]string{ "name": service.Name, "command": service.Command, } jsonData, err := json.Marshal(bodyData) if err != nil { return err } log.Printf("Node api key: %v", nodeApiKey) req, err := http.NewRequest(http.MethodPost, apiURL, bytes.NewBuffer(jsonData)) req.Header.Set("Content-Type", "application/json") req.Header.Set("X-Node-API-Key", nodeApiKey) if err != nil { return fmt.Errorf("Erreur lors de la création de la requête POST : %v", err) } client := http.Client{ Timeout: 10 * time.Second, } resp, err := client.Do(req) if err != nil { return fmt.Errorf("Erreur lors de l'exécution de la requête POST : %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusOK { return fmt.Errorf("Requête POST échouée pour %s. Statut : %s", apiURL, resp.Status) } log.Printf("Service %v ajouté avec succès. Réponse : %s", service.Name, resp.Status) return nil } func (r *NodeRepository) AddServiceToNode(service *models.Service, node *models.NodeInfo, nodeApiKey string) error { //tx, err := r.DB.Begin() //r.RegisterService(*service, node.Id) log.Printf("add service name %v, on nodeId %v", service.Name, node.Id) err := addServiceToNode(node, service, nodeApiKey) if err != nil { //tx.Rollback() return fmt.Errorf("échec sur le node, annulation du changement en base de données : %w", err) } //return tx.Commit() return err }