Skip to content

feat: add Cloudflare Worker for Resend webhook push notifications#20

Open
yushanwebdev wants to merge 8 commits intomainfrom
claude/analyze-email-webhook-zXaBA
Open

feat: add Cloudflare Worker for Resend webhook push notifications#20
yushanwebdev wants to merge 8 commits intomainfrom
claude/analyze-email-webhook-zXaBA

Conversation

@yushanwebdev
Copy link
Copy Markdown
Owner

@yushanwebdev yushanwebdev commented Mar 30, 2026

Summary

  • Add a Cloudflare Worker (worker/) that receives Resend webhook events (via Svix verification) and sends Expo push notifications when new emails arrive
  • Worker stores push tokens in Cloudflare KV with bearer token auth on the /api/push-token endpoint
  • App registers its Expo push token with the worker on startup and inserts email metadata from notification payloads into the local SQLite DB

Test plan

  • Deploy worker with wrangler deploy and verify it starts
  • Configure Resend webhook to point to the worker URL
  • Send a test email and verify push notification is received on device
  • Verify email row appears in inbox without manual refresh
  • Confirm unauthorized requests to /api/push-token are rejected

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features
    • Push notifications now sent when new emails arrive
    • Added real-time notification system for incoming email alerts

claude and others added 8 commits March 29, 2026 16:36
Add a Cloudflare Worker that receives Resend email webhooks (verified
via Svix), stores the user's Expo push token in Workers KV, and sends
push notifications when new emails arrive. On the client side, incoming
notifications now insert email metadata directly into SQLite so the
inbox updates instantly without polling.

https://claude.ai/code/session_01Ek7JZyGJyN82CEkpExxQKM
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Secure the /api/push-token endpoint with a shared API key validated
via Hono's bearerAuth middleware, preventing unauthorized token
registration from arbitrary callers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace raw fetch to Expo Push API with the official expo-server-sdk,
gaining automatic retries, gzip compression, and proper token
validation. Configure Expo access token for authenticated push sends.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The SDK's undici dependency uses MessagePort which is unavailable in
the Cloudflare Workers runtime. Replace with native fetch using Expo
access token auth and SDK-compatible push token validation (bracket
and UUID formats).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevent Cloudflare Worker development secrets from being committed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the startsWith check with the exported isExpoPushToken helper
that validates both bracket and UUID token formats.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 2026

📝 Walkthrough

Walkthrough

The PR introduces a Cloudflare Worker backend to handle push notifications for the yumail app. It adds environment variables for worker configuration, establishes HTTP endpoints for push token registration and webhook verification from Resend, and updates the mobile app to register tokens with the worker and handle foreground notifications by inserting email records.

Changes

Cohort / File(s) Summary
Environment & Configuration
.env.example, .gitignore, app.config.ts
Added Cloudflare Worker environment variables (EXPO_PUBLIC_WORKER_URL, WORKER_API_KEY); updated gitignore to exclude .dev.vars; configured workerApiKey in Expo config.
Mobile App Integration
hooks/useNotifications.ts, db/syncEngine.ts
Exported toLocalDateString() utility; enhanced push notification registration to POST tokens to worker endpoint; added foreground notification handler to parse email data and insert records via insertEmails().
Worker Infrastructure
worker/package.json, worker/tsconfig.json, worker/wrangler.toml
Established worker project configuration with Hono/Svix dependencies, TypeScript strict mode, and KV namespace binding for PUSH_TOKENS.
Worker Application
worker/src/index.ts, worker/src/types.ts, worker/src/push.ts, worker/src/webhooks.ts
Implemented Hono-based worker with two POST endpoints: /api/push-token (bearer-auth token storage in KV) and /api/webhooks/resend (webhook verification + push notification dispatch); defined Env and EmailReceivedEvent types; added Expo token validation and push notification sender logic.

Sequence Diagram(s)

