summaryrefslogtreecommitdiff
path: root/internal/client/config
diff options
context:
space:
mode:
Diffstat (limited to 'internal/client/config')
-rw-r--r--internal/client/config/cache.go126
-rw-r--r--internal/client/config/config.go60
2 files changed, 157 insertions, 29 deletions
diff --git a/internal/client/config/cache.go b/internal/client/config/cache.go
new file mode 100644
index 0000000..a78425b
--- /dev/null
+++ b/internal/client/config/cache.go
@@ -0,0 +1,126 @@
+package config
+
+import (
+ "crypto/rand"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "os"
+ "path/filepath"
+ "reflect"
+
+ "github.com/kyren223/eko/pkg/assert"
+)
+
+type Cache struct {
+ // Key is a server URL like "eko.kyren.codes" and value is the base 16 hash
+ TosHashes map[string]string `json:"tos_hashes"`
+ DeviceID string `json:"device_id"`
+}
+
+func DefaultCache() Cache {
+ return Cache{
+ TosHashes: map[string]string{},
+ DeviceID: "",
+ }
+}
+
+func VerifyAndFixCache(cache *Cache) error {
+ if cache.DeviceID == "" {
+ cache.DeviceID = GenerateDeviceID()
+ }
+ return nil
+}
+
+func GenerateDeviceID() string {
+ // Generate a random 32-byte device ID
+ // Gurantees uniqueness, so it's not reverseable and it is annonymized
+ // For legal reasons: this may still be logged on the server, so associated with an IP address
+ deviceId := [32]byte{}
+ _, err := rand.Read(deviceId[:])
+ assert.NoError(err, "random never fails")
+ return fmt.Sprintf("%x", deviceId)
+}
+
+var (
+ cache = Cache{}
+ CacheDir string
+ CacheFile string
+)
+
+func LoadCache() error {
+ userCacheDir, err := os.UserCacheDir()
+ if err != nil {
+ return err
+ }
+
+ CacheDir = filepath.Join(userCacheDir, "eko")
+ err = os.MkdirAll(CacheDir, 0o750)
+ if err != nil {
+ return err
+ }
+
+ CacheFile = filepath.Join(CacheDir, "cache.json")
+ contents, err := os.ReadFile(CacheFile) // #nosec 304
+ if errors.Is(err, os.ErrNotExist) {
+ cache = DefaultCache()
+ return writeCache()
+ }
+ if err != nil {
+ return err
+ }
+
+ var rawMap map[string]json.RawMessage
+ if err := json.Unmarshal(contents, &rawMap); err != nil {
+ return err
+ }
+
+ defaultVal := reflect.ValueOf(DefaultCache())
+ finalCache := reflect.New(defaultVal.Type()).Elem()
+ finalCache.Set(defaultVal)
+
+ for i := 0; i < defaultVal.NumField(); i++ {
+ field := defaultVal.Type().Field(i)
+ jsonTag := field.Tag.Get("json")
+ if jsonTag == "" {
+ jsonTag = field.Name
+ }
+
+ rawValue, found := rawMap[jsonTag]
+ fieldValue := finalCache.Field(i)
+ if !found || !fieldValue.CanAddr() {
+ continue
+ }
+
+ err := json.Unmarshal(rawValue, fieldValue.Addr().Interface())
+ if err != nil {
+ return fmt.Errorf("error unmarshaling field %s: %w", field.Name, err)
+ }
+ }
+
+ cache = finalCache.Interface().(Cache)
+
+ err = VerifyAndFixCache(&cache)
+ if err != nil {
+ return err
+ }
+
+ return writeCache()
+}
+
+func writeCache() error {
+ b, err := json.MarshalIndent(cache, "", " ")
+ if err != nil {
+ return err
+ }
+ return os.WriteFile(CacheFile, b, 0o600)
+}
+
+func ReadCache() Cache {
+ return cache
+}
+
+func UseCache(f func(cache *Cache)) error {
+ f(&cache)
+ return writeCache()
+}
diff --git a/internal/client/config/config.go b/internal/client/config/config.go
index 4ceea6b..2de0ea4 100644
--- a/internal/client/config/config.go
+++ b/internal/client/config/config.go
@@ -12,28 +12,30 @@ import (
)
type Config struct {
- ServerName string `json:"server_name"`
- PrivateKeyPath string `json:"private_key_path"`
- InsertModeTabToSpace bool `json:"insert_mode_tab_to_space"`
- InsertModeSpacesPerTab uint8 `json:"insert_mode_spaces_per_tab"`
- InsecureDebugMode bool `json:"insecure_debug_mode"`
- Colors []string `json:"colors"`
+ ServerName string `json:"server_name"`
+ PrivateKeyPath string `json:"private_key_path"`
+ InsertModeTabToSpace bool `json:"insert_mode_tab_to_space"`
+ InsertModeSpacesPerTab uint8 `json:"insert_mode_spaces_per_tab"`
+ InsecureDebugMode bool `json:"insecure_debug_mode"`
+ Colors []string `json:"colors"`
+ AnonymousDeviceAnalytics bool `json:"anonymous_device_analytics"`
}
-func Default() Config {
+func DefaultConfig() Config {
return Config{
- ServerName: "eko.kyren.codes",
- PrivateKeyPath: "",
- InsertModeTabToSpace: true,
- InsertModeSpacesPerTab: 4,
- InsecureDebugMode: false,
- Colors: nil,
+ ServerName: "eko.kyren.codes",
+ PrivateKeyPath: "",
+ InsertModeTabToSpace: true,
+ InsertModeSpacesPerTab: 4,
+ InsecureDebugMode: false,
+ Colors: nil,
+ AnonymousDeviceAnalytics: true,
}
}
-func VerifyAndFix(config *Config) error {
+func VerifyAndFixConfig(config *Config) error {
if config.ServerName == "" {
- config.ServerName = Default().ServerName
+ config.ServerName = DefaultConfig().ServerName
}
if config.Colors != nil {
@@ -58,27 +60,27 @@ func VerifyAndFix(config *Config) error {
var (
config = Config{}
- Dir string
+ ConfigDir string
ConfigFile string
)
-func Load() error {
+func LoadConfig() error {
userConfigDir, err := os.UserConfigDir()
if err != nil {
return err
}
- Dir = filepath.Join(userConfigDir, "eko")
- err = os.MkdirAll(Dir, 0o750)
+ ConfigDir = filepath.Join(userConfigDir, "eko")
+ err = os.MkdirAll(ConfigDir, 0o750)
if err != nil {
return err
}
- ConfigFile = filepath.Join(Dir, "config.json")
+ ConfigFile = filepath.Join(ConfigDir, "config.json")
contents, err := os.ReadFile(ConfigFile) // #nosec 304
if errors.Is(err, os.ErrNotExist) {
- config = Default()
- return write()
+ config = DefaultConfig()
+ return writeConfig()
}
if err != nil {
return err
@@ -89,7 +91,7 @@ func Load() error {
return err
}
- defaultVal := reflect.ValueOf(Default())
+ defaultVal := reflect.ValueOf(DefaultConfig())
finalConfig := reflect.New(defaultVal.Type()).Elem()
finalConfig.Set(defaultVal)
@@ -114,15 +116,15 @@ func Load() error {
config = finalConfig.Interface().(Config)
- err = VerifyAndFix(&config)
+ err = VerifyAndFixConfig(&config)
if err != nil {
return err
}
- return write()
+ return writeConfig()
}
-func write() error {
+func writeConfig() error {
b, err := json.MarshalIndent(config, "", " ")
if err != nil {
return err
@@ -130,11 +132,11 @@ func write() error {
return os.WriteFile(ConfigFile, b, 0o600)
}
-func Read() Config {
+func ReadConfig() Config {
return config
}
-func Use(f func(config *Config)) error {
+func UseConfig(f func(config *Config)) error {
f(&config)
- return write()
+ return writeConfig()
}