Conversation
…abase migration (scan.progress field)
📝 WalkthroughWalkthroughAdded scan progress tracking and cancellation capability across the backend and frontend. The Prisma schema now includes a ChangesScan Progress Tracking & Cancellation
Sequence DiagramsequenceDiagram
actor User
participant Frontend as Frontend UI
participant API as Backend API
participant Service as Scan Service
participant DB as Prisma DB
User->>Frontend: Clicks cancel button on<br/>RUNNING or PENDING scan
Frontend->>API: POST /scans/{id}/cancel
API->>Service: cancelScan(userId, scanId)
Service->>DB: getScanById (verify ownership)
DB-->>Service: Scan record
alt Scan is RUNNING/QUEUED/PENDING
Service->>DB: update(status=CANCELED,<br/>completedAt=now)
DB-->>Service: Updated scan
end
Service-->>API: true
API-->>Frontend: 200 OK
Frontend->>Frontend: Invalidate ['scans']<br/>query via React Query
Frontend->>Frontend: Refetch and<br/>re-render scan list
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/pages/scans/ui/Page.tsx (1)
42-60:⚠️ Potential issue | 🟠 Major | ⚡ Quick winHandle
QUEUEDscans in the UI.The backend now creates scans in
QUEUED, but these branches still treat that state as unknown/default and only expose cancel forRUNNING/PENDING. That means a freshly queued scan will show the restart path and cannot be canceled from the UI.Please add
QUEUEDto the shared scan status contract and include it in the status helpers and action column.♻️ Suggested fixes
switch (status) { case 'RUNNING': return 'text-cyber-green'; case 'PENDING': return 'text-blue-400'; + case 'QUEUED': return 'text-blue-400'; case 'COMPLETED': return 'text-purple-400'; case 'FAILED': return 'text-cyber-red'; case 'CANCELED': return 'text-white/40';- ) : scan.status === 'RUNNING' || scan.status === 'PENDING' ? ( + ) : scan.status === 'RUNNING' || scan.status === 'PENDING' || scan.status === 'QUEUED' ? (Also applies to: 170-188
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/pages/scans/ui/Page.tsx` around lines 42 - 60, Add the new QUEUED scan state to the status contract and UI helpers: update the shared ScanStatus type/enum to include 'QUEUED', then add a 'QUEUED' case in getStatusColor (choose an appropriate CSS class, e.g., same as PENDING or RUNNING) and in getStatusIcon (pick a suitable icon like Clock or Activity). Also update the action column logic (the component that decides cancel/restart buttons around the branch that currently checks for 'RUNNING'/'PENDING') to treat 'QUEUED' as cancelable (same behavior as PENDING), ensuring queued scans show the proper status color, icon, and expose the cancel action instead of a restart.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@backend/src/services/scans.ts`:
- Around line 184-205: Make the cancellation atomic and job-aware by replacing
the read-then-update pattern in cancelScan with a conditional single-step update
(e.g., use prisma.scan.update or updateMany with a where that includes id:
scanId and status in ['RUNNING','QUEUED','PENDING'] and set status to
ScanStatus.CANCELED and completedAt); after the conditional update, check
whether any row was changed and, if so, use the scan.jobId (or the field that
stores BullMQ job id on the Scan) to locate the BullMQ job via the Queue API
(e.g., queue.getJob(jobId)) and remove/discard/fail it so the worker stops,
returning false if no row was updated; keep worker logic to re-check scan status
before completing work.
In `@frontend/src/entities/scan/model/types.ts`:
- Around line 3-9: The Scan interface currently declares progress as optional;
change the Scan type definition so the progress property is required (remove the
optional modifier from progress) so progress: number; in the Scan interface
(frontend/src/entities/scan/model/types.ts, symbol: Scan) and update any usages
that defensively check for undefined to rely on a numeric value instead (e.g.,
callers that treat 0 vs missing).
---
Outside diff comments:
In `@frontend/src/pages/scans/ui/Page.tsx`:
- Around line 42-60: Add the new QUEUED scan state to the status contract and UI
helpers: update the shared ScanStatus type/enum to include 'QUEUED', then add a
'QUEUED' case in getStatusColor (choose an appropriate CSS class, e.g., same as
PENDING or RUNNING) and in getStatusIcon (pick a suitable icon like Clock or
Activity). Also update the action column logic (the component that decides
cancel/restart buttons around the branch that currently checks for
'RUNNING'/'PENDING') to treat 'QUEUED' as cancelable (same behavior as PENDING),
ensuring queued scans show the proper status color, icon, and expose the cancel
action instead of a restart.
🪄 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: 6dd0c440-9d90-4cb0-98ce-0669a47e63a9
📒 Files selected for processing (8)
backend/prisma/schema.prismabackend/src/controllers/scans.tsbackend/src/routes/scans.tsbackend/src/services/scans.tsbackend/src/worker/sqli/orchestrator.tsfrontend/src/entities/scan/api/scanApi.tsfrontend/src/entities/scan/model/types.tsfrontend/src/pages/scans/ui/Page.tsx
| export const cancelScan = async (userId: string, scanId: string) => { | ||
| // 1. التأكد من وجود الفحص وصلاحيات المستخدم | ||
| const scan = await getScanById(userId, scanId); | ||
|
|
||
| // 2. تحديث الحالة فقط إذا كانت RUNNING أو QUEUED أو PENDING | ||
| const activeStatuses = ['RUNNING', 'QUEUED', 'PENDING']; | ||
| if (activeStatuses.includes(scan.status)) { | ||
| await prisma.scan.update({ | ||
| where: { id: scanId }, | ||
| data: { | ||
| status: ScanStatus.CANCELED, | ||
| completedAt: new Date() // نعتبره مكتملاً (متوقفاً) لتسجيل الوقت | ||
| } | ||
| }); | ||
|
|
||
| // ملاحظة: إيقاف الـ Job الفعلي من BullMQ يتطلب معرف JobId. | ||
| // في هذه المرحلة، نكتفي بتحديث حالة قاعدة البيانات. | ||
| // الـ Worker يجب أن يتحقق من حالة الفحص في قاعدة البيانات قبل الاستمرار. | ||
| } | ||
|
|
||
| return true; | ||
| }; No newline at end of file |
There was a problem hiding this comment.
Cancellation needs to be atomic and job-aware.
The current read-then-update flow leaves a TOCTOU window, and the BullMQ job is never removed or stopped. A scan can still finish after cancellation and be written back as COMPLETED, which makes the cancel action look successful while the work keeps running.
Please make the state transition conditional in one step and coordinate queue removal or an early worker exit before returning success.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@backend/src/services/scans.ts` around lines 184 - 205, Make the cancellation
atomic and job-aware by replacing the read-then-update pattern in cancelScan
with a conditional single-step update (e.g., use prisma.scan.update or
updateMany with a where that includes id: scanId and status in
['RUNNING','QUEUED','PENDING'] and set status to ScanStatus.CANCELED and
completedAt); after the conditional update, check whether any row was changed
and, if so, use the scan.jobId (or the field that stores BullMQ job id on the
Scan) to locate the BullMQ job via the Queue API (e.g., queue.getJob(jobId)) and
remove/discard/fail it so the worker stops, returning false if no row was
updated; keep worker logic to re-check scan status before completing work.
| export interface Scan { | ||
| id: string; | ||
| status: ScanStatus; | ||
| progress?: number; // 🆕 نسبة التقدم (0-100) | ||
| startedAt: string | null; | ||
| completedAt: string | null; | ||
| createdAt: string; |
There was a problem hiding this comment.
Make progress required.
The backend now guarantees a numeric progress value for every scan, so keeping this optional only forces consumers into undefined/falsy handling and makes 0% indistinguishable from missing data.
♻️ Suggested tweak
export interface Scan {
id: string;
status: ScanStatus;
- progress?: number; // 🆕 نسبة التقدم (0-100)
+ progress: number; // 0-100
startedAt: string | null;
completedAt: string | null;
createdAt: string;📝 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.
| export interface Scan { | |
| id: string; | |
| status: ScanStatus; | |
| progress?: number; // 🆕 نسبة التقدم (0-100) | |
| startedAt: string | null; | |
| completedAt: string | null; | |
| createdAt: string; | |
| export interface Scan { | |
| id: string; | |
| status: ScanStatus; | |
| progress: number; // 0-100 | |
| startedAt: string | null; | |
| completedAt: string | null; | |
| createdAt: string; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/entities/scan/model/types.ts` around lines 3 - 9, The Scan
interface currently declares progress as optional; change the Scan type
definition so the progress property is required (remove the optional modifier
from progress) so progress: number; in the Scan interface
(frontend/src/entities/scan/model/types.ts, symbol: Scan) and update any usages
that defensively check for undefined to rely on a numeric value instead (e.g.,
callers that treat 0 vs missing).
There was a problem hiding this comment.
4 issues found across 8 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="backend/src/services/scans.ts">
<violation number="1" location="backend/src/services/scans.ts:191">
P1: Make the cancel update atomic by enforcing allowed statuses in the database write condition; the current read-then-update flow can overwrite a status changed concurrently.</violation>
</file>
<file name="backend/src/worker/sqli/orchestrator.ts">
<violation number="1" location="backend/src/worker/sqli/orchestrator.ts:112">
P2: The final completion write unconditionally sets `progress: 100` without the stale-client fallback used above, so schema/client drift can cause successful scans to be marked as FAILED at the very end.</violation>
</file>
<file name="frontend/src/pages/scans/ui/Page.tsx">
<violation number="1" location="frontend/src/pages/scans/ui/Page.tsx:139">
P2: `!scan.progress` treats a valid `0` progress as missing, so 0% running scans are shown as indeterminate.</violation>
<violation number="2" location="frontend/src/pages/scans/ui/Page.tsx:157">
P2: The progress label can show `0%` for completed scans, which conflicts with the 100% completed bar state.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| where: { id: scanId }, | ||
| data: { | ||
| status: 'COMPLETED', | ||
| progress: 100, // 🆕 تأكيد الوصول لـ 100% |
There was a problem hiding this comment.
P2: The final completion write unconditionally sets progress: 100 without the stale-client fallback used above, so schema/client drift can cause successful scans to be marked as FAILED at the very end.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/src/worker/sqli/orchestrator.ts, line 112:
<comment>The final completion write unconditionally sets `progress: 100` without the stale-client fallback used above, so schema/client drift can cause successful scans to be marked as FAILED at the very end.</comment>
<file context>
@@ -84,12 +101,15 @@ export async function runSqliScan(job: Job, prisma: PrismaClient): Promise<void>
where: { id: scanId },
data: {
status: 'COMPLETED',
+ progress: 100, // 🆕 تأكيد الوصول لـ 100%
completedAt: new Date(),
// يمكن إضافة حقل لعدد النتائج إذا كان مدعوماً في قاعدة البيانات
</file context>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="backend/src/services/scans.ts">
<violation number="1" location="backend/src/services/scans.ts:191">
P2: The BullMQ job is never removed or signaled to stop after marking the scan as `CANCELED`. The worker will continue processing and could overwrite the status back to `COMPLETED` when it finishes, making the cancel appear successful while work continues. Consider storing the BullMQ job ID on the scan record and calling `job.remove()` or using the `AbortSignal`-based cancellation pattern here, or at minimum ensure the worker checks the DB status before writing terminal states.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
| // 2. تحديث الحالة فقط إذا كانت RUNNING أو QUEUED أو PENDING | ||
| const activeStatuses = ['RUNNING', 'QUEUED', 'PENDING']; | ||
| if (activeStatuses.includes(scan.status)) { | ||
| await prisma.scan.updateMany({ |
There was a problem hiding this comment.
P2: The BullMQ job is never removed or signaled to stop after marking the scan as CANCELED. The worker will continue processing and could overwrite the status back to COMPLETED when it finishes, making the cancel appear successful while work continues. Consider storing the BullMQ job ID on the scan record and calling job.remove() or using the AbortSignal-based cancellation pattern here, or at minimum ensure the worker checks the DB status before writing terminal states.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/src/services/scans.ts, line 191:
<comment>The BullMQ job is never removed or signaled to stop after marking the scan as `CANCELED`. The worker will continue processing and could overwrite the status back to `COMPLETED` when it finishes, making the cancel appear successful while work continues. Consider storing the BullMQ job ID on the scan record and calling `job.remove()` or using the `AbortSignal`-based cancellation pattern here, or at minimum ensure the worker checks the DB status before writing terminal states.</comment>
<file context>
@@ -188,14 +188,16 @@ export const cancelScan = async (userId: string, scanId: string) => {
- await prisma.scan.update({
- where: { id: scanId },
- data: {
+ await prisma.scan.updateMany({
+ where: {
+ id: scanId,
</file context>
Tip: Review your code locally with the cubic CLI to iterate faster.
Summary by cubic
Adds end‑to‑end scan progress tracking and a cancel action so users can monitor progress and stop pending or running scans. The worker writes progress to the DB, and the UI shows a determinate/indeterminate bar with percent or “ANALYZING…”, plus a cancel button with loading state.
New Features
Scan.progress(0–100) andPOST /scans/:id/cancelto cancelPENDING/QUEUED/RUNNING; sets statusCANCELEDandcompletedAt.COMPLETED+progress: 100.scanApi.cancel(id)with mutation that invalidates the list; progress bar switches between indeterminate and percent with a label;CANCELEDicon/color; cancel button forPENDING/RUNNINGwith spinner and disabled state.Migration
progresstoScanand regenerate the client.STOPPEDtoCANCELED.Written for commit 841f15f. Summary will update on new commits.
Summary by CodeRabbit
Release Notes
New Features