feat: add agent-friendly ufile CLI
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type configFile struct {
|
||||
Host string `json:"host,omitempty"`
|
||||
Session string `json:"session,omitempty"`
|
||||
ExpiresAt time.Time `json:"expiresAt,omitempty"`
|
||||
}
|
||||
|
||||
func defaultConfigPath() string {
|
||||
if value := os.Getenv("UFILE_CONFIG"); value != "" {
|
||||
return value
|
||||
}
|
||||
dir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return "ufile-cli.json"
|
||||
}
|
||||
return filepath.Join(dir, "ufile-cli", "config.json")
|
||||
}
|
||||
|
||||
func loadConfig(path string) (configFile, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return configFile{}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return configFile{}, err
|
||||
}
|
||||
var config configFile
|
||||
if err := json.Unmarshal(data, &config); err != nil {
|
||||
return configFile{}, fmt.Errorf("配置文件格式错误: %w", err)
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func saveConfig(path string, config configFile) error {
|
||||
dir := filepath.Dir(path)
|
||||
if err := os.MkdirAll(dir, 0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := json.MarshalIndent(config, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data = append(data, '\n')
|
||||
temp, err := os.CreateTemp(dir, ".config-*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tempName := temp.Name()
|
||||
defer os.Remove(tempName)
|
||||
if err := temp.Chmod(0o600); err != nil {
|
||||
temp.Close()
|
||||
return err
|
||||
}
|
||||
if _, err := temp.Write(data); err != nil {
|
||||
temp.Close()
|
||||
return err
|
||||
}
|
||||
if err := temp.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Rename(tempName, path); err == nil {
|
||||
return nil
|
||||
}
|
||||
if err := os.Remove(path); err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
return os.Rename(tempName, path)
|
||||
}
|
||||
|
||||
func normalizeHost(value string) (string, error) {
|
||||
value = strings.TrimSpace(value)
|
||||
parsed, err := url.Parse(value)
|
||||
if err != nil || parsed.Scheme == "" || parsed.Host == "" {
|
||||
return "", fmt.Errorf("无效的服务地址 %q,需要包含 http:// 或 https://", value)
|
||||
}
|
||||
if parsed.Scheme != "http" && parsed.Scheme != "https" {
|
||||
return "", fmt.Errorf("服务地址只支持 http 或 https")
|
||||
}
|
||||
if parsed.RawQuery != "" || parsed.Fragment != "" {
|
||||
return "", fmt.Errorf("服务地址不能包含查询参数或片段")
|
||||
}
|
||||
parsed.Path = strings.TrimRight(parsed.Path, "/")
|
||||
return strings.TrimRight(parsed.String(), "/"), nil
|
||||
}
|
||||
Reference in New Issue
Block a user