mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 22:26:13 +00:00
chore(release): v1.47.4
This commit is contained in:
1 parent
02f4f8116c
commit
fbb3ad7852
4 files changed
+136
-46
No files matched your search
@@ -2,6 +2,91 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.47.4] — 2026-11-12
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Web Push comes back when you turn it on again.** After a reinstall of
|
||||
the Home Screen app, cleared site data, or turning push off on your
|
||||
last device, the push channel switched itself off for good. Turning
|
||||
push on again stored the browser and sent a test push, but no reminder
|
||||
ever followed. Subscribing now turns the channel back on and clears
|
||||
earlier failures, opening the push settings repairs a lost
|
||||
subscription, and migration `0497_reenable_subscribed_web_push_channels`
|
||||
turns push back on for every account that has a subscribed browser but
|
||||
was switched off this way. A channel that has nobody to send to (no
|
||||
browser, no paired iPhone, no mail server) now pauses for an hour and
|
||||
says why on the status screen instead of counting failures.
|
||||
- **No more invisible push for every recorded dose.** Logging a dose sent
|
||||
a silent push to every browser to close the reminder. Safari does not
|
||||
allow silent pushes and could switch push off for the iPhone app
|
||||
without a word. A reminder already on screen now stays until you
|
||||
dismiss it or the next one replaces it, and the dose count earlier
|
||||
releases left on the Home Screen icon is cleared.
|
||||
- **Signing out signs this device out of push too.** The browser kept
|
||||
receiving the signed-out account's medication reminders, also after
|
||||
someone else signed in. Every sign-out button now removes this
|
||||
browser's subscription once the server confirmed the sign-out, and a
|
||||
browser only carries the reminders of the account that subscribed it
|
||||
last. If signing out fails, the app stays where it is and says you are
|
||||
still signed in, instead of showing the sign-in page.
|
||||
- **A shared browser no longer shows the previous account's data.** The
|
||||
dashboard snapshot is restored only once the signed-in account is
|
||||
known and is its owner, the sign-in pages clear the previous account's
|
||||
copies as soon as the server confirms there is no session, and single
|
||||
sign-on and the profile take-over clear them too. The daily briefing
|
||||
text is no longer written to the browser's storage.
|
||||
- **Offline, the open page stays.** Without a connection, a tap on a tab
|
||||
or a link ended on the bare offline page, although the banner promised
|
||||
your last synced data. The page you have open now stays readable, and
|
||||
a note says other pages open once you are back online; the banner and
|
||||
the README say exactly that. The service worker no longer stores API
|
||||
answers on the device, and answers older releases stored are deleted
|
||||
on update.
|
||||
- **Links arrive where they pointed.** A Home Screen shortcut, a
|
||||
notification or a bookmark opened while signed out takes you to that
|
||||
page after you sign in. After you sign out yourself, the next sign-in
|
||||
starts from the dashboard, and Back no longer reopens the page you
|
||||
left.
|
||||
- **Back works as expected.** Back after signing in, registering or
|
||||
taking over a profile no longer shows the form you just sent, and Back
|
||||
from a detail page returns to where you were in the list.
|
||||
- **Sign-in errors say what happened, in your language.** A wrong
|
||||
password or code says the sign-in failed, a lockout says to wait a few
|
||||
minutes, and registration says when a username or address is taken or
|
||||
an invitation is no longer valid. Passkey and single-sign-on refusals
|
||||
say what went wrong.
|
||||
- **The iPhone push hint.** In Safari on iPhone and iPad the push
|
||||
settings now tell you to add HealthLog to your Home Screen first,
|
||||
instead of saying push is not supported. The test push reaches every
|
||||
device you subscribed, in your language, and says why it failed.
|
||||
- **A 401 or 403 from a proxy or firewall in front of HealthLog no longer
|
||||
signs you out.** An account that expires while the app is open now ends
|
||||
the session, so the next save does not fail with "sign in again".
|
||||
- Tapping a reminder while the app already shows that page no longer
|
||||
reloads the app, and setup pages seen while signed in stay out of the
|
||||
offline page cache. Single sign-on starts even when the browser's
|
||||
storage answers slowly.
|
||||
- The clock and battery of the installed iPhone app are readable again in
|
||||
the light theme.
|
||||
|
||||
### Changed
|
||||
|
||||
- **Demo instances.** Visitors can sign out, each visitor's language stays
|
||||
in their own browser instead of the shared account, refused language
|
||||
and timing requests no longer show up on every page, and a demo allows
|
||||
30 password sign-ins per 15 minutes per address. Other instances keep
|
||||
their limits.
|
||||
- The "machine translated" notice no longer appears on the sign-in,
|
||||
registration and profile take-over pages.
|
||||
- The service worker file is half its former size.
|
||||
|
||||
### Migration
|
||||
|
||||
- `0497_reenable_subscribed_web_push_channels` turns Web Push back on for
|
||||
accounts whose channel was switched off by failed sends while a browser
|
||||
is still subscribed. A channel you switched off yourself stays off.
|
||||
|
||||
## [1.47.3] — 2026-11-12
|
||||
|
||||
### Changed
|
||||
|
||||
+49
-44
@@ -1,7 +1,7 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: HealthLog API
|
||||
version: 1.47.3
|
||||
version: 1.47.4
|
||||
description: >-
|
||||
Self-hosted personal-health-tracking PWA — public API surface for the iOS native client and external ingest.
|
||||
|
||||
@@ -4016,15 +4016,16 @@ paths:
|
||||
"403":
|
||||
description: Refused before any work. Either the operator runs `OIDC_ONLY=true` (`meta.errorCode` = `oidc_only`), or
|
||||
self-registration is closed and the request carried no invite, or the invite it carried was invalid, expired
|
||||
or exhausted. The three are distinguishable by prose but not by code.
|
||||
or exhausted (`auth.register.invite_invalid`).
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorEnvelope"
|
||||
"409":
|
||||
description: The email or the username is taken. Deliberately one message for both, so this is not an account-existence
|
||||
oracle. Also the answer when two concurrent signups race past the pre-check and the database unique index
|
||||
catches the second.
|
||||
description: The email or the username is taken (`auth.register.taken`). Deliberately one message and one code for both,
|
||||
so this is not an account-existence oracle. Also the answer when two concurrent signups race past the
|
||||
pre-check and the database unique index catches the second. `auth.already_authenticated` when the request
|
||||
carries a signed-in session.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
@@ -24719,8 +24720,9 @@ paths:
|
||||
per minute per user.
|
||||
|
||||
|
||||
Read `ok`, not the status. A push that every service rejects still answers 200, with `ok: false` and a `reason`.
|
||||
No endpoint is returned: the full endpoint is the routing secret for that subscription.
|
||||
Read `ok`, not the status. A push that every service rejects still answers 200, with `ok: false`, a `reason`,
|
||||
and `meta.errorCode`: `upstream_rejected`, or `connection_failed` when no push service answered. No endpoint is
|
||||
returned: the full endpoint is the routing secret for that subscription.
|
||||
responses:
|
||||
"200":
|
||||
description: The test ran. `ok` says whether the push was accepted; a false here is a real failure reported inside a
|
||||
@@ -39702,42 +39704,43 @@ components:
|
||||
anamnesis.lifeConfirm.invalid, anamnesis.lifeSections.invalid; auth — auth.account.deletion_pending,
|
||||
auth.admin.required, auth.already_authenticated, auth.mfa.already_active, auth.mfa.code_invalid,
|
||||
auth.missing, auth.passkey.last_method, auth.refresh.invalid, auth.refresh.reuse, auth.refresh.revoked,
|
||||
auth.reproof.failed, auth.reproof.required, auth.reproof.too_weak, auth.scope.insufficient,
|
||||
auth.session.insecure_transport, auth.session.unavailable, auth.stepup.required, auth.token.expired,
|
||||
auth.token.invalid; backup — backup.foreign_reference, backup.key.missing, backup.owner.exists,
|
||||
backup.owner.missing, backup.payload.undecryptable, backup.restore.active,
|
||||
backup.restore.rows_exist_elsewhere, backup.section.missing, backup.target.admin_account,
|
||||
backup.target.holds_records, backup.target.managed_profile, backup.target.not_found,
|
||||
backup.target.pending_deletion, backup.target.unconfirmed, backup.target.username_taken,
|
||||
backup.upload.too_large; biomarkers — biomarkers.unit.locked; body-site — body-site.invalid; coach —
|
||||
coach.budget.exceeded, coach.conversation.invalidTitle, coach.conversation.notFound,
|
||||
coach.fenced.attachmentConflict, coach.fenced.attachmentLimit, coach.fenced.attachmentNotFound,
|
||||
coach.fenced.attachmentNotIndexed, coach.fenced.attachmentRequired, coach.fenced.attachmentUnavailable,
|
||||
coach.fenced.invalid, coach.request.invalid; coach-prefs — coach-prefs.body.invalid_json,
|
||||
coach-prefs.body.invalid_shape; consent — consent.ai.confirmRequired, consent.ai.required; customMetric
|
||||
— customMetric.name_taken; cycle — cycle.bulk.invalid, cycle.bulk.too_large, cycle.calendar.invalid,
|
||||
cycle.calendar.range, cycle.cycles.invalid, cycle.day-log.conflict, cycle.day-log.invalid,
|
||||
cycle.disabled, cycle.feed.api_disabled, cycle.feed.cookie_only, cycle.feed.exists,
|
||||
cycle.feed.fertile_goal, cycle.feed.none, cycle.feed.notFound, cycle.feed.rateLimited,
|
||||
cycle.period.invalid, cycle.symptom.custom.invalid, cycle.symptom.custom.limit; cycle-prefs —
|
||||
cycle-prefs.body.invalid_json, cycle-prefs.invalid; disable-coach — disable-coach.body.invalid_json;
|
||||
data_wipe — data_wipe.copy_failed; dawarich — dawarich.credentials_rejected,
|
||||
dawarich.home_place_unknown, dawarich.invalid, dawarich.not_connected, dawarich.rate_limited,
|
||||
dawarich.unreachable; day — day.invalid; demo — demo.readOnly; documents —
|
||||
documents.chat.conversationNotFound, documents.chat.invalid, documents.inbound.alreadyConfirmed,
|
||||
documents.inbound.alreadyPartlyConfirmed, documents.inbound.budgetExceeded,
|
||||
documents.inbound.decryptFailed, documents.inbound.encounterNotFound, documents.inbound.episodeNotFound,
|
||||
documents.inbound.extractFailed, documents.inbound.factNotFound, documents.inbound.factNotPending,
|
||||
documents.inbound.factTypeMismatch, documents.inbound.fileTooLarge, documents.inbound.fileType,
|
||||
documents.inbound.invalidMetadata, documents.inbound.invalidQuery, documents.inbound.invalidUpdate,
|
||||
documents.inbound.localOcrDisabled, documents.inbound.notDeleted, documents.inbound.notFound,
|
||||
documents.inbound.notIndexed, documents.inbound.outputLimit, documents.inbound.pdfRendererUnavailable,
|
||||
documents.inbound.providerUnsupported, documents.inbound.quotaExceeded, documents.inbound.rateLimited,
|
||||
documents.inbound.restoreDuplicate, documents.inbound.restoreGone, documents.inbound.sourceAliasLimit,
|
||||
documents.inbound.uploadBusy, documents.inbound.uploadTimeout, documents.inbound.vaccinationNotFound,
|
||||
documents.sources.authRefused, documents.sources.badResponse, documents.sources.browserOnly,
|
||||
documents.sources.invalidAddress, documents.sources.linkTargetNotFound, documents.sources.notConnected,
|
||||
documents.sources.notFound, documents.sources.organizationRequired, documents.sources.originNotAllowed,
|
||||
auth.register.invite_invalid, auth.register.taken, auth.reproof.failed, auth.reproof.required,
|
||||
auth.reproof.too_weak, auth.scope.insufficient, auth.session.insecure_transport,
|
||||
auth.session.unavailable, auth.stepup.required, auth.token.expired, auth.token.invalid; backup —
|
||||
backup.foreign_reference, backup.key.missing, backup.owner.exists, backup.owner.missing,
|
||||
backup.payload.undecryptable, backup.restore.active, backup.restore.rows_exist_elsewhere,
|
||||
backup.section.missing, backup.target.admin_account, backup.target.holds_records,
|
||||
backup.target.managed_profile, backup.target.not_found, backup.target.pending_deletion,
|
||||
backup.target.unconfirmed, backup.target.username_taken, backup.upload.too_large; biomarkers —
|
||||
biomarkers.unit.locked; body-site — body-site.invalid; coach — coach.budget.exceeded,
|
||||
coach.conversation.invalidTitle, coach.conversation.notFound, coach.fenced.attachmentConflict,
|
||||
coach.fenced.attachmentLimit, coach.fenced.attachmentNotFound, coach.fenced.attachmentNotIndexed,
|
||||
coach.fenced.attachmentRequired, coach.fenced.attachmentUnavailable, coach.fenced.invalid,
|
||||
coach.request.invalid; coach-prefs — coach-prefs.body.invalid_json, coach-prefs.body.invalid_shape;
|
||||
consent — consent.ai.confirmRequired, consent.ai.required; customMetric — customMetric.name_taken; cycle
|
||||
— cycle.bulk.invalid, cycle.bulk.too_large, cycle.calendar.invalid, cycle.calendar.range,
|
||||
cycle.cycles.invalid, cycle.day-log.conflict, cycle.day-log.invalid, cycle.disabled,
|
||||
cycle.feed.api_disabled, cycle.feed.cookie_only, cycle.feed.exists, cycle.feed.fertile_goal,
|
||||
cycle.feed.none, cycle.feed.notFound, cycle.feed.rateLimited, cycle.period.invalid,
|
||||
cycle.symptom.custom.invalid, cycle.symptom.custom.limit; cycle-prefs — cycle-prefs.body.invalid_json,
|
||||
cycle-prefs.invalid; disable-coach — disable-coach.body.invalid_json; data_wipe — data_wipe.copy_failed;
|
||||
dawarich — dawarich.credentials_rejected, dawarich.home_place_unknown, dawarich.invalid,
|
||||
dawarich.not_connected, dawarich.rate_limited, dawarich.unreachable; day — day.invalid; demo —
|
||||
demo.readOnly; documents — documents.chat.conversationNotFound, documents.chat.invalid,
|
||||
documents.inbound.alreadyConfirmed, documents.inbound.alreadyPartlyConfirmed,
|
||||
documents.inbound.budgetExceeded, documents.inbound.decryptFailed, documents.inbound.encounterNotFound,
|
||||
documents.inbound.episodeNotFound, documents.inbound.extractFailed, documents.inbound.factNotFound,
|
||||
documents.inbound.factNotPending, documents.inbound.factTypeMismatch, documents.inbound.fileTooLarge,
|
||||
documents.inbound.fileType, documents.inbound.invalidMetadata, documents.inbound.invalidQuery,
|
||||
documents.inbound.invalidUpdate, documents.inbound.localOcrDisabled, documents.inbound.notDeleted,
|
||||
documents.inbound.notFound, documents.inbound.notIndexed, documents.inbound.outputLimit,
|
||||
documents.inbound.pdfRendererUnavailable, documents.inbound.providerUnsupported,
|
||||
documents.inbound.quotaExceeded, documents.inbound.rateLimited, documents.inbound.restoreDuplicate,
|
||||
documents.inbound.restoreGone, documents.inbound.sourceAliasLimit, documents.inbound.uploadBusy,
|
||||
documents.inbound.uploadTimeout, documents.inbound.vaccinationNotFound, documents.sources.authRefused,
|
||||
documents.sources.badResponse, documents.sources.browserOnly, documents.sources.invalidAddress,
|
||||
documents.sources.linkTargetNotFound, documents.sources.notConnected, documents.sources.notFound,
|
||||
documents.sources.organizationRequired, documents.sources.originNotAllowed,
|
||||
documents.sources.permissionMissing, documents.sources.rateLimited, documents.sources.redirected,
|
||||
documents.sources.tokenRequired, documents.sources.unavailable, documents.sources.unreachable,
|
||||
documents.sources.versionTooOld; encounter — encounter.invalid, encounter.link.invalid,
|
||||
@@ -90798,7 +90801,9 @@ components:
|
||||
- sending_paused
|
||||
description: Server-derived. `auto_disabled` — the dispatcher turned it off after repeated hard rejects
|
||||
(`disabledReason` says why). `manually_disabled` — the user turned it off. `sending_paused` — still enabled
|
||||
but inside a retry cooldown until `nextRetryAt`. `active` — enabled and sending.
|
||||
but inside a retry cooldown until `nextRetryAt`, after a transient failure or after a send that had nobody
|
||||
to go to (no browser subscribed, no iPhone paired, a transport the operator has not set up;
|
||||
`lastFailureReason` names it, `consecutiveFailures` is not raised). `active` — enabled and sending.
|
||||
disabledReason:
|
||||
description: Why the dispatcher auto-disabled the channel; null when it did not. This is the field that separates
|
||||
`auto_disabled` from `manually_disabled`.
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "healthlog",
|
||||
"version": "1.47.3",
|
||||
"version": "1.47.4",
|
||||
"description": "Self-hosted personal-health-tracking PWA with Withings integration, AI insights, and doctor-report PDF export.",
|
||||
"license": "PolyForm-Noncommercial-1.0.0",
|
||||
"homepage": "https://healthlog.dev",
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ try {
|
||||
}
|
||||
const CACHE_VERSION =
|
||||
(typeof self !== "undefined" && self.__APP_VERSION__) ||
|
||||
/* @sw-version-fallback */ "v1.47.3";
|
||||
/* @sw-version-fallback */ "v1.47.4";
|
||||
const STATIC_CACHE = `healthlog-static-${CACHE_VERSION}`;
|
||||
const PAGE_CACHE = `healthlog-pages-${CACHE_VERSION}`;
|
||||
// Every HealthLog cache, current and legacy, and nothing another app on the
|
||||
|
||||
Reference in new issue
Block a user