feat: save short cut image to remote by ssh

- add settings for ssh config
- implement save remote function by ssh
This commit is contained in:
2026-06-11 17:10:58 +08:00
parent 3e4d071b4c
commit 050f2b74c3
17 changed files with 1463 additions and 46 deletions
+59
View File
@@ -0,0 +1,59 @@
// uploader.go — small adapter that satisfies application.SSHUploader by
// dialing a one-shot connection per upload. This keeps the application
// layer free of any direct dependency on golang.org/x/crypto/ssh while
// still exposing a clean, unit-testable contract.
package ssh
import (
"context"
"os"
"time"
"github.com/mmmy/snapgo/internal/domain"
)
// Uploader is the production implementation of application.SSHUploader.
//
// We dial fresh per-upload because:
// - Captures happen sparsely (a few per minute at peak), so the cost of
// a TCP+SSH handshake is dwarfed by user think-time.
// - Holding a long-lived connection would force us to add keepalives,
// reconnection logic, and config-change invalidation — not worth it
// for the current usage profile.
type Uploader struct {
cfg domain.SSHConfig
}
// NewUploader returns a ready-to-use uploader. The configuration is
// captured by value so a simultaneous in-flight upload cannot be re-
// targeted by a settings save.
func NewUploader(cfg domain.SSHConfig) *Uploader {
return &Uploader{cfg: cfg}
}
// Upload satisfies application.SSHUploader. We log entry/exit at INFO so
// the operator sees a single line per upload in the normal happy path,
// and ERROR with elapsed time when something fails.
func (u *Uploader) Upload(ctx context.Context, remoteRelPath string, data []byte, mode os.FileMode) error {
start := time.Now()
sshLog().Info("uploader.Upload start",
"host", u.cfg.Host, "user", u.cfg.User, "port", u.cfg.Port,
"remote_path", remoteRelPath, "size", len(data))
client, err := Dial(ctx, u.cfg)
if err != nil {
sshLog().Error("uploader.Upload dial failed",
"host", u.cfg.Host, "elapsed", time.Since(start), "err", err)
return err
}
defer client.Close()
if err := client.CopyFile(ctx, remoteRelPath, data, mode); err != nil {
sshLog().Error("uploader.Upload copy failed",
"remote_path", remoteRelPath, "elapsed", time.Since(start), "err", err)
return err
}
sshLog().Info("uploader.Upload done",
"remote_path", remoteRelPath, "size", len(data),
"total_elapsed", time.Since(start))
return nil
}