package main import ( "database/sql" "fmt" "log" "strings" "github.com/lib/pq" ) // connectToDB подключается к базе данных PostgreSQL // Сначала пытается подключиться к primary, если не удалось - к secondary func connectToDB(config Config) *sql.DB { log.Println("Попытка подключения к первичной БД...") connStr := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", config.PrimaryDBHost, config.DBPort, config.DBUser, config.DBPassword, config.DBName) db, err := sql.Open("postgres", connStr) if err == nil { err = db.Ping() if err == nil { log.Printf("Успешное подключение к первичной БД: %s", config.PrimaryDBHost) return db } } log.Printf("Не удалось подключиться к первичной БД: %v", err) log.Println("Попытка подключения к вторичной БД...") connStr = fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", config.SecondaryDBHost, config.DBPort, config.DBUser, config.DBPassword, config.DBName) db, err = sql.Open("postgres", connStr) if err != nil { log.Fatalf("Ошибка открытия соединения с вторичной БД: %v", err) } err = db.Ping() if err != nil { log.Fatalf("Ошибка подключения к вторичной БД: %v", err) } log.Printf("Успешное подключение к вторичной БД: %s", config.SecondaryDBHost) return db } // getInstancesByHostname получает все instances для hostname из таблицы instances // Возвращает массив instances, т.к. один hostname может быть связан с несколькими instance func getInstancesByHostname(db *sql.DB, hostname string) ([]string, error) { query := "SELECT instance FROM instances_new WHERE hostname = $1 ORDER BY instance" rows, err := db.Query(query, hostname) if err != nil { return nil, fmt.Errorf("ошибка выполнения запроса для hostname %s: %w", hostname, err) } defer rows.Close() var instances []string for rows.Next() { var instance string if err := rows.Scan(&instance); err != nil { return nil, fmt.Errorf("ошибка чтения instance: %w", err) } instances = append(instances, instance) } if err = rows.Err(); err != nil { return nil, fmt.Errorf("ошибка обработки результатов: %w", err) } if len(instances) == 0 { return nil, fmt.Errorf("instances не найдены для hostname %s", hostname) } return instances, nil } // checkHostBelongsToInstance проверяет принадлежит ли хост к указанному instance func checkHostBelongsToInstance(db *sql.DB, hostname, instance string) (bool, error) { query := ` SELECT COUNT(*) FROM instances_new WHERE hostname = $1 AND instance = $2 ` var count int err := db.QueryRow(query, hostname, instance).Scan(&count) if err != nil { return false, fmt.Errorf("ошибка проверки принадлежности хоста: %w", err) } return count > 0, nil } // getClientInfoByInstance получает всех клиентов для заданного waf_instance func getClientInfoByInstance(db *sql.DB, instance string) ([]ClientInfo, error) { query := ` SELECT containers_count, ptaf_config, client_title, fluent_bit_port, waf_instance, docker_image, docker_image_download, debug, shm_size, ptaf_fallback_code, sp_antiddos, real_ip_networks, worker_processes, one_container, sid_block FROM client_info WHERE waf_instance = $1 ` rows, err := db.Query(query, instance) if err != nil { if strings.Contains(err.Error(), "does not exist") { return nil, fmt.Errorf("ошибка структуры таблицы client_info: %w (возможно изменилась схема БД)", err) } return nil, fmt.Errorf("ошибка выполнения запроса: %w", err) } defer rows.Close() var clientInfoList []ClientInfo for rows.Next() { var ci ClientInfo var wafInstance sql.NullString err := rows.Scan( &ci.ContainersCount, &ci.PTAFConfig, &ci.ClientTitle, &ci.FluentBitPort, &wafInstance, &ci.DockerImage, &ci.DockerImageDownload, &ci.Debug, &ci.ShmSize, &ci.PtafFallbackCode, &ci.SpAntiddos, pq.Array(&ci.RealIPNetworks), &ci.WorkerProcesses, &ci.OneContainer, &ci.SidBlock, ) if err != nil { return nil, fmt.Errorf("ошибка сканирования строки: %w", err) } if wafInstance.Valid { ci.WAFInstance = wafInstance.String } clientInfoList = append(clientInfoList, ci) } return clientInfoList, nil } // getAppsSettingsByClientTitle получает apps_settings для заданного client_title func getAppsSettingsByClientTitle(db *sql.DB, clientTitle string) ([]AppsSettings, error) { query := ` SELECT l7resourceid, waf_enabled, waf_vendor, custom_input_http_ports, custom_input_https_ports, custom_output_http_ports, custom_output_https_ports, upstream_angie_custom, server_nginx_custom, server_angie_custom, client_title, upstream_nginx_custom, location_angie_custom, location_nginx_custom, server_directives_angie_custom, server_directives_nginx_custom, sni, mode, ssl_enabled, custom_angie_ssl, max_fails, fail_timeout, balancing_method, proxy_next_upstream_codes, custom_sw_nginx_ssl FROM apps_settings WHERE client_title = $1 ORDER BY l7resourceid ASC ` rows, err := db.Query(query, clientTitle) if err != nil { if strings.Contains(err.Error(), "does not exist") { return nil, fmt.Errorf("ошибка структуры таблицы apps_settings: %w (возможно изменилась схема БД)", err) } return nil, fmt.Errorf("ошибка выполнения запроса: %w", err) } defer rows.Close() var appsSettingsList []AppsSettings for rows.Next() { var a AppsSettings err := rows.Scan( &a.L7ResourceID, &a.WAFEnabled, &a.WAFVendor, &a.CustomInputHTTPPorts, &a.CustomInputHTTPSPorts, &a.CustomOutputHTTPPorts, &a.CustomOutputHTTPSPorts, &a.UpstreamAngieCustom, &a.ServerNginxCustom, &a.ServerAngieCustom, &a.ClientTitle, &a.UpstreamNginxCustom, &a.LocationAngieCustom, &a.LocationNginxCustom, &a.ServerDirectivesAngieCustom, &a.ServerDirectivesNginxCustom, &a.SNI, &a.Mode, &a.SSLEnabled, &a.CustomAngieSSL, &a.MaxFails, &a.FailTimeout, &a.BalancingMethod, &a.ProxyNextUpstreamCodes, &a.CustomSWNginxSSL, ) if err != nil { return nil, fmt.Errorf("ошибка сканирования строки: %w", err) } appsSettingsList = append(appsSettingsList, a) } return appsSettingsList, nil } // getClientInfoByClientTitle получает информацию о клиенте из таблицы client_info func getClientInfoByClientTitle(db *sql.DB, clientTitle string) (*ClientInfo, error) { query := ` SELECT containers_count, ptaf_config, client_title, fluent_bit_port, waf_instance, docker_image, docker_image_download, debug, shm_size, ptaf_fallback_code, sp_antiddos, real_ip_networks, worker_processes, one_container, sid_block FROM client_info WHERE client_title = $1 ` var clientInfo ClientInfo var wafInstance sql.NullString err := db.QueryRow(query, clientTitle).Scan( &clientInfo.ContainersCount, &clientInfo.PTAFConfig, &clientInfo.ClientTitle, &clientInfo.FluentBitPort, &wafInstance, &clientInfo.DockerImage, &clientInfo.DockerImageDownload, &clientInfo.Debug, &clientInfo.ShmSize, &clientInfo.PtafFallbackCode, &clientInfo.SpAntiddos, pq.Array(&clientInfo.RealIPNetworks), &clientInfo.WorkerProcesses, &clientInfo.OneContainer, &clientInfo.SidBlock, ) if err != nil { if strings.Contains(err.Error(), "does not exist") { return nil, fmt.Errorf("ошибка структуры таблицы client_info: %w (возможно изменилась схема БД)", err) } return nil, fmt.Errorf("ошибка получения данных из client_info: %w", err) } if wafInstance.Valid { clientInfo.WAFInstance = wafInstance.String } return &clientInfo, nil }