serverless arch
This commit is contained in:
@@ -70,6 +70,33 @@ func All() ([]Remote, error) {
|
||||
return remotes, nil
|
||||
}
|
||||
|
||||
func One(gameID string) (Remote, error) {
|
||||
content, err := os.ReadFile(filepath.Join(datastorepath, gameID, "remote.json"))
|
||||
if err != nil {
|
||||
return Remote{}, err
|
||||
}
|
||||
|
||||
var r Remote
|
||||
err = json.Unmarshal(content, &r)
|
||||
if err != nil {
|
||||
return Remote{}, fmt.Errorf("corrupted datastore: failed to parse %s/remote.json: %w", gameID, err)
|
||||
}
|
||||
|
||||
content, err = os.ReadFile(filepath.Join(datastorepath, gameID, "metadata.json"))
|
||||
if err != nil {
|
||||
return Remote{}, fmt.Errorf("corrupted datastore: failed to read %s/metadata.json: %w", gameID, err)
|
||||
}
|
||||
|
||||
var m game.Metadata
|
||||
err = json.Unmarshal(content, &m)
|
||||
if err != nil {
|
||||
return Remote{}, fmt.Errorf("corrupted datastore: failed to parse %s/metadata.json: %w", gameID, err)
|
||||
}
|
||||
|
||||
r.GameID = m.ID
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func Set(gameID, url string) error {
|
||||
r := Remote{
|
||||
URL: url,
|
||||
|
||||
99
pkg/remote/sync.go
Normal file
99
pkg/remote/sync.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package remote
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/pkg/sftp"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func ConnectWithKey(host, user string) (*sftp.Client, error) {
|
||||
authMethods := loadSshKeys()
|
||||
|
||||
// Create SSH client configuration
|
||||
config := &ssh.ClientConfig{
|
||||
User: user,
|
||||
Auth: authMethods,
|
||||
}
|
||||
|
||||
return connect(host, config)
|
||||
}
|
||||
|
||||
func ConnectWithPassword(host, user string) (*sftp.Client, error) {
|
||||
fmt.Printf("%s@%s's password:", user, host)
|
||||
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create SSH client configuration
|
||||
config := &ssh.ClientConfig{
|
||||
User: user,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.Password(string(bytePassword)),
|
||||
},
|
||||
}
|
||||
|
||||
return connect(host, config)
|
||||
}
|
||||
|
||||
func connect(host string, config *ssh.ClientConfig) (*sftp.Client, error) {
|
||||
// Connect to the SSH server
|
||||
conn, err := ssh.Dial("tcp", host, config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot connect to ssh server: %w", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// Open SFTP session
|
||||
sftpClient, err := sftp.NewClient(conn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot connect to ssh server: %w", err)
|
||||
}
|
||||
return sftpClient, nil
|
||||
}
|
||||
|
||||
func loadSshKeys() []ssh.AuthMethod {
|
||||
dirname, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var auths []ssh.AuthMethod
|
||||
entries, err := os.ReadDir(filepath.Join(dirname, ".ssh"))
|
||||
if err != nil {
|
||||
return auths
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
name := entry.Name()
|
||||
keyPath := filepath.Join(dirname, name)
|
||||
keyData, err := os.ReadFile(keyPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
signer, err := ssh.ParsePrivateKey(keyData)
|
||||
if err != nil {
|
||||
fmt.Printf("%s's passphrase:", entry.Name())
|
||||
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
signer, err = ssh.ParsePrivateKeyWithPassphrase(keyData, bytePassword)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
auths = append(auths, ssh.PublicKeys(signer))
|
||||
}
|
||||
|
||||
return auths
|
||||
}
|
||||
32
pkg/sync/ssh/ssh.go
Normal file
32
pkg/sync/ssh/ssh.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"cloudsave/pkg/remote"
|
||||
"fmt"
|
||||
"log"
|
||||
"os/user"
|
||||
)
|
||||
|
||||
type (
|
||||
SFTPSyncer struct {
|
||||
}
|
||||
)
|
||||
|
||||
func (SFTPSyncer) Sync(r remote.Remote) error {
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get current user: %v", err)
|
||||
}
|
||||
cli, err := remote.ConnectWithKey(r.URL, currentUser.Username)
|
||||
if err != nil {
|
||||
cli, err = remote.ConnectWithPassword(r.URL, currentUser.Username)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to host: %w", err)
|
||||
}
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user