Skip to content

correct payment stripe - #148

Merged
mAmineChniti merged 25 commits into
masterfrom
paymentw
May 5, 2026
Merged

mAmineChniti merged 25 commits into
masterfrom
paymentw

Conversation

@khadraouihiba02

@khadraouihiba02 khadraouihiba02 commented May 1, 2026 •

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • Bug Fixes

    • Payment completion now conditionally confirms with backend, preserves/clears checkout state, and shows localized success/error toasts.
  • New Features

    • Checkout responses may include a receipt identifier.
    • Added an application metrics endpoint for Prometheus scraping.
  • Tests

    • Added Vitest setup, config, coverage, and new tests for invoices, products (AI Insights), and logs.
  • Chores

    • CI/workflow, Dockerfile, Jenkins, monitoring/Prometheus/Grafana configs, Sonar/tsconfig, lint/Prettier, .gitignore, and README updates.

@vercel

vercel Bot commented May 1, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
accountia Ready Ready Preview, Comment May 5, 2026 3:42pm

@coderabbitai

coderabbitai Bot commented May 1, 2026 •

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR persists checkout-session state (sessionId and optional receiptId) in the Invoices UI and, upon Stripe checkout completion, conditionally confirms payment with the backend using those IDs; it also adds the optional receiptId type, multiple Vitest tests and config, CI/build/docker/jenkins/monitoring configs, and various tooling updates.

Changes

Invoices UI & Types

Layer / File(s) Summary
Data Shape
types/services/invoicesResponses.ts
Adds optional receiptId?: string to InvoiceCheckoutSessionResponse.
Core UI Flow
components/app/invoices/Invoices.tsx
Persists sessionId and receiptId from checkout-session creation, clears state on dialog close/completion, and on checkout completion conditionally calls InvoicesService.confirmPayment(sessionId, receiptId) when IDs exist; on both paths it invalidates invoices query and surfaces success/error toasts.
Tests
test/Invoices.test.tsx
New test renders Invoices with mocks and asserts page title and refresh control presence for the updated flow.

Tests & Test Runner

Layer / File(s) Summary
Test Suites
test/BusinessProducts.test.tsx, test/Logs.test.tsx, test/Invoices.test.tsx
Adds three Vitest test suites that mock react-query, services, and UI primitives to validate BusinessProducts (AI Insights), Logs, and Invoices views.
Test Tooling
package.json
Adds test and test:cov scripts and devDependencies: vitest, @vitest/coverage-v8, @testing-library/react, @testing-library/dom, @testing-library/jest-dom, jsdom, @vitejs/plugin-react, plus an optional dependency entry.
Test Config / Setup
vitest.config.ts, vitest.setup.ts
Adds Vitest config (jsdom, globals, setup file, v8 coverage reporters) and a setup file enabling jest-dom matchers, setting test env vars, and mocking ResizeObserver.

CI / Build / Containerization

Layer / File(s) Summary
GitHub Actions
.github/workflows/ci.yml
Adds CI workflow with test and build jobs (uses Bun for install/lint/tests) and uploads .next as an artifact for push events.
Docker Build
Dockerfile, .dockerignore
Adds multi-stage Dockerfile (builder + runner) and .dockerignore excluding common artifacts.
Repo Ignores
.gitignore
Adds jenkins_home/, coverage/, and .scannerwork/ to ignore patterns.
Jenkins Pipeline
Jenkinsfile
Adds declarative pipeline: prepare build dir, install, lint/tests (allowed to fail), SonarQube scan, Docker image build/tag, and deploy stage.
Compose for Jenkins
docker-compose-jenkins.yml
Adds Compose service for Jenkins with volumes, Docker socket mount, ports, and restart policy.

Monitoring & Observability

Layer / File(s) Summary
Prometheus Config
prometheus.yml, alert_rules.yml
Adds prometheus.yml with accountia-api and accountia-web scrape jobs and alert_rules.yml with InstanceDown alert rule.
Monitoring Compose
docker-compose-monitoring.yml
Adds Compose stack for Prometheus, Grafana, and Alertmanager with ports and mounts.
App Metrics Endpoint
app/api/metrics/route.ts
Adds Next.js API route exporting Prometheus metrics via prom-client register with Content-Type: text/plain; version=0.0.4 and cache-control headers.
Static Analysis for Sonar
sonar-project.properties, tsconfig.sonar.json
Adds SonarQube properties and a tsconfig.sonar.json for Sonar TypeScript analysis.

Misc Tooling & Formatting

