diff --git a/README.md b/README.md index 854eb8cc3..65c2c13f3 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ Verify the running version: curl -s http://localhost:3000/api/version # returns version + buildSha + builtAt ``` -`SESSION_COOKIE_SECURE=false` is the one setting plain-HTTP self-hosts must set: without it the session cookie carries the `Secure` flag, the browser drops it over HTTP, and login silently fails. Leave it unset (or `true`) only when a TLS reverse proxy serves HTTPS — and then set `NEXT_PUBLIC_APP_URL` and `APP_URL` to the public URL first (see the [self-hosting guide](https://docs.healthlog.dev/self-hosting/)). +`SESSION_COOKIE_SECURE=false` is the one setting plain-HTTP self-hosts must set: without it the session cookie carries the `Secure` flag, the browser drops it over HTTP, and login silently fails. Leave it unset (or `true`) only when a TLS reverse proxy serves HTTPS — and then set `NEXT_PUBLIC_APP_URL` and `APP_URL` to the public URL first (see the [self-hosting guide](https://docs.healthlog.dev/getting-started/self-host-in-10-minutes/)). Persistent state lives in exactly one named volume (`postgres-data`, the Postgres data); the app container is stateless, so backing up the database backs up everything. An install from v1.45 or earlier moves its Postgres 16 database into it on the first start ([details](docs/ops/postgres-major-upgrade.md)). diff --git a/src/__tests__/baselines/version-stamp-comments.json b/src/__tests__/baselines/version-stamp-comments.json index 95b97061a..6902bda8e 100644 --- a/src/__tests__/baselines/version-stamp-comments.json +++ b/src/__tests__/baselines/version-stamp-comments.json @@ -1,7 +1,7 @@ { "$comment": "Comment lines that open with a release version, per file under src/. Maintained by src/__tests__/version-stamp-ratchet.test.ts.", "count": 2897, - "total": 9246, + "total": 9243, "entries": { "__tests__/account-payload-consumer-guard.test.ts": 2, "__tests__/acting-account-boundary-guard.test.ts": 7, @@ -1381,7 +1381,7 @@ "components/settings/integrations/connections-panel.tsx": 5, "components/settings/integrations/garmin-info-note.tsx": 1, "components/settings/integrations/notification-channels-panel.tsx": 1, - "components/settings/integrations/setup-guide-link.tsx": 5, + "components/settings/integrations/setup-guide-link.tsx": 2, "components/settings/integrations/shared.tsx": 3, "components/settings/labs-section.tsx": 1, "components/settings/layout-groups.ts": 2, diff --git a/src/components/admin/backups-section.tsx b/src/components/admin/backups-section.tsx index d8dcb62c2..0eb220c00 100644 --- a/src/components/admin/backups-section.tsx +++ b/src/components/admin/backups-section.tsx @@ -98,7 +98,7 @@ export function BackupsSection() { {/* External docs link — `noopener noreferrer` because this leaves the admin shell. */} { for (const provider of PROVIDERS) { expect(html).toContain(`data-testid="${provider}-setup-guide"`); expect(html).toContain( - `href="https://docs.healthlog.dev/integrations/${provider}"`, + `href="https://docs.healthlog.dev/guides/connect-${provider}/"`, ); } + // Garmin has no connector; its note goes straight to the section of the + // Google Health guide that explains the route, not through a redirect. + expect(html).toContain( + 'href="https://docs.healthlog.dev/guides/connect-google-health/#garmin-owners"', + ); }); }); diff --git a/src/components/settings/__tests__/managed-integration-status.test.tsx b/src/components/settings/__tests__/managed-integration-status.test.tsx index d7126b5dd..f805c891a 100644 --- a/src/components/settings/__tests__/managed-integration-status.test.tsx +++ b/src/components/settings/__tests__/managed-integration-status.test.tsx @@ -139,7 +139,7 @@ describe("", () => { "Services you connect here send their data to Managed record's record, not to yours.", ); expect(html).toContain( - 'href="https://docs.healthlog.dev/self-hosting/account-sharing#records-with-no-login"', + 'href="https://docs.healthlog.dev/guides/managed-profiles/#connected-services"', ); }); diff --git a/src/components/settings/integrations/__tests__/fitbit-retirement-notice.test.tsx b/src/components/settings/integrations/__tests__/fitbit-retirement-notice.test.tsx index 21fd77d23..753be99d2 100644 --- a/src/components/settings/integrations/__tests__/fitbit-retirement-notice.test.tsx +++ b/src/components/settings/integrations/__tests__/fitbit-retirement-notice.test.tsx @@ -31,7 +31,7 @@ describe("", () => { expect(html).toContain("Google Health reads the same Fitbit and Pixel"); expect(html).toContain("Everything already imported stays."); expect(html).toContain('href="#google-health"'); - expect(html).toContain("/google-health"); + expect(html).toContain("/guides/connect-google-health/"); }); it("says the connection has stopped once the day has come", () => { diff --git a/src/components/settings/integrations/garmin-info-note.tsx b/src/components/settings/integrations/garmin-info-note.tsx index 4d2986acc..10c3fd645 100644 --- a/src/components/settings/integrations/garmin-info-note.tsx +++ b/src/components/settings/integrations/garmin-info-note.tsx @@ -11,7 +11,7 @@ import { ExternalLink, Watch } from "lucide-react"; import { SettingsCard } from "@/components/settings/settings-card"; import { SettingsCardHeader } from "@/components/settings/_card-header"; -import { INTEGRATION_DOCS_BASE } from "@/components/settings/integrations/setup-guide-link"; +import { GARMIN_DOCS_HREF } from "@/components/settings/integrations/setup-guide-link"; import { useTranslations } from "@/lib/i18n/context"; export function GarminInfoNote() { @@ -25,7 +25,7 @@ export function GarminInfoNote() {

