159 lines
4.1 KiB
Go
159 lines
4.1 KiB
Go
|
|
package cursor
|
||
|
|
|
||
|
|
import (
|
||
|
|
"bytes"
|
||
|
|
"encoding/json"
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
"sync"
|
||
|
|
|
||
|
|
"cursor/internal/appdata"
|
||
|
|
"cursor/internal/securefile"
|
||
|
|
)
|
||
|
|
|
||
|
|
var proxySettingsMu sync.Mutex
|
||
|
|
|
||
|
|
type proxySettingsBackup struct {
|
||
|
|
Original map[string]json.RawMessage `json:"original"`
|
||
|
|
Applied map[string]json.RawMessage `json:"applied"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func settingsBackupPath() string {
|
||
|
|
return filepath.Join(appdata.DataRootPath(), "proxy-settings-backup.json")
|
||
|
|
}
|
||
|
|
func rawEqual(a, b json.RawMessage) bool {
|
||
|
|
var x, y bytes.Buffer
|
||
|
|
if json.Compact(&x, a) != nil || json.Compact(&y, b) != nil {
|
||
|
|
return len(a) == 0 && len(b) == 0
|
||
|
|
}
|
||
|
|
return bytes.Equal(x.Bytes(), y.Bytes())
|
||
|
|
}
|
||
|
|
func loadSettings(path string) (map[string]json.RawMessage, error) {
|
||
|
|
data, err := os.ReadFile(path)
|
||
|
|
if errors.Is(err, os.ErrNotExist) {
|
||
|
|
return map[string]json.RawMessage{}, nil
|
||
|
|
}
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
data, err = normalizeJSONC(data)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
if len(bytes.TrimSpace(data)) == 0 {
|
||
|
|
return map[string]json.RawMessage{}, nil
|
||
|
|
}
|
||
|
|
var settings map[string]json.RawMessage
|
||
|
|
if err = json.Unmarshal(data, &settings); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
if settings == nil {
|
||
|
|
return nil, fmt.Errorf("Cursor 配置必须是 JSON 对象")
|
||
|
|
}
|
||
|
|
return settings, nil
|
||
|
|
}
|
||
|
|
func readSettingsBackup() (proxySettingsBackup, error) {
|
||
|
|
backup := proxySettingsBackup{Original: map[string]json.RawMessage{}, Applied: map[string]json.RawMessage{}}
|
||
|
|
data, err := os.ReadFile(settingsBackupPath())
|
||
|
|
if errors.Is(err, os.ErrNotExist) {
|
||
|
|
return backup, nil
|
||
|
|
}
|
||
|
|
if err != nil {
|
||
|
|
return backup, err
|
||
|
|
}
|
||
|
|
err = json.Unmarshal(data, &backup)
|
||
|
|
if err == nil && (backup.Original == nil || backup.Applied == nil) {
|
||
|
|
err = fmt.Errorf("代理配置备份损坏")
|
||
|
|
}
|
||
|
|
return backup, err
|
||
|
|
}
|
||
|
|
|
||
|
|
func WriteUserProxySettings(proxyURL string) error {
|
||
|
|
if proxyURL == "" {
|
||
|
|
return fmt.Errorf("代理地址为空")
|
||
|
|
}
|
||
|
|
proxySettingsMu.Lock()
|
||
|
|
defer proxySettingsMu.Unlock()
|
||
|
|
path, err := resolveCursorSettingsPath()
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
settings, err := loadSettings(path)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("解析 Cursor 配置失败,原文件已保留: %w", err)
|
||
|
|
}
|
||
|
|
backup, err := readSettingsBackup()
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
values := map[string]any{"http.proxy": proxyURL, "http.proxyKerberosServicePrincipal": proxyURL, "http.proxySupport": "on", "cursor.general.disableHttp2": true, "http.experimental.systemCertificatesV2": true}
|
||
|
|
for key, value := range values {
|
||
|
|
if previous, exists := settings[key]; exists {
|
||
|
|
if !rawEqual(previous, backup.Applied[key]) {
|
||
|
|
backup.Original[key] = append(json.RawMessage(nil), previous...)
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
delete(backup.Original, key)
|
||
|
|
}
|
||
|
|
encoded, _ := json.Marshal(value)
|
||
|
|
backup.Applied[key] = encoded
|
||
|
|
settings[key] = encoded
|
||
|
|
}
|
||
|
|
backupData, err := json.Marshal(backup)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if err = securefile.Write(settingsBackupPath(), backupData); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
// Preserve the product's intentional permanent auto-update setting.
|
||
|
|
settings["update.mode"] = json.RawMessage(`"none"`)
|
||
|
|
data, err := json.MarshalIndent(settings, "", " ")
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
return securefile.Write(path, append(data, '\n'))
|
||
|
|
}
|
||
|
|
|
||
|
|
func ClearUserProxySettings() error {
|
||
|
|
proxySettingsMu.Lock()
|
||
|
|
defer proxySettingsMu.Unlock()
|
||
|
|
path, err := resolveCursorSettingsPath()
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
backup, err := readSettingsBackup()
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if len(backup.Applied) == 0 {
|
||
|
|
return nil
|
||
|
|
} // No ownership proof: preserve user values.
|
||
|
|
settings, err := loadSettings(path)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("解析 Cursor 配置失败,原文件已保留: %w", err)
|
||
|
|
}
|
||
|
|
for key, applied := range backup.Applied {
|
||
|
|
if rawEqual(settings[key], applied) {
|
||
|
|
if previous, exists := backup.Original[key]; exists {
|
||
|
|
settings[key] = previous
|
||
|
|
} else {
|
||
|
|
delete(settings, key)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
data, err := json.MarshalIndent(settings, "", " ")
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if err = securefile.Write(path, append(data, '\n')); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if err = os.Remove(settingsBackupPath()); err != nil && !os.IsNotExist(err) {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|