Skip to content

feat: implement PWA offline mode with IndexedDB queue and sync manager#71

Open
SaumyaSngh323 wants to merge 3 commits into
jpdevhub:mainfrom
SaumyaSngh323:feat/pwa-offline-mode
Open

feat: implement PWA offline mode with IndexedDB queue and sync manager#71
SaumyaSngh323 wants to merge 3 commits into
jpdevhub:mainfrom
SaumyaSngh323:feat/pwa-offline-mode

Conversation

@SaumyaSngh323

@SaumyaSngh323 SaumyaSngh323 commented Jun 3, 2026

Copy link
Copy Markdown

Description

Checklist

  • npm run lint passes with no errors
  • npm run build compiles without TypeScript errors
  • python -m pytest passes (including new tests I added)
  • No .env files, API keys, secrets, model weights, or __pycache__ in this diff
  • Branch is rebased on main, not merged

Closes #6

Summary by CodeRabbit

  • New Features
    • Added offline scan queueing—scans initiated while offline are now stored and automatically synced to the server when connectivity is restored.
    • Enhanced Progressive Web App capabilities with improved caching and manifest configuration.
    • Implemented automatic theme initialization on app startup.

@vercel

vercel Bot commented Jun 3, 2026

Copy link
Copy Markdown

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

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

🎉 Thank you for your Pull Request! We're thrilled to have your contribution to FreshScan AI.

Before we review, please make sure you have:

  • Followed the CONTRIBUTING.md guidelines.
  • Ensured all automated CI checks (linting, tests) are passing.
  • Checked that your commit messages follow the Conventional Commits format.

A maintainer will review your code as soon as possible!

@vercel

vercel Bot commented Jun 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
fresh-scan-ai Ready Ready Preview, Comment Jun 6, 2026 9:44am

@jpdevhub

jpdevhub commented Jun 6, 2026

Copy link
Copy Markdown
Owner

Thier are many error in the code first git pull as many backend new code are been added due to the onnx update then fix some listed issue then i will review the code:
Service Worker Crash: Overwrote vite.config.ts, completely removing the globIgnores fix we added recently! If merged, the app will try to precache the 26MB .wasm file again and crash.
Fake API Endpoint: Used an API endpoint (/api/scans) that doesn't exist on your backend.
Invalid Data Format: They are trying to use JSON.stringify() to send images, but your backend requires multipart/form-data for file uploads.
Disconnected Code: The queue isn't actually wired up to the ScannerPage.tsx interface.

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid input: expected object, received boolean at "reviews.auto_review"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
📝 Walkthrough

Walkthrough

This PR implements PWA offline-first scanning by adding IndexedDB-backed scan queue storage, automatic sync-on-online logic, app startup integration, and PWA manifest and workbox caching configuration to support offline capture and deferred upload workflows.

Changes

PWA Offline Scanning and Sync

Layer / File(s) Summary
Offline Scan Queue Storage
src/utils/offlineQueue.ts
IndexedDB module with FreshScanDB database and scanQueue store. Exports openDB(), queueScan(), getPendingScans(), and removeScan() to persist scans locally with timestamp metadata.
Sync Manager and Online Handler
src/utils/syncManager.ts
Exports syncOfflineScans() that loads pending scans from IndexedDB, POSTs each to /api/scans, removes successful uploads from queue, and logs per-scan failures. Registers window online event listener to trigger sync when connectivity returns.
App Startup Sync Integration
src/main.tsx
Imports and invokes syncOfflineScans() during app initialization after theme setup and before app render, ensuring pending scans sync on each launch.
PWA Configuration and Workbox Caching
vite.config.ts
Configures VitePWA plugin with explicit manifest (app name, description, icons, colors), includes static assets, and adds workbox NetworkFirst runtime caching for /api requests with api-cache expiration policy. Updates dev proxy for /api to http://localhost:8000.
PWA and Workbox Dependencies
package.json
Adds vite-plugin-pwa, workbox-window to dependencies, and @vite-pwa/assets-generator to devDependencies alongside tailwindcss.

Sequence Diagram

