Comprehensive review fixes: security, performance, code quality, and UI polish
Some checks failed
Validate / Hassfest (push) Has been cancelled
Some checks failed
Validate / Hassfest (push) Has been cancelled
Backend: Fix CORS wildcard+credentials, add secret key warning, remove raw API keys from sync endpoint, fix N+1 queries in watcher/sync, fix AttributeError on event_types, delete dead scheduled.py/templates.py, add limit cap on history, re-validate server on URL/key update, apply tracking/template config IDs in update_target. HA Integration: Replace datetime.now() with dt_util.now(), fix notification queue to only remove successfully sent items, use album UUID for entity unique IDs, add shared links dirty flag and users cache hourly refresh, deduplicate _is_quiet_hours, add HTTP timeouts, cache albums in config flow, change iot_class to local_polling. Frontend: Make i18n reactive via $state (remove window.location.reload), add Modal transitions/a11y/Escape key, create ConfirmModal replacing all confirm() calls, add error handling to all pages, replace Unicode nav icons with MDI SVGs, add card hover effects, dashboard stat icons, global focus-visible styles, form slide transitions, mobile responsive bottom nav, fix password error color, add ~20 i18n keys (EN/RU). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,13 +39,16 @@ from .const import (
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
_CONNECT_TIMEOUT = aiohttp.ClientTimeout(total=10)
|
||||
|
||||
|
||||
async def validate_connection(
|
||||
session: aiohttp.ClientSession, url: str, api_key: str
|
||||
) -> dict[str, Any]:
|
||||
"""Validate the Immich connection and return server info."""
|
||||
headers = {"x-api-key": api_key}
|
||||
async with session.get(
|
||||
f"{url.rstrip('/')}/api/server/ping", headers=headers
|
||||
f"{url.rstrip('/')}/api/server/ping", headers=headers, timeout=_CONNECT_TIMEOUT
|
||||
) as response:
|
||||
if response.status == 401:
|
||||
raise InvalidAuth
|
||||
@@ -169,23 +172,7 @@ class ImmichAlbumSubentryFlowHandler(ConfigSubentryFlow):
|
||||
url = config_entry.data[CONF_IMMICH_URL]
|
||||
api_key = config_entry.data[CONF_API_KEY]
|
||||
|
||||
# Fetch available albums
|
||||
session = async_get_clientsession(self.hass)
|
||||
try:
|
||||
self._albums = await fetch_albums(session, url, api_key)
|
||||
except Exception:
|
||||
_LOGGER.exception("Failed to fetch albums")
|
||||
errors["base"] = "cannot_connect"
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=vol.Schema({}),
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
if not self._albums:
|
||||
return self.async_abort(reason="no_albums")
|
||||
|
||||
if user_input is not None:
|
||||
if user_input is not None and self._albums:
|
||||
album_id = user_input[CONF_ALBUM_ID]
|
||||
|
||||
# Check if album is already configured
|
||||
@@ -208,6 +195,23 @@ class ImmichAlbumSubentryFlowHandler(ConfigSubentryFlow):
|
||||
},
|
||||
)
|
||||
|
||||
# Fetch available albums (only when displaying the form)
|
||||
if not self._albums:
|
||||
session = async_get_clientsession(self.hass)
|
||||
try:
|
||||
self._albums = await fetch_albums(session, url, api_key)
|
||||
except Exception:
|
||||
_LOGGER.exception("Failed to fetch albums")
|
||||
errors["base"] = "cannot_connect"
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=vol.Schema({}),
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
if not self._albums:
|
||||
return self.async_abort(reason="no_albums")
|
||||
|
||||
# Get already configured album IDs
|
||||
configured_albums = set()
|
||||
for subentry in config_entry.subentries.values():
|
||||
|
||||
Reference in New Issue
Block a user