sequenceDiagram
    participant App as Mobile App
    participant Worker as Cloudflare Worker
    participant KV as KV Storage
    participant Resend as Resend Service
    participant Expo as Expo Push Service

    rect rgba(100, 150, 200, 0.5)
    Note over App,KV: Push Token Registration Flow
    App->>Worker: POST /api/push-token (Bearer auth)
    Worker->>Worker: Validate Expo token format
    Worker->>KV: Store token under 'latest' key
    KV-->>Worker: Acknowledged
    Worker-->>App: { success: true }
    end

    rect rgba(200, 150, 100, 0.5)
    Note over Resend,Expo: Email Webhook → Push Notification Flow
    Resend->>Worker: POST /api/webhooks/resend (email.received)
    Worker->>Worker: Verify webhook signature (Svix)
    Worker->>KV: Retrieve latest push token
    KV-->>Worker: Token data
    Worker->>Expo: POST /send (push notification + email data)
    Expo-->>Worker: Response
    Worker-->>Resend: { success: true }
    end

    rect rgba(150, 200, 100, 0.5)
    Note over App: Foreground Notification Handling
    App->>App: Receive foreground notification
    App->>App: Extract emailId, parse createdAt
    App->>App: Insert email record via insertEmails()
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 A worker's built with haste and care,
To catch the tokens floating there,
Webhooks verified, emails in flight,
Push notifications burn so bright!
Hono and Svix join the fray—
Let notifications have their day! ✉️⚡

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding a Cloudflare Worker for handling Resend webhook events and sending Expo push notifications.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/analyze-email-webhook-zXaBA

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app.config.ts (1)

77-82: ⚠️ Potential issue | 🔴 Critical

Remove WORKER_API_KEY from Expo extra and implement proper server-side authentication.

Values placed under extra in the app config are bundled into the public manifest and readable at runtime via Constants.expoConfig.extra. This exposes the /api/push-token bearer token as a static secret in the client, allowing anyone who extracts the app to register arbitrary Expo tokens and access email notification metadata. Implement real server-side authentication instead of embedding secrets in the client config.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app.config.ts` around lines 77 - 82, Remove the workerApiKey entry from the
Expo app config extra block (the extra.eas.workerApiKey / workerApiKey symbol)
so the secret is not bundled into Constants.expoConfig.extra; instead, store the
secret only on the server (env var used by server-side code) and change the
/api/push-token endpoint handler (the route that registers push tokens) to
authenticate incoming requests server-side using proper auth (e.g., user
session/JWT or an API key validated against server-only env var) rather than
trusting a client-supplied bearer token extracted from the app bundle; rotate
the secret and update any server code that currently reads
expoConfig.extra.workerApiKey to read process.env on the server and reject
unauthenticated requests.
🧹 Nitpick comments (2)
worker/src/webhooks.ts (1)

19-25: Unchecked type cast may cause runtime errors if payload shape differs.

The wh.verify() return is cast directly to EmailReceivedEvent without runtime validation. If Resend sends an unexpected event type or malformed payload, downstream code accessing event.data.email_id etc. will fail with unclear errors.

Consider validating the payload structure before returning, or at minimum check that required fields exist.

♻️ Suggested validation approach
  try {
-   const event = wh.verify(payload, {
+   const raw = wh.verify(payload, {
      'svix-id': svixId,
      'svix-timestamp': svixTimestamp,
      'svix-signature': svixSignature,
-   }) as EmailReceivedEvent;
+   });
+   
+   // Basic runtime validation
+   if (
+     typeof raw !== 'object' ||
+     raw === null ||
+     typeof (raw as Record<string, unknown>).type !== 'string' ||
+     typeof (raw as Record<string, unknown>).data !== 'object'
+   ) {
+     throw new WebhookError('Invalid webhook payload structure', 400);
+   }
+   
+   const event = raw as EmailReceivedEvent;
    return event;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@worker/src/webhooks.ts` around lines 19 - 25, The code casts the result of
wh.verify(...) directly to EmailReceivedEvent which can blow up if the payload
shape differs; add a runtime check (e.g., implement an isEmailReceivedEvent type
guard) that verifies event.type is the expected value (like 'email.received')
and required fields exist (e.g., event.data and event.data.email_id) before
returning; if validation fails, throw a clear error or return a safe fallback
value and update any callers of the function that assume EmailReceivedEvent to
handle the validated/guarded result.
worker/src/push.ts (1)

58-60: Silent failure on push send may warrant alerting.

