feat: automated push notifications via pg_cron and FCM (#52)#69
feat: automated push notifications via pg_cron and FCM (#52)#69Excalibur677 wants to merge 5 commits into
Conversation
|
@Excalibur677 is attempting to deploy a commit to the karan3431's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
🎉 Thanks for your contribution, @Excalibur677! Please make sure CI passes and the checklist in the PR template is complete. A maintainer will review this soon. — The AgroNavis team |
|
Warning Review limit reached
More reviews will be available in 43 minutes and 54 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughImplements end-to-end Firebase Cloud Messaging push notifications: a ChangesPush Notifications via FCM
Sequence DiagramsequenceDiagram
participant Browser as Browser
participant SW as firebase-messaging-sw.js
participant Hook as usePushNotifications
participant BackendAPI as POST /api/device-tokens
participant Cron as pg_cron
participant EdgeFn as send-daily-tasks
participant FCM as Firebase FCM v1
rect rgba(100, 149, 237, 0.5)
note over Browser,BackendAPI: Token Registration (on app mount)
Browser->>Hook: mount
Hook->>Browser: requestPermission()
Browser-->>Hook: granted
Hook->>SW: register service worker
Hook->>FCM: getToken(vapidKey, swReg)
FCM-->>Hook: fcm_token
Hook->>BackendAPI: POST {fcm_token, device_type} + Bearer
BackendAPI-->>Hook: 201 upserted row
end
rect rgba(60, 179, 113, 0.5)
note over Cron,FCM: Daily Notification Dispatch (00:30 UTC)
Cron->>EdgeFn: HTTP POST
EdgeFn->>EdgeFn: Query farm_tasks & crops due today
EdgeFn->>EdgeFn: Fetch active device_tokens per farmer
EdgeFn->>EdgeFn: Sign Firebase JWT → exchange for access_token
loop per farmer token
EdgeFn->>FCM: POST messages:send (title, body)
FCM-->>Browser: push notification
SW->>Browser: showNotification (background)
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 12
🧹 Nitpick comments (4)
.gitignore (1)
103-103: 💤 Low valueDuplicate .gitignore entry.
Line 85 already has
backend/supabase/.tempwhich matches both files and directories with that name. This trailing-slash variant is redundant.Proposed fix - remove duplicate
-backend/supabase/.temp/🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.gitignore at line 103, The .gitignore file contains a redundant entry at line 103 with `backend/supabase/.temp/` that duplicates the entry at line 85 which uses `backend/supabase/.temp` without the trailing slash. Since the entry without the trailing slash already matches both files and directories, the variant with the trailing slash is unnecessary. Remove the duplicate entry `backend/supabase/.temp/` from line 103 to keep the .gitignore file clean and avoid confusion.frontend/src/pages/_app.tsx (1)
21-21: 💤 Low valueConsider surfacing push notification errors to users.
The hook returns
{ token, error }but the return value is ignored. If push notification registration fails, users won't be notified. Consider capturing the error and displaying it in the UI.💡 Optional improvement
function MyApp({ Component, pageProps }: AppProps) { useHighContrastMode(); - usePushNotifications(); + const { error: pushError } = usePushNotifications(); + + // Optionally display error to user via toast/snackbar + useEffect(() => { + if (pushError) { + console.error("Push notification setup failed:", pushError); + // Could show a toast notification here + } + }, [pushError]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/pages/_app.tsx` at line 21, The usePushNotifications hook is being called on line 21 but its return value is being ignored, which means any push notification registration errors are not captured or displayed to users. Modify the call to usePushNotifications to destructure both the token and error properties from its return value, then implement error handling logic to display the error message to the user in the UI when the error property is not null or undefined.frontend/src/hooks/usePushNotifications.ts (1)
6-13: ⚡ Quick winConsider validating Firebase configuration completeness.
If any
NEXT_PUBLIC_FIREBASE_*environment variable is undefined, Firebase initialization may fail silently or produce cryptic errors. Consider adding validation or an early return if the configuration is incomplete.✨ Proposed validation
const firebaseConfig = { apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, }; + +const isFirebaseConfigValid = Object.values(firebaseConfig).every(Boolean);Then in the hook:
useEffect(() => { if (typeof window === "undefined" || !("Notification" in window) || !("serviceWorker" in navigator)) return; + if (!isFirebaseConfigValid) { + console.warn("Firebase configuration incomplete, push notifications disabled"); + return; + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/hooks/usePushNotifications.ts` around lines 6 - 13, Add validation to the firebaseConfig object in the usePushNotifications hook to ensure all required NEXT_PUBLIC_FIREBASE_* environment variables are defined before Firebase initialization. After constructing the firebaseConfig object, check if any of the required properties (apiKey, authDomain, projectId, storageBucket, messagingSenderId, appId) are undefined, and either log a meaningful error and return early or throw an error to prevent silent failures during Firebase initialization..env.example (1)
59-59: 💤 Low valueAdd a comment describing the expected format for
FIREBASE_SERVICE_ACCOUNT.The Edge Function expects
FIREBASE_SERVICE_ACCOUNTto contain a complete Firebase service account JSON. Consider adding a comment to clarify this, since JSON in an environment variable requires special handling (escaping quotes, minifying, etc.).📝 Proposed improvement
NEXT_PUBLIC_FIREBASE_VAPID_KEY="" FIREBASE_PROJECT_ID="" -FIREBASE_SERVICE_ACCOUNT="" +FIREBASE_SERVICE_ACCOUNT="" # Full Firebase service account JSON (minified, quotes escaped)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.env.example at line 59, Add a comment to the FIREBASE_SERVICE_ACCOUNT environment variable in the .env.example file explaining that it expects a complete Firebase service account JSON string. The comment should clarify the special handling requirements such as escaping quotes and minifying the JSON when placing it in an environment variable, to help users understand how to properly configure this value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.env.example:
- Around line 51-57: The Firebase configuration variables in the .env.example
file use the NEXT_PUBLIC_ prefix, but these are not accessible in the service
worker context (firebase-messaging-sw.js). You need to implement a build-time
generation mechanism to inject these Firebase configuration values into the
service worker. Create a build script or configuration that reads the Firebase
environment variables and generates or updates the firebase-messaging-sw.js file
with the actual configuration values at build time, ensuring the service worker
has access to FIREBASE_API_KEY, FIREBASE_AUTH_DOMAIN, FIREBASE_PROJECT_ID,
FIREBASE_STORAGE_BUCKET, FIREBASE_MESSAGING_SENDER_ID, FIREBASE_APP_ID, and
FIREBASE_VAPID_KEY through this injected mechanism rather than relying on
Next.js's NEXT_PUBLIC_ variable handling.
In `@backend/main.py`:
- Around line 583-589: The current upsert operation on the device_tokens table
using on_conflict="fcm_token" allows a malicious user to claim ownership of
another user's FCM token by overwriting the farmer_id column. Before performing
the upsert in the device token registration logic, add a validation check that
queries the device_tokens table to see if the provided fcm_token already exists
for a different farmer_id, and if so, raise an HTTPException with a 409 status
code to prevent the ownership takeover. Only proceed with the upsert if the
token is new or already belongs to the current user.
- Around line 590-592: Remove the duplicate return statement that appears on
line 592. The identical return statement `return {"success": True, "data":
(res.data[0] if res.data else None)}` is repeated twice consecutively, with the
second occurrence being unreachable dead code. Delete the second occurrence to
leave only one return statement.
In `@backend/supabase/functions/send-daily-tasks/index.ts`:
- Line 133: The separator used in the join() method call on notif.bodies
contains a corrupted Unicode replacement character (U+FFFD) instead of a valid
separator. Replace the malformed separator `" � "` in the notif.bodies.slice(0,
2).join() call with an appropriate and properly-encoded separator character that
will display correctly in user-facing notifications.
- Around line 29-35: The crypto.subtle.importKey call in the privateKey import
block is attempting to import a PEM-encoded private key string directly as DER
binary data, which will fail at runtime. To fix this, extract the base64 content
from the PEM string by removing the "-----BEGIN PRIVATE KEY-----" and "-----END
PRIVATE KEY-----" headers along with any whitespace or newlines, then decode the
base64 content to binary using a base64 decoder, and pass the resulting binary
data as an ArrayBuffer or Uint8Array to crypto.subtle.importKey instead of the
UTF-8 encoded PEM string.
- Around line 62-78: The fetch request to the FCM API endpoint does not capture
or validate the response, causing failures to go unnoticed silently. Capture the
response from the fetch call and check its status code. If the response is not
successful, parse the response body to extract error details and log them for
debugging. Additionally, based on specific FCM error codes like UNREGISTERED or
INVALID_ARGUMENT, mark the corresponding fcmToken as inactive in the database to
prevent future failures with invalid tokens. This ensures failed notifications
are tracked and invalid tokens are no longer considered active.
In `@backend/supabase/migrations/20260619000000_push_notifications_pg_cron.sql`:
- Around line 50-51: The http_post function is incorrectly qualified with the
extensions schema, but the pg_net extension creates its functions in the net
schema. Replace the schema qualifier in the http_post function call from
extensions.http_post to net.http_post to correctly reference the function
created by the pg_net extension.
In `@frontend/public/firebase-messaging-sw.js`:
- Around line 19-20: The firebase-messaging-sw.js file contains hardcoded paths
to icon assets in the icon and badge properties that reference non-existent
files in the public directory. Either add the missing icon assets
(icon-192x192.png and icon-72x72.png) to the appropriate location in the
public/icons directory, or update the hardcoded paths to reference icon files
that actually exist in the project. Verify the correct asset locations and
update both the icon property value and badge property value to point to valid
image files.
- Around line 4-11: The firebase.initializeApp() call in
firebase-messaging-sw.js is referencing self.FIREBASE_API_KEY,
self.FIREBASE_AUTH_DOMAIN, self.FIREBASE_PROJECT_ID,
self.FIREBASE_STORAGE_BUCKET, self.FIREBASE_MESSAGING_SENDER_ID, and
self.FIREBASE_APP_ID variables that are never defined. Since service workers run
in a separate context and cannot access process.env or Next.js environment
variables, you need to pass the Firebase configuration from the main thread to
the service worker. Either dynamically generate the service worker at build time
with these environment variables baked into the file, use postMessage from the
main thread (in usePushNotifications.ts) to send the config object after service
worker registration, or append the config values as query parameters when
registering the service worker and extract them in the initialization logic.
Update the firebase.initializeApp() call to use these values once they are
available in the service worker context.
- Around line 1-2: The service worker is loading Firebase version 9.0.0 from CDN
in the importScripts calls, which contains a documented security vulnerability,
but package.json specifies Firebase ^12.15.0. Update both importScripts URLs
(for firebase-app-compat.js and firebase-messaging-compat.js) to use version
12.15.0 instead of 9.0.0 to match the npm package version and eliminate the
security vulnerability.
In `@frontend/src/hooks/usePushNotifications.ts`:
- Around line 44-51: The fetch call to the `/device-tokens` endpoint lacks error
handling for both network failures and unsuccessful HTTP responses. Add error
handling to check if the response is successful (using response.ok or
response.status), and handle failures appropriately by throwing an error or
logging the issue. Wrap the fetch call in a try-catch block to catch network
errors and ensure that any failure to register the device token is properly
handled rather than silently ignored.
- Around line 53-60: The onMessage callback registration in the
usePushNotifications hook is missing cleanup logic. The onMessage function
returns an unsubscribe function that must be captured and called when the effect
unmounts. Modify the effect so that you capture the unsubscribe function
returned by onMessage (the callback that handles payload.notification) and
return a cleanup function from the useEffect that calls this unsubscribe
function to prevent duplicate listeners from being registered on effect re-runs.
---
Nitpick comments:
In @.env.example:
- Line 59: Add a comment to the FIREBASE_SERVICE_ACCOUNT environment variable in
the .env.example file explaining that it expects a complete Firebase service
account JSON string. The comment should clarify the special handling
requirements such as escaping quotes and minifying the JSON when placing it in
an environment variable, to help users understand how to properly configure this
value.
In @.gitignore:
- Line 103: The .gitignore file contains a redundant entry at line 103 with
`backend/supabase/.temp/` that duplicates the entry at line 85 which uses
`backend/supabase/.temp` without the trailing slash. Since the entry without the
trailing slash already matches both files and directories, the variant with the
trailing slash is unnecessary. Remove the duplicate entry
`backend/supabase/.temp/` from line 103 to keep the .gitignore file clean and
avoid confusion.
In `@frontend/src/hooks/usePushNotifications.ts`:
- Around line 6-13: Add validation to the firebaseConfig object in the
usePushNotifications hook to ensure all required NEXT_PUBLIC_FIREBASE_*
environment variables are defined before Firebase initialization. After
constructing the firebaseConfig object, check if any of the required properties
(apiKey, authDomain, projectId, storageBucket, messagingSenderId, appId) are
undefined, and either log a meaningful error and return early or throw an error
to prevent silent failures during Firebase initialization.
In `@frontend/src/pages/_app.tsx`:
- Line 21: The usePushNotifications hook is being called on line 21 but its
return value is being ignored, which means any push notification registration
errors are not captured or displayed to users. Modify the call to
usePushNotifications to destructure both the token and error properties from its
return value, then implement error handling logic to display the error message
to the user in the UI when the error property is not null or undefined.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: c91079e9-c148-4067-8f3e-a12b0d140b1b
⛔ Files ignored due to path filters (8)
backend/supabase/.temp/cli-latestis excluded by!**/.temp/**backend/supabase/.temp/gotrue-versionis excluded by!**/.temp/**backend/supabase/.temp/pooler-urlis excluded by!**/.temp/**backend/supabase/.temp/postgres-versionis excluded by!**/.temp/**backend/supabase/.temp/project-refis excluded by!**/.temp/**backend/supabase/.temp/rest-versionis excluded by!**/.temp/**backend/supabase/.temp/storage-versionis excluded by!**/.temp/**frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (9)
.env.example.gitignorebackend/main.pybackend/supabase/functions/send-daily-tasks/index.tsbackend/supabase/migrations/20260619000000_push_notifications_pg_cron.sqlfrontend/package.jsonfrontend/public/firebase-messaging-sw.jsfrontend/src/hooks/usePushNotifications.tsfrontend/src/pages/_app.tsx
| NEXT_PUBLIC_FIREBASE_API_KEY="" | ||
| NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN="" | ||
| NEXT_PUBLIC_FIREBASE_PROJECT_ID="" | ||
| NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET="" | ||
| NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID="" | ||
| NEXT_PUBLIC_FIREBASE_APP_ID="" | ||
| NEXT_PUBLIC_FIREBASE_VAPID_KEY="" |
There was a problem hiding this comment.
Service worker cannot access NEXT_PUBLIC_* environment variables.
The service worker (firebase-messaging-sw.js) references self.FIREBASE_API_KEY, etc., but these NEXT_PUBLIC_* environment variables are not available in the service worker context. Next.js only makes NEXT_PUBLIC_* variables available in client-side JavaScript bundles, not in separately served static files like service workers.
This is related to the critical issue flagged in the service worker review. You need a build-time generation mechanism or runtime injection to make these values available to the service worker.
🧰 Tools
🪛 dotenv-linter (4.0.0)
[warning] 51-51: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
[warning] 52-52: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
[warning] 53-53: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
[warning] 54-54: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
[warning] 55-55: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
[warning] 55-55: [UnorderedKey] The NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID key should go before the NEXT_PUBLIC_FIREBASE_PROJECT_ID key
(UnorderedKey)
[warning] 56-56: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
[warning] 56-56: [UnorderedKey] The NEXT_PUBLIC_FIREBASE_APP_ID key should go before the NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN key
(UnorderedKey)
[warning] 57-57: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.env.example around lines 51 - 57, The Firebase configuration variables in
the .env.example file use the NEXT_PUBLIC_ prefix, but these are not accessible
in the service worker context (firebase-messaging-sw.js). You need to implement
a build-time generation mechanism to inject these Firebase configuration values
into the service worker. Create a build script or configuration that reads the
Firebase environment variables and generates or updates the
firebase-messaging-sw.js file with the actual configuration values at build
time, ensuring the service worker has access to FIREBASE_API_KEY,
FIREBASE_AUTH_DOMAIN, FIREBASE_PROJECT_ID, FIREBASE_STORAGE_BUCKET,
FIREBASE_MESSAGING_SENDER_ID, FIREBASE_APP_ID, and FIREBASE_VAPID_KEY through
this injected mechanism rather than relying on Next.js's NEXT_PUBLIC_ variable
handling.
- Add device_tokens table with RLS to store farmer FCM tokens - Enable pg_cron and pg_net extensions - Schedule daily cron job to trigger send-daily-tasks Edge Function - Add send-daily-tasks Edge Function checking due tasks/harvests and sending FCM notifications - Add POST /api/device-tokens backend endpoint to register tokens - Add usePushNotifications hook for permission request and token registration - Add firebase-messaging-sw.js for background notification handling Closes jpdevhub#52
db25b46 to
4af7d17
Compare
- Remove duplicate return statement in device-tokens endpoint - Prevent upsert from overwriting another user's token ownership - Fix PEM private key decoding before crypto.subtle.importKey - Check FCM response status, log failures, deactivate dead tokens - Bump service worker Firebase CDN version to match package.json (12.15.0) - Pass Firebase config to service worker via query params since it can't read NEXT_PUBLIC_* env vars - Point notification icons to existing asset instead of missing files - Add error handling for backend token registration fetch - Add cleanup for onMessage listener on unmount
- Fix corrupted bullet separator character in notification body - Fix net.http_post schema reference (pg_net functions live in net schema, not extensions)
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/main.py`:
- Around line 583-593: The separate ownership check at line 583 followed by the
upsert at line 593 creates a race condition where concurrent requests can bypass
the ownership validation. Replace this two-step approach with a single atomic
database operation by creating a Supabase RPC function or using raw SQL that
performs an INSERT with ON CONFLICT DO UPDATE WHERE clause in one transaction.
The operation should only update the farmer_id if the existing token belongs to
the same user, and should return appropriate status codes (403 for different
farmer ownership, 409 for conflict) to indicate the failure reason. Remove the
separate ownership check and call this atomic operation instead of the current
select followed by upsert pattern.
- Around line 210-213: In the DeviceTokenCreate class, add validation
constraints to match the database contract. For the device_type field, use an
Enum or Field with allowed values constraint to restrict it to only the valid
types that the database accepts, preventing invalid values from reaching the
database and causing 5xx errors. For the fcm_token field, add validation using
Field with min_length constraint and ensure it rejects empty or whitespace-only
strings by setting appropriate validation rules. This will ensure invalid inputs
are caught at the validation layer and return clean 4xx errors instead of
failing at the database persistence layer.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 2b6c7554-6fef-467d-89b6-87e1b4022e9a
⛔ Files ignored due to path filters (8)
backend/supabase/.temp/cli-latestis excluded by!**/.temp/**backend/supabase/.temp/gotrue-versionis excluded by!**/.temp/**backend/supabase/.temp/pooler-urlis excluded by!**/.temp/**backend/supabase/.temp/postgres-versionis excluded by!**/.temp/**backend/supabase/.temp/project-refis excluded by!**/.temp/**backend/supabase/.temp/rest-versionis excluded by!**/.temp/**backend/supabase/.temp/storage-versionis excluded by!**/.temp/**frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (9)
.env.example.gitignorebackend/main.pybackend/supabase/functions/send-daily-tasks/index.tsbackend/supabase/migrations/20260619000000_push_notifications_pg_cron.sqlfrontend/package.jsonfrontend/public/firebase-messaging-sw.jsfrontend/src/hooks/usePushNotifications.tsfrontend/src/pages/_app.tsx
🚧 Files skipped from review as they are similar to previous changes (7)
- frontend/public/firebase-messaging-sw.js
- frontend/package.json
- .gitignore
- frontend/src/pages/_app.tsx
- backend/supabase/migrations/20260619000000_push_notifications_pg_cron.sql
- frontend/src/hooks/usePushNotifications.ts
- backend/supabase/functions/send-daily-tasks/index.ts
- Constrain device_type to allowed enum values and reject empty fcm_token via Pydantic validation - Replace check-then-upsert with atomic upsert_device_token() SQL function to close TOCTOU race on token ownership
Summary
Adds automated push notifications for daily farm tasks and harvest reminders, using pg_cron + a Supabase Edge Function + Firebase Cloud Messaging.
Changes
device_tokenstable (with RLS) to store each farmer's FCM tokenpg_cronandpg_netextensionssend-daily-tasksEdge Function viapg_netsend-daily-tasksEdge Function — checks for tasks due today and crops with today's harvest date, sends FCM notifications via the Firebase Admin REST APIPOST /api/device-tokensbackend endpoint to register/update a farmer's FCM token (kept this on the backend instead of calling Supabase directly from the frontend, sincelib/supabase.tssays auth-only, no data queries from the client)usePushNotificationshook — asks for permission, registers the service worker, grabs the FCM token, hits the backend endpointfirebase-messaging-sw.jsfor background notifications.env.exampleTesting
Ran
supabase db resetand confirmed the migration applies cleanly —device_tokenstable, RLS policies, and thesend-daily-task-notificationscron job all show up correctly in Studio.Started the backend, logged in as the local dev user, and hit
/api/device-tokensdirectly. It resolvesfarmer_idfrom the token correctly, inserts the row, and re-posting the same token updatesupdated_atinstead of creating a duplicate (upsert onfcm_tokenworks as expected).Couldn't fully test the Edge Function / actual FCM delivery locally since that needs a real Firebase project (API key, VAPID key, service account JSON). The function reuses the same Supabase query patterns as the rest of the codebase, so I'm fairly confident in the logic, but flagging this in case you want to double check once Firebase creds are in place.
One thing before this goes to prod — the cron job URL in the migration has
<YOUR_PROJECT_REF>as a placeholder, that'll need to be swapped for the actual project ref, and the Firebase secrets need to be set viasupabase secrets set.Checklist
.envvalues are committedCloses #52
Summary by CodeRabbit