feat: add ftp root path setting

This commit is contained in:
2026-07-13 13:20:31 +08:00
parent 50551de105
commit b69ef00013
7 changed files with 104 additions and 29 deletions
+13 -8
View File
@@ -40,9 +40,9 @@ type CaptureAndFTPService struct {
Cfg domain.FTPConfig
}
// ExecuteWithBytes uploads previously captured PNG bytes and copies the exact
// remote path semantics shown in Settings: FTP paths are rooted at "/", while
// SFTP paths are relative to the authenticated user's home directory.
// ExecuteWithBytes uploads previously captured PNG bytes and copies a display
// path built from the configured root directory plus the uploaded relative
// path. RootDirectory deliberately does not affect the upload destination.
func (s *CaptureAndFTPService) ExecuteWithBytes(ctx context.Context, pngBytes []byte) error {
if s.Uploader == nil {
s.notifyFailure("FTP/SFTP not configured")
@@ -80,7 +80,7 @@ func (s *CaptureAndFTPService) ExecuteWithBytes(ctx context.Context, pngBytes []
return err
}
clipText := ftpShareText(protocol, relPath)
clipText := ftpShareText(protocol, s.Cfg.RootDirectory, relPath)
if s.Clipboard != nil {
if err := s.Clipboard.WriteText(clipText); err != nil {
s.notifyFailure("clipboard write failed: " + err.Error())
@@ -131,11 +131,16 @@ func normalizedFTPProtocol(protocol string) string {
return domain.FTPProtocolFTP
}
func ftpShareText(protocol, relPath string) string {
if protocol == domain.FTPProtocolSFTP {
return "~/" + relPath
func ftpShareText(protocol, rootDirectory, relPath string) string {
rootDirectory = strings.TrimSpace(rootDirectory)
if rootDirectory == "" {
if protocol == domain.FTPProtocolSFTP {
rootDirectory = "~/"
} else {
rootDirectory = "/"
}
}
return "/" + relPath
return strings.TrimRight(rootDirectory, "/") + "/" + strings.TrimLeft(relPath, "/")
}
func (s *CaptureAndFTPService) notifyFailure(reason string) {
+34 -5
View File
@@ -52,23 +52,52 @@ func TestCaptureAndFTPServiceUploadsAndCopiesFTPPath(t *testing.T) {
}
}
func TestCaptureAndFTPServiceCopiesSFTPHomePath(t *testing.T) {
func TestCaptureAndFTPServiceCopiesConfiguredRootPath(t *testing.T) {
uploader := &fakeFTPUploader{}
clip := &fakeClipboard{}
svc := &CaptureAndFTPService{
Uploader: uploader,
Clipboard: clip,
Cfg: domain.FTPConfig{
Protocol: domain.FTPProtocolSFTP,
PathPrefix: "images",
Protocol: domain.FTPProtocolSFTP,
RootDirectory: "~/ftp/",
PathPrefix: "images",
},
}
if err := svc.ExecuteWithBytes(context.Background(), []byte("png")); err != nil {
t.Fatalf("upload SFTP screenshot: %v", err)
}
if clip.text != "~/"+uploader.path {
t.Fatalf("expected SFTP home path copied, got %q", clip.text)
if clip.text != "~/ftp/"+uploader.path {
t.Fatalf("expected configured SFTP root path copied, got %q", clip.text)
}
}
func TestFTPShareTextJoinsConfiguredRootAndRemoteDirectory(t *testing.T) {
const relPath = "snapgo/2026/07/20260713-110413-18lnbx.png"
const want = "~/ftp/snapgo/2026/07/20260713-110413-18lnbx.png"
if got := ftpShareText(domain.FTPProtocolSFTP, "~/ftp/", relPath); got != want {
t.Fatalf("ftpShareText() = %q, want %q", got, want)
}
}
func TestFTPShareTextUsesProtocolDefaultWhenRootDirectoryIsEmpty(t *testing.T) {
const relPath = "snapgo/2026/07/image.png"
tests := []struct {
name string
protocol string
want string
}{
{name: "FTP", protocol: domain.FTPProtocolFTP, want: "/" + relPath},
{name: "SFTP", protocol: domain.FTPProtocolSFTP, want: "~/" + relPath},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ftpShareText(tt.protocol, "", relPath); got != tt.want {
t.Fatalf("ftpShareText() = %q, want %q", got, tt.want)
}
})
}
}