When response.ok is false, the error is logged but the function returns void. For a production system, consider whether failed push notifications should trigger alerts or be tracked in metrics, especially if they indicate misconfiguration (invalid token, expired access token, etc.).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@worker/src/push.ts` around lines 58 - 60, The code currently logs failed push
responses but returns void; update the push-sending function (e.g.,
sendPushNotification / the function that awaits response) to surface failures:
when response.ok is false, capture response.text() and either throw a
descriptive Error (including status and body) or return an explicit failure
result (boolean or Result object), and also emit/record a metric or alert (e.g.,
increment a push_failure counter or call alerting/monitoring API) so production
failures are tracked; ensure you reference the existing response and response.ok
checks and preserve the logged message while adding the thrown error/returned
failure and the metrics/alerting call.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@hooks/useNotifications.ts`:
- Around line 106-121: The current insertEmails call only runs inside
Notifications.addNotificationReceivedListener (notificationListener.current) so
notifications tapped from the tray (handled via getLastNotificationResponse()
and addNotificationResponseReceivedListener()) never persist; extract the insert
logic into a small helper (e.g. persistNotificationEmail(data) or
persistNotificationIfEmail) and call it from the existing notification-received
listener, from handleNotificationResponse, and when processing
getLastNotificationResponse()/addNotificationResponseReceivedListener() flows so
tapped notifications also create the SQLite row; reference
notificationListener.current, Notifications.addNotificationReceivedListener,
getLastNotificationResponse, addNotificationResponseReceivedListener,
handleNotificationResponse, and insertEmails when updating the code.
- Around line 59-70: The current fire-and-forget fetch to
`${workerUrl}/api/push-token` treats any HTTP error as success; update the
registration logic (the fetch call that uses workerUrl and workerApiKey) to
await the response, check response.ok (or specific status codes), and handle
non-OK responses by logging the response status/body and optionally throwing or
retrying; ensure errors are caught and surfaced (replace the current .catch-only
approach with an async try/catch around await fetch and parse error details from
the response for more actionable diagnostics).

In `@worker/src/index.ts`:
- Around line 13-18: The call to c.req.json() can throw on malformed JSON
leading to a 500; wrap the JSON parse in a try/catch inside the async request
handler (the anonymous async (c) function) and on a SyntaxError or parse failure
return c.json({ error: 'Invalid JSON' }, 400); then continue to validate
body.token with isExpoPushToken as before so malformed requests produce a 400
instead of an unhandled exception.

In `@worker/src/push.ts`:
- Around line 27-30: The error log in push.ts currently prints the full push
token when isExpoPushToken(token) fails; change the logging in the early-return
block inside the function that validates tokens (the isExpoPushToken check) to
avoid exposing the full token by either logging a masked/truncated version
(e.g., show first 4 and last 4 chars) or a generic message like "Invalid Expo
push token" with no token payload, and keep the existing console.error
call/context so the function still returns early on invalid tokens.

---

Outside diff comments:
In `@app.config.ts`:
- Around line 77-82: Remove the workerApiKey entry from the Expo app config
extra block (the extra.eas.workerApiKey / workerApiKey symbol) so the secret is
not bundled into Constants.expoConfig.extra; instead, store the secret only on
the server (env var used by server-side code) and change the /api/push-token
endpoint handler (the route that registers push tokens) to authenticate incoming
requests server-side using proper auth (e.g., user session/JWT or an API key
validated against server-only env var) rather than trusting a client-supplied
bearer token extracted from the app bundle; rotate the secret and update any
server code that currently reads expoConfig.extra.workerApiKey to read
process.env on the server and reject unauthenticated requests.

---

Nitpick comments:
In `@worker/src/push.ts`:
- Around line 58-60: The code currently logs failed push responses but returns
void; update the push-sending function (e.g., sendPushNotification / the
function that awaits response) to surface failures: when response.ok is false,
capture response.text() and either throw a descriptive Error (including status
and body) or return an explicit failure result (boolean or Result object), and
also emit/record a metric or alert (e.g., increment a push_failure counter or
call alerting/monitoring API) so production failures are tracked; ensure you
reference the existing response and response.ok checks and preserve the logged
message while adding the thrown error/returned failure and the metrics/alerting
call.

In `@worker/src/webhooks.ts`:
- Around line 19-25: The code casts the result of wh.verify(...) directly to
EmailReceivedEvent which can blow up if the payload shape differs; add a runtime
check (e.g., implement an isEmailReceivedEvent type guard) that verifies
event.type is the expected value (like 'email.received') and required fields
exist (e.g., event.data and event.data.email_id) before returning; if validation
fails, throw a clear error or return a safe fallback value and update any
callers of the function that assume EmailReceivedEvent to handle the
validated/guarded result.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: c4212cda-2fa3-46e5-b4c6-226b2265ddb8

