Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d7daadadc2 | |||
| e04ad16ca6 | |||
| d7d0a5d921 |
+8
-31
@@ -1,42 +1,19 @@
|
|||||||
## v0.2.4 (2026-04-22)
|
# v0.2.5 (2026-04-22)
|
||||||
|
|
||||||
Telegram media cache rebuilt around **thumbhash validation** — asset cache
|
Hotfix release on top of v0.2.4 — the settings page couldn't save numeric
|
||||||
entries now invalidate when the visual content changes, not after a fixed
|
fields after the cache-TTL / max-entries rework. See v0.2.4 notes for the
|
||||||
TTL — plus a settings-page overhaul (cache stats, clear button, timezone /
|
main changes (thumbhash-validated cache, settings UX overhaul, mobile-nav
|
||||||
locale pickers) and full mobile-nav parity with the desktop sidebar.
|
parity).
|
||||||
|
|
||||||
### Features
|
## Bug Fixes
|
||||||
|
|
||||||
#### Telegram media cache
|
- **Accept numeric values in settings update payload** — Svelte's `bind:value` on `<input type="number">` coerces to a JS number, and Pydantic v2 wouldn't auto-coerce `int → str`, producing a 422 on every save that touched a numeric setting (TTL, max entries) after v0.2.4. Widened numeric fields to `int | str | None` in `SettingsUpdate` and normalized to `str` before persisting. ([d7d0a5d](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/d7d0a5d))
|
||||||
|
|
||||||
- **Thumbhash-validated asset cache** — dispatcher builds an `asset.id → thumbhash` resolver from `event.added_assets` (Immich already populates `thumbhash` in `extra`) and passes it to `TelegramClient`. Asset-cache entries now invalidate on visual change rather than age. ([2be608b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/2be608b))
|
|
||||||
- **Configurable cache cap & stats** — `TelegramFileCache` gets a `max_entries` LRU cap (applies in both TTL and thumbhash modes), `ttl_seconds <= 0` disables TTL entirely, and a `stats()` method exposes per-bucket counts / sizes / oldest+newest timestamps. New settings: `telegram_asset_cache_max_entries` (default 5000); `telegram_cache_ttl_hours` default bumped `48 → 720` (30 days) and is now URL-only. ([2be608b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/2be608b))
|
|
||||||
- **Cache admin endpoints** — `GET /settings/telegram-cache/stats` and `POST /settings/telegram-cache/clear`. `PUT /settings` now soft-resets the in-memory caches when cache-shaping keys change (on-disk `file_id`s preserved). ([2be608b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/2be608b))
|
|
||||||
|
|
||||||
#### Settings page
|
|
||||||
|
|
||||||
- **Cache stats card** — per-bucket (URL / asset) counts, cumulative uploaded-to-Telegram byte size, oldest/newest timestamps, and a hint explaining what the size means. Clear-cache button behind a confirm modal. ([2be608b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/2be608b))
|
|
||||||
- **New `TimezoneSelector` and `LocaleSelector` components** replace the raw inputs with IANA-aware searchable pickers. Max-entries input exposed; TTL range widened to `0..8760` hours (`0` = disabled). ([2be608b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/2be608b))
|
|
||||||
|
|
||||||
#### Mobile nav
|
|
||||||
|
|
||||||
- **Full sidebar parity in the *More* panel** — now mirrors the desktop sidebar tree (groups + subnodes) so every destination is reachable from mobile. Previously the panel carried a hand-picked flat list that drifted behind newly-added routes. ([2be608b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/2be608b))
|
|
||||||
- **Safe-area handling** — nav height uses `env(safe-area-inset-bottom)`; panel bottom + `z-index` fixed so page content can no longer visually overlay the bottom bar. ([2be608b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/2be608b))
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
- **`update_settings` TypeError** — `any(await ... for ...)` was an async generator (not an iterator) and raised at runtime; replaced with an explicit loop so settings updates actually commit. ([2be608b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/2be608b))
|
|
||||||
|
|
||||||
### Accessibility
|
|
||||||
|
|
||||||
- **Password-manager association** on the password-change form — hidden `username` field + `autocomplete` hints on all three password inputs so browsers stop warning and password managers fill correctly. ([2be608b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/2be608b))
|
|
||||||
- **Telegram webhook secret** wrapped in a no-op form with `autocomplete=off` to silence DOM/a11y warnings. ([2be608b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/2be608b))
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>All Commits</summary>
|
<summary>All Commits</summary>
|
||||||
|
|
||||||
- [2be608b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/2be608b) — feat(cache): thumbhash-validated asset cache + settings UX overhaul *(alexei.dolgolyov)*
|
- [d7d0a5d](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/d7d0a5d) — fix(settings): accept numeric values in update payload *(alexei.dolgolyov)*
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "notify-bridge-frontend",
|
"name": "notify-bridge-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.2.4",
|
"version": "0.2.5",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "notify-bridge-core"
|
name = "notify-bridge-core"
|
||||||
version = "0.2.4"
|
version = "0.2.5"
|
||||||
description = "Core library for Notify Bridge — service provider abstractions, models, notifications, and templates"
|
description = "Core library for Notify Bridge — service provider abstractions, models, notifications, and templates"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "notify-bridge-server"
|
name = "notify-bridge-server"
|
||||||
version = "0.2.4"
|
version = "0.2.5"
|
||||||
description = "Standalone Notify Bridge server — FastAPI REST API with SQLite database"
|
description = "Standalone Notify Bridge server — FastAPI REST API with SQLite database"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
|||||||
@@ -56,10 +56,14 @@ async def get_setting(session: AsyncSession, key: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
class SettingsUpdate(BaseModel):
|
class SettingsUpdate(BaseModel):
|
||||||
|
# Numeric fields declared as int|str so clients can send either form.
|
||||||
|
# Svelte's bind:value on <input type="number"> coerces to a JS number,
|
||||||
|
# so the frontend sends ints for these; older/manual clients may send
|
||||||
|
# strings. We normalize to str before persisting.
|
||||||
external_url: str | None = None
|
external_url: str | None = None
|
||||||
telegram_webhook_secret: str | None = None
|
telegram_webhook_secret: str | None = None
|
||||||
telegram_cache_ttl_hours: str | None = None
|
telegram_cache_ttl_hours: int | str | None = None
|
||||||
telegram_asset_cache_max_entries: str | None = None
|
telegram_asset_cache_max_entries: int | str | None = None
|
||||||
supported_locales: str | None = None
|
supported_locales: str | None = None
|
||||||
timezone: str | None = None
|
timezone: str | None = None
|
||||||
|
|
||||||
@@ -95,11 +99,12 @@ async def update_settings(
|
|||||||
value = getattr(body, key, None)
|
value = getattr(body, key, None)
|
||||||
if value is None:
|
if value is None:
|
||||||
continue
|
continue
|
||||||
|
value_str = str(value)
|
||||||
row = await session.get(AppSetting, key)
|
row = await session.get(AppSetting, key)
|
||||||
if row:
|
if row:
|
||||||
row.value = value
|
row.value = value_str
|
||||||
else:
|
else:
|
||||||
row = AppSetting(key=key, value=value)
|
row = AppSetting(key=key, value=value_str)
|
||||||
session.add(row)
|
session.add(row)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user