sequenceDiagram
  participant Browser as Browser/App
  participant IndexedDB as IndexedDB<br/>(scanQueue)
  participant SyncMgr as syncOfflineScans()
  participant API as /api/scans

  Browser->>IndexedDB: Store scan offline<br/>(queueScan)
  Browser->>Browser: Come online
  Browser->>SyncMgr: online event fired
  SyncMgr->>IndexedDB: Load pending scans<br/>(getPendingScans)
  IndexedDB->>SyncMgr: Return scan array
  SyncMgr->>API: POST scan
  API->>SyncMgr: 200 OK + response
  SyncMgr->>IndexedDB: Remove synced scan<br/>(removeScan)
  SyncMgr->>Browser: Continue sync loop
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A queue beneath the ground so deep,
Where scans in IndexedDB sleep,
When signals dance and networks wake,
The sync does upload for market's sake!
Offline no more—to cloud they flee,
With PWA and workbox spree! 📦✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR addresses core PWA requirements (vite-plugin-pwa config, Service Worker caching, IndexedDB queue, sync mechanism) but multiple critical issues were identified: Service Worker crash risk from large .wasm file, non-existent API endpoint, invalid multipart/form-data handling, and missing UI integration. Fix Service Worker caching configuration to exclude large .wasm files, use valid backend API endpoint, implement multipart/form-data for file uploads, and wire offline queue integration to ScannerPage.tsx.
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 (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main changes: PWA offline mode implementation using IndexedDB queue and sync manager for offline scan storage and synchronization.
Out of Scope Changes check ✅ Passed Changes focus on PWA offline features (vite-plugin-pwa config, IndexedDB queue, sync manager) with package.json and build config updates supporting these objectives; no unrelated scope creep detected.

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

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

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Biome (2.4.16)
vite.config.ts

File contains syntax errors that prevent linting: Line 87: Expected a semicolon or an implicit semicolon after a statement, but found none; Line 92: Expected a semicolon or an implicit semicolon after a statement, but found none; Line 94: Expected a semicolon or an implicit semicolon after a statement, but found none; Line 95: Expected a statement but instead found ','.; Line 96: Expected a statement but instead found ','.; Line 97: Expected a statement but instead found ',
})
})'.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/main.tsx

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

src/utils/offlineQueue.ts

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

src/utils/syncManager.ts

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

  • 1 others

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.

@SaumyaSngh323

Copy link
Copy Markdown
Author

Hi @jpdevhub, I have resolved the merge conflicts and updated the PR. Could you please review it again? Thank you.

@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: 10

Caution

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

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

1-8: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Duplicate import statements cause syntax error.

Lines 1-4 declare imports without semicolons, then lines 5-8 repeat the exact same imports with semicolons. This duplicate code will cause a build failure.

The old import statements (lines 5-8) were not removed when the new ones (lines 1-4) were added.

🔧 Proposed fix to remove duplicate imports
 import { defineConfig } from 'vite'
 import react from '`@vitejs/plugin-react`'
 import tailwindcss from '`@tailwindcss/vite`'
 import { VitePWA } from 'vite-plugin-pwa'
-
-import { defineConfig } from 'vite';
-import react from '`@vitejs/plugin-react`';
-import tailwindcss from '`@tailwindcss/vite`';
-import { VitePWA } from 'vite-plugin-pwa';
-
🤖 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 `@vite.config.ts` around lines 1 - 8, Remove the duplicate import block so each
module is imported only once: keep a single set of imports for defineConfig,
react, tailwindcss, and VitePWA in vite.config.ts (refer to the symbols
defineConfig, react, tailwindcss, VitePWA) and delete the redundant repeated
import statements to eliminate the syntax error.
🧹 Nitpick comments (1)
src/main.tsx (1)

14-14: 💤 Low value

Consider adding error handling for startup sync.

syncOfflineScans() is async but called without await or .catch(). While the function has internal error handling, adding a .catch() here would defensively guard against unexpected startup failures.

