feat: enhance sidebar edit functionality and prevent text selection
- Added aria-label and type attributes to the edit button in the sidebar. - Implemented a mechanism to prevent multiple edit triggers using requestAnimationFrame. - Updated mousedown event handling to allow real controls while preventing text selection in WKWebView. feat: add reconnectSession method to TabManager - Introduced reconnectSession method to facilitate session reconnections by ID. feat: implement disconnected overlay in terminal - Added setDisconnectedOverlay method to show/hide a disconnected overlay for terminal sessions. - Created _ensureDisconnectOverlay method to manage the overlay's existence and content. feat: extend Service API with file-related functions - Added FileExists and FileIsPrivateKey functions to check file existence and identify private keys. - Introduced ListSSHKeys function to retrieve SSH private key paths from the user's .ssh directory. - Implemented SFTPStat and SFTPUploadData functions for enhanced SFTP operations. refactor: clean up code formatting and improve readability - Adjusted code formatting for better readability across various files, including consistent use of line breaks and indentation.
This commit is contained in:
@@ -3,14 +3,17 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -145,6 +148,99 @@ func (s *Service) ListProfiles() ([]models.Profile, error) {
|
||||
return derefProfiles(profiles), nil
|
||||
}
|
||||
|
||||
// ListSSHKeys returns absolute paths to candidate SSH private keys in ~/.ssh.
|
||||
func (s *Service) ListSSHKeys() ([]string, error) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
dir := filepath.Join(home, ".ssh")
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return []string{}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ignored := map[string]bool{
|
||||
"authorized_keys": true,
|
||||
"authorized_keys2": true,
|
||||
"config": true,
|
||||
"known_hosts": true,
|
||||
"known_hosts.old": true,
|
||||
"ssh_config": true,
|
||||
"ssh_known_hosts": true,
|
||||
"ssh_known_hosts2": true,
|
||||
}
|
||||
|
||||
keys := make([]string, 0)
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
name := entry.Name()
|
||||
if ignored[name] {
|
||||
continue
|
||||
}
|
||||
if strings.HasSuffix(name, ".pub") {
|
||||
continue
|
||||
}
|
||||
if strings.HasSuffix(name, ".old") {
|
||||
continue
|
||||
}
|
||||
fullPath := filepath.Join(dir, name)
|
||||
if !isLikelyPrivateKey(fullPath) {
|
||||
continue
|
||||
}
|
||||
keys = append(keys, fullPath)
|
||||
}
|
||||
|
||||
sort.Strings(keys)
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
func isLikelyPrivateKey(path string) bool {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
buf := make([]byte, 512)
|
||||
n, err := file.Read(buf)
|
||||
if err != nil && n == 0 {
|
||||
return false
|
||||
}
|
||||
head := buf[:n]
|
||||
|
||||
if bytes.HasPrefix(head, []byte("openssh-key-v1")) {
|
||||
return true
|
||||
}
|
||||
|
||||
if bytes.Contains(head, []byte("BEGIN OPENSSH PRIVATE KEY")) {
|
||||
return true
|
||||
}
|
||||
if bytes.Contains(head, []byte("BEGIN RSA PRIVATE KEY")) {
|
||||
return true
|
||||
}
|
||||
if bytes.Contains(head, []byte("BEGIN DSA PRIVATE KEY")) {
|
||||
return true
|
||||
}
|
||||
if bytes.Contains(head, []byte("BEGIN EC PRIVATE KEY")) {
|
||||
return true
|
||||
}
|
||||
if bytes.Contains(head, []byte("BEGIN ED25519 PRIVATE KEY")) {
|
||||
return true
|
||||
}
|
||||
if bytes.Contains(head, []byte("BEGIN PRIVATE KEY")) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SearchProfiles searches profiles by display name, host, or tags.
|
||||
func (s *Service) SearchProfiles(query string) ([]models.Profile, error) {
|
||||
profiles, err := s.store.SearchProfiles(query)
|
||||
@@ -733,6 +829,29 @@ func (s *Service) SaveFile(title string, defaultName string) (string, error) {
|
||||
return path, nil
|
||||
}
|
||||
|
||||
// FileExists returns true if the path exists and is not a directory.
|
||||
func (s *Service) FileExists(path string) (bool, error) {
|
||||
if path == "" {
|
||||
return false, nil
|
||||
}
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
return !info.IsDir(), nil
|
||||
}
|
||||
|
||||
// FileIsPrivateKey returns true if the path appears to be an SSH private key.
|
||||
func (s *Service) FileIsPrivateKey(path string) (bool, error) {
|
||||
if path == "" {
|
||||
return false, nil
|
||||
}
|
||||
return isLikelyPrivateKey(path), nil
|
||||
}
|
||||
|
||||
// GetAppInfo returns build and environment information.
|
||||
func (s *Service) GetAppInfo() map[string]string {
|
||||
return map[string]string{
|
||||
|
||||
Reference in New Issue
Block a user