NewBook credentials from central Settings service

Stack-wide NewBook config lives in the Settings app (LXC 116) and is
fetched live via SETTINGS_URL/SETTINGS_SECRET — same pattern as cashup,
room-planner and maintenance. App-local system_config credentials remain
as a fallback for standalone/dev use. The app's Settings → Newbook page
no longer edits credentials; it points to the central app and keeps
Test Connection.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-07-04 19:36:41 +00:00
parent 851747b561
commit aeb99650bd
10 changed files with 226 additions and 230 deletions

View file

@ -25,7 +25,15 @@ def get_config_value(db, key: str) -> Optional[str]:
def load_newbook_credentials(db) -> dict:
"""Load Newbook API credentials from database config."""
"""
Load Newbook API credentials central Settings service first (stack-wide
config), falling back to the app-local database config.
"""
from services.central_settings import get_newbook_credentials_sync
central = get_newbook_credentials_sync()
if central:
return central
import base64
def decrypt(value: str) -> str:

View file

@ -225,34 +225,45 @@ async def run_fetch_current_rates(horizon_days: int = 720, start_date: date = No
import base64
from services.newbook_rates_client import NewbookRatesClient
# Get credentials from config (decrypt encrypted values)
config_result = db.execute(
text("""
SELECT config_key, config_value, COALESCE(is_encrypted, false) as is_encrypted
FROM system_config
WHERE config_key IN ('newbook_api_key', 'newbook_username', 'newbook_password', 'newbook_region')
""")
)
config = {}
for row in config_result.fetchall():
value = row.config_value
if row.is_encrypted and value:
try:
value = base64.b64decode(value.encode()).decode()
except Exception:
pass # Use raw value if decryption fails
config[row.config_key] = value
# Credentials: central Settings service first, app-local config fallback
from services.central_settings import get_newbook_credentials_sync
creds = get_newbook_credentials_sync()
if not all(k in config for k in ['newbook_api_key', 'newbook_username', 'newbook_password', 'newbook_region']):
logger.error("Newbook credentials not configured")
return
if not creds:
config_result = db.execute(
text("""
SELECT config_key, config_value, COALESCE(is_encrypted, false) as is_encrypted
FROM system_config
WHERE config_key IN ('newbook_api_key', 'newbook_username', 'newbook_password', 'newbook_region')
""")
)
config = {}
for row in config_result.fetchall():
value = row.config_value
if row.is_encrypted and value:
try:
value = base64.b64decode(value.encode()).decode()
except Exception:
pass # Use raw value if decryption fails
config[row.config_key] = value
if not all(k in config for k in ['newbook_api_key', 'newbook_username', 'newbook_password', 'newbook_region']):
logger.error("Newbook credentials not configured")
return
creds = {
'api_key': config['newbook_api_key'],
'username': config['newbook_username'],
'password': config['newbook_password'],
'region': config['newbook_region'],
}
# Create client
client = NewbookRatesClient(
api_key=config['newbook_api_key'],
username=config['newbook_username'],
password=config['newbook_password'],
region=config['newbook_region'],
api_key=creds['api_key'],
username=creds['username'],
password=creds['password'],
region=creds['region'],
vat_rate=Decimal(vat_rate_str)
)