🛡️ Optional: Add defensive error handling
-syncOfflineScans()
+syncOfflineScans().catch(err => console.error('Startup sync failed:', err))
🤖 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 `@src/main.tsx` at line 14, syncOfflineScans() is called without awaiting or
handling rejections; wrap the startup call to syncOfflineScans so any
thrown/rejected error is caught (either await it inside an async startup
function or append .catch(...) to the returned promise) and log/report the error
(e.g., console.error or the app logger) to prevent unhandled promise rejections
during app boot; update the call site where syncOfflineScans() is invoked to use
await or .catch and ensure errors are surfaced.
🤖 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 `@package.json`:
- Around line 33-35: Remove duplicated build-time packages from the
"dependencies" block: delete "vite-plugin-pwa", "workbox-window" and
"tailwindcss" entries so they only exist under "devDependencies" (keep the
existing devDependencies entries for vite-plugin-pwa and workbox-window), and
verify the versions in "devDependencies" match the intended versions; update
package.json accordingly and run an install to ensure no remaining duplication.

In `@src/main.tsx`:
- Around line 7-9: There is a duplicate import of initTheme in the module;
remove one of the import statements so initTheme is only imported once (remove
either the first or the second import line that references initTheme) and ensure
any other imports like syncOfflineScans remain unchanged; update the import
block that contains initTheme to have a single import declaration for initTheme
from './lib/theme'.

In `@src/utils/offlineQueue.ts`:
- Around line 26-34: Change getPendingScans to return the concrete
ScanQueueItem[] type instead of Record<string, unknown>[]. Update its Promise
generic and the onsuccess cast to use ScanQueueItem so the function signature
and resolved value match the queueScan item interface; locate getPendingScans,
ScanQueueItem, queueScan, STORE_NAME and openDB in this file and replace the
loose Record typing with ScanQueueItem throughout the function.
- Around line 16-24: Define a concrete ScanQueueItem interface matching the scan
submission contract (include image: Blob, required fields from src/lib/api.ts
lines 105–121, and optional edge inference metadata fields) and replace the
loose Record<string, unknown> parameter in queueScan with this ScanQueueItem
type; ensure the object passed into tx.objectStore(STORE_NAME).add(...) uses the
ScanQueueItem shape (including timestamp) so the sync uploader can rely on typed
fields and callers have clear compile-time guidance.

In `@src/utils/syncManager.ts`:
- Around line 9-17: The uploadScan function is calling the wrong endpoint and
sending JSON (which cannot include Blob images); change it to POST to
/api/v1/scan-auto, build a FormData instance, append the ScanItem image Blob
(use the same field name the backend expects, e.g., "image") and append other
scalar fields as strings, do NOT set the Content-Type header (let the browser
set multipart boundaries), then await the fetch response, throw with the
response body or status on non-ok, and return the parsed JSON; update references
to uploadScan and ScanItem accordingly.

