mirror of
https://github.com/MengMengCode/VoCat.git
synced 2026-08-20 06:43:42 +08:00
feat: expand device networking and management
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
package developer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"vocat/internal/exportproxy"
|
||||
"vocat/internal/httpsmode"
|
||||
"vocat/internal/store"
|
||||
)
|
||||
|
||||
func Enabled(ctx context.Context, database *store.Store) bool {
|
||||
setting, err := database.AppSetting(ctx, EnabledSettingKey)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
var document struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
return json.Unmarshal(setting.Value, &document) == nil && document.Enabled
|
||||
}
|
||||
|
||||
const (
|
||||
EnabledSettingKey = "developer.enabled"
|
||||
DeviceLimitSettingKey = "developer.device_limit"
|
||||
DefaultDeviceLimit = 5
|
||||
MaxDeviceLimit = 128
|
||||
)
|
||||
|
||||
func DeviceLimit(ctx context.Context, database *store.Store, enabled bool) int {
|
||||
if !enabled {
|
||||
return DefaultDeviceLimit
|
||||
}
|
||||
setting, err := database.AppSetting(ctx, DeviceLimitSettingKey)
|
||||
if err != nil {
|
||||
return DefaultDeviceLimit
|
||||
}
|
||||
var document struct {
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
if json.Unmarshal(setting.Value, &document) != nil || document.Limit < 1 || document.Limit > MaxDeviceLimit {
|
||||
return DefaultDeviceLimit
|
||||
}
|
||||
return document.Limit
|
||||
}
|
||||
|
||||
func SetDeviceLimit(ctx context.Context, database *store.Store, limit int) error {
|
||||
if limit < 1 || limit > MaxDeviceLimit {
|
||||
return fmt.Errorf("device limit must be between 1 and %d", MaxDeviceLimit)
|
||||
}
|
||||
value, err := json.Marshal(map[string]int{"limit": limit})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return database.UpsertAppSetting(ctx, store.AppSetting{Key: DeviceLimitSettingKey, Value: value})
|
||||
}
|
||||
|
||||
// ResetExperimental restores every mutable developer-only setting. It is
|
||||
// called both by `vocat develop off` and at startup whenever developer mode is
|
||||
// disabled, so stale database values cannot silently remain active.
|
||||
func ResetExperimental(ctx context.Context, database *store.Store) error {
|
||||
httpsValue, err := json.Marshal(map[string]bool{"enabled": false})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var resetErrors []error
|
||||
if err := database.UpsertAppSetting(ctx, store.AppSetting{Key: httpsmode.SettingKey, Value: httpsValue}); err != nil {
|
||||
resetErrors = append(resetErrors, fmt.Errorf("reset self-signed HTTPS: %w", err))
|
||||
}
|
||||
if err := SetDeviceLimit(ctx, database, DefaultDeviceLimit); err != nil {
|
||||
resetErrors = append(resetErrors, fmt.Errorf("reset device limit: %w", err))
|
||||
}
|
||||
if err := database.DeleteAppSetting(ctx, exportproxy.SettingKey); err != nil && !errors.Is(err, store.ErrNotFound) {
|
||||
resetErrors = append(resetErrors, fmt.Errorf("delete export proxy configurations: %w", err))
|
||||
}
|
||||
devices, err := database.ListDevices(ctx)
|
||||
if err != nil {
|
||||
resetErrors = append(resetErrors, fmt.Errorf("list devices while disabling roaming data: %w", err))
|
||||
} else {
|
||||
for _, device := range devices {
|
||||
if !device.NetworkEnabled {
|
||||
continue
|
||||
}
|
||||
device.NetworkEnabled = false
|
||||
if err := database.UpsertDevice(ctx, device); err != nil {
|
||||
resetErrors = append(resetErrors, fmt.Errorf("disable roaming data for device %s: %w", device.ID, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
policies, err := database.ListCardPolicies(ctx)
|
||||
if err != nil {
|
||||
resetErrors = append(resetErrors, fmt.Errorf("list card policies while disabling roaming data: %w", err))
|
||||
} else {
|
||||
for _, policy := range policies {
|
||||
if !policy.NetworkEnabled {
|
||||
continue
|
||||
}
|
||||
policy.NetworkEnabled = false
|
||||
if err := database.UpsertCardPolicy(ctx, policy); err != nil {
|
||||
resetErrors = append(resetErrors, fmt.Errorf("disable roaming policy for card %s: %w", policy.ICCID, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.Join(resetErrors...)
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package developer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"vocat/internal/exportproxy"
|
||||
"vocat/internal/httpsmode"
|
||||
"vocat/internal/store"
|
||||
)
|
||||
|
||||
func TestResetExperimentalRestoresDefaults(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
database, err := store.Open(ctx, filepath.Join(t.TempDir(), "vocat.db"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer database.Close()
|
||||
if err := SetDeviceLimit(ctx, database, 24); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
enabled, _ := json.Marshal(map[string]bool{"enabled": true})
|
||||
if err := database.UpsertAppSetting(ctx, store.AppSetting{Key: httpsmode.SettingKey, Value: enabled}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := database.UpsertDevice(ctx, store.Device{ID: "modem-1", Name: "modem-1", NetworkEnabled: true}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := database.UpsertCardPolicy(ctx, store.CardPolicy{ICCID: "8901000000000000001", NetworkEnabled: true, IPVersion: "IPV4V6"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := database.UpsertAppSetting(ctx, store.AppSetting{Key: exportproxy.SettingKey, Value: json.RawMessage(`[]`)}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ResetExperimental(ctx, database); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if limit := DeviceLimit(ctx, database, true); limit != DefaultDeviceLimit {
|
||||
t.Fatalf("device limit = %d, want %d", limit, DefaultDeviceLimit)
|
||||
}
|
||||
setting, err := database.AppSetting(ctx, httpsmode.SettingKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var document struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
if err := json.Unmarshal(setting.Value, &document); err != nil || document.Enabled {
|
||||
t.Fatalf("HTTPS setting = %s, error = %v", setting.Value, err)
|
||||
}
|
||||
device, err := database.Device(ctx, "modem-1")
|
||||
if err != nil || device.NetworkEnabled {
|
||||
t.Fatalf("device roaming data was not disabled: %+v, %v", device, err)
|
||||
}
|
||||
policy, err := database.CardPolicy(ctx, "8901000000000000001")
|
||||
if err != nil || policy.NetworkEnabled {
|
||||
t.Fatalf("card roaming policy was not disabled: %+v, %v", policy, err)
|
||||
}
|
||||
if _, err := database.AppSetting(ctx, exportproxy.SettingKey); !errors.Is(err, store.ErrNotFound) {
|
||||
t.Fatalf("export proxy configurations were not deleted: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetDeviceLimitValidatesRange(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
database, err := store.Open(ctx, filepath.Join(t.TempDir(), "vocat.db"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer database.Close()
|
||||
if SetDeviceLimit(ctx, database, 0) == nil || SetDeviceLimit(ctx, database, MaxDeviceLimit+1) == nil {
|
||||
t.Fatal("out-of-range device limit was accepted")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user