grafana_gen/database.go
2026-02-25 14:47:07 +03:00

123 lines
3.7 KiB
Go

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,
MAX(ci.four_hundred) OVER (PARTITION BY a.client_title) as limit_4xx,
MAX(ci.five_hundred) OVER (PARTITION BY a.client_title) as limit_5xx
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
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 string
var rpsLimit, rpsCommercialLimit int
var limit4xx, limit5xx sql.NullInt64
if err := rows.Scan(&clientTitle, &sid, &domainName, &aliasesJSON, &rpsLimit, &rpsCommercialLimit, &limit4xx, &limit5xx); err != nil {
return nil, fmt.Errorf("scan failed: %w", err)
}
// Распарсить JSON массив aliases
aliasesStr := parseAliasesJSON(aliasesJSON)
if _, exists := clients[clientTitle]; !exists {
clients[clientTitle] = ClientData{
ClientTitle: clientTitle,
Domains: []DomainInfo{},
RPSLimit: rpsLimit,
RPSCommercialLimit: rpsCommercialLimit,
Limit4xx: nullIntToInt(limit4xx),
Limit5xx: nullIntToInt(limit5xx),
}
}
client := clients[clientTitle]
client.Domains = append(client.Domains, DomainInfo{
SID: sid,
DomainName: domainName,
Aliases: aliasesStr,
})
clients[clientTitle] = client
}
return clients, rows.Err()
}
// nullIntToInt конвертирует sql.NullInt64 в int (0 если NULL)
func nullIntToInt(n sql.NullInt64) int {
if n.Valid {
return int(n.Int64)
}
return 0
}
// 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, ", ")
}