In `@vite.config.ts`:
- Around line 15-54: There are two VitePWA(...) plugin calls causing a syntax
error and the new one overwrote important runtimeCaching rules; remove the
duplicate plugin and merge the old runtimeCaching entries into the single
VitePWA call (keep the existing api NetworkFirst rule and add back CacheFirst
rules for large assets like .onnx and .wasm with appropriate
cacheName/expiration settings), ensure globPatterns and globIgnores are updated
so large model/wasm files are not precached (use globIgnores to exclude *.wasm
and large model files) and keep any asset types you need (e.g., .woff2/.gif) in
includeAssets/globPatterns as appropriate; locate and update the VitePWA
invocation and its runtimeCaching, globPatterns, and globIgnores blocks to
perform this merge and remove the old duplicate call.
- Around line 17-37: The config currently defines an inline PWA manifest object
(manifest with name/short_name/description/icons, referenced alongside
includeAssets) while also keeping the old override manifest: false; remove the
stale override (the manifest: false entry) so the inline manifest object is
used; search for the manifest: false setting in the Vite config and delete it,
leaving the manifest object (and includeAssets) intact.
- Around line 98-99: The file contains duplicate closing braces at the end of
the Vite configuration; remove the extra closing brace so the exported config
block (the object passed to defineConfig / the export default) is properly
closed only once; locate the export default / defineConfig block in
vite.config.ts and delete the redundant closing brace and any stray semicolon so
the object and surrounding parentheses/braces match correctly.
- Line 42: The runtimeCaching rule's urlPattern in vite.config.ts currently uses
the hostname-bound regex /^https?:\/\/localhost:8000\/api\// which only matches
local dev requests; update the urlPattern used in the service worker
runtimeCaching entry (the urlPattern field) to a host-agnostic pattern that
matches your API path in production (for example a relative-path regex like
/^\/api\// or /\/api\//) so /api/* requests are cached regardless of hostname.
- Around line 92-96: The syncManager currently POSTs JSON to '/api/scans' which
the backend doesn't accept; update the upload logic in src/utils/syncManager.ts
(the function that builds and sends the offline scan request) to POST multipart
FormData to the correct backend routes (/api/v1/scan for vendor uploads
requiring body_image/eye_image/gill_image plus vendor_id and is_target_domain,
or /api/v1/scan-auto for single-image auto uploads). Build a FormData instance
instead of JSON.stringify(scan), append the file fields using the original
File/Blob objects (keys: body_image, eye_image, gill_image or image), append
vendor_id and is_target_domain for /api/v1/scan, remove explicit Content-Type so
the browser sets the multipart boundary, and update the fetch URL to use
'/api/v1/scan' or '/api/v1/scan-auto' accordingly and handle the response/errors
as before.

---

Outside diff comments:
In `@vite.config.ts`:
- Around line 1-8: Remove the duplicate import block so each module is imported
only once: keep a single set of imports for defineConfig, react, tailwindcss,
and VitePWA in vite.config.ts (refer to the symbols defineConfig, react,
tailwindcss, VitePWA) and delete the redundant repeated import statements to
eliminate the syntax error.

---

Nitpick comments:
In `@src/main.tsx`:
- Line 14: syncOfflineScans() is called without awaiting or handling rejections;
wrap the startup call to syncOfflineScans so any thrown/rejected error is caught
(either await it inside an async startup function or append .catch(...) to the
returned promise) and log/report the error (e.g., console.error or the app
logger) to prevent unhandled promise rejections during app boot; update the call
site where syncOfflineScans() is invoked to use await or .catch and ensure
errors are surfaced.
🪄 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 Plus

Run ID: 4fa144d4-7d59-44e5-a7aa-0a6908973b6c

📥 Commits

Reviewing files that changed from the base of the PR and between a621093 and f212f88.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (5)
  • package.json
  • src/main.tsx
  • src/utils/offlineQueue.ts
  • src/utils/syncManager.ts
  • vite.config.ts

Comment thread package.json
Comment thread src/main.tsx
Comment on lines +7 to 9
import { initTheme } from './lib/theme';
import { syncOfflineScans } from './utils/syncManager'
import { initTheme } from './lib/theme'

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 | ⚡ Quick win

Remove duplicate import.

initTheme is imported twice on lines 7 and 9. This will likely cause a build or linting error.

🔧 Fix: Remove the duplicate import
 import { initTheme } from './lib/theme';
 import { syncOfflineScans } from './utils/syncManager'
-import { initTheme } from './lib/theme'
 import "./i18n";
📝 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
import { initTheme } from './lib/theme';
import { syncOfflineScans } from './utils/syncManager'
import { initTheme } from './lib/theme'
import { initTheme } from './lib/theme';
import { syncOfflineScans } from './utils/syncManager'
import "./i18n";
🤖 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 `@src/main.tsx` around lines 7 - 9, There is a duplicate import of initTheme in
the module; remove one of the import statements so initTheme is only imported
once (remove either the first or the second import line that references
initTheme) and ensure any other imports like syncOfflineScans remain unchanged;
update the import block that contains initTheme to have a single import
declaration for initTheme from './lib/theme'.

Comment thread src/utils/offlineQueue.ts
Comment thread src/utils/offlineQueue.ts
Comment on lines +26 to +34
export async function getPendingScans() {
const db = await openDB()
return new Promise<Record<string, unknown>[]>((resolve, reject) => {
const tx = db.transaction(STORE_NAME, 'readonly')
const req = tx.objectStore(STORE_NAME).getAll()
req.onsuccess = () => resolve(req.result as Record<string, unknown>[])
req.onerror = () => reject(req.error)
})
}

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 | ⚡ Quick win

Return type should match the queue item interface.

The return type Record<string, unknown>[] is too loose and should use the same ScanQueueItem interface suggested for queueScan. This ensures type safety across the entire queue lifecycle.

📝 Proposed type fix
-export async function getPendingScans() {
+export async function getPendingScans(): Promise<(ScanQueueItem & { id: number; timestamp: number })[]> {
   const db = await openDB()
-  return new Promise<Record<string, unknown>[]>((resolve, reject) => {
+  return new Promise((resolve, reject) => {
     const tx = db.transaction(STORE_NAME, 'readonly')
     const req = tx.objectStore(STORE_NAME).getAll()
-    req.onsuccess = () => resolve(req.result as Record<string, unknown>[])
+    req.onsuccess = () => resolve(req.result)
     req.onerror = () => reject(req.error)
   })
 }
🤖 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 `@src/utils/offlineQueue.ts` around lines 26 - 34, Change getPendingScans to
return the concrete ScanQueueItem[] type instead of Record<string, unknown>[].
Update its Promise generic and the onsuccess cast to use ScanQueueItem so the
function signature and resolved value match the queueScan item interface; locate
getPendingScans, ScanQueueItem, queueScan, STORE_NAME and openDB in this file
and replace the loose Record typing with ScanQueueItem throughout the function.

Comment thread src/utils/syncManager.ts
Comment thread vite.config.ts
Comment on lines 15 to +54
VitePWA({
registerType: 'autoUpdate',
includeAssets: ['favicon.ico', 'apple-touch-icon.png'],
manifest: {
name: 'FreshScan AI',
short_name: 'FreshScan',
description: 'AI-powered fish freshness scanner',
theme_color: '#ffffff',
background_color: '#ffffff',
display: 'standalone',
icons: [
{
src: 'pwa-192x192.png',
sizes: '192x192',
type: 'image/png'
},
{
src: 'pwa-512x512.png',
sizes: '512x512',
type: 'image/png'
}
]
},
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],
runtimeCaching: [
{
urlPattern: /^https?:\/\/localhost:8000\/api\//,
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
expiration: {
maxEntries: 50,
maxAgeSeconds: 60 * 60 * 24
}
}
}
]
}
})

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 | 🏗️ Heavy lift

Critical: VitePWA plugin configured twice — incomplete merge removed critical caching rules.

The PR adds a new VitePWA configuration (lines 15-54) but fails to remove the old configuration (lines 56-87). This creates:

  1. Syntax error: Two VitePWA() calls within the same plugins array
  2. Lost runtime caching: The old config (lines 66-85) includes critical CacheFirst rules for .onnx model files (~12-26 MB) and .wasm files. The new config (lines 40-52) only caches /api/ requests, completely removing model/wasm caching.
  3. Service worker crash: Without runtime caching for large model files, the service worker will attempt to precache them and timeout/crash (as flagged by the PR reviewer).

This directly confirms the reviewer's concern: "The PR reportedly overwrote vite.config.ts and removed a recent globIgnores fix. If merged, the app would attempt to precache a large .wasm file (~26MB) and crash."

🔧 Proposed fix to merge configurations correctly

Remove the duplicate old configuration and merge the runtime caching rules:

     VitePWA({
       registerType: 'autoUpdate',
       includeAssets: ['favicon.ico', 'apple-touch-icon.png'],
       manifest: {
         name: 'FreshScan AI',
         short_name: 'FreshScan',
         description: 'AI-powered fish freshness scanner',
         theme_color: '`#ffffff`',
         background_color: '`#ffffff`',
         display: 'standalone',
         icons: [
           {
             src: 'pwa-192x192.png',
             sizes: '192x192',
             type: 'image/png'
           },
           {
             src: 'pwa-512x512.png',
             sizes: '512x512',
             type: 'image/png'
           }
         ]
       },
       workbox: {
-        globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],
+        globPatterns: ['**/*.{js,css,html,ico,svg,gif,png}'],
         runtimeCaching: [
+          {
+            urlPattern: /\/models\/.*\.onnx$/i,
+            handler: 'CacheFirst',
+            options: {
+              cacheName: 'onnx-models',
+              expiration: { maxAgeSeconds: 30 * 24 * 60 * 60 },
+              cacheableResponse: { statuses: [0, 200] },
+            },
+          },
+          {
+            urlPattern: /.*\.wasm$/i,
+            handler: 'CacheFirst',
+            options: {
+              cacheName: 'wasm-cache',
+              expiration: { maxAgeSeconds: 30 * 24 * 60 * 60 },
+              cacheableResponse: { statuses: [0, 200] },
+            },
+          },
           {
             urlPattern: /^https?:\/\/localhost:8000\/api\//,
             handler: 'NetworkFirst',
             options: {
               cacheName: 'api-cache',
               expiration: {
                 maxEntries: 50,
                 maxAgeSeconds: 60 * 60 * 24
               }
             }
           }
         ]
       }
     })
   ],
