sshkeeper/internal/model/server.go

162 lines
4.0 KiB
Go

package model
import (
"strings"
"time"
)
type AuthMethod string
const (
AuthPassword AuthMethod = "password"
AuthKey AuthMethod = "key"
AuthKeyPassphrase AuthMethod = "key_passphrase"
AuthAgent AuthMethod = "agent"
)
type TestStatus string
const (
TestUnknown TestStatus = "unknown"
TestOK TestStatus = "ok"
TestFailed TestStatus = "failed"
)
type Server struct {
ID int64 `json:"id"`
Alias string `json:"alias"`
DisplayName string `json:"display_name"`
Host string `json:"host"`
Port int `json:"port"`
User string `json:"user"`
AuthMethod AuthMethod `json:"auth_method"`
IdentityFile string `json:"identity_file"`
ProxyJump string `json:"proxy_jump"`
Route Route `json:"route"`
GroupName string `json:"group_name"`
Notes string `json:"notes"`
StartupCommand string `json:"startup_command"`
Tags []string `json:"tags"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
LastConnectedAt *time.Time `json:"last_connected_at"`
LastTestAt *time.Time `json:"last_test_at"`
LastTestStatus TestStatus `json:"last_test_status"`
LastTestError string `json:"last_test_error"`
}
type SecretType string
const (
SecretSSHPassword SecretType = "ssh_password"
SecretKeyPassphrase SecretType = "key_passphrase"
SecretSudoPassword SecretType = "sudo_password"
SecretCustom SecretType = "custom_secret"
)
type Secret struct {
ID string `json:"id"`
Type SecretType `json:"type"`
Nonce []byte `json:"nonce"`
Data []byte `json:"data"`
}
type ForwardType string
const (
ForwardLocal ForwardType = "local"
ForwardRemote ForwardType = "remote"
ForwardDynamic ForwardType = "dynamic"
)
type Forward struct {
ID int64 `json:"id"`
ServerID int64 `json:"server_id"`
Type ForwardType `json:"type"`
LocalAddr string `json:"local_addr"`
LocalPort int `json:"local_port"`
RemoteAddr string `json:"remote_addr"`
RemotePort int `json:"remote_port"`
}
type Tag struct {
ID int64 `json:"id"`
Name string `json:"name"`
}
// --- Route ---
// RouteHop represents a single jump host in a route.
// IsProfile: true = use Alias (references a sshkeeper profile), false = use Raw (literal address).
type RouteHop struct {
Alias string `json:"alias"`
Raw string `json:"raw"`
IsProfile bool `json:"is_profile"`
}
// Route represents the SSH jump route for a server.
// Mode is computed from Hops length: 0=direct, 1=via, 2+=chain
type Route struct {
Hops []RouteHop `json:"hops"`
}
// RouteMode returns the computed route mode.
func (r Route) RouteMode() string {
switch len(r.Hops) {
case 0:
return "direct"
case 1:
return "via"
default:
return "chain"
}
}
// ProxyJumpString builds the -J argument value from hops.
func (r Route) ProxyJumpString() string {
parts := make([]string, len(r.Hops))
for i, h := range r.Hops {
if h.IsProfile {
parts[i] = h.Alias
} else {
parts[i] = h.Raw
}
}
return strings.Join(parts, ",")
}
// DisplaySummary returns a human-readable route summary.
// direct → target / bastion → target / bastion → dmz-gw → target
func (r Route) DisplaySummary(target string) string {
if len(r.Hops) == 0 {
return "direct → " + target
}
names := make([]string, len(r.Hops))
for i, h := range r.Hops {
if h.IsProfile {
names[i] = h.Alias
} else {
names[i] = h.Raw
}
}
return strings.Join(names, " → ") + " → " + target
}
// HasProfileLinks returns true if any hop references a known profile.
func (r Route) HasProfileLinks() bool {
for _, h := range r.Hops {
if h.IsProfile {
return true
}
}
return false
}
type CommandTemplate struct {
ID int64 `json:"id"`
ServerID int64 `json:"server_id"`
Name string `json:"name"`
Command string `json:"command"`
Description string `json:"description"`
}