feat(consul): Update consul config on fitbit token refresh
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 3m4s

When fitbit tokens are refreshed, the new tokens are now written back to Consul if it is being used as the configuration source.

This creates a closed loop, ensuring the tokens in Consul do not become stale.
This commit is contained in:
2025-12-15 08:29:22 -08:00
parent 410e85b665
commit dd212f2554

View File

@@ -155,20 +155,12 @@ class ConfigManager:
try: try:
# Attempt 1: Assume the value is the direct UTF-8 bytes of the JSON string. # Attempt 1: Assume the value is the direct UTF-8 bytes of the JSON string.
# The python-consul client automatically base64-decodes the value.
decoded_json_str = raw_value_from_consul.decode('utf-8') decoded_json_str = raw_value_from_consul.decode('utf-8')
logger.info("Successfully decoded Consul value directly as UTF-8.") logger.info("Successfully decoded Consul value directly as UTF-8.")
except UnicodeDecodeError: except Exception as e:
logger.warning("Direct UTF-8 decoding failed. Falling back to base64 decoding.") logger.error(f"Failed to decode consul value: {e}")
# Attempt 2: Assume the value is base64 encoded. return # Can't proceed if we can't decode
encoded_value = raw_value_from_consul
# Add padding if necessary for base64 decoding
padding_needed = len(encoded_value) % 4
if padding_needed != 0:
encoded_value += b'=' * (4 - padding_needed)
decoded_json_str = base64.b64decode(encoded_value).decode('utf-8')
logger.info("Successfully decoded Consul value using base64 fallback.")
logger.debug(f"Decoded JSON string: {decoded_json_str}") logger.debug(f"Decoded JSON string: {decoded_json_str}")
consul_conf = json.loads(decoded_json_str) # Parse the JSON consul_conf = json.loads(decoded_json_str) # Parse the JSON
@@ -485,6 +477,22 @@ class ConsulStateManager:
return status_info return status_info
def update_config(self, config_dict: Dict) -> bool:
"""Update the full configuration in Consul."""
full_config_key = f"{self.prefix}/config"
try:
# Serialize the dictionary to a JSON string
config_json = json.dumps(config_dict, indent=2)
# Base64 encode the JSON string. Note: Consul client handles the bytes conversion.
# The client library expects a string, which it will encode to bytes.
self.client.kv.put(full_config_key, config_json)
logger.info("Successfully updated configuration in Consul.")
return True
except Exception as e:
logger.error(f"Failed to update configuration in Consul: {e}")
return False
class FitbitClient: class FitbitClient:
"""Client for Fitbit API using python-fitbit""" """Client for Fitbit API using python-fitbit"""
@@ -683,6 +691,13 @@ class FitbitClient:
access_token=token['access_token'], access_token=token['access_token'],
refresh_token=token['refresh_token'] refresh_token=token['refresh_token']
) )
# If using Consul, update the configuration there as well.
if os.getenv('CONFIG_SOURCE') == 'consul':
logger.info("Updating Fitbit tokens in Consul...")
# We need a state manager instance to talk to consul
consul_manager = ConsulStateManager(self.config)
consul_manager.update_config(self.config.config)
async def get_weight_data(self, start_date: datetime, end_date: datetime) -> List[WeightRecord]: async def get_weight_data(self, start_date: datetime, end_date: datetime) -> List[WeightRecord]:
"""Fetch weight data from Fitbit API""" """Fetch weight data from Fitbit API"""