feat: implement PWA offline mode with IndexedDB queue and sync manager#71
feat: implement PWA offline mode with IndexedDB queue and sync manager#71SaumyaSngh323 wants to merge 3 commits into
Conversation
|
@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. |
|
🎉 Thank you for your Pull Request! We're thrilled to have your contribution to FreshScan AI. Before we review, please make sure you have:
A maintainer will review your code as soon as possible! |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
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: |
|
Warning
|
| 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
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 | 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 | 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.
Comment @coderabbitai help to get the list of available commands and usage tips.
|
Hi @jpdevhub, I have resolved the merge conflicts and updated the PR. Could you please review it again? Thank you. |
There was a problem hiding this comment.
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 winCritical: 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 valueConsider adding error handling for startup sync.
syncOfflineScans()is async but called withoutawaitor.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
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (5)
package.jsonsrc/main.tsxsrc/utils/offlineQueue.tssrc/utils/syncManager.tsvite.config.ts
| import { initTheme } from './lib/theme'; | ||
| import { syncOfflineScans } from './utils/syncManager' | ||
| import { initTheme } from './lib/theme' |
There was a problem hiding this comment.
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.
| 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'.
| 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) | ||
| }) | ||
| } |
There was a problem hiding this comment.
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.
| 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 | ||
| } | ||
| } | ||
| } | ||
| ] | ||
| } | ||
| }) |
There was a problem hiding this comment.
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:
- Syntax error: Two
VitePWA()calls within the samepluginsarray - Lost runtime caching: The old config (lines 66-85) includes critical
CacheFirstrules for.onnxmodel files (~12-26 MB) and.wasmfiles. The new config (lines 40-52) only caches/api/requests, completely removing model/wasm caching. - 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.
| globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'], | ||
| runtimeCaching: [ | ||
| { | ||
| urlPattern: /^https?:\/\/localhost:8000\/api\//, |
There was a problem hiding this comment.
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.
| 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.
| '/api': { | ||
| target: 'http://localhost:8000', | ||
| changeOrigin: true, | ||
| }, | ||
| }, |
There was a problem hiding this comment.
🧩 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 -20Repository: 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 || trueRepository: 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 || trueRepository: 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" || trueRepository: 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."
fiRepository: 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 -SRepository: 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" || trueRepository: 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.tssyncs offline scans byPOSTing JSON to/api/scans(Content-Type: application/json,body: JSON.stringify(scan)).- With the
/apiproxy, that request goes to the backend as/scans, butbackend/main.pyonly defines scan uploads as multipart endpoints:POST /api/v1/scan(expectsbody_image/eye_image/gill_imageviaFile(...)+vendor_id/is_target_domainviaForm(...))POST /api/v1/scan-auto(expectsimageviaFile(...))- There is no
POSTroute for/scans(orPOST /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.
|
its failing the test. @vite-pwa/assets-generator@1.0.2 |
Description
Checklist
npm run lintpasses with no errorsnpm run buildcompiles without TypeScript errorspython -m pytestpasses (including new tests I added).envfiles, API keys, secrets, model weights, or__pycache__in this diffmain, not mergedCloses #6
Summary by CodeRabbit