🐛 fix(health_endpoints): resolve TEST_EMAIL_ADDRESS not read from DB in email health check

- fetch fresh decrypted config from DB when store_model_in_db is enabled
  so TEST_EMAIL_ADDRESS is correctly resolved in DB mode
- fall back to os.getenv for YAML / env-var deployments (no-op path)
- add silent mode to handleSaveEmailSettings to suppress notifications
  when called programmatically from the test email flow
- silently persist form values before triggering email health check so
  the backend can read TEST_EMAIL_ADDRESS from DB (DB mode)
- swallow errors in silent mode to allow test flow to proceed using
  env-var / YAML config values as fallback
This commit is contained in:
yangdx 2026-03-26 14:15:10 +08:00
parent b8adffc932
commit 687b1c1869
2 changed files with 32 additions and 4 deletions

View file

@ -438,6 +438,22 @@ async def health_services_endpoint( # noqa: PLR0915
},
)
if service == "email":
from litellm.proxy.proxy_server import proxy_config, store_model_in_db
# TEST_EMAIL_ADDRESS is stored encrypted in the DB when the proxy
# is running in DB mode. Calling get_config() ensures the value is
# freshly decrypted and available both in the returned config dict
# and in os.environ. For YAML / env-var deployments the call is a
# cheap no-op and os.getenv() still works as the fallback.
if store_model_in_db and prisma_client is not None:
_fresh_config = await proxy_config.get_config()
_env_vars = _fresh_config.get("environment_variables", {})
_test_email_address = _env_vars.get(
"TEST_EMAIL_ADDRESS"
) or os.getenv("TEST_EMAIL_ADDRESS")
else:
_test_email_address = os.getenv("TEST_EMAIL_ADDRESS")
webhook_event = WebhookEvent(
event="key_created",
event_group=Litellm_EntityType.KEY,
@ -447,7 +463,7 @@ async def health_services_endpoint( # noqa: PLR0915
spend=0,
max_budget=0,
user_id=user_api_key_dict.user_id,
user_email=os.getenv("TEST_EMAIL_ADDRESS"),
user_email=_test_email_address,
team_id=user_api_key_dict.team_id,
)

View file

@ -14,7 +14,7 @@ interface EmailSettingsProps {
}
const EmailSettings: React.FC<EmailSettingsProps> = ({ accessToken, premiumUser, alerts }) => {
const handleSaveEmailSettings = async () => {
const handleSaveEmailSettings = async ({ silent = false }: { silent?: boolean } = {}) => {
if (!accessToken) {
return;
}
@ -43,9 +43,15 @@ const EmailSettings: React.FC<EmailSettingsProps> = ({ accessToken, premiumUser,
};
try {
await setCallbacksCall(accessToken, payload);
NotificationManager.success("Email settings updated successfully");
if (!silent) {
NotificationManager.success("Email settings updated successfully");
}
} catch (error) {
NotificationManager.fromBackend(error);
if (!silent) {
NotificationManager.fromBackend(error);
}
// In silent mode (called from test flow) swallow the error so that
// the test can still proceed using env-var / YAML config values.
}
};
@ -163,6 +169,12 @@ const EmailSettings: React.FC<EmailSettingsProps> = ({ accessToken, premiumUser,
onClick={async () => {
if (!accessToken) return;
try {
// Silently attempt to persist the current form values so the
// backend can read TEST_EMAIL_ADDRESS from the DB (DB mode).
// If saving is not supported (e.g. STORE_MODEL_IN_DB=False /
// YAML mode), this is a no-op and the backend will fall back to
// the TEST_EMAIL_ADDRESS environment variable instead.
await handleSaveEmailSettings({ silent: true });
await serviceHealthCheck(accessToken, "email");
NotificationManager.success("Email test triggered. Check your configured email inbox/logs.");
} catch (error) {