mirror of
https://github.com/swissmakers/fail2ban-ui.git
synced 2026-04-17 05:53:15 +02:00
Consolidate JailInfo function to own connector_global.go and also remove old FetchBanEvents function
This commit is contained in:
@@ -9,7 +9,6 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -157,45 +156,6 @@ func (ac *AgentConnector) SetFilterConfig(ctx context.Context, jail, content str
|
||||
return ac.put(ctx, fmt.Sprintf("/v1/filters/%s", url.PathEscape(jail)), payload, nil)
|
||||
}
|
||||
|
||||
func (ac *AgentConnector) FetchBanEvents(ctx context.Context, limit int) ([]BanEvent, error) {
|
||||
query := url.Values{}
|
||||
if limit > 0 {
|
||||
query.Set("limit", strconv.Itoa(limit))
|
||||
}
|
||||
var resp struct {
|
||||
Events []struct {
|
||||
IP string `json:"ip"`
|
||||
Jail string `json:"jail"`
|
||||
Hostname string `json:"hostname"`
|
||||
Failures string `json:"failures"`
|
||||
Whois string `json:"whois"`
|
||||
Logs string `json:"logs"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
} `json:"events"`
|
||||
}
|
||||
endpoint := "/v1/events"
|
||||
if encoded := query.Encode(); encoded != "" {
|
||||
endpoint += "?" + encoded
|
||||
}
|
||||
if err := ac.get(ctx, endpoint, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]BanEvent, 0, len(resp.Events))
|
||||
for _, evt := range resp.Events {
|
||||
ts, err := time.Parse(time.RFC3339, evt.Timestamp)
|
||||
if err != nil {
|
||||
ts = time.Now()
|
||||
}
|
||||
result = append(result, BanEvent{
|
||||
Time: ts,
|
||||
Jail: evt.Jail,
|
||||
IP: evt.IP,
|
||||
LogLine: fmt.Sprintf("%s %s", evt.Hostname, evt.Failures),
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// HTTP Helpers
|
||||
// =========================================================================
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Fail2ban UI - A Swiss made, management interface for Fail2ban.
|
||||
//
|
||||
// Copyright (C) 2025 Swissmakers GmbH (https://swissmakers.ch)
|
||||
// Copyright (C) 2026 Swissmakers GmbH (https://swissmakers.ch)
|
||||
//
|
||||
// Licensed under the GNU General Public License, Version 3 (GPL-3.0)
|
||||
// You may not use this file except in compliance with the License.
|
||||
@@ -14,14 +14,20 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Shared types, helpers, and high-level functions used across all connectors.
|
||||
package fail2ban
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// =========================================================================
|
||||
// Types
|
||||
// =========================================================================
|
||||
|
||||
// JailInfo holds summary data for a single Fail2ban jail.
|
||||
type JailInfo struct {
|
||||
JailName string `json:"jailName"`
|
||||
TotalBanned int `json:"totalBanned"`
|
||||
@@ -31,55 +37,10 @@ type JailInfo struct {
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Client Functions
|
||||
// Service Control
|
||||
// =========================================================================
|
||||
|
||||
// Returns jail names from the default server.
|
||||
func GetJails() ([]string, error) {
|
||||
conn, err := GetManager().DefaultConnector()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
infos, err := conn.GetJailInfos(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
names := make([]string, 0, len(infos))
|
||||
for _, info := range infos {
|
||||
names = append(names, info.JailName)
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
// Returns a slice of currently banned IPs for a specific jail.
|
||||
func GetBannedIPs(jail string) ([]string, error) {
|
||||
conn, err := GetManager().DefaultConnector()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn.GetBannedIPs(context.Background(), jail)
|
||||
}
|
||||
|
||||
// Unbans an IP from the given jail.
|
||||
func UnbanIP(jail, ip string) error {
|
||||
conn, err := GetManager().DefaultConnector()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return conn.UnbanIP(context.Background(), jail, ip)
|
||||
}
|
||||
|
||||
// Returns extended info for each jail on the default server.
|
||||
func BuildJailInfos(_ string) ([]JailInfo, error) {
|
||||
conn, err := GetManager().DefaultConnector()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn.GetJailInfos(context.Background())
|
||||
}
|
||||
|
||||
// Restarts (or reloads) the Fail2ban service.
|
||||
// RestartFail2ban restarts (or reloads) the Fail2ban service on the given server.
|
||||
func RestartFail2ban(serverID string) (string, error) {
|
||||
manager := GetManager()
|
||||
var (
|
||||
@@ -104,3 +65,61 @@ func RestartFail2ban(serverID string) (string, error) {
|
||||
}
|
||||
return "restart", nil
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Jail Info Collection
|
||||
// =========================================================================
|
||||
|
||||
// bannedIPsFn is the signature used by any connector's GetBannedIPs method.
|
||||
type bannedIPsFn func(ctx context.Context, jail string) ([]string, error)
|
||||
|
||||
// collectJailInfos fans out to fetch banned IPs for each jail concurrently,
|
||||
// then returns the results sorted alphabetically. Both the local and SSH
|
||||
// connectors delegate to this function from their GetJailInfos methods.
|
||||
func collectJailInfos(ctx context.Context, jails []string, getBannedIPs bannedIPsFn) ([]JailInfo, error) {
|
||||
type jailResult struct {
|
||||
jail JailInfo
|
||||
err error
|
||||
}
|
||||
results := make(chan jailResult, len(jails))
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for _, jail := range jails {
|
||||
wg.Add(1)
|
||||
go func(j string) {
|
||||
defer wg.Done()
|
||||
ips, err := getBannedIPs(ctx, j)
|
||||
if err != nil {
|
||||
results <- jailResult{err: err}
|
||||
return
|
||||
}
|
||||
results <- jailResult{
|
||||
jail: JailInfo{
|
||||
JailName: j,
|
||||
TotalBanned: len(ips),
|
||||
BannedIPs: ips,
|
||||
Enabled: true,
|
||||
},
|
||||
}
|
||||
}(jail)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(results)
|
||||
}()
|
||||
|
||||
var infos []JailInfo
|
||||
for r := range results {
|
||||
if r.err != nil {
|
||||
continue
|
||||
}
|
||||
infos = append(infos, r.jail)
|
||||
}
|
||||
|
||||
sort.SliceStable(infos, func(i, j int) bool {
|
||||
return infos[i].JailName < infos[j].JailName
|
||||
})
|
||||
|
||||
return infos, nil
|
||||
}
|
||||
@@ -6,10 +6,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/swissmakers/fail2ban-ui/internal/config"
|
||||
)
|
||||
@@ -36,71 +33,13 @@ func (lc *LocalConnector) Server() config.Fail2banServer {
|
||||
return lc.server
|
||||
}
|
||||
|
||||
// Get jail information.
|
||||
// Collects jail status for every active local jail.
|
||||
func (lc *LocalConnector) GetJailInfos(ctx context.Context) ([]JailInfo, error) {
|
||||
jails, err := lc.getJails(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logPath := lc.server.LogPath // LEGACY, WILL BE REMOVED IN FUTURE VERSIONS.
|
||||
if logPath == "" {
|
||||
logPath = "/var/log/fail2ban.log"
|
||||
}
|
||||
banHistory, err := ParseBanLog(logPath) // LEGACY, WILL BE REMOVED IN FUTURE VERSIONS.
|
||||
if err != nil {
|
||||
banHistory = make(map[string][]BanEvent)
|
||||
}
|
||||
oneHourAgo := time.Now().Add(-1 * time.Hour)
|
||||
type jailResult struct {
|
||||
jail JailInfo
|
||||
err error
|
||||
}
|
||||
results := make(chan jailResult, len(jails))
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for _, jail := range jails {
|
||||
wg.Add(1)
|
||||
go func(j string) {
|
||||
defer wg.Done()
|
||||
bannedIPs, err := lc.GetBannedIPs(ctx, j)
|
||||
if err != nil {
|
||||
results <- jailResult{err: err}
|
||||
return
|
||||
}
|
||||
newInLastHour := 0
|
||||
if events, ok := banHistory[j]; ok {
|
||||
for _, e := range events {
|
||||
if e.Time.After(oneHourAgo) {
|
||||
newInLastHour++
|
||||
}
|
||||
}
|
||||
}
|
||||
results <- jailResult{
|
||||
jail: JailInfo{
|
||||
JailName: j,
|
||||
TotalBanned: len(bannedIPs),
|
||||
NewInLastHour: newInLastHour,
|
||||
BannedIPs: bannedIPs,
|
||||
Enabled: true,
|
||||
},
|
||||
}
|
||||
}(jail)
|
||||
}
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(results)
|
||||
}()
|
||||
var finalResults []JailInfo
|
||||
for result := range results {
|
||||
if result.err != nil {
|
||||
continue
|
||||
}
|
||||
finalResults = append(finalResults, result.jail)
|
||||
}
|
||||
sort.SliceStable(finalResults, func(i, j int) bool {
|
||||
return finalResults[i].JailName < finalResults[j].JailName
|
||||
})
|
||||
return finalResults, nil
|
||||
return collectJailInfos(ctx, jails, lc.GetBannedIPs)
|
||||
}
|
||||
|
||||
// Get banned IPs for a given jail.
|
||||
@@ -196,29 +135,6 @@ func (lc *LocalConnector) SetFilterConfig(ctx context.Context, jail, content str
|
||||
return SetFilterConfigLocal(jail, content)
|
||||
}
|
||||
|
||||
// REMOVE THIS FUNCTION
|
||||
func (lc *LocalConnector) FetchBanEvents(ctx context.Context, limit int) ([]BanEvent, error) {
|
||||
logPath := lc.server.LogPath
|
||||
if logPath == "" {
|
||||
logPath = "/var/log/fail2ban.log"
|
||||
}
|
||||
eventsByJail, err := ParseBanLog(logPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var all []BanEvent
|
||||
for _, evs := range eventsByJail {
|
||||
all = append(all, evs...)
|
||||
}
|
||||
sort.SliceStable(all, func(i, j int) bool {
|
||||
return all[i].Time.After(all[j].Time)
|
||||
})
|
||||
if limit > 0 && len(all) > limit {
|
||||
all = all[:limit]
|
||||
}
|
||||
return all, nil
|
||||
}
|
||||
|
||||
// Get all jails.
|
||||
func (lc *LocalConnector) getJails(ctx context.Context) ([]string, error) {
|
||||
out, err := lc.runFail2banClient(ctx, "status")
|
||||
|
||||
@@ -84,55 +84,13 @@ func (sc *SSHConnector) Server() config.Fail2banServer {
|
||||
return sc.server
|
||||
}
|
||||
|
||||
// Get jail infos for all jails.
|
||||
// Collects jail status for every active remote jail.
|
||||
func (sc *SSHConnector) GetJailInfos(ctx context.Context) ([]JailInfo, error) {
|
||||
jails, err := sc.getJails(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
type jailResult struct {
|
||||
jail JailInfo
|
||||
err error
|
||||
}
|
||||
results := make(chan jailResult, len(jails))
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for _, jail := range jails {
|
||||
wg.Add(1)
|
||||
go func(j string) {
|
||||
defer wg.Done()
|
||||
ips, err := sc.GetBannedIPs(ctx, j)
|
||||
if err != nil {
|
||||
results <- jailResult{err: err}
|
||||
return
|
||||
}
|
||||
results <- jailResult{
|
||||
jail: JailInfo{
|
||||
JailName: j,
|
||||
TotalBanned: len(ips),
|
||||
NewInLastHour: 0,
|
||||
BannedIPs: ips,
|
||||
Enabled: true,
|
||||
},
|
||||
}
|
||||
}(jail)
|
||||
}
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(results)
|
||||
}()
|
||||
var infos []JailInfo
|
||||
for result := range results {
|
||||
if result.err != nil {
|
||||
continue
|
||||
}
|
||||
infos = append(infos, result.jail)
|
||||
}
|
||||
sort.SliceStable(infos, func(i, j int) bool {
|
||||
return infos[i].JailName < infos[j].JailName
|
||||
})
|
||||
return infos, nil
|
||||
return collectJailInfos(ctx, jails, sc.GetBannedIPs)
|
||||
}
|
||||
|
||||
// Get banned IPs for a given jail.
|
||||
@@ -252,10 +210,6 @@ func (sc *SSHConnector) SetFilterConfig(ctx context.Context, filterName, content
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sc *SSHConnector) FetchBanEvents(ctx context.Context, limit int) ([]BanEvent, error) {
|
||||
return []BanEvent{}, nil
|
||||
}
|
||||
|
||||
func (sc *SSHConnector) ensureAction(ctx context.Context) error {
|
||||
callbackURL := config.GetCallbackURL()
|
||||
settings := config.GetSettings()
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
// Fail2ban UI - A Swiss made, management interface for Fail2ban.
|
||||
//
|
||||
// Copyright (C) 2025 Swissmakers GmbH (https://swissmakers.ch)
|
||||
//
|
||||
// Licensed under the GNU General Public License, Version 3 (GPL-3.0)
|
||||
// You may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// LEGACY, WILL BE REMOVED IN FUTURE VERSIONS.
|
||||
package fail2ban
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
// =========================================================================
|
||||
// Types
|
||||
// =========================================================================
|
||||
|
||||
var logRegex = regexp.MustCompile(`^(\S+\s+\S+) fail2ban\.actions.*?\[\d+\]: NOTICE\s+\[(\S+)\]\s+Ban\s+(\S+)`)
|
||||
|
||||
// This is a single ban event from the fail2ban log. REMOVE THIS TYPE.
|
||||
type BanEvent struct {
|
||||
Time time.Time
|
||||
Jail string
|
||||
IP string
|
||||
LogLine string
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Log Parsing
|
||||
// =========================================================================
|
||||
|
||||
// ParseBanLog reads the fail2ban log and returns events grouped by jail.
|
||||
func ParseBanLog(logPath string) (map[string][]BanEvent, error) {
|
||||
file, err := os.Open(logPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open fail2ban log: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
eventsByJail := make(map[string][]BanEvent)
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
matches := logRegex.FindStringSubmatch(line)
|
||||
if len(matches) == 4 {
|
||||
timestampStr := matches[1]
|
||||
jail := matches[2]
|
||||
ip := matches[3]
|
||||
|
||||
parsedTime, err := time.Parse("2006-01-02 15:04:05,000", timestampStr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
ev := BanEvent{
|
||||
Time: parsedTime,
|
||||
Jail: jail,
|
||||
IP: ip,
|
||||
LogLine: line,
|
||||
}
|
||||
|
||||
eventsByJail[jail] = append(eventsByJail[jail], ev)
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return eventsByJail, nil
|
||||
}
|
||||
@@ -25,7 +25,6 @@ type Connector interface {
|
||||
Restart(ctx context.Context) error
|
||||
GetFilterConfig(ctx context.Context, jail string) (string, string, error)
|
||||
SetFilterConfig(ctx context.Context, jail, content string) error
|
||||
FetchBanEvents(ctx context.Context, limit int) ([]BanEvent, error)
|
||||
|
||||
// Jail management
|
||||
GetAllJails(ctx context.Context) ([]JailInfo, error)
|
||||
|
||||
@@ -309,7 +309,6 @@
|
||||
"servers.form.socket_path": "Fail2ban-Socket-Pfad",
|
||||
"servers.form.socket_path_placeholder": "/var/run/fail2ban/fail2ban.sock",
|
||||
"servers.form.log_path": "Fail2ban-Logpfad",
|
||||
"servers.form.log_path_placeholder": "/var/log/fail2ban.log",
|
||||
"servers.form.hostname": "Server-Hostname",
|
||||
"servers.form.hostname_placeholder": "optional",
|
||||
"servers.form.ssh_user": "SSH-Benutzer",
|
||||
|
||||
@@ -309,7 +309,6 @@
|
||||
"servers.form.socket_path": "Fail2ban-Socket-Pfad",
|
||||
"servers.form.socket_path_placeholder": "/var/run/fail2ban/fail2ban.sock",
|
||||
"servers.form.log_path": "Fail2ban-Logpfad",
|
||||
"servers.form.log_path_placeholder": "/var/log/fail2ban.log",
|
||||
"servers.form.hostname": "Server-Hostname",
|
||||
"servers.form.hostname_placeholder": "optional",
|
||||
"servers.form.ssh_user": "SSH-Benutzer",
|
||||
|
||||
@@ -309,7 +309,6 @@
|
||||
"servers.form.socket_path": "Fail2ban Socket Path",
|
||||
"servers.form.socket_path_placeholder": "/var/run/fail2ban/fail2ban.sock",
|
||||
"servers.form.log_path": "Fail2ban Log Path",
|
||||
"servers.form.log_path_placeholder": "/var/log/fail2ban.log",
|
||||
"servers.form.hostname": "Server Hostname",
|
||||
"servers.form.hostname_placeholder": "optional",
|
||||
"servers.form.ssh_user": "SSH User",
|
||||
|
||||
@@ -309,7 +309,6 @@
|
||||
"servers.form.socket_path": "Ruta del socket de Fail2ban",
|
||||
"servers.form.socket_path_placeholder": "/var/run/fail2ban/fail2ban.sock",
|
||||
"servers.form.log_path": "Ruta del log de Fail2ban",
|
||||
"servers.form.log_path_placeholder": "/var/log/fail2ban.log",
|
||||
"servers.form.hostname": "Nombre de host del servidor",
|
||||
"servers.form.hostname_placeholder": "opcional",
|
||||
"servers.form.ssh_user": "Usuario SSH",
|
||||
|
||||
@@ -309,7 +309,6 @@
|
||||
"servers.form.socket_path": "Chemin du socket Fail2ban",
|
||||
"servers.form.socket_path_placeholder": "/var/run/fail2ban/fail2ban.sock",
|
||||
"servers.form.log_path": "Chemin du log Fail2ban",
|
||||
"servers.form.log_path_placeholder": "/var/log/fail2ban.log",
|
||||
"servers.form.hostname": "Nom d'hôte du serveur",
|
||||
"servers.form.hostname_placeholder": "optionnel",
|
||||
"servers.form.ssh_user": "Utilisateur SSH",
|
||||
|
||||
@@ -309,7 +309,6 @@
|
||||
"servers.form.socket_path": "Percorso del socket Fail2ban",
|
||||
"servers.form.socket_path_placeholder": "/var/run/fail2ban/fail2ban.sock",
|
||||
"servers.form.log_path": "Percorso del log Fail2ban",
|
||||
"servers.form.log_path_placeholder": "/var/log/fail2ban.log",
|
||||
"servers.form.hostname": "Nome host del server",
|
||||
"servers.form.hostname_placeholder": "opzionale",
|
||||
"servers.form.ssh_user": "Utente SSH",
|
||||
|
||||
@@ -102,7 +102,6 @@ type ServerRecord struct {
|
||||
Host string
|
||||
Port int
|
||||
SocketPath string
|
||||
LogPath string
|
||||
SSHUser string
|
||||
SSHKeyPath string
|
||||
AgentURL string
|
||||
@@ -344,7 +343,7 @@ func ListServers(ctx context.Context) ([]ServerRecord, error) {
|
||||
}
|
||||
|
||||
rows, err := db.QueryContext(ctx, `
|
||||
SELECT id, name, type, host, port, socket_path, log_path, ssh_user, ssh_key_path, agent_url, agent_secret, hostname, tags, is_default, enabled, needs_restart, created_at, updated_at
|
||||
SELECT id, name, type, host, port, socket_path, ssh_user, ssh_key_path, agent_url, agent_secret, hostname, tags, is_default, enabled, needs_restart, created_at, updated_at
|
||||
FROM servers
|
||||
ORDER BY created_at`)
|
||||
if err != nil {
|
||||
@@ -355,7 +354,7 @@ ORDER BY created_at`)
|
||||
var records []ServerRecord
|
||||
for rows.Next() {
|
||||
var rec ServerRecord
|
||||
var host, socket, logPath, sshUser, sshKey, agentURL, agentSecret, hostname, tags sql.NullString
|
||||
var host, socket, sshUser, sshKey, agentURL, agentSecret, hostname, tags sql.NullString
|
||||
var name, serverType sql.NullString
|
||||
var created, updated sql.NullString
|
||||
var port sql.NullInt64
|
||||
@@ -368,7 +367,6 @@ ORDER BY created_at`)
|
||||
&host,
|
||||
&port,
|
||||
&socket,
|
||||
&logPath,
|
||||
&sshUser,
|
||||
&sshKey,
|
||||
&agentURL,
|
||||
@@ -389,7 +387,6 @@ ORDER BY created_at`)
|
||||
rec.Host = stringFromNull(host)
|
||||
rec.Port = intFromNull(port)
|
||||
rec.SocketPath = stringFromNull(socket)
|
||||
rec.LogPath = stringFromNull(logPath)
|
||||
rec.SSHUser = stringFromNull(sshUser)
|
||||
rec.SSHKeyPath = stringFromNull(sshKey)
|
||||
rec.AgentURL = stringFromNull(agentURL)
|
||||
@@ -438,9 +435,9 @@ func ReplaceServers(ctx context.Context, servers []ServerRecord) error {
|
||||
|
||||
stmt, err := tx.PrepareContext(ctx, `
|
||||
INSERT INTO servers (
|
||||
id, name, type, host, port, socket_path, log_path, ssh_user, ssh_key_path, agent_url, agent_secret, hostname, tags, is_default, enabled, needs_restart, created_at, updated_at
|
||||
id, name, type, host, port, socket_path, ssh_user, ssh_key_path, agent_url, agent_secret, hostname, tags, is_default, enabled, needs_restart, created_at, updated_at
|
||||
) VALUES (
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
||||
)`)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -463,7 +460,6 @@ INSERT INTO servers (
|
||||
srv.Host,
|
||||
srv.Port,
|
||||
srv.SocketPath,
|
||||
srv.LogPath,
|
||||
srv.SSHUser,
|
||||
srv.SSHKeyPath,
|
||||
srv.AgentURL,
|
||||
@@ -1028,7 +1024,6 @@ CREATE TABLE IF NOT EXISTS servers (
|
||||
host TEXT,
|
||||
port INTEGER,
|
||||
socket_path TEXT,
|
||||
log_path TEXT,
|
||||
ssh_user TEXT,
|
||||
ssh_key_path TEXT,
|
||||
agent_url TEXT,
|
||||
|
||||
Reference in New Issue
Block a user