diff --git a/server/service/openframe/openframe_machine_id_provider.go b/server/service/openframe/openframe_machine_id_provider.go new file mode 100644 index 00000000000..527e18b39de --- /dev/null +++ b/server/service/openframe/openframe_machine_id_provider.go @@ -0,0 +1,90 @@ +package openframe + +import ( + "os" + "path/filepath" + "runtime" + "strings" + "sync" + + "github.com/rs/zerolog/log" +) + +type OpenFrameMachineIdProvider struct { + machineId string + initialized bool + mu sync.RWMutex +} + +func NewOpenFrameMachineIdProvider() *OpenFrameMachineIdProvider { + return &OpenFrameMachineIdProvider{} +} + +// GetMachineId returns the machine ID, reading from file if not cached +func (p *OpenFrameMachineIdProvider) GetMachineId() string { + p.mu.RLock() + if p.initialized { + machineId := p.machineId + p.mu.RUnlock() + return machineId + } + p.mu.RUnlock() + + p.mu.Lock() + defer p.mu.Unlock() + + if p.initialized { + return p.machineId + } + + p.machineId = p.readFromFile() + p.initialized = true + return p.machineId +} + +// Refresh forces a re-read of machine ID from file +func (p *OpenFrameMachineIdProvider) Refresh() { + p.mu.Lock() + defer p.mu.Unlock() + + p.machineId = p.readFromFile() + p.initialized = true +} + +func (p *OpenFrameMachineIdProvider) getFilePath() string { + switch runtime.GOOS { + case "windows": + programData := os.Getenv("ProgramData") + if programData == "" { + return "" + } + return filepath.Join(programData, "OpenFrame", "machine_id") + case "darwin": + return "/Library/Application Support/OpenFrame/machine_id" + default: + return "/var/lib/openframe/machine_id" + } +} + +func (p *OpenFrameMachineIdProvider) readFromFile() string { + path := p.getFilePath() + if path == "" { + log.Warn().Msg("Could not determine machine_id file path") + return "" + } + + data, err := os.ReadFile(path) + if err != nil { + log.Debug().Str("path", path).Err(err).Msg("Could not read machine_id file") + return "" + } + + machineId := strings.TrimSpace(string(data)) + if machineId == "" { + log.Warn().Str("path", path).Msg("Machine ID file is empty") + return "" + } + + log.Debug().Str("path", path).Msg("Read machine ID from file") + return machineId +} diff --git a/server/service/orbit_client.go b/server/service/orbit_client.go index 1fe39109f35..04b2831a4ee 100644 --- a/server/service/orbit_client.go +++ b/server/service/orbit_client.go @@ -77,8 +77,9 @@ type OrbitClient struct { openSSOWindow func() error // openframe mode - openFrameMode bool - authManager *openframe.OpenFrameAuthorizationManager + openFrameMode bool + authManager *openframe.OpenFrameAuthorizationManager + machineIdProvider *openframe.OpenFrameMachineIdProvider } // time-to-live for config cache @@ -155,10 +156,18 @@ func (oc *OrbitClient) requestWithExternal(verb string, pathOrURL string, params // Add custom header for all requests authToken := oc.authManager.GetToken() if authToken != "" { - request.Header.Add("Authorization", "Bearer " + authToken) + request.Header.Add("Authorization", "Bearer "+authToken) } else { log.Warn().Msg("authToken is empty, not adding Authorization header") } + + // Add x-machine-id header for rate limiting + if oc.machineIdProvider != nil { + machineId := oc.machineIdProvider.GetMachineId() + if machineId != "" { + request.Header.Add("x-machine-id", machineId) + } + } } // Log the request details for debugging @@ -233,6 +242,11 @@ func NewOrbitClient( nodeKeyFilePath := filepath.Join(rootDir, constant.OrbitNodeKeyFileName) ctx, cancelFunc := context.WithCancel(context.Background()) + var machineIdProvider *openframe.OpenFrameMachineIdProvider + if openFrameMode { + machineIdProvider = openframe.NewOpenFrameMachineIdProvider() + } + return &OrbitClient{ nodeKeyFilePath: nodeKeyFilePath, baseClient: bc, @@ -247,6 +261,7 @@ func NewOrbitClient( hostIdentityCertPath: hostIdentityCertPath, authManager: authManager, openFrameMode: openFrameMode, + machineIdProvider: machineIdProvider, }, nil }