-
-      // Use the existing manifest.json in public/
-      manifest: false,
-      workbox: {
-        // Precache ONLY small app-shell assets (JS/CSS/HTML/icons).
-        // DO NOT include .wasm or .onnx here — they are large (12–26 MB each)
-        // and would cause the Service Worker install to time out.
-        // They are handled below with lazy runtime caching instead.
-        globPatterns: ['**/*.{js,css,html,ico,svg,gif,png}'],
-        // Runtime caching: WASM and ONNX files are cached on first use
-        // (CacheFirst) so subsequent offline scans load instantly.
-        runtimeCaching: [
-          {
-            urlPattern: /\/models\/.*\.onnx$/i,
-            handler: 'CacheFirst',
-            options: {
-              cacheName: 'onnx-models',
-              expiration: { maxAgeSeconds: 30 * 24 * 60 * 60 }, // 30 days
-              cacheableResponse: { statuses: [0, 200] },
-            },
-          },
-          {
-            urlPattern: /.*\.wasm$/i,
-            handler: 'CacheFirst',
-            options: {
-              cacheName: 'wasm-cache',
-              expiration: { maxAgeSeconds: 30 * 24 * 60 * 60 },
-              cacheableResponse: { statuses: [0, 200] },
-            },
-          },
-        ],
-      },
-    }),
-  ],
-

