Commit Graph

16 Commits

Author SHA1 Message Date
null bf4cc41cd9 refactor(functions): B6d finish logger migration in shared helpers
Migrate the last console.* call sites (the shared helpers entitlementLogic.ts and
pruneTokens.ts) to firebase-functions/logger, completing the structured-logging sweep.
Zero console.* remain under functions/src.

Final verification of the whole v1→v2 migration:
- tsc clean under firebase-functions v7.2.5; 70 jest tests green.
- Emulator discovery loads all 36 functions in us-central1 with 0 errors and no
  outdated-SDK warning; onUserDelete remains a v1 auth trigger, the rest are v2.
- Grep gates clean: no functions.https.onCall/.firestore.document/.pubsub.schedule,
  no context.auth/app/params, no console.* in src, no messaging.send outside push.ts,
  no raw FCM tokens in any log.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 00:10:10 -05:00
null e0c2d67373 refactor(functions): B3 migrate 12 callables to v2 + harden
Migrate all callables off functions.https.onCall to firebase-functions/v2/https onCall:
createInvite, acceptInvite, leaveCouple, submitOutcome, sendGentleReminder,
sendThinkingOfYou, checkDeviceIntegrity, syncEntitlement, assignDailyQuestionCallable,
wrapReleaseKey. context.auth/app → request.auth/app, data arg → request.data. The 8
client-hardcoded callable names are preserved verbatim (verified via emulator discovery).
The manual `if (!request.app)` App Check check is a 1:1 port (no enforceAppCheck switch).

Hardening folded in:
- acceptInviteCallable: await the previously fire-and-forget partner_joined push (gen 2
  freezes the instance after the response) — still swallows push errors so a failed push
  never fails the accept.
- checkDeviceIntegrity: 10s timeout on the Play Integrity client.request so a hung upstream
  can't pin the instance (fail-closed catch already handles the throw); memory 512MiB.
- wrapReleaseKey: memory 512MiB (tink); HttpsError swapped to v2; lazy tink require + graceful
  failure preserved.
- Error mapping with `if (e instanceof HttpsError) throw e` re-throw guard around the risky
  DB sections in acceptInvite, leaveCouple, submitOutcome, sendGentleReminder,
  sendThinkingOfYou — raw errors map to a clean 'internal' without masking intentional codes
  (resource-exhausted rate limits, permission-denied, etc.). leaveCouple's best-effort
  recursiveDelete sweep now swallows errors (the transactional leave already succeeded).
- Adopt shared sendPushToUser()/logger; remove copied token readers + plaintext token logging.

Delete dead placeholder callables notifications/reminders.ts (sendDailyQuestionReminder,
sendPartnerAnsweredNotification) — no client caller; wrote sent:false rows nothing consumed.

Build clean; 70 tests green; discovery loads all callables as v2 in us-central1. dist rebuilt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 23:58:32 -05:00
null 6681bf1518 refactor(functions): B2 migrate 8 scheduled functions to v2 + harden
Migrate all scheduled jobs off functions.pubsub.schedule().onRun() to
firebase-functions/v2/scheduler onSchedule({ schedule, timeZone, ...opts }, handler):
sendChallengeDayReminders, unlockDueMemoryCapsules, sendDailyQuestionProactiveReminder,
sendStreakReminder, sendReengagementReminder, assignDailyQuestion (scheduled export),
aggregateOutcomeStats, scheduledOutcomesReminder.

Hardening folded in:
- Fan-out isolation: Promise.all → Promise.allSettled in dailyQuestionReminder (outer+inner),
  reengagement, gameRetention (both jobs), scheduledOutcomesReminder — one bad couple can no
  longer abort a whole run. streakReminder / assignDailyQuestion already isolated.
- Resource options: assignDailyQuestion + aggregateOutcomeStats memory 512MiB + timeout 300s
  (they iterate all couples); the four fan-out reminders get timeout 180s.
- Adopt shared sendPushToUser()/logger everywhere; remove five copied getUserTokens() and the
  copied send/prune blocks (no plaintext token logging remains here).
- Consolidate duplicated date/time helpers into notifications/time.ts (chicagoDateKey, toMillis),
  replacing streakReminder's + scheduledOutcomesReminder's per-file copies.

assignDailyQuestion.ts callable export stays v1 for now (migrates in B3); its tested CST helpers
are untouched. Scanner pagination for assignDailyQuestion/aggregateOutcomes is deferred to B6.

Build clean; 70 tests green (tested pure helpers preserved). dist rebuilt. Still on v5.1.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 23:50:59 -05:00
null 4040abbf28 refactor(functions): B1 migrate 13 Firestore triggers to v2 + harden
Migrate all Firestore triggers off the v1 API to firebase-functions/v2/firestore
(onDocumentCreated/Updated/Written); context.params→event.params, snap→event.data,
change.before/after→event.data.before/after. Region stays us-central1 (global option).

