From 203639af7858b3959b5ec233c4b9bfc8aaab1f9c Mon Sep 17 00:00:00 2001 From: jtricerolph Date: Tue, 28 Jul 2026 14:38:15 +0000 Subject: [PATCH] Configure utilities API creds via Settings UI, not .env Follows the same app_settings/DB pattern already used for the Forecasting integration: utilities_url and utilities_api_key are now editable from Reports Settings, with env vars kept only as a fallback. Avoids needing to SSH in and hand-edit .env to authenticate against the utilities app's internal API. Co-Authored-By: Claude Sonnet 5 --- backend/src/db.js | 4 +++- backend/src/lib/utilities-client.js | 8 +++++--- backend/src/routes/settings.js | 2 +- frontend/src/pages/SettingsPage.tsx | 15 +++++++++++++-- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/backend/src/db.js b/backend/src/db.js index 9ea51551..faaf981d 100644 --- a/backend/src/db.js +++ b/backend/src/db.js @@ -81,7 +81,9 @@ export async function initDb() { await pool.query(` INSERT INTO app_settings (key, value) VALUES ('forecasting_url', ''), - ('forecasting_api_key', '') + ('forecasting_api_key', ''), + ('utilities_url', ''), + ('utilities_api_key', '') ON CONFLICT (key) DO NOTHING `) } diff --git a/backend/src/lib/utilities-client.js b/backend/src/lib/utilities-client.js index 961738d8..3357c528 100644 --- a/backend/src/lib/utilities-client.js +++ b/backend/src/lib/utilities-client.js @@ -1,11 +1,13 @@ // Client for the `utilities` app's internal API (meter readings, tariffs, energy costs). // Mirrors the existing forecasting integration pattern used in // routes/directors-forecast.js and routes/weekly-actual.js (X-API-Key header, -// base URL + key from env, throw on non-2xx). +// base URL + key from the app_settings table — set via Settings UI — with +// env vars as a fallback, throw on non-2xx). +import { getSetting } from '../db.js' async function utilFetch(path) { - const apiKey = process.env.UTILITIES_API_KEY - const baseUrl = process.env.UTILITIES_URL || 'http://10.10.10.127:3080' + const apiKey = await getSetting('utilities_api_key') || process.env.UTILITIES_API_KEY + const baseUrl = await getSetting('utilities_url') || process.env.UTILITIES_URL || 'http://10.10.10.127:3080' if (!apiKey) throw new Error('UTILITIES_API_KEY not configured') const res = await fetch(`${baseUrl}${path}`, { headers: { 'X-API-Key': apiKey }, diff --git a/backend/src/routes/settings.js b/backend/src/routes/settings.js index 07ade62f..f9eb670b 100644 --- a/backend/src/routes/settings.js +++ b/backend/src/routes/settings.js @@ -1,7 +1,7 @@ import { requireAuth } from '../auth.js' import { pool } from '../db.js' -const ALLOWED_KEYS = new Set(['forecasting_url', 'forecasting_api_key']) +const ALLOWED_KEYS = new Set(['forecasting_url', 'forecasting_api_key', 'utilities_url', 'utilities_api_key']) export async function settingsRoutes(fastify) { fastify.addHook('preHandler', requireAuth) diff --git a/frontend/src/pages/SettingsPage.tsx b/frontend/src/pages/SettingsPage.tsx index 1be31307..06e327bf 100644 --- a/frontend/src/pages/SettingsPage.tsx +++ b/frontend/src/pages/SettingsPage.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react' import { Save } from 'lucide-react' import { getSettings, saveSettings, type AppSetting } from '../api' -const SETTING_LABELS: Record = { +const SETTING_LABELS: Record = { forecasting_api_key: { label: 'Forecasting API Key', hint: 'API key for the Forecasting app public API. Create one in Forecasting → Settings → API Keys.', @@ -11,6 +11,17 @@ const SETTING_LABELS: Record setValues(v => ({ ...v, [s.key]: e.target.value }))} className="setting-input" autoComplete="off"