feat: add option to set storage.json as read-only.

1. Default does not set storage.json file to read-only mode
2. Default retains other configuration information in storage.json
This commit is contained in:
Ricky 2024-12-18 11:18:27 +08:00
parent 5884f82d66
commit 791156055f

170
main.go
View File

@ -9,6 +9,7 @@ import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
"flag"
"fmt" "fmt"
"log" "log"
"os" "os"
@ -45,21 +46,22 @@ const (
type ( type (
// TextResource stores multilingual text / 存储多语言文本 // TextResource stores multilingual text / 存储多语言文本
TextResource struct { TextResource struct {
SuccessMessage string SuccessMessage string
RestartMessage string RestartMessage string
ReadingConfig string ReadingConfig string
GeneratingIds string GeneratingIds string
PressEnterToExit string PressEnterToExit string
ErrorPrefix string ErrorPrefix string
PrivilegeError string PrivilegeError string
RunAsAdmin string RunAsAdmin string
RunWithSudo string RunWithSudo string
SudoExample string SudoExample string
ConfigLocation string ConfigLocation string
CheckingProcesses string CheckingProcesses string
ClosingProcesses string ClosingProcesses string
ProcessesClosed string ProcessesClosed string
PleaseWait string PleaseWait string
SetReadOnlyMessage string
} }
// StorageConfig optimized storage configuration / 优化的存储配置 // StorageConfig optimized storage configuration / 优化的存储配置
@ -117,42 +119,46 @@ var (
texts = map[Language]TextResource{ texts = map[Language]TextResource{
CN: { CN: {
SuccessMessage: "[√] 配置文件已成功更新!", SuccessMessage: "[√] 配置文件已成功更新!",
RestartMessage: "[!] 请手动重启 Cursor 以使更新生效", RestartMessage: "[!] 请手动重启 Cursor 以使更新生效",
ReadingConfig: "正在读取配置文件...", ReadingConfig: "正在读取配置文件...",
GeneratingIds: "正在生成新的标识符...", GeneratingIds: "正在生成新的标识符...",
PressEnterToExit: "按回车键退出程序...", PressEnterToExit: "按回车键退出程序...",
ErrorPrefix: "程序发生严重错误: %v", ErrorPrefix: "程序发生严重错误: %v",
PrivilegeError: "\n[!] 错误:需要管理员权限", PrivilegeError: "\n[!] 错误:需要管理员权限",
RunAsAdmin: "请右键点击程序,选择「以管理员身份运行」", RunAsAdmin: "请右键点击程序,选择「以管理员身份运行」",
RunWithSudo: "请使用 sudo 命令运行此程序", RunWithSudo: "请使用 sudo 命令运行此程序",
SudoExample: "示例: sudo %s", SudoExample: "示例: sudo %s",
ConfigLocation: "配置文件位置:", ConfigLocation: "配置文件位置:",
CheckingProcesses: "正在检查运行中的 Cursor 实例...", CheckingProcesses: "正在检查运行中的 Cursor 实例...",
ClosingProcesses: "正在关闭 Cursor 实例...", ClosingProcesses: "正在关闭 Cursor 实例...",
ProcessesClosed: "所有 Cursor 实例已关闭", ProcessesClosed: "所有 Cursor 实例已关闭",
PleaseWait: "请稍候...", PleaseWait: "请稍候...",
SetReadOnlyMessage: "设置 storage.json 为只读模式, 这将导致 workspace 记录信息丢失等问题",
}, },
EN: { EN: {
SuccessMessage: "[√] Configuration file updated successfully!", SuccessMessage: "[√] Configuration file updated successfully!",
RestartMessage: "[!] Please restart Cursor manually for changes to take effect", RestartMessage: "[!] Please restart Cursor manually for changes to take effect",
ReadingConfig: "Reading configuration file...", ReadingConfig: "Reading configuration file...",
GeneratingIds: "Generating new identifiers...", GeneratingIds: "Generating new identifiers...",
PressEnterToExit: "Press Enter to exit...", PressEnterToExit: "Press Enter to exit...",
ErrorPrefix: "Program encountered a serious error: %v", ErrorPrefix: "Program encountered a serious error: %v",
PrivilegeError: "\n[!] Error: Administrator privileges required", PrivilegeError: "\n[!] Error: Administrator privileges required",
RunAsAdmin: "Please right-click and select 'Run as Administrator'", RunAsAdmin: "Please right-click and select 'Run as Administrator'",
RunWithSudo: "Please run this program with sudo", RunWithSudo: "Please run this program with sudo",
SudoExample: "Example: sudo %s", SudoExample: "Example: sudo %s",
ConfigLocation: "Config file location:", ConfigLocation: "Config file location:",
CheckingProcesses: "Checking for running Cursor instances...", CheckingProcesses: "Checking for running Cursor instances...",
ClosingProcesses: "Closing Cursor instances...", ClosingProcesses: "Closing Cursor instances...",
ProcessesClosed: "All Cursor instances have been closed", ProcessesClosed: "All Cursor instances have been closed",
PleaseWait: "Please wait...", PleaseWait: "Please wait...",
SetReadOnlyMessage: "Set storage.json to read-only mode, which will cause issues such as lost workspace records",
}, },
} }
) )
var setReadOnly *bool = flag.Bool("r", false, "set storage.json to read-only mode")
// Error Implementation / 错误实现 // Error Implementation / 错误实现
func (e *AppError) Error() string { func (e *AppError) Error() string {
if e.Context != nil { if e.Context != nil {
@ -225,16 +231,6 @@ func saveConfig(config *StorageConfig, username string) error { // Modified to t
return err return err
} }
content, err := json.MarshalIndent(config, "", " ")
if err != nil {
return &AppError{
Type: ErrSystem,
Op: "generate JSON",
Path: "",
Err: err,
}
}
// Create parent directories with proper permissions // Create parent directories with proper permissions
dir := filepath.Dir(configPath) dir := filepath.Dir(configPath)
if err := os.MkdirAll(dir, 0755); err != nil { if err := os.MkdirAll(dir, 0755); err != nil {
@ -256,9 +252,53 @@ func saveConfig(config *StorageConfig, username string) error { // Modified to t
} }
} }
originalFileStat, err := os.Stat(configPath)
if err != nil {
return &AppError{
Type: ErrSystem,
Op: "get file mode",
Path: configPath,
Err: err,
}
}
originalFileMode := originalFileStat.Mode()
originalFileContent, err := os.ReadFile(configPath)
if err != nil {
return &AppError{
Type: ErrSystem,
Op: "read original file",
Path: configPath,
Err: err,
}
}
var originalFile map[string]any
if err := json.Unmarshal(originalFileContent, &originalFile); err != nil {
return &AppError{
Type: ErrSystem,
Op: "unmarshal original file",
Path: configPath,
Err: err,
}
}
originalFile["telemetry.sqmId"] = config.TelemetrySqmId
originalFile["telemetry.macMachineId"] = config.TelemetryMacMachineId
originalFile["telemetry.machineId"] = config.TelemetryMachineId
originalFile["telemetry.devDeviceId"] = config.TelemetryDevDeviceId
newFileContent, err := json.MarshalIndent(originalFile, "", " ")
if err != nil {
return &AppError{
Type: ErrSystem,
Op: "marshal new file",
Path: configPath,
Err: err,
}
}
// Write to temporary file first // Write to temporary file first
tmpPath := configPath + ".tmp" tmpPath := configPath + ".tmp"
if err := os.WriteFile(tmpPath, content, 0666); err != nil { if err := os.WriteFile(tmpPath, newFileContent, 0666); err != nil {
return &AppError{ return &AppError{
Type: ErrSystem, Type: ErrSystem,
Op: "write temporary file", Op: "write temporary file",
@ -267,8 +307,12 @@ func saveConfig(config *StorageConfig, username string) error { // Modified to t
} }
} }
if *setReadOnly {
originalFileMode = 0444
}
// Ensure proper permissions on temporary file // Ensure proper permissions on temporary file
if err := os.Chmod(tmpPath, 0444); err != nil { if err := os.Chmod(tmpPath, originalFileMode); err != nil {
os.Remove(tmpPath) os.Remove(tmpPath)
return &AppError{ return &AppError{
Type: ErrSystem, Type: ErrSystem,
@ -495,6 +539,15 @@ func showSuccess() {
} }
} }
func showReadOnlyMessage() {
if *setReadOnly {
warningColor := color.New(color.FgYellow, color.Bold)
warningColor.Printf("%s\n", texts[currentLanguage].SetReadOnlyMessage)
fmt.Println("Press Enter to continue...")
bufio.NewReader(os.Stdin).ReadString('\n')
}
}
func showPrivilegeError() { func showPrivilegeError() {
text := texts[currentLanguage] text := texts[currentLanguage]
red := color.New(color.FgRed, color.Bold) red := color.New(color.FgRed, color.Bold)
@ -691,6 +744,9 @@ func main() {
} }
}() }()
flag.Parse()
showReadOnlyMessage()
var username string var username string
if username = os.Getenv("SUDO_USER"); username == "" { if username = os.Getenv("SUDO_USER"); username == "" {
user, err := user.Current() user, err := user.Current()