serverless arch

This commit is contained in:
2025-05-11 02:32:33 +02:00
parent 15b1efcc48
commit 30c71cb449
8 changed files with 291 additions and 27 deletions

View File

@@ -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
View 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
View 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
}