169 lines
4.4 KiB
Go
169 lines
4.4 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/orohi/vpn-panel/internal/models"
|
|
)
|
|
|
|
func scanNode(scanner interface {
|
|
Scan(dest ...any) error
|
|
}) (*models.Node, error) {
|
|
n := &models.Node{}
|
|
var lastSeen sql.NullTime
|
|
var status string
|
|
var profileID uuid.NullUUID
|
|
var profileName sql.NullString
|
|
err := scanner.Scan(
|
|
&n.ID, &n.Name, &n.Host, &n.SSHPort, &n.SSHUser, &n.SSHAuthType, &n.SSHSecretEnc,
|
|
&n.NodePort, &n.SecretKey, &status, &n.InstallMode, &n.LastError, &n.InstallLog,
|
|
&n.RuntimeLog, &n.AgentVersion, &lastSeen, &n.CreatedAt, &n.UpdatedAt,
|
|
&profileID, &profileName,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
n.Status = models.NodeStatus(status)
|
|
if lastSeen.Valid {
|
|
t := lastSeen.Time
|
|
n.LastSeenAt = &t
|
|
}
|
|
if profileID.Valid {
|
|
id := profileID.UUID
|
|
n.ProfileID = &id
|
|
}
|
|
if profileName.Valid {
|
|
n.ProfileName = profileName.String
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
const nodeColumns = `
|
|
n.id, n.name, n.host, n.ssh_port, n.ssh_user, n.ssh_auth_type, n.ssh_secret_enc,
|
|
n.node_port, n.secret_key, n.status, n.install_mode, n.last_error, n.install_log,
|
|
n.runtime_log, n.agent_version, n.last_seen_at, n.created_at, n.updated_at,
|
|
n.profile_id, COALESCE(cp.name, '')`
|
|
|
|
const nodeFrom = `
|
|
FROM nodes n
|
|
LEFT JOIN config_profiles cp ON cp.id = n.profile_id`
|
|
|
|
func CreateNode(db *sql.DB, n *models.Node) error {
|
|
if n.ID == uuid.Nil {
|
|
n.ID = uuid.New()
|
|
}
|
|
now := time.Now().UTC()
|
|
n.CreatedAt = now
|
|
n.UpdatedAt = now
|
|
if n.Status == "" {
|
|
n.Status = models.NodeStatusPending
|
|
}
|
|
_, err := db.Exec(`
|
|
INSERT INTO nodes (
|
|
id, name, host, ssh_port, ssh_user, ssh_auth_type, ssh_secret_enc,
|
|
node_port, secret_key, status, install_mode, last_error, install_log,
|
|
runtime_log, agent_version, last_seen_at, created_at, updated_at, profile_id
|
|
) VALUES (
|
|
$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19
|
|
)`,
|
|
n.ID, n.Name, n.Host, n.SSHPort, n.SSHUser, n.SSHAuthType, n.SSHSecretEnc,
|
|
n.NodePort, n.SecretKey, string(n.Status), n.InstallMode, n.LastError, n.InstallLog,
|
|
n.RuntimeLog, n.AgentVersion, n.LastSeenAt, n.CreatedAt, n.UpdatedAt, n.ProfileID,
|
|
)
|
|
return err
|
|
}
|
|
|
|
func GetNode(db *sql.DB, id uuid.UUID) (*models.Node, error) {
|
|
row := db.QueryRow(`SELECT `+nodeColumns+nodeFrom+` WHERE n.id = $1`, id)
|
|
n, err := scanNode(row)
|
|
if err == sql.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
return n, err
|
|
}
|
|
|
|
func ListNodes(db *sql.DB) ([]models.Node, error) {
|
|
rows, err := db.Query(`SELECT ` + nodeColumns + nodeFrom + ` ORDER BY n.created_at DESC`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var list []models.Node
|
|
for rows.Next() {
|
|
n, err := scanNode(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
list = append(list, *n)
|
|
}
|
|
return list, rows.Err()
|
|
}
|
|
|
|
func UpdateNodeStatus(db *sql.DB, id uuid.UUID, status models.NodeStatus, lastError string) error {
|
|
_, err := db.Exec(`
|
|
UPDATE nodes
|
|
SET status = $2, last_error = $3, updated_at = NOW()
|
|
WHERE id = $1`, id, string(status), lastError)
|
|
return err
|
|
}
|
|
|
|
func ClearNodeInstallLog(db *sql.DB, id uuid.UUID) error {
|
|
_, err := db.Exec(`UPDATE nodes SET install_log = '', updated_at = NOW() WHERE id = $1`, id)
|
|
return err
|
|
}
|
|
|
|
func AppendNodeInstallLog(db *sql.DB, id uuid.UUID, chunk string) error {
|
|
_, err := db.Exec(`
|
|
UPDATE nodes
|
|
SET install_log = CASE
|
|
WHEN install_log = '' OR install_log IS NULL THEN $2
|
|
ELSE install_log || E'\n' || $2
|
|
END, updated_at = NOW()
|
|
WHERE id = $1`, id, strings.TrimRight(chunk, "\n"))
|
|
return err
|
|
}
|
|
|
|
func SetNodeRuntimeLog(db *sql.DB, id uuid.UUID, logText string) error {
|
|
const max = 200_000
|
|
if len(logText) > max {
|
|
logText = logText[len(logText)-max:]
|
|
}
|
|
_, err := db.Exec(`
|
|
UPDATE nodes SET runtime_log = $2, updated_at = NOW() WHERE id = $1`, id, logText)
|
|
return err
|
|
}
|
|
|
|
func MarkNodeSeen(db *sql.DB, id uuid.UUID, version string, status models.NodeStatus) error {
|
|
_, err := db.Exec(`
|
|
UPDATE nodes
|
|
SET last_seen_at = NOW(), agent_version = $2, status = $3, last_error = '', updated_at = NOW()
|
|
WHERE id = $1`, id, version, string(status))
|
|
return err
|
|
}
|
|
|
|
func SetNodeProfile(db *sql.DB, nodeID uuid.UUID, profileID *uuid.UUID) error {
|
|
_, err := db.Exec(`UPDATE nodes SET profile_id = $2, updated_at = NOW() WHERE id = $1`, nodeID, profileID)
|
|
return err
|
|
}
|
|
|
|
func DeleteNode(db *sql.DB, id uuid.UUID) error {
|
|
res, err := db.Exec(`DELETE FROM nodes WHERE id = $1`, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
n, err := res.RowsAffected()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if n == 0 {
|
|
return fmt.Errorf("node not found")
|
|
}
|
|
return nil
|
|
}
|