From bb76ea4f98e3428072948c7516c12768b632702b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Mon, 11 May 2026 00:34:16 +0200 Subject: [PATCH] chore(deps): add @parse/node-apn for APNs send-side scaffolding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the APNs Provider library the v1.5 iOS app needs so per-metric alerts can land via Apple's push service alongside the existing Telegram → ntfy → Web Push cascade. Bundles the env-var contract (APNS_KEY_ID / APNS_TEAM_ID / APNS_BUNDLE_ID plus one of APNS_KEY or APNS_KEY_FILE) in .env.example so a fresh deploy knows the four required values up front. The dispatcher wiring + sender ship in follow-up commits. Co-Authored-By: Claude Opus 4.7 (1M context) --- .env.example | 21 ++++++++++ package.json | 1 + pnpm-lock.yaml | 101 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+) diff --git a/.env.example b/.env.example index 8e5ab0679..d809bbdee 100644 --- a/.env.example +++ b/.env.example @@ -151,6 +151,27 @@ API_TOKEN_HMAC_KEY="REPLACE-WITH-OUTPUT-OF-openssl-rand-hex-32" # BACKUP_ENCRYPTION_KEY="" # 64-hex-char or base64 32-byte; SEPARATE from ENCRYPTION_KEY +# ----------------------------------------------------------------------------- +# APNs — Apple Push Notification service (v1.4.23+, optional) +# ----------------------------------------------------------------------------- +# All four core APNS_* vars must be set together for APNs to fan out to iOS +# devices. Leaving them unset disables the channel silently — the dispatcher +# falls back to Telegram → ntfy → Web Push as before. +# +# Provision an APNs auth key (.p8) at https://developer.apple.com/account/ +# under Certificates, Identifiers & Profiles → Keys → APNs. +# +# APNS_KEY (preferred for 12-factor / Coolify env-block) and APNS_KEY_FILE +# (filesystem path) are mutually exclusive — pick one. APNS_KEY accepts the +# multi-line PEM body with literal newlines OR `\n` escapes. +# APNS_KEY_ID="" # 10-char Key ID from Apple Developer (e.g. ABCDE12345) +# APNS_TEAM_ID="" # 10-char Team ID from Apple Developer +# APNS_BUNDLE_ID="" # iOS app bundle id (e.g. dev.healthlog.ios) +# APNS_KEY="" # APNs auth key as PEM body (multi-line OK with \n escapes); preferred over APNS_KEY_FILE +# APNS_KEY_FILE="" # Alternate: filesystem path to .p8 file +# APNS_PRODUCTION="" # "true" to force the production gateway; default = mirror Device.apnsEnvironment + + # ----------------------------------------------------------------------------- # Auto-deploy webhook (v1.4.15 — phase C2) # ----------------------------------------------------------------------------- diff --git a/package.json b/package.json index 4220f2f10..fd640072c 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@aws-sdk/client-s3": "^3.1045.0", "@hookform/resolvers": "^5.2.2", "@node-rs/argon2": "^2.0.2", + "@parse/node-apn": "^8.1.0", "@prisma/adapter-pg": "^7.8.0", "@prisma/client": "^7.8.0", "@simplewebauthn/browser": "^13.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e9d2529b9..ea7dfec66 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@node-rs/argon2': specifier: ^2.0.2 version: 2.0.2 + '@parse/node-apn': + specifier: ^8.1.0 + version: 8.1.0 '@prisma/adapter-pg': specifier: ^7.8.0 version: 7.8.0 @@ -1278,6 +1281,10 @@ packages: '@open-draft/until@2.1.0': resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@parse/node-apn@8.1.0': + resolution: {integrity: sha512-LowcdkKPDikbbzIr3zwEdoFW5wfEbbTzpYQeen3S8kKLePG0AQK586Li+OLC7bonyLmlk//Yk3smHZOLSV6TZA==} + engines: {node: 20 || 22 || 24} + '@peculiar/asn1-android@2.6.0': resolution: {integrity: sha512-cBRCKtYPF7vJGN76/yG8VbxRcHLPF3HnkoHhKOZeHpoVtbMYfY9ROKtH3DtYUY9m8uI1Mh47PRhHf2hSK3xcSQ==} @@ -3035,6 +3042,10 @@ packages: resolution: {integrity: sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==} engines: {node: '>=12.0.0'} + assert-plus@1.0.0: + resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} + engines: {node: '>=0.8'} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -3347,6 +3358,9 @@ packages: core-js@3.49.0: resolution: {integrity: sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==} + core-util-is@1.0.2: + resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} + core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -3856,6 +3870,10 @@ packages: exsolve@1.0.8: resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + extsprintf@1.4.1: + resolution: {integrity: sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==} + engines: {'0': node >=0.6.0} + fast-check@3.23.2: resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} engines: {node: '>=8.0.0'} @@ -4479,6 +4497,10 @@ packages: jsonfile@6.2.1: resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} + jsonwebtoken@9.0.3: + resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==} + engines: {node: '>=12', npm: '>=6'} + jspdf-autotable@5.0.7: resolution: {integrity: sha512-2wr7H6liNDBYNwt25hMQwXkEWFOEopgKIvR1Eukuw6Zmprm/ZcnmLTQEjW7Xx3FCbD3v7pflLcnMAv/h1jFDQw==} peerDependencies: @@ -4607,9 +4629,30 @@ packages: lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} @@ -4813,6 +4856,10 @@ packages: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-forge@1.4.0: + resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==} + engines: {node: '>= 6.13.0'} + node-releases@2.0.38: resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==} @@ -5956,6 +6003,10 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + verror@1.10.1: + resolution: {integrity: sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==} + engines: {node: '>=0.6.0'} + victory-vendor@37.3.6: resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} @@ -7425,6 +7476,15 @@ snapshots: '@open-draft/until@2.1.0': {} + '@parse/node-apn@8.1.0': + dependencies: + debug: 4.4.3 + jsonwebtoken: 9.0.3 + node-forge: 1.4.0 + verror: 1.10.1 + transitivePeerDependencies: + - supports-color + '@peculiar/asn1-android@2.6.0': dependencies: '@peculiar/asn1-schema': 2.6.0 @@ -9447,6 +9507,8 @@ snapshots: pvutils: 1.1.5 tslib: 2.8.1 + assert-plus@1.0.0: {} + assertion-error@2.0.1: {} ast-types-flow@0.0.8: {} @@ -9737,6 +9799,8 @@ snapshots: core-js@3.49.0: optional: true + core-util-is@1.0.2: {} + core-util-is@1.0.3: {} cors@2.8.6: @@ -10429,6 +10493,8 @@ snapshots: exsolve@1.0.8: {} + extsprintf@1.4.1: {} + fast-check@3.23.2: dependencies: pure-rand: 6.1.0 @@ -11004,6 +11070,19 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsonwebtoken@9.0.3: + dependencies: + jws: 4.0.1 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.8.0 + jspdf-autotable@5.0.7(jspdf@4.2.1): dependencies: jspdf: 4.2.1 @@ -11117,8 +11196,22 @@ snapshots: lodash.camelcase@4.3.0: {} + lodash.includes@4.3.0: {} + + lodash.isboolean@3.0.3: {} + + lodash.isinteger@4.0.4: {} + + lodash.isnumber@3.0.3: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + lodash.merge@4.6.2: {} + lodash.once@4.1.1: {} + lodash@4.18.1: {} log-symbols@6.0.0: @@ -11316,6 +11409,8 @@ snapshots: fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 + node-forge@1.4.0: {} + node-releases@2.0.38: {} non-error@0.1.0: {} @@ -12683,6 +12778,12 @@ snapshots: vary@1.1.2: {} + verror@1.10.1: + dependencies: + assert-plus: 1.0.0 + core-util-is: 1.0.2 + extsprintf: 1.4.1 + victory-vendor@37.3.6: dependencies: '@types/d3-array': 3.2.2