Skip to content

feat: automated push notifications via pg_cron and FCM (#52)#69

Open
Excalibur677 wants to merge 5 commits into
jpdevhub:mainfrom
Excalibur677:feat/push-notifications-pg-cron
Open

feat: automated push notifications via pg_cron and FCM (#52)#69
Excalibur677 wants to merge 5 commits into
jpdevhub:mainfrom
Excalibur677:feat/push-notifications-pg-cron

Conversation

@Excalibur677

@Excalibur677 Excalibur677 commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds automated push notifications for daily farm tasks and harvest reminders, using pg_cron + a Supabase Edge Function + Firebase Cloud Messaging.

Changes

  • New device_tokens table (with RLS) to store each farmer's FCM token
  • Enabled pg_cron and pg_net extensions
  • Daily cron job (06:00 IST) that calls a send-daily-tasks Edge Function via pg_net
  • send-daily-tasks Edge Function — checks for tasks due today and crops with today's harvest date, sends FCM notifications via the Firebase Admin REST API
  • POST /api/device-tokens backend endpoint to register/update a farmer's FCM token (kept this on the backend instead of calling Supabase directly from the frontend, since lib/supabase.ts says auth-only, no data queries from the client)
  • usePushNotifications hook — asks for permission, registers the service worker, grabs the FCM token, hits the backend endpoint
  • firebase-messaging-sw.js for background notifications
  • Added the new Firebase env vars to .env.example

Testing

  • Tested locally
  • Automated tests

Ran supabase db reset and confirmed the migration applies cleanly — device_tokens table, RLS policies, and the send-daily-task-notifications cron job all show up correctly in Studio.

Started the backend, logged in as the local dev user, and hit /api/device-tokens directly. It resolves farmer_id from the token correctly, inserts the row, and re-posting the same token updates updated_at instead of creating a duplicate (upsert on fcm_token works 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 via supabase secrets set.

Checklist

  • Code follows the project's TypeScript / Python style conventions
  • No secrets or .env values are committed
  • CI passes

Closes #52

Summary by CodeRabbit

  • New Features
    • Added push notifications for daily farm tasks and harvest events, including background notifications via a Firebase messaging service worker
    • Implemented automatic device registration and token management per signed-in user
    • Notifications are sent on a daily schedule using an Edge Function, aggregating task and crop updates into a single message
  • Documentation
    • Updated environment variable template with Firebase configuration placeholders for push notifications

@vercel

vercel Bot commented Jun 20, 2026

Copy link
Copy Markdown

@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.

@github-actions

Copy link
Copy Markdown

🎉 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

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Excalibur677, we couldn't start this review because you've reached your PR review rate limit.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 17a17b12-82a9-4da0-af2e-98a2b880311b

📥 Commits

Reviewing files that changed from the base of the PR and between a5f0648 and 9d00179.

📒 Files selected for processing (3)
  • backend/main.py
  • backend/supabase/migrations/20260620000000_device_token_upsert_function.sql
  • frontend/src/pages/_app.tsx
📝 Walkthrough

Walkthrough

Implements end-to-end Firebase Cloud Messaging push notifications: a device_tokens table with RLS and a pg_cron daily job in a new migration, a POST /api/device-tokens backend endpoint, a Deno Edge Function that signs Firebase JWTs and dispatches FCM messages, and a frontend usePushNotifications hook with a service worker that registers tokens and handles messages.

Changes

Push Notifications via FCM

Layer / File(s) Summary
device_tokens table, RLS policies, and pg_cron job
backend/supabase/migrations/20260619000000_push_notifications_pg_cron.sql
Enables pg_cron and pg_net extensions, creates public.device_tokens with farmer FK, device_type constraint, unique fcm_token, updated_at trigger, four farmer-scoped RLS policies, and a daily pg_cron schedule that POSTs to the Edge Function.
DeviceTokenCreate model and POST /api/device-tokens
backend/main.py
Adds DeviceTokenCreate Pydantic schema (fcm_token, device_type) and an authenticated endpoint that upserts the token for the current user into device_tokens on fcm_token conflict.
Deno Edge Function: Firebase JWT auth and FCM notification dispatch
backend/supabase/functions/send-daily-tasks/index.ts
Implements runtime configuration, getFirebaseAccessToken (manual base64url JWT, RS256 signing via crypto.subtle, Google token exchange), sendFCMNotification (FCM v1 messages:send), and the main handler that queries farm_tasks and crops due today, aggregates per-farmer payloads, fetches active tokens, sends notifications per token, deactivates failed tokens, and returns sent/failed counts.
Firebase SDK, service worker, hook, and app wiring
frontend/package.json, frontend/public/firebase-messaging-sw.js, frontend/src/hooks/usePushNotifications.ts, frontend/src/pages/_app.tsx
Adds firebase ^12.15.0, a background-message service worker, the usePushNotifications hook that requests permission, registers the service worker, obtains an FCM token, POSTs it to the backend with Supabase Bearer auth, and handles foreground messages; hook is called in MyApp.
Environment variables and .gitignore
.env.example, .gitignore
Adds NEXT_PUBLIC_FIREBASE_*, FIREBASE_PROJECT_ID, and FIREBASE_SERVICE_ACCOUNT placeholders to .env.example and adds backend/supabase/.temp/ to .gitignore.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇 Hippity-hop, a notification pops,
A cron job runs when the clock goes tick-tock.
Firebase signs a JWT with cryptic delight,
The farmer gets pinged for harvests tonight.
Service workers and tokens, all in a row—
Now tasks never vanish while bunnies lay low! 🌾

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: automated push notifications via pg_cron and FCM (#52)' accurately and concisely describes the primary change: implementing automated push notifications using pg_cron and Firebase Cloud Messaging.
Linked Issues check ✅ Passed The PR successfully implements all core requirements from issue #52: Supabase Edge Function for daily checks, pg_cron automation, Firebase Cloud Messaging integration, device token registration endpoint, frontend hooks, and service worker setup.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing automated push notifications. The .env.example and .gitignore updates support the Firebase and backend configuration needs.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (4)
.gitignore (1)

103-103: 💤 Low value

Duplicate .gitignore entry.

Line 85 already has backend/supabase/.temp which 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 value

Consider 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 win

Consider 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 value

Add a comment describing the expected format for FIREBASE_SERVICE_ACCOUNT.

The Edge Function expects FIREBASE_SERVICE_ACCOUNT to 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

📥 Commits

Reviewing files that changed from the base of the PR and between e220859 and db25b46.

⛔ Files ignored due to path filters (8)
  • backend/supabase/.temp/cli-latest is excluded by !**/.temp/**
  • backend/supabase/.temp/gotrue-version is excluded by !**/.temp/**
  • backend/supabase/.temp/pooler-url is excluded by !**/.temp/**
  • backend/supabase/.temp/postgres-version is excluded by !**/.temp/**
  • backend/supabase/.temp/project-ref is excluded by !**/.temp/**
  • backend/supabase/.temp/rest-version is excluded by !**/.temp/**
  • backend/supabase/.temp/storage-version is excluded by !**/.temp/**
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (9)
  • .env.example
  • .gitignore
  • backend/main.py
  • backend/supabase/functions/send-daily-tasks/index.ts
  • backend/supabase/migrations/20260619000000_push_notifications_pg_cron.sql
  • frontend/package.json
  • frontend/public/firebase-messaging-sw.js
  • frontend/src/hooks/usePushNotifications.ts
  • frontend/src/pages/_app.tsx

Comment thread .env.example
Comment on lines +51 to +57
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=""

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

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.

Comment thread backend/main.py Outdated
Comment thread backend/main.py Outdated
Comment thread backend/supabase/functions/send-daily-tasks/index.ts
Comment thread backend/supabase/functions/send-daily-tasks/index.ts Outdated
Comment thread frontend/public/firebase-messaging-sw.js Outdated
Comment thread frontend/public/firebase-messaging-sw.js
Comment thread frontend/public/firebase-messaging-sw.js Outdated
Comment thread frontend/src/hooks/usePushNotifications.ts Outdated
Comment thread frontend/src/hooks/usePushNotifications.ts Outdated
- 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
@Excalibur677 Excalibur677 force-pushed the feat/push-notifications-pg-cron branch from db25b46 to 4af7d17 Compare June 20, 2026 13:31
- 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)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between db25b46 and a5f0648.

⛔ Files ignored due to path filters (8)
  • backend/supabase/.temp/cli-latest is excluded by !**/.temp/**
  • backend/supabase/.temp/gotrue-version is excluded by !**/.temp/**
  • backend/supabase/.temp/pooler-url is excluded by !**/.temp/**
  • backend/supabase/.temp/postgres-version is excluded by !**/.temp/**
  • backend/supabase/.temp/project-ref is excluded by !**/.temp/**
  • backend/supabase/.temp/rest-version is excluded by !**/.temp/**
  • backend/supabase/.temp/storage-version is excluded by !**/.temp/**
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (9)
  • .env.example
  • .gitignore
  • backend/main.py
  • backend/supabase/functions/send-daily-tasks/index.ts
  • backend/supabase/migrations/20260619000000_push_notifications_pg_cron.sql
  • frontend/package.json
  • frontend/public/firebase-messaging-sw.js
  • frontend/src/hooks/usePushNotifications.ts
  • frontend/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

Comment thread backend/main.py
Comment thread backend/main.py Outdated
- 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Automated Push Notifications via pg_cron

1 participant