📥 Commits

Reviewing files that changed from the base of the PR and between 8ea99cd and 931f075.

⛔ Files ignored due to path filters (1)
  • worker/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • .env.example
  • .gitignore
  • app.config.ts
  • db/syncEngine.ts
  • hooks/useNotifications.ts
  • worker/package.json
  • worker/src/index.ts
  • worker/src/push.ts
  • worker/src/types.ts
  • worker/src/webhooks.ts
  • worker/tsconfig.json
  • worker/wrangler.toml

Comment on lines +59 to +70
// Register push token with the Cloudflare Worker
const workerUrl = process.env.EXPO_PUBLIC_WORKER_URL;
const workerApiKey = Constants.expoConfig?.extra?.workerApiKey;
if (workerUrl) {
fetch(`${workerUrl}/api/push-token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(workerApiKey && { Authorization: `Bearer ${workerApiKey}` }),
},
body: JSON.stringify({ token }),
}).catch((err) => console.warn('Failed to register push token with server:', err));
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 | 🟠 Major

Check /api/push-token responses instead of fire-and-forget.

fetch only rejects on transport failures. A 400/401/500 from the worker is currently treated as success, so push registration can fail silently until the next app launch.

💡 Proposed fix
   if (workerUrl) {
-    fetch(`${workerUrl}/api/push-token`, {
-      method: 'POST',
-      headers: {
-        'Content-Type': 'application/json',
-        ...(workerApiKey && { Authorization: `Bearer ${workerApiKey}` }),
-      },
-      body: JSON.stringify({ token }),
-    }).catch((err) => console.warn('Failed to register push token with server:', err));
+    try {
+      const response = await fetch(`${workerUrl}/api/push-token`, {
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json',
+          ...(workerApiKey && { Authorization: `Bearer ${workerApiKey}` }),
+        },
+        body: JSON.stringify({ token }),
+      });
+      if (!response.ok) {
+        console.warn(
+          'Failed to register push token with server:',
+          response.status,
+          await response.text(),
+        );
+      }
+    } catch (err) {
+      console.warn('Failed to register push token with server:', err);
+    }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Register push token with the Cloudflare Worker
const workerUrl = process.env.EXPO_PUBLIC_WORKER_URL;
const workerApiKey = Constants.expoConfig?.extra?.workerApiKey;
if (workerUrl) {
fetch(`${workerUrl}/api/push-token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(workerApiKey && { Authorization: `Bearer ${workerApiKey}` }),
},
body: JSON.stringify({ token }),
}).catch((err) => console.warn('Failed to register push token with server:', err));
// Register push token with the Cloudflare Worker
const workerUrl = process.env.EXPO_PUBLIC_WORKER_URL;
const workerApiKey = Constants.expoConfig?.extra?.workerApiKey;
if (workerUrl) {
try {
const response = await fetch(`${workerUrl}/api/push-token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(workerApiKey && { Authorization: `Bearer ${workerApiKey}` }),
},
body: JSON.stringify({ token }),
});
if (!response.ok) {
console.warn(
'Failed to register push token with server:',
response.status,
await response.text(),
);
}
} catch (err) {
console.warn('Failed to register push token with server:', err);
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/useNotifications.ts` around lines 59 - 70, The current fire-and-forget
fetch to `${workerUrl}/api/push-token` treats any HTTP error as success; update
the registration logic (the fetch call that uses workerUrl and workerApiKey) to
await the response, check response.ok (or specific status codes), and handle
non-OK responses by logging the response status/body and optionally throwing or
retrying; ensure errors are caught and surfaced (replace the current .catch-only
approach with an async try/catch around await fetch and parse error details from
the response for more actionable diagnostics).

Comment on lines +106 to +121
notificationListener.current = Notifications.addNotificationReceivedListener((notification) => {
const data = notification.request.content.data;
if (data?.emailId && typeof data.emailId === 'string') {
const parsed = data.createdAt ? parseDate(data.createdAt as string) : null;
const ms = parsed ? parsed.getTime() : Date.now();
const createdDate = parsed ? toLocalDateString(parsed) : toLocalDateString(new Date());
insertEmails([{
id: data.emailId,
from_address: (data.from as string) || '',
subject: (data.subject as string) || '(No subject)',
snippet: '',
created_date: createdDate,
created_at_ms: ms,
is_read: 0,
}]);
}
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 | 🟠 Major

Persist tapped notifications too.

This insert only runs in the notification-received path. The cold-start and tap flows (getLastNotificationResponse() and addNotificationResponseReceivedListener()) still go straight to handleNotificationResponse(), so emails opened from the tray never get the SQLite row this feature depends on until a later sync.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/useNotifications.ts` around lines 106 - 121, The current insertEmails
call only runs inside Notifications.addNotificationReceivedListener
(notificationListener.current) so notifications tapped from the tray (handled
via getLastNotificationResponse() and addNotificationResponseReceivedListener())
never persist; extract the insert logic into a small helper (e.g.
persistNotificationEmail(data) or persistNotificationIfEmail) and call it from
the existing notification-received listener, from handleNotificationResponse,
and when processing
getLastNotificationResponse()/addNotificationResponseReceivedListener() flows so
tapped notifications also create the SQLite row; reference
notificationListener.current, Notifications.addNotificationReceivedListener,
getLastNotificationResponse, addNotificationResponseReceivedListener,
handleNotificationResponse, and insertEmails when updating the code.

Comment on lines +13 to +18
async (c) => {
const body = await c.req.json<{ token?: string }>();

if (!body.token || !isExpoPushToken(body.token)) {
return c.json({ error: 'Invalid Expo push token' }, 400);
}
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 | 🟡 Minor

JSON parsing can throw on malformed requests.

c.req.json() throws if the request body is not valid JSON, resulting in an unhandled error that would return a 500 instead of a more appropriate 400.

🛡️ Suggested fix with error handling
  async (c) => {
-   const body = await c.req.json<{ token?: string }>();
+   let body: { token?: string };
+   try {
+     body = await c.req.json<{ token?: string }>();
+   } catch {
+     return c.json({ error: 'Invalid JSON body' }, 400);
+   }

    if (!body.token || !isExpoPushToken(body.token)) {
      return c.json({ error: 'Invalid Expo push token' }, 400);
    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async (c) => {
const body = await c.req.json<{ token?: string }>();
if (!body.token || !isExpoPushToken(body.token)) {
return c.json({ error: 'Invalid Expo push token' }, 400);
}
async (c) => {
let body: { token?: string };
try {
body = await c.req.json<{ token?: string }>();
} catch {
return c.json({ error: 'Invalid JSON body' }, 400);
}
if (!body.token || !isExpoPushToken(body.token)) {
return c.json({ error: 'Invalid Expo push token' }, 400);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@worker/src/index.ts` around lines 13 - 18, The call to c.req.json() can throw
on malformed JSON leading to a 500; wrap the JSON parse in a try/catch inside
the async request handler (the anonymous async (c) function) and on a
SyntaxError or parse failure return c.json({ error: 'Invalid JSON' }, 400); then
continue to validate body.token with isExpoPushToken as before so malformed
requests produce a 400 instead of an unhandled exception.

Comment on lines +27 to +30
if (!isExpoPushToken(token)) {
console.error('Invalid Expo push token:', token);
return;
}
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 | 🟡 Minor

Avoid logging sensitive token values.

Logging the full push token in error scenarios could expose sensitive device identifiers in production logs. Consider logging only a truncated portion or just indicating an invalid token was found.

🛡️ Suggested fix
  if (!isExpoPushToken(token)) {
-   console.error('Invalid Expo push token:', token);
+   console.error('Invalid Expo push token format detected');
    return;
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!isExpoPushToken(token)) {
console.error('Invalid Expo push token:', token);
return;
}
if (!isExpoPushToken(token)) {
console.error('Invalid Expo push token format detected');
return;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@worker/src/push.ts` around lines 27 - 30, The error log in push.ts currently
prints the full push token when isExpoPushToken(token) fails; change the logging
in the early-return block inside the function that validates tokens (the
isExpoPushToken check) to avoid exposing the full token by either logging a
masked/truncated version (e.g., show first 4 and last 4 chars) or a generic
message like "Invalid Expo push token" with no token payload, and keep the
existing console.error call/context so the function still returns early on
invalid tokens.

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.

2 participants