Note: Also exclude .woff2 from globPatterns if font files are large, and add .gif (for favicon) as shown in the old config.

Also applies to: 56-87

🤖 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 `@vite.config.ts` around lines 15 - 54, There are two VitePWA(...) plugin calls
causing a syntax error and the new one overwrote important runtimeCaching rules;
remove the duplicate plugin and merge the old runtimeCaching entries into the
single VitePWA call (keep the existing api NetworkFirst rule and add back
CacheFirst rules for large assets like .onnx and .wasm with appropriate
cacheName/expiration settings), ensure globPatterns and globIgnores are updated
so large model/wasm files are not precached (use globIgnores to exclude *.wasm
and large model files) and keep any asset types you need (e.g., .woff2/.gif) in
includeAssets/globPatterns as appropriate; locate and update the VitePWA
invocation and its runtimeCaching, globPatterns, and globIgnores blocks to
perform this merge and remove the old duplicate call.

Comment thread vite.config.ts
Comment thread vite.config.ts
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],
runtimeCaching: [
{
urlPattern: /^https?:\/\/localhost:8000\/api\//,

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 | ⚡ Quick win

Runtime caching pattern only matches localhost development requests.

The urlPattern at line 42 uses ^https?:\/\/localhost:8000\/api\/ which will only match requests during local development. In production, API requests won't go to localhost:8000, so this cache rule will never apply.

You need to use a pattern that matches production API URLs or use a relative path pattern.

♻️ Proposed fix to cache production API requests
-            urlPattern: /^https?:\/\/localhost:8000\/api\//,
+            urlPattern: ({ url }) => url.pathname.startsWith('/api/'),
             handler: 'NetworkFirst',

This will match /api/* requests regardless of the hostname (localhost or production domain).

📝 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
urlPattern: /^https?:\/\/localhost:8000\/api\//,
urlPattern: ({ url }) => url.pathname.startsWith('/api/'),
handler: 'NetworkFirst',
🤖 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 `@vite.config.ts` at line 42, The runtimeCaching rule's urlPattern in
vite.config.ts currently uses the hostname-bound regex
/^https?:\/\/localhost:8000\/api\// which only matches local dev requests;
update the urlPattern used in the service worker runtimeCaching entry (the
urlPattern field) to a host-agnostic pattern that matches your API path in
production (for example a relative-path regex like /^\/api\// or /\/api\//) so
/api/* requests are cached regardless of hostname.

Comment thread vite.config.ts
Comment on lines 92 to 96
'/api': {
target: 'http://localhost:8000',
changeOrigin: true,
},
},

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify backend API endpoints and scan upload implementation

echo "=== Checking if /api/scans endpoint exists in backend ==="
fd -e py -e ts -e js . | xargs rg -n "POST.*['\"]/?scans['\"]|@.*route.*['\"]/?scans['\"]" 2>/dev/null || echo "No POST /scans route found"

echo ""
echo "=== Checking syncManager upload format ==="
rg -A 5 "fetch\('/api/scans'" src/utils/syncManager.ts

echo ""
echo "=== Checking if backend expects multipart/form-data ==="
fd -e py . | xargs rg -n "multipart|UploadFile|File\(" 2>/dev/null | head -20

Repository: jpdevhub/FreshScanAi

Length of output: 932


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Find any occurrences of '/scans' or 'scans' route strings in backend ==="
rg -n --hidden --no-ignore -S '"/scans"|'\''/scans'\''|/scans|scans' backend || true

echo ""
echo "=== Inspect backend/main.py around UploadFile/File handlers (likely scan upload endpoints) ==="
python3 - <<'PY'
import re, pathlib
p = pathlib.Path("backend/main.py")
txt = p.read_text(encoding="utf-8", errors="ignore").splitlines()
for i,line in enumerate(txt, start=1):
    if "UploadFile" in line or "File(" in line:
        start=max(1,i-10); end=min(len(txt), i+20)
        print(f"\n--- backend/main.py:{i} ---")
        for j in range(start,end+1):
            print(f"{j:5d}:{txt[j-1]}")
PY

echo ""
echo "=== Check FastAPI app/prefix/router mounting in backend/main.py ==="
rg -n 'include_router|APIRouter|prefix=|app\.' backend/main.py || true

Repository: jpdevhub/FreshScanAi

Length of output: 17815


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Show syncManager.ts upload request code around fetch('/api/scans') ==="
fd -e ts -e tsx -e js -e jsx syncManager src || true
fd -e ts -e tsx -e js -e jsx syncManager.ts . || true

FILE="$(fd -e ts -e tsx -e js -e jsx syncManager.ts . | head -n 1)"
echo "Using: $FILE"

nl -ba "$FILE" | sed -n '1,200p' | sed -n '1,120p' >/dev/null || true
# Print from around the fetch call
nl -ba "$FILE" | rg -n "fetch\\('\\/api\\/scans'\\)" -n --context=12 || true

Repository: jpdevhub/FreshScanAi

Length of output: 425


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="src/utils/syncManager.ts"
[ -f "$FILE" ] || { echo "Missing $FILE"; exit 1; }

echo "=== Locate fetch('/api/scans' usage in $FILE ==="
rg -n "fetch\\('\\/api\\/scans'\\)|\\/api\\/scans" "$FILE" || true

echo ""
echo "=== Print syncManager.ts around first /api/scans occurrence ==="
LINE="$(rg -n "fetch\\('\\/api\\/scans'\\)|\\/api\\/scans" "$FILE" | head -n 1 | cut -d: -f1 || true)"
if [ -n "${LINE:-}" ]; then
  START=$((LINE-20)); END=$((LINE+25));
  START=$(( START<1 ? 1 : START ))
  sed -n "${START},${END}p" "$FILE" | cat -n
else
  echo "No /api/scans occurrence found."
fi

echo ""
echo "=== Also check whether syncManager calls /api/v1/scan or /api/v1/scan-auto ==="
rg -n "\\/api\\/v1\\/scan|\\/api\\/v1\\/scan-auto" "$FILE" || true

Repository: jpdevhub/FreshScanAi

Length of output: 652


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="src/utils/syncManager.ts"
[ -f "$FILE" ] || { echo "Missing $FILE"; exit 1; }

echo "=== Fixed-string matches for endpoints in $FILE ==="
for s in "/api/scans" "/api/v1/scan" "/api/v1/scan-auto" "/api/v1/scans"; do
  echo "--- $s ---"
  rg -nF "$s" "$FILE" || true
  echo ""
done

echo "=== Print around first '/api/scans' match ==="
LINE="$(rg -nF "/api/scans" "$FILE" | head -n 1 | cut -d: -f1 || true)"
if [ -n "${LINE:-}" ]; then
  START=$((LINE-30)); END=$((LINE+30));
  START=$(( START<1 ? 1 : START ))
  sed -n "${START},${END}p" "$FILE" | cat -n
else
  echo "No /api/scans match found."
fi

Repository: jpdevhub/FreshScanAi

Length of output: 1723


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "getPendingScans|offlineQueue" src/utils -S
fd -e ts offlineQueue src/utils -a 2>/dev/null || true
FILE="$(fd -e ts offlineQueue src/utils | head -n 1 || true)"
echo "Offline queue file: $FILE"
[ -f "$FILE" ] && sed -n '1,220p' "$FILE" | cat -n
rg -n "uploadScan\\(|syncOfflineScans\\(|scan\\.id|pending" src/utils -S

Repository: jpdevhub/FreshScanAi

Length of output: 2919


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE="vite.config.ts"
[ -f "$FILE" ] || { echo "Missing $FILE"; exit 1; }
echo "=== vite.config.ts: showing lines 70-120 ==="
sed -n '70,120p' "$FILE" | cat -n
echo ""
echo "=== Find proxy config for /api ==="
rg -n "\"/api\"|'/api'|proxy" "$FILE" || true

Repository: jpdevhub/FreshScanAi

Length of output: 1081


Fix sync manager upload: wrong backend route and wrong content type

File: vite.config.ts
Lines: 92-96

      '/api': {
        target: 'http://localhost:8000',
        changeOrigin: true,
      },
  • src/utils/syncManager.ts syncs offline scans by POSTing JSON to /api/scans (Content-Type: application/json, body: JSON.stringify(scan)).
  • With the /api proxy, that request goes to the backend as /scans, but backend/main.py only defines scan uploads as multipart endpoints:
    • POST /api/v1/scan (expects body_image/eye_image/gill_image via File(...) + vendor_id/is_target_domain via Form(...))
    • POST /api/v1/scan-auto (expects image via File(...))
    • There is no POST route for /scans (or POST /api/v1/scans).

Adjust syncManager to call the correct endpoint (/api/v1/scan or /api/v1/scan-auto) and send a FormData multipart request with the required file fields (and vendor_id/is_target_domain for /api/v1/scan).

🧰 Tools
🪛 Biome (2.4.16)

[error] 92-92: Expected a semicolon or an implicit semicolon after a statement, but found none

(parse)


[error] 94-94: Expected a semicolon or an implicit semicolon after a statement, but found none

(parse)


[error] 95-95: Expected a statement but instead found ','.

(parse)


[error] 96-96: Expected a statement but instead found ','.

(parse)

🤖 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 `@vite.config.ts` around lines 92 - 96, The syncManager currently POSTs JSON to
'/api/scans' which the backend doesn't accept; update the upload logic in
src/utils/syncManager.ts (the function that builds and sends the offline scan
request) to POST multipart FormData to the correct backend routes (/api/v1/scan
for vendor uploads requiring body_image/eye_image/gill_image plus vendor_id and
is_target_domain, or /api/v1/scan-auto for single-image auto uploads). Build a
FormData instance instead of JSON.stringify(scan), append the file fields using
the original File/Blob objects (keys: body_image, eye_image, gill_image or
image), append vendor_id and is_target_domain for /api/v1/scan, remove explicit
Content-Type so the browser sets the multipart boundary, and update the fetch
URL to use '/api/v1/scan' or '/api/v1/scan-auto' accordingly and handle the
response/errors as before.

Comment thread vite.config.ts
@jpdevhub

jpdevhub commented Jun 11, 2026

Copy link
Copy Markdown
Owner

its failing the test.
Reason:
The lock file is missing entries for numerous packages, including:

@vite-pwa/assets-generator@1.0.2
sharp and all its platform-specific variants
cac, colorette, consola, and others listed in devDependencies
This typically happens when dependencies were added to package.json but the lock file wasn't regenerated.
also consider the ai major highlited flaws specifically.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FE-02: Progressive Web App (PWA) Offline Mode

3 participants