feat: add FTP and SFTP upload support
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
// Package domain — configuration types.
|
||||
//
|
||||
// S3Config and SSHConfig are intentionally split into their own structs so
|
||||
// that future providers can introduce their own configuration types side-
|
||||
// by-side without polluting the core domain types file.
|
||||
// S3Config, SSHConfig, and FTPConfig are intentionally split into their own
|
||||
// structs so future providers can introduce configuration types side-by-side
|
||||
// without polluting the core domain types file.
|
||||
package domain
|
||||
|
||||
// S3Config describes the connection parameters for any S3-compatible
|
||||
@@ -72,6 +72,32 @@ type SSHConfig struct {
|
||||
ConnectTimeoutSecs int `json:"connectTimeoutSecs"`
|
||||
}
|
||||
|
||||
// FTPConfig describes a file-transfer destination reached through FTP or
|
||||
// SFTP. It is intentionally separate from SSHConfig: SFTP uses the SSH
|
||||
// transport, but it is a different file-transfer protocol from the existing
|
||||
// SCP action and has its own remote-root semantics.
|
||||
//
|
||||
// Field design notes:
|
||||
// - Protocol is either "ftp" or "sftp". Their default ports are 21 and 22.
|
||||
// - AuthMethod is used only by SFTP. "password" uses Password, while "key"
|
||||
// reuses the local ssh-agent and ~/.ssh/id_* discovery used by SSH/SCP.
|
||||
// - PathPrefix is relative to the account's login root. The application
|
||||
// rejects absolute paths and traversal segments before uploading.
|
||||
// - StrictHostKey and KnownHostsPath apply only to SFTP. Plain FTP has no
|
||||
// host-key mechanism and sends credentials and data without encryption.
|
||||
type FTPConfig struct {
|
||||
Protocol string `json:"protocol"`
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
User string `json:"user"`
|
||||
AuthMethod string `json:"authMethod"`
|
||||
Password string `json:"password"`
|
||||
PathPrefix string `json:"pathPrefix"`
|
||||
StrictHostKey bool `json:"strictHostKey"`
|
||||
KnownHostsPath string `json:"knownHostsPath"`
|
||||
ConnectTimeoutSecs int `json:"connectTimeoutSecs"`
|
||||
}
|
||||
|
||||
// LLMProviderConfig stores one OpenAI-compatible multimodal chat endpoint.
|
||||
//
|
||||
// The three built-in providers (Qwen, Doubao/Ark, OpenAI-compatible) all use
|
||||
@@ -145,6 +171,12 @@ const (
|
||||
SSHAuthKerberos = "kerberos"
|
||||
)
|
||||
|
||||
// File-transfer protocol identifiers stored in FTPConfig.Protocol.
|
||||
const (
|
||||
FTPProtocolFTP = "ftp"
|
||||
FTPProtocolSFTP = "sftp"
|
||||
)
|
||||
|
||||
// Built-in LLM provider identifiers.
|
||||
const (
|
||||
LLMProviderQwen = "qwen"
|
||||
@@ -181,8 +213,8 @@ func (c SSHConfig) IsKerberos() bool {
|
||||
|
||||
// AppConfig is the top-level on-disk configuration document.
|
||||
//
|
||||
// We keep S3 / SSH nested so that adding more providers later (e.g. AliyunOSS,
|
||||
// COS, FTP) only requires a new sibling field rather than a schema rewrite.
|
||||
// We keep S3 / SSH / FTP nested so adding another provider later only requires
|
||||
// a new sibling field rather than a schema rewrite.
|
||||
type AppConfig struct {
|
||||
// Hotkey describes the global shortcut that triggers a capture.
|
||||
// Stored as a human-readable string like "cmd+shift+a"; parsing happens
|
||||
@@ -199,6 +231,9 @@ type AppConfig struct {
|
||||
// destination triggered by the save-remote toolbar button.
|
||||
SSH SSHConfig `json:"ssh"`
|
||||
|
||||
// FTP holds the destination used by the dedicated FTP/SFTP toolbar action.
|
||||
FTP FTPConfig `json:"ftp"`
|
||||
|
||||
// LLM holds provider settings for the "copy summary" screenshot action.
|
||||
LLM LLMConfig `json:"llm"`
|
||||
|
||||
@@ -221,6 +256,14 @@ func DefaultAppConfig() AppConfig {
|
||||
ConnectTimeoutSecs: 10,
|
||||
StrictHostKey: false,
|
||||
},
|
||||
FTP: FTPConfig{
|
||||
Protocol: FTPProtocolFTP,
|
||||
Port: 21,
|
||||
AuthMethod: SSHAuthPassword,
|
||||
PathPrefix: "snapgo/",
|
||||
ConnectTimeoutSecs: 10,
|
||||
StrictHostKey: false,
|
||||
},
|
||||
LLM: DefaultLLMConfig(),
|
||||
OCR: DefaultOCRConfig(),
|
||||
}
|
||||
@@ -315,6 +358,25 @@ func (c *AppConfig) Normalize() {
|
||||
if c.SSH.ConnectTimeoutSecs == 0 {
|
||||
c.SSH.ConnectTimeoutSecs = 10
|
||||
}
|
||||
if c.FTP.Protocol != FTPProtocolFTP && c.FTP.Protocol != FTPProtocolSFTP {
|
||||
c.FTP.Protocol = FTPProtocolFTP
|
||||
}
|
||||
if c.FTP.Port == 0 {
|
||||
if c.FTP.Protocol == FTPProtocolSFTP {
|
||||
c.FTP.Port = 22
|
||||
} else {
|
||||
c.FTP.Port = 21
|
||||
}
|
||||
}
|
||||
if c.FTP.AuthMethod != SSHAuthPassword && c.FTP.AuthMethod != SSHAuthKey {
|
||||
c.FTP.AuthMethod = SSHAuthPassword
|
||||
}
|
||||
if c.FTP.PathPrefix == "" {
|
||||
c.FTP.PathPrefix = "snapgo/"
|
||||
}
|
||||
if c.FTP.ConnectTimeoutSecs == 0 {
|
||||
c.FTP.ConnectTimeoutSecs = 10
|
||||
}
|
||||
|
||||
defaultLLM := DefaultLLMConfig()
|
||||
if c.LLM.ActiveProvider == "" {
|
||||
@@ -409,6 +471,14 @@ func (c AppConfig) IsSSHConfigured() bool {
|
||||
return c.SSH.Host != "" && c.SSH.User != ""
|
||||
}
|
||||
|
||||
// IsFTPConfigured reports whether the FTP/SFTP destination has the minimum
|
||||
// fields required to attempt a connection. Password is not required because
|
||||
// SFTP key authentication and password-less FTP accounts are both valid.
|
||||
func (c AppConfig) IsFTPConfigured() bool {
|
||||
return (c.FTP.Protocol == FTPProtocolFTP || c.FTP.Protocol == FTPProtocolSFTP) &&
|
||||
c.FTP.Host != "" && c.FTP.User != ""
|
||||
}
|
||||
|
||||
// ActiveLLMProvider returns the selected provider config plus a boolean
|
||||
// indicating whether the selection exists.
|
||||
func (c AppConfig) ActiveLLMProvider() (string, LLMProviderConfig, bool) {
|
||||
|
||||
Reference in New Issue
Block a user