{t("settings.garminInfo.body")}{" "} /`, e.g. `https://docs.healthlog.dev/guides/connect-whoop/`. + * These are the pages themselves: the older `/integrations/` paths + * only resolve through redirects. Kept as a single constant so the host never + * drifts across cards. */ -export const INTEGRATION_DOCS_BASE = "https://docs.healthlog.dev/integrations"; +export const DOCS_GUIDES_BASE = "https://docs.healthlog.dev/guides"; + +/** Garmin has no connector; the Google Health guide explains the route. */ +export const GARMIN_DOCS_HREF = `${DOCS_GUIDES_BASE}/connect-google-health/#garmin-owners`; export type IntegrationDocsProvider = | "whoop" @@ -30,15 +35,12 @@ export type IntegrationDocsProvider = | "polar" | "oura" | "nightscout" - // v1.27.0 — Google Health runbook (docs.healthlog.dev/integrations/google-health). | "google-health" - // v1.28.x — Strava runbook (docs.healthlog.dev/integrations/strava). | "strava" - // v1.43 — Dawarich runbook (docs.healthlog.dev/integrations/dawarich). | "dawarich"; export function integrationDocsHref(provider: IntegrationDocsProvider): string { - return `${INTEGRATION_DOCS_BASE}/${provider}`; + return `${DOCS_GUIDES_BASE}/connect-${provider}/`; } /** diff --git a/src/components/settings/managed-integration-status.tsx b/src/components/settings/managed-integration-status.tsx index 6f84aa551..1498add4a 100644 --- a/src/components/settings/managed-integration-status.tsx +++ b/src/components/settings/managed-integration-status.tsx @@ -54,7 +54,7 @@ const INTEGRATION_DISPLAY_NAMES = { /** Where the guardian boundary is explained, connected services included. */ const SHARING_DOCS_HREF = - "https://docs.healthlog.dev/self-hosting/account-sharing#records-with-no-login"; + "https://docs.healthlog.dev/guides/managed-profiles/#connected-services"; function iconFor(key: ManagedIntegrationKey) { if (key === "nightscout") return Droplet; diff --git a/src/lib/google-health/__tests__/setup-docs-parity.test.ts b/src/lib/google-health/__tests__/setup-docs-parity.test.ts index 9e8a3a094..653b72cfe 100644 --- a/src/lib/google-health/__tests__/setup-docs-parity.test.ts +++ b/src/lib/google-health/__tests__/setup-docs-parity.test.ts @@ -2,8 +2,8 @@ * Doc ↔ code parity guard for the Google Health operator runbook. * * The setup-guide link on the Settings card sends a mid-setup user to - * `docs.healthlog.dev/integrations/google-health`, rendered from - * `docs/integrations/google-health.md`. If the client ever requests a scope the + * `docs.healthlog.dev/guides/connect-google-health/`; the operator runbook in + * this repository is `docs/integrations/google-health.md`. If the client ever requests a scope the * runbook doesn't list, or the walkthrough regresses back to a wrong provider * (the file was once a Fitbit-Web-API walkthrough), a self-hoster follows a * broken recipe and the OAuth consent screen rejects the connection. @@ -20,7 +20,7 @@ import { describe, expect, it } from "vitest"; import { GOOGLE_HEALTH_CORE_SCOPES } from "@/lib/google-health/oauth"; import { - INTEGRATION_DOCS_BASE, + DOCS_GUIDES_BASE, integrationDocsHref, } from "@/components/settings/integrations/setup-guide-link"; @@ -70,7 +70,7 @@ describe("google-health setup runbook parity", () => { // The literal argument also compile-checks that "google-health" is a member // of the IntegrationDocsProvider union. expect(integrationDocsHref("google-health")).toBe( - `${INTEGRATION_DOCS_BASE}/google-health`, + `${DOCS_GUIDES_BASE}/connect-google-health/`, ); }); });