Layer / File(s) Summary
Formatter / Lint
prettier.config.js, eslint.config.mjs
Prettier: adds endOfLine: 'auto'. ESLint: adds coverage/** to globalIgnores.
App Layout
app/layout.tsx
Replaces next/font/google usage with simple constants providing the original CSS variable names (no behavioral changes).
Docs
README.md
Adds Live Deployment and Deliverables links.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant InvoicesUI as Invoices Component
  participant Stripe as Stripe Checkout
  participant Backend as InvoicesService/API
  participant Cache as ReactQuery Cache

  Browser->>InvoicesUI: open checkout dialog
  InvoicesUI->>Stripe: createCheckoutSession()
  Stripe-->>InvoicesUI: checkoutSessionResponse { sessionId, receiptId? }
  InvoicesUI->>InvoicesUI: persist sessionId, receiptId
  Browser->>Stripe: complete payment
  Stripe-->>InvoicesUI: sessionId (callback)
  alt sessionId and receiptId present
    InvoicesUI->>Backend: confirmPayment(sessionId, receiptId)
    Backend-->>InvoicesUI: confirmation success
    InvoicesUI->>Cache: invalidate invoices query
    InvoicesUI->>Browser: show success toast, clear state
  else missing IDs
    InvoicesUI->>Cache: invalidate invoices query
    InvoicesUI->>Browser: clear state, show fallback
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇
I stash the session, tuck the receipt in my sleeve,
I hop to confirm when the charges believe,
If IDs are present I whisper to the API,
Then clear my footprints and nibble a pie. 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

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.
Title check ❓ Inconclusive The title 'correct payment stripe' is vague and does not clearly convey the main changes in the PR. The changeset includes extensive infrastructure additions (testing setup, CI/CD, Docker, monitoring, SonarQube) and a specific payment confirmation feature, but the title fails to indicate the primary focus or purpose. Revise the title to be more specific and descriptive, such as 'Add Stripe payment confirmation flow with backend validation' or 'Implement payment confirmation with testing and deployment infrastructure' to clearly communicate the main objective.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch paymentw

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
components/app/invoices/Invoices.tsx (2)

254-255: ⚡ Quick win

Consider using invoice.id as fallback for receiptId.

The receiptId field is optional in the response, but you already pass invoice.id to createIndividualCheckoutSession. If the backend doesn't return receiptId, the confirmation flow at line 1311 will be skipped. Using the invoice ID as a fallback would make this more robust.

Proposed fix using mutation variables
-    onSuccess: async (data) => {
+    onSuccess: async (data, invoice) => {
       // Stripe Embedded Checkout gère l'affichage du succès/échec
       setSelectedInvoice(undefined);
       setPaymentInvoiceLabel('');
       setPaymentClientSecret(data.clientSecret ?? '');
       setPaymentSessionId(data.sessionId);
-      setPaymentReceiptId(data.receiptId);
+      setPaymentReceiptId(data.receiptId ?? invoice.id);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/app/invoices/Invoices.tsx` around lines 254 - 255, The backend's
optional data.receiptId may be undefined causing the confirmation flow to be
skipped; update the call site after createIndividualCheckoutSession to
setPaymentReceiptId(data.receiptId ?? invoice.id) so the invoice.id is used as a
fallback while still calling setPaymentSessionId(data.sessionId); modify the
logic around setPaymentReceiptId in the component to prefer data.receiptId but
default to invoice.id to ensure the confirmation flow (which checks the receipt
ID) always runs.

1329-1337: 💤 Low value

Consider clearing dialog state after confirmation failure.

When confirmPayment fails, the error toast is shown but the payment dialog remains open with stale state. Since the Stripe payment may have already succeeded, leaving the checkout UI visible could confuse the user. Consider clearing state in the catch block as well.

Proposed fix
                         .catch((error: unknown) => {
+                          setPaymentClientSecret(undefined);
+                          setPaymentSessionId(undefined);
+                          setPaymentReceiptId(undefined);
+                          setPaymentInvoiceLabel('');
                           toast.error(
                             localizeErrorMessage(
                               error,
                               dictionary,
                               t.fetchError
                             )
                           );
+                          void queryClient.invalidateQueries({
+                            queryKey: ['received-invoices'],
+                          });
                         });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/app/invoices/Invoices.tsx` around lines 1329 - 1337, When
confirmPayment fails in the .catch block, clear the payment dialog state so
stale UI doesn't remain; after showing the toast call the same state-reset logic
used on successful close — e.g. call setIsPaymentDialogOpen(false) (or the
component's equivalent), resetPaymentForm() (or clear payment form fields),
setSelectedPaymentMethod(null), and setPaymentProcessing(false) so the dialog
closes and internal payment-related state is cleared; reference the
confirmPayment catch handler and the state setters/reset helpers used elsewhere
in Invoices.tsx (like setIsPaymentDialogOpen, resetPaymentForm,
setSelectedPaymentMethod) to implement the cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/app/invoices/Invoices.tsx`:
- Around line 1338-1346: The else branch that clears payment state
(setPaymentClientSecret, setPaymentSessionId, setPaymentReceiptId,
setPaymentInvoiceLabel) and invalidates the received-invoices query currently
gives no user feedback; update that branch to also show the same success
toast/notification used in the success path (the one invoked around line 1326)
so users receive confirmation after completing Stripe checkout even when
session/receipt IDs are absent, ensuring the toast is called after state clears
and before/after queryClient.invalidateQueries(['received-invoices']) as
appropriate.

---

Nitpick comments:
In `@components/app/invoices/Invoices.tsx`:
- Around line 254-255: The backend's optional data.receiptId may be undefined
causing the confirmation flow to be skipped; update the call site after
createIndividualCheckoutSession to setPaymentReceiptId(data.receiptId ??
invoice.id) so the invoice.id is used as a fallback while still calling
setPaymentSessionId(data.sessionId); modify the logic around setPaymentReceiptId
in the component to prefer data.receiptId but default to invoice.id to ensure
the confirmation flow (which checks the receipt ID) always runs.
- Around line 1329-1337: When confirmPayment fails in the .catch block, clear
the payment dialog state so stale UI doesn't remain; after showing the toast
call the same state-reset logic used on successful close — e.g. call
setIsPaymentDialogOpen(false) (or the component's equivalent),
resetPaymentForm() (or clear payment form fields),
setSelectedPaymentMethod(null), and setPaymentProcessing(false) so the dialog
closes and internal payment-related state is cleared; reference the
confirmPayment catch handler and the state setters/reset helpers used elsewhere
in Invoices.tsx (like setIsPaymentDialogOpen, resetPaymentForm,
setSelectedPaymentMethod) to implement the cleanup.
🪄 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: 20d80269-c2b7-4386-b36c-5684e77d0284

📥 Commits

Reviewing files that changed from the base of the PR and between aa22bb8 and aabcb9b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • components/app/invoices/Invoices.tsx
  • types/services/invoicesResponses.ts

Comment on lines +1338 to +1346
} else {
setPaymentClientSecret(undefined);
setPaymentSessionId(undefined);
setPaymentReceiptId(undefined);
setPaymentInvoiceLabel('');
void queryClient.invalidateQueries({
queryKey: ['received-invoices'],
});
}

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

Missing user feedback when IDs are absent.

When paymentSessionId or paymentReceiptId is missing, the else branch clears state and invalidates queries but shows no success toast. The user completes payment in Stripe's UI but receives no confirmation message, which creates an inconsistent experience compared to the success path at line 1326.

Proposed fix to add success feedback
                     } else {
                       setPaymentClientSecret(undefined);
                       setPaymentSessionId(undefined);
                       setPaymentReceiptId(undefined);
                       setPaymentInvoiceLabel('');
                       void queryClient.invalidateQueries({
                         queryKey: ['received-invoices'],
-                      });
+                      }).then(() => {
+                        toast.success(t.payment.successful);
+                      });
                     }
📝 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
} else {
setPaymentClientSecret(undefined);
setPaymentSessionId(undefined);
setPaymentReceiptId(undefined);
setPaymentInvoiceLabel('');
void queryClient.invalidateQueries({
queryKey: ['received-invoices'],
});
}
} else {
setPaymentClientSecret(undefined);
setPaymentSessionId(undefined);
setPaymentReceiptId(undefined);
setPaymentInvoiceLabel('');
void queryClient.invalidateQueries({
queryKey: ['received-invoices'],
}).then(() => {
toast.success(t.payment.successful);
});
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/app/invoices/Invoices.tsx` around lines 1338 - 1346, The else
branch that clears payment state (setPaymentClientSecret, setPaymentSessionId,
setPaymentReceiptId, setPaymentInvoiceLabel) and invalidates the
received-invoices query currently gives no user feedback; update that branch to
also show the same success toast/notification used in the success path (the one
invoked around line 1326) so users receive confirmation after completing Stripe
checkout even when session/receipt IDs are absent, ensuring the toast is called
after state clears and before/after
queryClient.invalidateQueries(['received-invoices']) as appropriate.

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

🧹 Nitpick comments (2)
test/Invoices.test.tsx (1)

11-15: ⚡ Quick win

Mock the full useQuery contract used by Invoices.

In components/app/invoices/Invoices.tsx (Lines 165-194), the component consumes isFetching, refetch, and isError. The current mock omits these and can let undefined behavior slip through tests.

♻️ Proposed mock shape update
 vi.mock('@tanstack/react-query', () => ({
-  useQuery: () => ({ data: undefined, isLoading: false, error: undefined }),
+  useQuery: () => ({
+    data: undefined,
+    isLoading: false,
+    isFetching: false,
+    isError: false,
+    error: undefined,
+    refetch: vi.fn(),
+  }),
   useMutation: () => ({ mutate: vi.fn(), isPending: false }),
   useQueryClient: () => ({ invalidateQueries: vi.fn() }),
 }));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/Invoices.test.tsx` around lines 11 - 15, The test mock for useQuery in
test/Invoices.test.tsx is missing fields the Invoices component reads
(isFetching, refetch, isError); update the vi.mock for '@tanstack/react-query'
so the useQuery mock returns the full contract used by
components/app/invoices/Invoices.tsx — include isFetching (boolean), isError
(boolean), and refetch (vi.fn() or async noop) alongside data/isLoading/error;
keep sensible defaults (false/undefined) and use vi.fn() for refetch to avoid
undefined behavior in tests.
vitest.setup.ts (1)

6-6: ⚡ Quick win

Align test backend URL with the app API base path.

Line 6 sets NEXT_PUBLIC_BACKEND to http://localhost:3000, while app request code defaults to an /api base (lib/requests.ts, Lines 66-68). Keeping tests on an API-shaped base avoids path-related false positives.

♻️ Proposed change
-process.env.NEXT_PUBLIC_BACKEND = 'http://localhost:3000';
+process.env.NEXT_PUBLIC_BACKEND = 'http://127.0.0.1:4789/api';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vitest.setup.ts` at line 6, The test env sets NEXT_PUBLIC_BACKEND to
'http://localhost:3000' which doesn't include the app's API base path used in
lib/requests.ts (the code that appends '/api' to requests), causing path
mismatches in tests; update the value in vitest.setup.ts to include the same API
base (for example 'http://localhost:3000/api') so tests use the same base path
as the request-building logic (referencing NEXT_PUBLIC_BACKEND and
lib/requests.ts).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/Invoices.test.tsx`:
- Around line 26-72: Add tests exercising the checkout-completion flow in
Invoices: simulate the component behavior when a checkout completion is detected
(e.g., URL query params or props that trigger the flow), mock the confirmPayment
function to return success and failure cases, mock/session storage helpers that
persist session/receipt (e.g., persistSession or persistReceipt) and ensure they
are read then cleared, and mock the cache invalidation function (e.g.,
invalidateQueries) and toast helpers (e.g., showToastSuccess/showToastError).
For success: assert confirmPayment is called, invalidateQueries is invoked,
session/receipt persistence is removed, and success toast was shown; for
failure: assert confirmPayment was called, cleanup still runs, and error toast
was shown. Use the Invoices component render with RTL, jest.fn() mocks for
confirmPayment/persistence/invalidation/toasts, and simulate the completion
trigger (query param or prop) to validate both success and error paths.

In `@test/Logs.test.tsx`:
- Around line 159-164: The test title and assertion are mismatched: update the
spec for the Logs component so it either (A) is renamed to reflect that it only
verifies the search input (change the it() description) or (B) actually asserts
the "no results" behavior by rendering Logs with an empty logs dataset (pass an
empty logs prop or a mock with zero items) and then asserting the no-results
message is shown (use screen.getByText(...) or getByRole to check the "no
results" string from mockDictionary). Locate the test containing render(<Logs
dictionary={mockDictionary} lang="en" />) and screen.getByPlaceholderText(...)
and apply one of these two fixes.

In `@vitest.config.ts`:
- Line 6: Replace the use of import.meta.dirname by deriving __dirname from the
imported fileURLToPath and path utilities: use fileURLToPath(import.meta.url)
and path.dirname to compute __dirname instead of import.meta.dirname; update the
const __dirname declaration that currently references import.meta.dirname to
call fileURLToPath(import.meta.url) wrapped by path.dirname so it works on Node
versions older than 20.11.0.

---

Nitpick comments:
In `@test/Invoices.test.tsx`:
- Around line 11-15: The test mock for useQuery in test/Invoices.test.tsx is
missing fields the Invoices component reads (isFetching, refetch, isError);
update the vi.mock for '@tanstack/react-query' so the useQuery mock returns the
full contract used by components/app/invoices/Invoices.tsx — include isFetching
(boolean), isError (boolean), and refetch (vi.fn() or async noop) alongside
data/isLoading/error; keep sensible defaults (false/undefined) and use vi.fn()
for refetch to avoid undefined behavior in tests.

In `@vitest.setup.ts`:
- Line 6: The test env sets NEXT_PUBLIC_BACKEND to 'http://localhost:3000' which
doesn't include the app's API base path used in lib/requests.ts (the code that
appends '/api' to requests), causing path mismatches in tests; update the value
in vitest.setup.ts to include the same API base (for example
'http://localhost:3000/api') so tests use the same base path as the
request-building logic (referencing NEXT_PUBLIC_BACKEND and lib/requests.ts).
🪄 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: ec517208-117c-4d74-b28c-99f803eefb83

📥 Commits

Reviewing files that changed from the base of the PR and between aabcb9b and 96cc703.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • package.json
  • sonar-project.properties
  • test/BusinessProducts.test.tsx
  • test/Invoices.test.tsx
  • test/Logs.test.tsx
  • vitest.config.ts
  • vitest.setup.ts
✅ Files skipped from review due to trivial changes (1)
  • sonar-project.properties

Comment thread test/Invoices.test.tsx Outdated
Comment on lines +26 to +72
describe('Invoices Component', () => {
const mockDictionary = {
pages: {
invoices: {
title: 'Mes Factures',
description: 'Consultez et gérez vos factures',
totalInvoices: 'Total des factures',
totalPaid: 'Total payé',
totalPending: 'Total en attente',
invoiceList: 'Liste des factures',
refresh: 'Actualiser',
createBusinessButton: 'Créer une nouvelle entreprise',
payment: {
securePayment: 'Paiement sécurisé',
completeCardInfo: 'Complétez les informations de votre carte',
validation: {
nameRequired: 'Nom du titulaire de la carte requis',
numberInvalid: 'Numéro de carte invalide',
expiryInvalid: "Date d'expiration invalide",
cvcInvalid: 'CVC invalide',
countryRequired: 'Pays requis',
},
countryGroups: {
northAfrica: 'Afrique du Nord',
},
},
stripeConnect: {
title: 'Stripe Connect',
description: 'Connectez votre compte Stripe',
},
},
},
common: {
retry: 'Réessayer',
},
} as unknown as Dictionary;

it('should render the title correctly', () => {
render(<Invoices dictionary={mockDictionary} lang="fr" />);
expect(screen.getByText('Mes Factures')).toBeDefined();
});

it('should render the refresh button', () => {
render(<Invoices dictionary={mockDictionary} lang="fr" />);
expect(screen.getByText('Actualiser')).toBeDefined();
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Add tests for the checkout-completion confirmation path changed in this PR.

Current cases validate static rendering only. The payment-critical behavior described in this PR (session/receipt persistence, confirmPayment call, invalidation, cleanup, success/error toasts) is not exercised.

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

In `@test/Invoices.test.tsx` around lines 26 - 72, Add tests exercising the
checkout-completion flow in Invoices: simulate the component behavior when a
checkout completion is detected (e.g., URL query params or props that trigger
the flow), mock the confirmPayment function to return success and failure cases,
mock/session storage helpers that persist session/receipt (e.g., persistSession
or persistReceipt) and ensure they are read then cleared, and mock the cache
invalidation function (e.g., invalidateQueries) and toast helpers (e.g.,
showToastSuccess/showToastError). For success: assert confirmPayment is called,
invalidateQueries is invoked, session/receipt persistence is removed, and
success toast was shown; for failure: assert confirmPayment was called, cleanup
still runs, and error toast was shown. Use the Invoices component render with
RTL, jest.fn() mocks for confirmPayment/persistence/invalidation/toasts, and
simulate the completion trigger (query param or prop) to validate both success
and error paths.

Comment thread test/Logs.test.tsx Outdated
Comment on lines +159 to +164
it('should show no results message when filtering results in 0 items', () => {
// We can't easily test client-side useMemo filtering without actual data changes
// But we can verify the search input exists
render(<Logs dictionary={mockDictionary} lang="en" />);
expect(screen.getByPlaceholderText(/search actor email.../i)).toBeDefined();
});

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

Test intent and assertion are mismatched.

Line 159 claims no-results behavior, but the assertion only checks that the search input exists. Rename the test or assert the actual no-results message with an empty logs mock.

✏️ Minimal fix (rename to match behavior)
-  it('should show no results message when filtering results in 0 items', () => {
+  it('should render the search input', () => {
     // We can't easily test client-side useMemo filtering without actual data changes
     // But we can verify the search input exists
     render(<Logs dictionary={mockDictionary} lang="en" />);
     expect(screen.getByPlaceholderText(/search actor email.../i)).toBeDefined();
   });
📝 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
it('should show no results message when filtering results in 0 items', () => {
// We can't easily test client-side useMemo filtering without actual data changes
// But we can verify the search input exists
render(<Logs dictionary={mockDictionary} lang="en" />);
expect(screen.getByPlaceholderText(/search actor email.../i)).toBeDefined();
});
it('should render the search input', () => {
// We can't easily test client-side useMemo filtering without actual data changes
// But we can verify the search input exists
render(<Logs dictionary={mockDictionary} lang="en" />);
expect(screen.getByPlaceholderText(/search actor email.../i)).toBeDefined();
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/Logs.test.tsx` around lines 159 - 164, The test title and assertion are
mismatched: update the spec for the Logs component so it either (A) is renamed
to reflect that it only verifies the search input (change the it() description)
or (B) actually asserts the "no results" behavior by rendering Logs with an
empty logs dataset (pass an empty logs prop or a mock with zero items) and then
asserting the no-results message is shown (use screen.getByText(...) or
getByRole to check the "no results" string from mockDictionary). Locate the test
containing render(<Logs dictionary={mockDictionary} lang="en" />) and
screen.getByPlaceholderText(...) and apply one of these two fixes.

Comment thread vitest.config.ts Outdated

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

♻️ Duplicate comments (1)
vitest.config.ts (1)

5-5: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Replace import.meta.dirname with a cross-version-safe dirname derivation.

Line 5 may break on environments that don’t support import.meta.dirname. Use fileURLToPath(import.meta.url) + path.dirname(...) instead.

🛠️ Proposed fix
 import { defineConfig } from 'vitest/config';
 import react from '@vitejs/plugin-react';
 import path from 'node:path';
+import { fileURLToPath } from 'node:url';

-const __dirname = import.meta.dirname;
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);

Run this read-only check to confirm the compatibility risk and current implementation:

#!/bin/bash
set -euo pipefail

echo "Engines declared in package.json:"
jq -r '.engines // {}' package.json

echo
echo "Current dirname usage in vitest config:"
rg -n --fixed-strings 'import.meta.dirname' vitest.config.ts

echo
echo "Cross-version-safe pattern presence:"
rg -n 'fileURLToPath\(import\.meta\.url\)|path\.dirname\(' vitest.config.ts
🤖 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 `@vitest.config.ts` at line 5, Replace the unsafe use of import.meta.dirname
when deriving __dirname: locate the declaration of the __dirname variable
(currently set via import.meta.dirname) and change it to compute __dirname using
fileURLToPath(import.meta.url) combined with path.dirname; ensure you import or
reference fileURLToPath and path (from 'url' and 'path' respectively) as needed
so __dirname is derived with path.dirname(fileURLToPath(import.meta.url)).
🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)

17-20: ⚡ Quick win

Pin Bun to an explicit version instead of latest for reproducible CI.

Using latest introduces risk of unexpected breakages as new Bun releases roll out. Pin to a stable version (e.g., 1.3.13) to ensure consistent lint/test/build outcomes across CI runs.

Also applies to: 37-40

🤖 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 @.github/workflows/ci.yml around lines 17 - 20, The CI uses the "Setup Bun"
step with bun-version: latest which risks non-reproducible failures; update the
GitHub Actions steps that use oven-sh/setup-bun@v1 (both occurrences of the
"Setup Bun" step) to pin bun-version to a specific stable release (for example
"1.3.13") instead of "latest" so builds/lints/tests are reproducible across CI
runs.
🤖 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 @.dockerignore:
- Around line 1-11: The .dockerignore currently omits common build artifacts but
not environment files, which risks leaking secrets into Docker build context;
update the .dockerignore (the file named ".dockerignore" itself) to explicitly
ignore environment files by adding entries such as .env, *.env, .env.*,
.env.local, .env.production, .env.development, .env.test, and any
project-specific secret filenames so they are excluded from the build context
and image layers.

In `@app/api/metrics/route.ts`:
- Around line 7-25: Move the call to collectDefaultMetrics out of the GET
handler and into module initialization so it runs once per process (e.g., call
collectDefaultMetrics({ register }) at top-level when the module loads), remove
the try-catch around register.metrics() in the GET function, and have GET simply
await register.metrics() and return it with the appropriate headers; refer to
collectDefaultMetrics, register, and the exported GET function when making the
change.

In `@app/layout.tsx`:
- Around line 2-3: The current change mocks next/font by replacing imports for
Geist, Geist_Mono, and Noto_Sans_Arabic with literal class tokens, which
prevents CSS variables and fonts from loading; restore the proper font loader
usage by re-importing from next/font/google (or switch to next/font/local with
checked-in files if build-time network access is blocked) and use the actual
loader output (the objects returned by Geist, Geist_Mono, Noto_Sans_Arabic) for
the variable property and className wherever you set
variable="--font-geist-sans" or similar so the generated class names and CSS
variables are applied; update the places referenced in the diff (the import
lines and the variable/className usages around lines noted) to use the real font
objects.

In `@docker-compose-jenkins.yml`:
- Around line 5-12: The compose snippet exposes a host-compromise path by using
privileged: true, user: root and binding /var/run/docker.sock; remove
privileged: true and user: root entries and stop mounting the host Docker socket
directly (the privileged/user symbols and the volumes entry referencing
/var/run/docker.sock), and instead add a controlled Docker-in-Docker sidecar
(e.g., image docker:dind) or a docker socket proxy and configure the Jenkins
container to use DOCKER_HOST (tcp) to talk to that sidecar over the compose
network; ensure the sidecar is hardened (use TLS or an authenticated proxy) and
keep the jenkins_home volume mount but avoid granting host root or direct socket
access.
- Around line 13-14: The compose file currently hardcodes Windows developer
paths in the workspace mounts; replace those two host-specific bindings (the
lines mounting C:\Users\Asus\Desktop\accountia-web and accountia-api) with
parameterized values or named volumes so other hosts/agents can run the stack.
Update the volume entries to use environment variables like
${WORKSPACE_ACCOUNTIA_WEB} and ${WORKSPACE_ACCOUNTIA_API} (and add them to the
.env or CI agent env), or switch to named volumes such as
jenkins_workspace_accountia_web and jenkins_workspace_accountia_api and create
those volumes in the compose file; ensure any documentation or .env.example is
updated so agents know to set the paths.

In `@docker-compose-monitoring.yml`:
- Line 5: Replace the three image tags that use :latest (e.g., "image:
prom/prometheus:latest" and the Grafana and AlertManager lines) with explicit
versioned tags to ensure reproducible deployments, and remove the hardcoded
Grafana admin credential by wiring GF_SECURITY_ADMIN_PASSWORD to an environment
variable or secret (e.g., GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD})
instead of using "admin"; also add GF_USERS_ALLOW_SIGN_UP=false and require the
deployer to supply a strong 12+ character password via the
GRAFANA_ADMIN_PASSWORD secret/ENV rather than embedding it in the compose file.

In `@Dockerfile`:
- Around line 16-26: The final image currently runs as root; create and use a
non-root user in the runner stage by adding a user (e.g., "appuser" or "node"),
chown the application directory (WORKDIR /app) and any copied files to that user
after the COPY steps, and set USER to that user before EXPOSE/CMD so the
container no longer runs as root; update Dockerfile runner stage (WORKDIR, COPY
lines, EXPOSE, CMD) to include the user creation, chown, and USER instruction.
- Around line 19-23: The runtime image is missing env.js which next.config.ts
imports (causing module resolution errors when running next start); update the
Dockerfile runtime COPY stage to also COPY --from=builder /app/env.js ./ (i.e.,
include env.js alongside next.config.ts and package.json) so env.js is present
at runtime for next.config.ts to import.

In `@Jenkinsfile`:
- Around line 11-12: Replace hardcoded SOURCE_DIR and any other hardcoded
workspace references (e.g., the value assigned to SOURCE_DIR and similar uses
around lines referenced) with Jenkins' provided environment variable
${WORKSPACE}; update BUILD_DIR usage if it assumes a fixed source path by
deriving it from ${WORKSPACE} or using WORKSPACE for any workspace-relative
operations. Locate assignments to SOURCE_DIR and any occurrences of the
hardcoded path in the Jenkinsfile and substitute them to reference the WORKSPACE
variable so the pipeline works across multibranch and different agents.
- Line 39: Remove the "|| true" that is suppressing failures in the pipeline:
locate the shell invocations using the pattern "sh 'npm run lint:check || true'"
(and the other occurrence with "|| true") and change them to simply run the
command without "|| true" so the Jenkins step fails on lint/test errors; if you
need to capture output while still failing, run the command normally and rely on
Jenkins to report the non-zero exit code or explicitly check the exit status
after the command.
- Around line 53-62: After running the SonarQube scanner in the "SonarQube
Analysis" stage, add a blocking quality-gate check by invoking
waitForQualityGate() inside the withSonarQubeEnv context (or immediately after
the scanner run) so the pipeline fails when the SonarQube quality gate is not
met; update the stage that calls sh "${SCANNER_HOME}/bin/sonar-scanner" to call
waitForQualityGate() and handle the returned result (or throw an error) to
prevent later "Docker Build" / "Deploy" stages from running on failed quality
gates.

---

Duplicate comments:
In `@vitest.config.ts`:
- Line 5: Replace the unsafe use of import.meta.dirname when deriving __dirname:
locate the declaration of the __dirname variable (currently set via
import.meta.dirname) and change it to compute __dirname using
fileURLToPath(import.meta.url) combined with path.dirname; ensure you import or
reference fileURLToPath and path (from 'url' and 'path' respectively) as needed
so __dirname is derived with path.dirname(fileURLToPath(import.meta.url)).

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 17-20: The CI uses the "Setup Bun" step with bun-version: latest
which risks non-reproducible failures; update the GitHub Actions steps that use
oven-sh/setup-bun@v1 (both occurrences of the "Setup Bun" step) to pin
bun-version to a specific stable release (for example "1.3.13") instead of
"latest" so builds/lints/tests are reproducible across CI runs.
🪄 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: 7e060139-a84f-4ef2-a6bf-937dd1f53ac8

📥 Commits

Reviewing files that changed from the base of the PR and between 96cc703 and 8c97761.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (17)
  • .dockerignore
  • .github/workflows/ci.yml
  • .gitignore
  • Dockerfile
  • Jenkinsfile
  • README.md
  • alert_rules.yml
  • app/api/metrics/route.ts
  • app/layout.tsx
  • docker-compose-jenkins.yml
  • docker-compose-monitoring.yml
  • eslint.config.mjs
  • package.json
  • prettier.config.js
  • prometheus.yml
  • sonar-project.properties
  • vitest.config.ts
✅ Files skipped from review due to trivial changes (4)
  • .gitignore
  • README.md
  • eslint.config.mjs
  • prettier.config.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • package.json
  • sonar-project.properties

Comment thread .dockerignore
Comment on lines +1 to +11
node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
.next
out
build
coverage
jenkins_home

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

Add environment file exclusions to prevent accidental secret inclusion

Because the Docker build copies the full context, missing .env ignores can leak secrets into image layers/build cache.

Suggested change
+.env
+.env.*
+!.env.example
📝 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
node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
.next
out
build
coverage
jenkins_home
node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
.next
out
build
coverage
jenkins_home
.env
.env.*
!.env.example
🤖 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 @.dockerignore around lines 1 - 11, The .dockerignore currently omits common
build artifacts but not environment files, which risks leaking secrets into
Docker build context; update the .dockerignore (the file named ".dockerignore"
itself) to explicitly ignore environment files by adding entries such as .env,
*.env, .env.*, .env.local, .env.production, .env.development, .env.test, and any
project-specific secret filenames so they are excluded from the build context
and image layers.

Comment thread app/api/metrics/route.ts
Comment on lines +7 to +25
export async function GET() {
try {
// On lance la collecte si elle n'est pas déjà active
client.collectDefaultMetrics({ register });

const metrics = await register.metrics();

return new NextResponse(metrics, {
headers: {
'Content-Type': 'text/plain; version=0.0.4; charset=utf-8',
'Cache-Control': 'no-store, no-cache, must-revalidate',
},
});
} catch {
// Si c'est déjà enregistré, on renvoie juste les métriques
const metrics = await register.metrics();
return new NextResponse(metrics, {
headers: { 'Content-Type': 'text/plain; version=0.0.4; charset=utf-8' },
});

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

🧩 Analysis chain

🌐 Web query:

For prom-client v15.x, is collectDefaultMetrics intended to be called once at startup (not per HTTP request), and what happens if called repeatedly on the same registry?

💡 Result:

For prom-client v15.x, collectDefaultMetrics is intended to be called once during process startup (not per HTTP request). Evidence: the prom-client docs state that “Default metrics are collected on scrape of metrics endpoint, not on an interval,” and show calling collectDefaultMetrics once (typically at app startup) rather than inside the request handler. [1][2] What happens if you call it repeatedly on the same registry? 1) It will re-register the same default metrics on the registry, which can cause errors/panics about duplicate metrics. Evidence: prom-client throws when a metric name is already registered (example error: “A metric with the name … has already been registered”), and a maintainer response explains that adding DefaultMetrics twice to the default Registry happens when collectDefaultMetrics is called from two places; the fix is to “only call collectDefaultMetrics once.” [3] 2) If you want to avoid the duplicate-registration behavior, use a separate Registry instance (or ensure collectDefaultMetrics is only invoked once per registry). Evidence: prom-client’s collectDefaultMetrics supports an explicit registry option, and docs describe registering default metrics to a custom registry to keep them separate from application metrics. [4][1] So: call collectDefaultMetrics once per process (once per registry). Calling it repeatedly for the same registry leads to duplicate metric registration attempts and will typically throw an error/panic for metric names already registered. [3]

Citations:


🏁 Script executed:

find . -name "route.ts" -path "*/api/metrics/*" | head -5

Repository: mAmineChniti/accountia-web

Length of output: 97


🏁 Script executed:

cat app/api/metrics/route.ts 2>/dev/null || echo "File not found, searching..."
git ls-files | grep -E "metrics.*route\.ts"

Repository: mAmineChniti/accountia-web

Length of output: 917


🏁 Script executed:

cat package.json | grep -A 2 -B 2 "prom-client"

Repository: mAmineChniti/accountia-web

Length of output: 203


Initialize default metrics once at module load; don't call on every request.

collectDefaultMetrics is called on every GET request (line 10), but prom-client v15.1.3 requires it to be called once per process. Calling it repeatedly on the same registry throws a "metric already registered" error. The catch block (lines 20–25) silently swallows these errors, accidentally working around the issue instead of fixing it. This violates the API contract and masks real failures.

Move the collectDefaultMetrics call to module initialization and remove the try-catch error suppression:

Proposed fix
 import { NextResponse } from 'next/server';
 import * as client from 'prom-client';

 // On utilise le registre par défaut de prom-client
 const register = client.register;
+let defaultMetricsInitialized = false;

 export async function GET() {
   try {
-    // On lance la collecte si elle n'est pas déjà active
-    client.collectDefaultMetrics({ register });
+    if (!defaultMetricsInitialized) {
+      client.collectDefaultMetrics({ register });
+      defaultMetricsInitialized = true;
+    }

     const metrics = await register.metrics();

     return new NextResponse(metrics, {
       headers: {
         'Content-Type': 'text/plain; version=0.0.4; charset=utf-8',
         'Cache-Control': 'no-store, no-cache, must-revalidate',
       },
     });
-  } catch {
-    // Si c'est déjà enregistré, on renvoie juste les métriques
-    const metrics = await register.metrics();
-    return new NextResponse(metrics, {
-      headers: { 'Content-Type': 'text/plain; version=0.0.4; charset=utf-8' },
-    });
+  } catch {
+    return NextResponse.json(
+      { error: 'Unable to produce metrics' },
+      { status: 500 }
+    );
   }
 }
🤖 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 `@app/api/metrics/route.ts` around lines 7 - 25, Move the call to
collectDefaultMetrics out of the GET handler and into module initialization so
it runs once per process (e.g., call collectDefaultMetrics({ register }) at
top-level when the module loads), remove the try-catch around register.metrics()
in the GET function, and have GET simply await register.metrics() and return it
with the appropriate headers; refer to collectDefaultMetrics, register, and the
exported GET function when making the change.

Comment thread app/layout.tsx Outdated
Comment on lines +5 to +12
privileged: true
user: root
ports:
- '8080:8080'
- '50000:50000'
volumes:
- ./jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock

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

Avoid running Jenkins as privileged root with host Docker socket access

Line 5, Line 6, and Line 12 together create a host-compromise path (container escape equivalent privileges). This is a release blocker for CI hardening.

🤖 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 `@docker-compose-jenkins.yml` around lines 5 - 12, The compose snippet exposes
a host-compromise path by using privileged: true, user: root and binding
/var/run/docker.sock; remove privileged: true and user: root entries and stop
mounting the host Docker socket directly (the privileged/user symbols and the
volumes entry referencing /var/run/docker.sock), and instead add a controlled
Docker-in-Docker sidecar (e.g., image docker:dind) or a docker socket proxy and
configure the Jenkins container to use DOCKER_HOST (tcp) to talk to that sidecar
over the compose network; ensure the sidecar is hardened (use TLS or an
authenticated proxy) and keep the jenkins_home volume mount but avoid granting
host root or direct socket access.

Comment on lines +13 to +14
- C:\Users\Asus\Desktop\accountia-web:/var/jenkins_home/workspace/accountia-web
- C:\Users\Asus\Desktop\accountia-api:/var/jenkins_home/workspace/accountia-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

Replace machine-specific Windows mounts with parameterized paths

Line 13 and Line 14 hardcode one developer machine path, so this compose file will fail on other hosts/agents. Use environment variables (or named volumes) for workspace mapping.

Suggested change
-      - C:\Users\Asus\Desktop\accountia-web:/var/jenkins_home/workspace/accountia-web
-      - C:\Users\Asus\Desktop\accountia-api:/var/jenkins_home/workspace/accountia-api
+      - ${ACCOUNTIA_WEB_PATH}:/var/jenkins_home/workspace/accountia-web
+      - ${ACCOUNTIA_API_PATH}:/var/jenkins_home/workspace/accountia-api
📝 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
- C:\Users\Asus\Desktop\accountia-web:/var/jenkins_home/workspace/accountia-web
- C:\Users\Asus\Desktop\accountia-api:/var/jenkins_home/workspace/accountia-api
- ${ACCOUNTIA_WEB_PATH}:/var/jenkins_home/workspace/accountia-web
- ${ACCOUNTIA_API_PATH}:/var/jenkins_home/workspace/accountia-api
🤖 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 `@docker-compose-jenkins.yml` around lines 13 - 14, The compose file currently
hardcodes Windows developer paths in the workspace mounts; replace those two
host-specific bindings (the lines mounting C:\Users\Asus\Desktop\accountia-web
and accountia-api) with parameterized values or named volumes so other
hosts/agents can run the stack. Update the volume entries to use environment
variables like ${WORKSPACE_ACCOUNTIA_WEB} and ${WORKSPACE_ACCOUNTIA_API} (and
add them to the .env or CI agent env), or switch to named volumes such as
jenkins_workspace_accountia_web and jenkins_workspace_accountia_api and create
those volumes in the compose file; ensure any documentation or .env.example is
updated so agents know to set the paths.

Comment thread Dockerfile
Comment on lines +16 to +26
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/next.config.ts ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json

EXPOSE 3000
CMD ["npm", "start"]

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

Run the final container as a non-root user

The runner stage has no USER, so the app runs as root (also flagged by DS-0002). Please drop privileges in the final image.

Suggested change
 FROM node:20-alpine AS runner
 WORKDIR /app
 ENV NODE_ENV=production
@@
 COPY --from=builder /app/package.json ./package.json
+RUN chown -R node:node /app
+USER node

 EXPOSE 3000
 CMD ["npm", "start"]
📝 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
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/next.config.ts ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
EXPOSE 3000
CMD ["npm", "start"]
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/next.config.ts ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
RUN chown -R node:node /app
USER node
EXPOSE 3000
CMD ["npm", "start"]
🤖 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 `@Dockerfile` around lines 16 - 26, The final image currently runs as root;
create and use a non-root user in the runner stage by adding a user (e.g.,
"appuser" or "node"), chown the application directory (WORKDIR /app) and any
copied files to that user after the COPY steps, and set USER to that user before
EXPOSE/CMD so the container no longer runs as root; update Dockerfile runner
stage (WORKDIR, COPY lines, EXPOSE, CMD) to include the user creation, chown,
and USER instruction.

Comment thread Dockerfile
Comment on lines +19 to +23
COPY --from=builder /app/next.config.ts ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify next.config.ts imports env.js and Dockerfile does/doesn't copy it.
rg -n "import './env.js'" next.config.ts
rg -n "COPY --from=builder /app/env.js" Dockerfile

Repository: mAmineChniti/accountia-web

Length of output: 91


Copy env.js into the runner image to avoid runtime config failure

Line 19 copies next.config.ts, which imports ./env.js on line 2; without copying env.js to the runtime stage, next start will fail with module resolution errors.

Suggested change
 COPY --from=builder /app/next.config.ts ./
+COPY --from=builder /app/env.js ./
 COPY --from=builder /app/public ./public
📝 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
COPY --from=builder /app/next.config.ts ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/next.config.ts ./
COPY --from=builder /app/env.js ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
🤖 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 `@Dockerfile` around lines 19 - 23, The runtime image is missing env.js which
next.config.ts imports (causing module resolution errors when running next
start); update the Dockerfile runtime COPY stage to also COPY --from=builder
/app/env.js ./ (i.e., include env.js alongside next.config.ts and package.json)
so env.js is present at runtime for next.config.ts to import.

Comment thread Jenkinsfile
Comment on lines +11 to +12
SOURCE_DIR = "/var/jenkins_home/workspace/accountia-web"
BUILD_DIR = "/tmp/accountia_build"

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

Use Jenkins-provided workspace instead of hardcoded source path

Line 11 hardcodes one workspace path, which breaks across multibranch/job naming and agent differences. Prefer ${WORKSPACE} directly.

Also applies to: 22-22, 76-76

🤖 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 `@Jenkinsfile` around lines 11 - 12, Replace hardcoded SOURCE_DIR and any other
hardcoded workspace references (e.g., the value assigned to SOURCE_DIR and
similar uses around lines referenced) with Jenkins' provided environment
variable ${WORKSPACE}; update BUILD_DIR usage if it assumes a fixed source path
by deriving it from ${WORKSPACE} or using WORKSPACE for any workspace-relative
operations. Locate assignments to SOURCE_DIR and any occurrences of the
hardcoded path in the Jenkinsfile and substitute them to reference the WORKSPACE
variable so the pipeline works across multibranch and different agents.

Comment thread Jenkinsfile
Comment thread Jenkinsfile
Comment on lines +53 to +62
stage('SonarQube Analysis') {
steps {
dir("${BUILD_DIR}") {
echo 'Analyse de la qualité du code (SonarQube)...'
withSonarQubeEnv('SonarQube') {
sh "${SCANNER_HOME}/bin/sonar-scanner"
}
}
}
}

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

🧩 Analysis chain

🏁 Script executed:

git ls-files | grep -i jenkinsfile | head -20

Repository: mAmineChniti/accountia-web

Length of output: 82


🏁 Script executed:

cat -n Jenkinsfile

Repository: mAmineChniti/accountia-web

Length of output: 3771


Add a SonarQube quality-gate blocking step before Docker build/deploy

The SonarQube Analysis stage runs the scanner but does not enforce quality gate results. Add waitForQualityGate() after the SonarQube stage to prevent builds with quality gate failures from proceeding to Docker Build and Deploy stages.

🤖 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 `@Jenkinsfile` around lines 53 - 62, After running the SonarQube scanner in the
"SonarQube Analysis" stage, add a blocking quality-gate check by invoking
waitForQualityGate() inside the withSonarQubeEnv context (or immediately after
the scanner run) so the pipeline fails when the SonarQube quality gate is not
met; update the stage that calls sh "${SCANNER_HOME}/bin/sonar-scanner" to call
waitForQualityGate() and handle the returned result (or throw an error) to
prevent later "Docker Build" / "Deploy" stages from running on failed quality
gates.

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

🤖 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 `@sonar-project.properties`:
- Line 13: Remove the hardcoded Sonar token from sonar.login in
sonar-project.properties and replace its usage with an environment-based
approach (e.g., read SONAR_TOKEN from CI/agent or pass -Dsonar.login at scan
time); revoke/rotate the exposed token immediately, remove the secret from the
repo (rewrite history or use git-secrets to purge), and update build/CI
configuration to inject SONAR_TOKEN securely so sonar.login is no longer stored
in source.
🪄 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: 67bc3916-2c1d-4f9b-9820-e0cc86a29a26

📥 Commits

Reviewing files that changed from the base of the PR and between 8c97761 and df47797.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • package.json
  • sonar-project.properties
  • tsconfig.sonar.json
✅ Files skipped from review due to trivial changes (1)
  • tsconfig.sonar.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json

Comment thread sonar-project.properties Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (3)
Jenkinsfile (3)

11-12: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use Jenkins workspace variables instead of hardcoded source paths.

Line 11 hardcodes a job-specific workspace path, which breaks across agents and multibranch job naming. Use ${WORKSPACE} (or pwd()) for workspace-relative operations.

Suggested patch
-        SOURCE_DIR = "/var/jenkins_home/workspace/accountia-web"
-        BUILD_DIR = "/tmp/accountia_build"
+        SOURCE_DIR = "${WORKSPACE}"
+        BUILD_DIR = "${WORKSPACE}/.jenkins-build-${BUILD_NUMBER}"

Also applies to: 22-22, 82-82

🤖 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 `@Jenkinsfile` around lines 11 - 12, The Jenkinsfile currently hardcodes
SOURCE_DIR and BUILD_DIR (SOURCE_DIR =
"/var/jenkins_home/workspace/accountia-web", BUILD_DIR =
"/tmp/accountia_build"), which breaks on different agents and multibranch jobs;
change SOURCE_DIR to use the Jenkins workspace variable (e.g., set SOURCE_DIR =
"${WORKSPACE}" or use pwd() where SOURCE_DIR is referenced) and make BUILD_DIR
workspace-relative (e.g., "${WORKSPACE}/tmp/accountia_build" or
"${WORKSPACE}/build") so all workspace operations use Jenkins-provided paths;
update every occurrence of SOURCE_DIR and BUILD_DIR in the Jenkinsfile
(including other instances noted around lines 22 and 82) to reference these
variables.

45-45: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not suppress lint/test failures in CI stages.

Line 45 and Line 54 use || true, so quality checks cannot fail the pipeline.

Suggested patch
-                    sh 'npm run lint:check || true'
+                    sh 'npm run lint:check'
@@
-                    sh 'npm run test:cov || true'
+                    sh 'npm run test:cov'

Also applies to: 54-54

🤖 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 `@Jenkinsfile` at line 45, The CI lint/test stages currently append "|| true"
to the shell steps (e.g., the sh lines containing "npm run lint:check || true"
and the similar command at the other occurrence), which masks failures; remove
the "|| true" from those sh commands in the Jenkinsfile so that failed lint or
test commands cause the pipeline to fail (ensure you update both occurrences).

59-68: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Block the pipeline on SonarQube quality gate before build/deploy.

Line 64 runs analysis, but the pipeline does not wait for gate status. Add a quality-gate wait to stop downstream Docker stages on failed quality.

Suggested patch
         stage('SonarQube Analysis') {
             steps {
                 dir("${BUILD_DIR}") {
                     echo 'Analyse de la qualite du code (SonarQube)...'
                     withSonarQubeEnv('SonarQube') {
                         sh "${SCANNER_HOME}/bin/sonar-scanner"
                     }
                 }
             }
         }
+        stage('SonarQube Quality Gate') {
+            steps {
+                timeout(time: 10, unit: 'MINUTES') {
+                    waitForQualityGate abortPipeline: true
+                }
+            }
+        }
🤖 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 `@Jenkinsfile` around lines 59 - 68, Add a blocking quality-gate wait after the
SonarQube analysis so the pipeline fails when the gate is not OK: inside the
"SonarQube Analysis" stage after running withSonarQubeEnv and sh
"${SCANNER_HOME}/bin/sonar-scanner", call waitForQualityGate() (optionally
wrapped in a timeout) and throw an error / error out the build if the returned
status is not "OK"; update the stage that currently uses withSonarQubeEnv and sh
to include this check so downstream Docker/build/deploy stages are prevented
when the quality gate fails.
🤖 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 `@Jenkinsfile`:
- Around line 63-64: Remove the hardcoded Sonar token from
sonar-project.properties (the sonar.login entry) and ensure the pipeline uses
Jenkins-managed credentials via withSonarQubeEnv('SonarQube') so the scanner run
(sh "${SCANNER_HOME}/bin/sonar-scanner") picks up the token from the
environment; update or remove any sonar.login lines in sonar-project.properties
and, if needed, set SONAR_TOKEN/sonar.login only via Jenkins credentials binding
or the SonarQube server configuration so no secret is stored in the repo.
- Around line 74-75: The pipeline currently tags and deploys the mutable
"latest" image which can cause wrong images to roll out; update the Docker
workflow so the deployment step uses the immutable tag built with
${BUILD_NUMBER} instead of ${DOCKER_IMAGE}:latest — i.e., ensure the build (sh
"docker build -t ${DOCKER_IMAGE}:${BUILD_NUMBER} .") and subsequent
tagging/deploy steps reference ${DOCKER_IMAGE}:${BUILD_NUMBER} (or remove the
automatic latest tag) so deployments always use the exact BUILD_NUMBER image.

---

Duplicate comments:
In `@Jenkinsfile`:
- Around line 11-12: The Jenkinsfile currently hardcodes SOURCE_DIR and
BUILD_DIR (SOURCE_DIR = "/var/jenkins_home/workspace/accountia-web", BUILD_DIR =
"/tmp/accountia_build"), which breaks on different agents and multibranch jobs;
change SOURCE_DIR to use the Jenkins workspace variable (e.g., set SOURCE_DIR =
"${WORKSPACE}" or use pwd() where SOURCE_DIR is referenced) and make BUILD_DIR
workspace-relative (e.g., "${WORKSPACE}/tmp/accountia_build" or
"${WORKSPACE}/build") so all workspace operations use Jenkins-provided paths;
update every occurrence of SOURCE_DIR and BUILD_DIR in the Jenkinsfile
(including other instances noted around lines 22 and 82) to reference these
variables.
- Line 45: The CI lint/test stages currently append "|| true" to the shell steps
(e.g., the sh lines containing "npm run lint:check || true" and the similar
command at the other occurrence), which masks failures; remove the "|| true"
from those sh commands in the Jenkinsfile so that failed lint or test commands
cause the pipeline to fail (ensure you update both occurrences).
- Around line 59-68: Add a blocking quality-gate wait after the SonarQube
analysis so the pipeline fails when the gate is not OK: inside the "SonarQube
Analysis" stage after running withSonarQubeEnv and sh
"${SCANNER_HOME}/bin/sonar-scanner", call waitForQualityGate() (optionally
wrapped in a timeout) and throw an error / error out the build if the returned
status is not "OK"; update the stage that currently uses withSonarQubeEnv and sh
to include this check so downstream Docker/build/deploy stages are prevented
when the quality gate fails.
🪄 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: 90d99f61-6085-43b8-9dc0-cd095400766f

📥 Commits

Reviewing files that changed from the base of the PR and between df47797 and 9ce085c.

📒 Files selected for processing (2)
  • Jenkinsfile
  • tsconfig.sonar.json
✅ Files skipped from review due to trivial changes (1)
  • tsconfig.sonar.json

Comment thread Jenkinsfile
Comment thread Jenkinsfile
Comment on lines +74 to +75
sh "docker build -t ${DOCKER_IMAGE}:${BUILD_NUMBER} ."
sh "docker tag ${DOCKER_IMAGE}:${BUILD_NUMBER} ${DOCKER_IMAGE}:latest"

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

Deploy the immutable build tag, not latest.

Line 87 deploys accountia-web-app:latest while build tags are created per ${BUILD_NUMBER}. Using latest can roll out the wrong image when builds overlap.

Suggested patch
-                    sh "docker run -d --name accountia-web-app -p 3001:3000 accountia-web-app:latest"
+                    sh "docker run -d --name accountia-web-app -p 3001:3000 ${DOCKER_IMAGE}:${BUILD_NUMBER}"

Also applies to: 87-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 `@Jenkinsfile` around lines 74 - 75, The pipeline currently tags and deploys
the mutable "latest" image which can cause wrong images to roll out; update the
Docker workflow so the deployment step uses the immutable tag built with
${BUILD_NUMBER} instead of ${DOCKER_IMAGE}:latest — i.e., ensure the build (sh
"docker build -t ${DOCKER_IMAGE}:${BUILD_NUMBER} .") and subsequent
tagging/deploy steps reference ${DOCKER_IMAGE}:${BUILD_NUMBER} (or remove the
automatic latest tag) so deployments always use the exact BUILD_NUMBER image.

@mAmineChniti
mAmineChniti merged commit 2b9b087 into master May 5, 2026
5 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request May 12, 2026

This branch was successfully deployed

1 active deployment
Preview — 32f9c697 Deployed May 5, 2026 by vercel[bot]
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