138 lines
4.8 KiB
Go
Executable file
138 lines
4.8 KiB
Go
Executable file
package main
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
|
|
_ "github.com/lib/pq"
|
|
)
|
|
|
|
func connectDB(config Config) (*sql.DB, error) {
|
|
connStr := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
|
|
config.DBHost, config.DBPort, config.DBUser, config.DBPassword, config.DBName)
|
|
|
|
db, err := sql.Open("postgres", connStr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := db.Ping(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return db, nil
|
|
}
|
|
|
|
func fetchClientsData(db *sql.DB) (map[string]ClientData, error) {
|
|
// aliases это JSON массив в БД, конвертируем в текст через ::text
|
|
// rps_limit берётся из client_info — NULL означает "алерт не нужен"
|
|
// Данные всегда берутся из обеих таблиц: sp_info + manual_info (UNION)
|
|
query := `
|
|
SELECT DISTINCT
|
|
COALESCE(a.client_title, 'Unknown') as client_title,
|
|
s.sid,
|
|
s.domain_name,
|
|
COALESCE(s.aliases::text, 'null') as aliases,
|
|
COALESCE(MAX(ci.rps_limit) OVER (PARTITION BY a.client_title), 0) as rps_limit,
|
|
COALESCE(MAX(ci.rps_commercial_limit) OVER (PARTITION BY a.client_title), 100) as rps_commercial_limit,
|
|
COALESCE(a.four_hundred, 20) as limit_4xx,
|
|
COALESCE(a.five_hundred, 10) as limit_5xx,
|
|
COALESCE(a.ptaf_fallback_code_alert_count, 1) as ptaf_fallback_code_alert_count,
|
|
COALESCE(MAX(ci.waf_provider) OVER (PARTITION BY a.client_title), '') as waf_provider,
|
|
COALESCE(MAX(ci.ptaf_fallback_code) OVER (PARTITION BY a.client_title), '') as ptaf_fallback_code,
|
|
COALESCE(MAX(ci.parent_client_title) OVER (PARTITION BY a.client_title), '') as parent_client_title
|
|
FROM (
|
|
SELECT sid, domain_name, aliases FROM sp_info WHERE sid IS NOT NULL
|
|
UNION
|
|
SELECT sid, domain_name, aliases FROM manual_info WHERE sid IS NOT NULL
|
|
) s
|
|
LEFT JOIN apps_settings a ON s.sid = a.l7resourceid
|
|
LEFT JOIN client_info ci ON a.client_title = ci.client_title
|
|
WHERE ci.waf_provider IS NOT NULL
|
|
ORDER BY COALESCE(a.client_title, 'Unknown'), s.sid
|
|
`
|
|
|
|
rows, err := db.Query(query)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("query failed: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
clients := make(map[string]ClientData)
|
|
|
|
for rows.Next() {
|
|
var clientTitle, sid, domainName, aliasesJSON, wafProvider, ptafFallbackCode, parentClientTitle string
|
|
var rpsLimit, rpsCommercialLimit, limit4xx, limit5xx, ptafFallbackCodeAlertCount int
|
|
|
|
if err := rows.Scan(&clientTitle, &sid, &domainName, &aliasesJSON, &rpsLimit, &rpsCommercialLimit, &limit4xx, &limit5xx, &ptafFallbackCodeAlertCount, &wafProvider, &ptafFallbackCode, &parentClientTitle); err != nil {
|
|
return nil, fmt.Errorf("scan failed: %w", err)
|
|
}
|
|
|
|
// Распарсить JSON массив aliases
|
|
aliasesStr := parseAliasesJSON(aliasesJSON)
|
|
|
|
// Если задан parent_client_title — группировать по нему
|
|
groupKey := clientTitle
|
|
if parentClientTitle != "" {
|
|
groupKey = parentClientTitle
|
|
}
|
|
|
|
if _, exists := clients[groupKey]; !exists {
|
|
clients[groupKey] = ClientData{
|
|
ClientTitle: groupKey,
|
|
Domains: []DomainInfo{},
|
|
RPSLimit: rpsLimit,
|
|
RPSCommercialLimit: rpsCommercialLimit,
|
|
WafProvider: wafProvider,
|
|
PtafFallbackCode: ptafFallbackCode,
|
|
}
|
|
} else {
|
|
// Обновляем лимиты — берём максимальные по группе
|
|
existing := clients[groupKey]
|
|
if rpsLimit > existing.RPSLimit {
|
|
existing.RPSLimit = rpsLimit
|
|
}
|
|
if rpsCommercialLimit > existing.RPSCommercialLimit {
|
|
existing.RPSCommercialLimit = rpsCommercialLimit
|
|
}
|
|
clients[groupKey] = existing
|
|
}
|
|
|
|
client := clients[groupKey]
|
|
client.Domains = append(client.Domains, DomainInfo{
|
|
SID: sid,
|
|
DomainName: domainName,
|
|
Aliases: aliasesStr,
|
|
Limit4xx: limit4xx,
|
|
Limit5xx: limit5xx,
|
|
PtafFallbackCodeAlertCount: ptafFallbackCodeAlertCount,
|
|
})
|
|
clients[groupKey] = client
|
|
}
|
|
|
|
return clients, rows.Err()
|
|
}
|
|
|
|
// parseAliasesJSON конвертирует JSON массив aliases в строку
|
|
// Input: ["domain1.com", "domain2.com"] или null
|
|
// Output: "domain1.com, domain2.com" или ""
|
|
func parseAliasesJSON(jsonStr string) string {
|
|
// Если null или пустое
|
|
if jsonStr == "" || jsonStr == "null" {
|
|
return ""
|
|
}
|
|
|
|
// Попытаться распарсить как JSON массив
|
|
var aliases []string
|
|
if err := json.Unmarshal([]byte(jsonStr), &aliases); err != nil {
|
|
// Если не удалось распарсить - вернуть как есть
|
|
log.Printf("Warning: Failed to parse aliases JSON: %s (error: %v)", jsonStr, err)
|
|
return jsonStr
|
|
}
|
|
|
|
// Соединить через запятую
|
|
return strings.Join(aliases, ", ")
|
|
}
|