Hardening folded in (all reuse in-repo patterns):
- Adopt the shared sendPushToUser()/getUserTokens() helper in every trigger, removing
  ~7 copied token readers and the copied send/prune blocks. FCM tokens are no longer
  logged in plaintext anywhere here (redacted inside push.ts).
- console.* → firebase-functions/logger (structured).
- Idempotency: new claimOnce() (atomic create-if-absent marker under
  couples/{id}/notif_marks) dedupes at-least-once redelivery on the non-idempotent
  senders (onAnswerWritten/Revealed, onMessageWritten, onCoupleLeave, onEntitlementChanged,
  onDateReflectionWritten/Revealed, onDateHistoryCreated). Fail-open. onGameSessionUpdate/
  onGamePartFinished/notifyOnDateMatch already had transactional claim-flags — preserved.
- onRestoreRequested: the plan's "claim" is implemented as the existing 60s time-WINDOW
  (lastRestorePartnerAlertAt), not a permanent recipientUid marker — a permanent marker
  would wrongly block legitimate re-requests (restore docs are deleted+recreated by design).

Faithful port of onGameSessionUpdate/onGamePartFinished (broad wildcard + allowlist kept);
the trigger split and read reorder are deferred to B6 as separate commits.

Build clean; 70 tests green (+ new idempotency.test.ts, push token/prune tests). Emulator
discovery loads all triggers as v2 in us-central1. firebase-functions still v5.1.1 (v6 bump
deferred to B6). dist rebuilt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 23:46:17 -05:00
null fbf4e7957c build(functions): B0 v2 foundation — global options, shared push helper, structured logging
Groundwork for the v1→v2 Cloud Functions migration; no function is migrated yet
(all 35 still load as v1, verified via emulator discovery).

- options.ts: setGlobalOptions({ region: 'us-central1', maxInstances: 20 }), imported
  first in index.ts so it applies before any v2 function is defined. Region pin is
  load-bearing — the Android client uses the default region.
- notifications/push.ts: single canonical getUserTokens() + sendPushToUser() that
  batches via messaging.sendEachForMulticast() and prunes dead tokens, to replace the
  ~10 copied token readers and ~19 copied send/prune blocks in later batches.
- log.ts: firebase-functions/logger re-export + redactToken() (FCM tokens are secrets).
- push.test.ts: 9 unit tests (token merge/dedupe, BatchResponse→dead-token mapping,
  send/prune/no-op/whole-batch-failure paths). 67 tests green.

firebase-functions stays at v5.1.1 for the migration (supports both the root v1 API and
the /v2 subpaths); bump to v6 is deferred to the final batch once nothing references the
root namespace, so the build stays green at every step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 23:36:16 -05:00
null d8408a2c44 chore(functions): rebuild dist with pruneDeadTokens wired into all 19 push sites 2026-06-30 23:45:42 -05:00
null d87603211a feat(activity): ActivityScreen timeline, HomeScreen activity feed, HomeViewModel wiring, PartnerNotificationManager, sendThinkingOfYouCallable Cloud Function 2026-06-30 03:54:01 -05:00
null 2a5c40508e feat(notifications): QuietHoursManager + NotificationSettingsScreen rewrite, Cloud Functions (streakReminder, quietHours, reengagement, gameRetention), UserRepository E2EE wiring, SettingsDataStore, firestore rules, wiring-scan 2026-06-30 00:38:06 -05:00
null 37ed7cebec feat: quiet hours notifications, settings UI, game session updates, docs 2026-06-28 10:00:25 -05:00
null 765916a8ef build(functions): recompile dist for E-OBS + E-003 results-ready changes
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:40:38 -05:00
null 658ead38cd security: App Check enforcement on all callables, fail-closed device integrity, no raw code in logs; release signing config; iOS RevenueCat log level 2026-06-23 10:56:42 -05:00
null 5e16177eb2 feat: code push -- notifications, cloud functions, iOS updates 2026-06-22 08:53:23 -05:00
null 57a3e35359 feat(outcomes): add 30/60/90 day check-in flow with baseline + reminders 2026-06-20 23:59:24 -05:00
null 0e75b3b536 feat: add gentle reminder callable and wire into HomeViewModel (batch v1.0.12)
- GENTLE_REMINDER notification type with warm copy
- sendGentleReminderCallable Cloud Function
- HomeViewModel.sendGentleReminder() calls function, shows snackbar
- Snackbar event consumed after display
2026-06-19 23:47:01 -05:00
null 0e0a33a6dd feat: add FCM game retention notification functions, messaging service updates, ThisOrThat screen fixes 2026-06-19 02:23:52 -05:00
null f3bad90ec6 feat(notifications): FCM token registration, quiet hours, notification permission helper, reminder Cloud Functions (batch 11) 2026-06-17 01:30:04 -05:00