Add SSH source support with dialstdio and UI components
- Implement SSH dial via stdio for remote connections - Add sources API and storage layer for managing connection sources - Add SourcePanel and SshFields web components for SSH configuration - Update app structure to support source-based connections - Update handlers and server for new sources endpoint Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
+46
-17
@@ -11,9 +11,9 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/arescom/dockmv/internal/dkr"
|
||||
"github.com/arescom/dockmv/internal/job"
|
||||
"github.com/arescom/dockmv/internal/sshx"
|
||||
"github.com/arescom/dockmv/internal/store"
|
||||
@@ -29,23 +29,31 @@ type Config struct {
|
||||
DataDir string
|
||||
// PackageDir is where migration packages are written.
|
||||
PackageDir string
|
||||
// DockerHost overrides the source daemon address.
|
||||
// DockerHost overrides the local source daemon address.
|
||||
DockerHost string
|
||||
// DockerHostSet reports that DockerHost was given explicitly on the command
|
||||
// line. It then wins over the source remembered from the last run.
|
||||
DockerHostSet bool
|
||||
// UI is the embedded web app; nil disables the UI.
|
||||
UI fs.FS
|
||||
// Logger receives request and error logs.
|
||||
Logger *slog.Logger
|
||||
}
|
||||
|
||||
// Server ties the Docker client, connection store and job manager to HTTP.
|
||||
// Server ties the source daemon, connection store and job manager to HTTP.
|
||||
type Server struct {
|
||||
cfg Config
|
||||
log *slog.Logger
|
||||
docker *dkr.Client
|
||||
conns *store.Connections
|
||||
hosts *sshx.KnownHosts
|
||||
jobs *job.Manager
|
||||
mux *http.ServeMux
|
||||
cfg Config
|
||||
log *slog.Logger
|
||||
sources *store.Sources
|
||||
conns *store.Connections
|
||||
hosts *sshx.KnownHosts
|
||||
jobs *job.Manager
|
||||
mux *http.ServeMux
|
||||
|
||||
// srcMu guards cur, the connection to the selected source. It is opened on
|
||||
// first use and replaced when the operator picks another source.
|
||||
srcMu sync.Mutex
|
||||
cur *sourceConn
|
||||
}
|
||||
|
||||
// New builds the server and everything it owns.
|
||||
@@ -60,10 +68,6 @@ func New(cfg Config) (*Server, error) {
|
||||
return nil, fmt.Errorf("create package directory: %w", err)
|
||||
}
|
||||
|
||||
docker, err := dkr.New(cfg.DockerHost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conns, err := store.NewConnections(filepath.Join(cfg.DataDir, "connections.json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -72,17 +76,35 @@ func New(cfg Config) (*Server, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
local := store.Source{Name: "this host", DockerHost: cfg.DockerHost}
|
||||
if local.DockerHost == "" {
|
||||
local.DockerHost = os.Getenv("DOCKER_HOST")
|
||||
}
|
||||
sources, err := store.NewSources(filepath.Join(cfg.DataDir, "sources.json"), local)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// An explicit --docker-host is an instruction for this run, so it overrides
|
||||
// the source remembered from the last one.
|
||||
if cfg.DockerHostSet {
|
||||
if err := sources.Select(store.LocalSourceID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
s := &Server{
|
||||
cfg: cfg, log: cfg.Logger, docker: docker,
|
||||
cfg: cfg, log: cfg.Logger, sources: sources,
|
||||
conns: conns, hosts: hosts, jobs: job.NewManager(), mux: http.NewServeMux(),
|
||||
}
|
||||
s.routes()
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Close releases the Docker connection.
|
||||
func (s *Server) Close() error { return s.docker.Close() }
|
||||
// Close releases the connection to the current source.
|
||||
func (s *Server) Close() error {
|
||||
s.invalidateSource("")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handler returns the root HTTP handler.
|
||||
func (s *Server) Handler() http.Handler {
|
||||
@@ -96,6 +118,13 @@ func (s *Server) routes() {
|
||||
m.HandleFunc("GET /api/source", s.handleSource)
|
||||
m.HandleFunc("GET /api/source/sizes", s.handleSourceSizes)
|
||||
|
||||
m.HandleFunc("GET /api/sources", s.handleListSources)
|
||||
m.HandleFunc("POST /api/sources", s.handleSaveSource)
|
||||
m.HandleFunc("DELETE /api/sources/{id}", s.handleDeleteSource)
|
||||
m.HandleFunc("POST /api/sources/{id}/select", s.handleSelectSource)
|
||||
m.HandleFunc("POST /api/sources/{id}/probe", s.handleSourceProbe)
|
||||
m.HandleFunc("POST /api/sources/{id}/trust", s.handleSourceTrust)
|
||||
|
||||
m.HandleFunc("GET /api/connections", s.handleListConnections)
|
||||
m.HandleFunc("POST /api/connections", s.handleSaveConnection)
|
||||
m.HandleFunc("DELETE /api/connections/{id}", s.handleDeleteConnection)
|
||||
|
||||
Reference in New Issue
Block a user