Skip to content

Fix/app clean up on logout - #408

Open
egalvis27 wants to merge 13 commits into
mainfrom
fix/app-clean-up-on-logout
Open

Fix/app clean up on logout#408
egalvis27 wants to merge 13 commits into
mainfrom
fix/app-clean-up-on-logout

Conversation

@egalvis27

@egalvis27 egalvis27 commented Jul 14, 2026

Copy link
Copy Markdown

What is Changed / Added


  • Added a centralized user-session shutdown flow in the main process:
    • New logout orchestrator with single-flight protection.
    • New resource cleanup entry point to close sync, antivirus, virtual drive, DB, and UI resources in one place.
  • Added logout synchronization for deeplink login:
    • Deeplink handler now waits for any in-flight logout before starting login.
    • Prevents cross-account race conditions during A -> B session switches.
  • Refactored auth utilities into focused modules:
    • Headers helpers, user-session getter, and logout logic are now separated for cleaner dependencies and easier testing.
  • Fixed config restore strategy for product flags:
    • Removed availableUserProducts from recoverable saved config.
    • Product availability now relies on fresh backend fetch on login.
  • Improved products update propagation:
    • Main process products service now emits updates reliably.
    • Renderer antivirus hook now reacts to live product updates and unlocks correctly when entitlement changes.
  • Improved dependency injection user cache handling:
    • Added explicit cache clear/update behavior to avoid stale user state after logout/login.
  • Added and updated tests across modified areas:
    • New tests for logout orchestration and deeplink synchronization.
    • Extended tests for auth, payments products flow, refresh-token behavior, antivirus hook updates, and DI user provider behavior.

Why

  • To guarantee complete and deterministic cleanup on logout.
  • To eliminate race conditions between logout and next login.
  • To avoid stale product entitlements from restored local state.
  • To ensure feature gating (especially Antivirus) reflects current backend entitlements.
  • To reduce auth-module coupling and improve maintainability.
  • To protect regressions with focused test coverage on critical session lifecycle paths.

Summary by CodeRabbit

  • New Features

    • Improved logout handling with comprehensive session cleanup and reliable resource shutdown.
    • Added configuration restoration and reset support, including removal of stored product data.
    • Added authentication helpers for session retrieval and secure API requests.
    • Product availability updates now refresh the app state when changes are detected.
    • Pending background synchronization and token refresh jobs can be canceled.
  • Bug Fixes

    • Improved deep-link login, session refresh, and antivirus availability handling.
  • Tests

    • Expanded coverage for authentication, logout, synchronization, configuration, and login flows.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

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 changes extract authentication helpers, add coordinated logout resource cleanup, update deeplink and user-session handling, move product synchronization in the login flow, and add tests for the new behavior and runtime mocks.

Changes

Authentication and session lifecycle

Layer / File(s) Summary
Logout cleanup orchestration
src/apps/main/auth/close-user-session-resources.ts, src/backend/features/auth/logout.ts, src/apps/main/token-scheduler/TokenScheduler.ts, src/apps/main/remote-sync/service.ts, src/backend/features/config/reset-config.ts
Logout now performs serialized, guarded cleanup for session resources, scheduled jobs, remote sync, cached users, configuration, and related application services.
Auth module boundaries and deeplink synchronization
src/backend/features/auth/*, src/apps/main/auth/deeplink/*, src/apps/main/auth/service.ts, src/apps/main/auth/handlers.ts, src/apps/main/interface.d.ts, src/apps/main/preload.d.ts, src/apps/main/realtime.ts, src/infra/drive-server/...
Authentication headers, user retrieval, logout, and configuration restoration use dedicated backend modules. Deeplink initialization updates the dependency-injection user provider.
Product synchronization and antivirus availability
src/backend/features/payments/services/get-user-available-products-and-store.ts, src/core/bootstrap/register-session-event-handlers.ts, src/apps/renderer/hooks/antivirus/useAntivirus.test.tsx
Product retrieval runs earlier during login, handles empty or failed responses, and updates the antivirus hook when product availability changes.
Runtime and test support
vitest.setup.main.ts, src/backend/features/virtual-drive/services/daemon.service.test.ts, src/apps/main/auth/refresh-token/*, src/context/shared/domain/value-objects/BucketEntry.ts
Mocks preserve real module exports, refresh-token tests load modules after mock setup, and the file-size constant import is corrected.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: alexismora

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title identifies the central logout cleanup work, although its wording is informal and slightly unclear.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/app-clean-up-on-logout

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.

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

🧹 Nitpick comments (1)
src/infra/drive-server/services/auth/auth.service.test.ts (1)

26-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add unit tests for the logout method.

While test coverage exists for login, access, and refresh, the logout method in AuthService lacks a dedicated test block. Consider adding a describe('logout', ...) block to test the success and error states of the logout request, particularly since its return value was recently updated to right(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 `@src/infra/drive-server/services/auth/auth.service.test.ts` around lines 26 -
27, Add a dedicated describe('logout', ...) block in the AuthService test suite
covering both successful and failing logout requests. Assert that the success
case returns right(true), and verify the error case preserves the expected
failure result while reusing the existing test setup and mocking patterns.
🤖 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 `@src/apps/main/auth/close-user-session-resources.ts`:
- Around line 40-45: Wrap the tray reset and widget hiding operations near lines
40-45 in runCleanupStep so synchronous UI errors are caught without aborting
subsequent cleanup. Also wrap widget destruction near lines 100-102 in
runCleanupStep, retrieving getWidget() inside that callback as required; apply
both changes in close-user-session-resources.ts.

In `@src/apps/main/auth/logout.test.ts`:
- Around line 102-103: Update the USER_LOGGED_OUT assertion in the logout test
to match the mock call structure returned by calls(eventBusEmitMock), wrapping
the event name in an argument array like the USER_AVAILABLE_PRODUCTS_UPDATED
assertion. Keep the existing event value unchanged.

In
`@src/apps/main/background-processes/antivirus/try-setup-antivirus-ipc-and-initialize.ts`:
- Around line 13-16: Update the initialization flow around setupAntivirusIpc()
to call cleanupAntivirusIpc() before registering new handlers, ensuring existing
IPC listeners are removed on repeated invocations while preserving the
subsequent removeAntivirusIpcHandlers assignment.

In `@src/apps/main/token-scheduler/TokenScheduler.ts`:
- Around line 10-12: Update TokenScheduler to maintain a static collection of
Job instances created by its schedule method. Store each non-null result from
nodeSchedule.scheduleJob in that collection, and change cancelAllJobs to cancel
only those tracked jobs instead of iterating over nodeSchedule.scheduledJobs
globally.

---

Nitpick comments:
In `@src/infra/drive-server/services/auth/auth.service.test.ts`:
- Around line 26-27: Add a dedicated describe('logout', ...) block in the
AuthService test suite covering both successful and failing logout requests.
Assert that the success case returns right(true), and verify the error case
preserves the expected failure result while reusing the existing test setup and
mocking patterns.
🪄 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: ae67c103-a741-412d-a529-01161a2635fd

📥 Commits

Reviewing files that changed from the base of the PR and between e6b1171 and 9ec33a4.

📒 Files selected for processing (32)
  • src/apps/main/auth/close-user-session-resources.ts
  • src/apps/main/auth/deeplink/handle-deeplink.test.ts
  • src/apps/main/auth/deeplink/handle-deeplink.ts
  • src/apps/main/auth/deeplink/initialize_current_user.ts
  • src/apps/main/auth/handlers.ts
  • src/apps/main/auth/headers.ts
  • src/apps/main/auth/logout.test.ts
  • src/apps/main/auth/logout.ts
  • src/apps/main/auth/refresh-token/create-token-schedule-with-retry.test.ts
  • src/apps/main/auth/refresh-token/refresh-token.test.ts
  • src/apps/main/auth/service.test.ts
  • src/apps/main/auth/service.ts
  • src/apps/main/auth/user-session.ts
  • src/apps/main/background-processes/antivirus/try-setup-antivirus-ipc-and-initialize.ts
  • src/apps/main/config.ts
  • src/apps/main/event-bus.ts
  • src/apps/main/remote-sync/service.ts
  • src/apps/main/token-scheduler/TokenScheduler.ts
  • src/apps/renderer/hooks/antivirus/useAntivirus.test.tsx
  • src/apps/renderer/hooks/antivirus/useAntivirus.tsx
  • src/apps/shared/dependency-injection/DependencyInjectionUserProvider.test.ts
  • src/apps/shared/dependency-injection/DependencyInjectionUserProvider.ts
  • src/backend/features/payments/services/get-user-available-products-and-store.test.ts
  • src/backend/features/payments/services/get-user-available-products-and-store.ts
  • src/backend/features/virtual-drive/services/daemon.service.test.ts
  • src/context/shared/domain/value-objects/BucketEntry.ts
  • src/core/bootstrap/register-session-event-handlers.ts
  • src/infra/drive-server/client/drive-server.client.instance.test.ts
  • src/infra/drive-server/client/drive-server.client.instance.ts
  • src/infra/drive-server/services/auth/auth.service.test.ts
  • src/infra/drive-server/services/auth/auth.service.ts
  • vitest.setup.main.ts

Comment thread src/apps/main/auth/close-user-session-resources.ts Outdated
Comment thread src/backend/features/auth/logout.test.ts
Comment on lines +13 to +16
try {
logger.debug({ tag: 'ANTIVIRUS', msg: '[Main] Setting up antivirus IPC handlers' });
setupAntivirusIpc();
const antivirusIpc = setupAntivirusIpc();
removeAntivirusIpcHandlers = antivirusIpc.removeMessagesHandlers ?? null;

@coderabbitai coderabbitai Bot Jul 14, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Prevent memory and IPC listener leaks on successive logins.

If this function is invoked multiple times across login sessions without a process restart, setupAntivirusIpc() will attach duplicate IPC handlers. Call cleanupAntivirusIpc() before setting up new handlers to clean up any existing ones.

🔧 Proposed fix
   try {
     logger.debug({ tag: 'ANTIVIRUS', msg: '[Main] Setting up antivirus IPC handlers' });
+    cleanupAntivirusIpc();
     const antivirusIpc = setupAntivirusIpc();
     removeAntivirusIpcHandlers = antivirusIpc.removeMessagesHandlers ?? null;
📝 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
try {
logger.debug({ tag: 'ANTIVIRUS', msg: '[Main] Setting up antivirus IPC handlers' });
setupAntivirusIpc();
const antivirusIpc = setupAntivirusIpc();
removeAntivirusIpcHandlers = antivirusIpc.removeMessagesHandlers ?? null;
try {
logger.debug({ tag: 'ANTIVIRUS', msg: '[Main] Setting up antivirus IPC handlers' });
cleanupAntivirusIpc();
const antivirusIpc = setupAntivirusIpc();
removeAntivirusIpcHandlers = antivirusIpc.removeMessagesHandlers ?? null;
🤖 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/apps/main/background-processes/antivirus/try-setup-antivirus-ipc-and-initialize.ts`
around lines 13 - 16, Update the initialization flow around setupAntivirusIpc()
to call cleanupAntivirusIpc() before registering new handlers, ensuring existing
IPC listeners are removed on repeated invocations while preserving the
subsequent removeAntivirusIpcHandlers assignment.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@egalvis27 check this message

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment thread src/apps/main/token-scheduler/TokenScheduler.ts

@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 `@src/apps/main/auth/close-user-session-resources.ts`:
- Around line 40-50: Update the widget destruction logic in
closeUserSessionResources by replacing the later outer-scope widget reference
with a separate runCleanupStep named “destroy-widget”. Inside its task, retrieve
the widget via getWidget(), guard against a missing or destroyed instance, and
call destroy() on the current widget; keep the existing hide-widget-and-tray
cleanup scoped to its own widget reference.
🪄 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: 72ac2a37-9a36-4757-a938-1d48f0fc9235

📥 Commits

Reviewing files that changed from the base of the PR and between 9ec33a4 and 64da3e3.

📒 Files selected for processing (1)
  • src/apps/main/auth/close-user-session-resources.ts

Comment on lines +40 to +50
await runCleanupStep({
step: 'hide-widget-and-tray',
task: () => {
resetTrayStatus('IDLE');
const widget = getWidget();

if (widget && !widget.isDestroyed()) {
widget.hide();
}
},
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Fix scoping to prevent ReferenceError on widget destruction.

Moving const widget = getWidget(); into this closure correctly guards the hiding logic but removes widget from the outer scope. This will either cause a ReferenceError later in the file at line 105 (if (widget && !widget.isDestroyed())) or silently skip destruction if an outer variable was left undefined.

As noted in a previous review, to safely destroy the widget, its destruction logic at lines 105-107 must also be wrapped in a cleanup step that retrieves the widget reference again.

🔧 Proposed fix for lines 105-107

Please replace lines 105-107 with the following:

  await runCleanupStep({
    step: 'destroy-widget',
    task: () => {
      const currentWidget = getWidget();
      if (currentWidget && !currentWidget.isDestroyed()) {
        currentWidget.destroy();
      }
    },
  });

Run the following script to verify if widget is still declared in the outer scope:

#!/bin/bash
# Description: Check if `widget` is declared in the outer scope.
rg -n 'widget' src/apps/main/auth/close-user-session-resources.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 `@src/apps/main/auth/close-user-session-resources.ts` around lines 40 - 50,
Update the widget destruction logic in closeUserSessionResources by replacing
the later outer-scope widget reference with a separate runCleanupStep named
“destroy-widget”. Inside its task, retrieve the widget via getWidget(), guard
against a missing or destroyed instance, and call destroy() on the current
widget; keep the existing hide-widget-and-tray cleanup scoped to its own widget
reference.

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

Caution

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

⚠️ Outside diff range comments (1)
src/apps/main/auth/deeplink/handle-deeplink.ts (1)

18-21: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Potential indefinite block if logout cleanup hangs.

waitForLogoutToFinish() awaits logoutInFlight with no timeout. If any guarded cleanup step in closeUserSessionResources (e.g., antivirus IPC, virtual-drive teardown) never settles rather than rejecting, this await blocks forever, permanently stalling new deeplink logins.

🔍 Verify cleanup-step timeout guarantees
#!/bin/bash
ast-grep run --pattern 'function runCleanupStep($$$) { $$$ }' --lang typescript src/apps/main/auth/close-user-session-resources.ts
cat -n src/apps/main/auth/logout.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 `@src/apps/main/auth/deeplink/handle-deeplink.ts` around lines 18 - 21, Update
handleDeeplink and the waitForLogoutToFinish flow so a logout cleanup that never
settles cannot block deeplink login indefinitely. Add a bounded timeout around
waiting for logoutInFlight, while preserving normal completion and rejection
handling and allowing the deeplink flow to proceed after the timeout.
🧹 Nitpick comments (1)
src/apps/main/auth/close-user-session-resources.test.ts (1)

98-164: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add coverage for guarded-cleanup resilience.

executeCloseUserSessionResources wraps each step in runCleanupStep, implying failures in one step shouldn't block subsequent steps (e.g., antivirus shutdown failing shouldn't prevent opening the auth window). No test here exercises that guarantee — consider adding a case where one step's mock rejects/throws and asserting later steps still execute.

🤖 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/apps/main/auth/close-user-session-resources.test.ts` around lines 98 -
164, Add a test for executeCloseUserSessionResources where an individual cleanup
mock, such as shutdownMock, rejects or throws; assert that subsequent cleanup
steps, including createAuthWindowMock and final resource cleanup, still execute.
Keep the existing successful and concurrent-deduplication coverage unchanged.
🤖 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 `@src/apps/main/auth/close-user-session-resources.test.ts`:
- Around line 109-163: Ensure shared Vitest mocks are reset between tests in
closeUserSessionResources tests, either by enabling the project’s existing
global clear-mocks configuration or adding explicit mock clearing in the test
setup. Preserve the existing assertions, especially the mid-flight
createAuthWindowMock check in “deduplicates concurrent cleanup requests,” so it
starts with no calls from the preceding test.

In `@src/apps/main/auth/deeplink/initialize-current-user.test.ts`:
- Around line 4-16: Fix the Prettier formatting in the hoisted mock declaration
at the top of initialize-current-user.test.ts, including the destructuring and
object literal layout around refreshMock, updateCredentialsMock, configGetMock,
and updateUserMock. Apply the repository’s standard formatter without changing
behavior.

In `@src/apps/main/remote-sync/service.test.ts`:
- Line 8: Reformat the long `vi.hoisted` mock declaration and the corresponding
line 56 declaration to comply with the 120-character max-len rule. Break the
destructured names and object properties across multiple lines while preserving
the existing mocks and behavior.
- Around line 71-112: Update the cancelPendingRemoteSync test to retrieve the
latest debounceMock result rather than index 0, ensuring it asserts against the
debounced function created by the current serviceModule import. Keep the
existing cancel invocation and expectation unchanged.

In `@src/core/bootstrap/register-session-event-handlers.test.ts`:
- Line 89: Remove the unused setBoundsOfWidgetByPathSpy declaration from the
test setup in register-session-event-handlers.test.ts; do not add an assertion
unless the test explicitly needs to verify that call.

---

Outside diff comments:
In `@src/apps/main/auth/deeplink/handle-deeplink.ts`:
- Around line 18-21: Update handleDeeplink and the waitForLogoutToFinish flow so
a logout cleanup that never settles cannot block deeplink login indefinitely.
Add a bounded timeout around waiting for logoutInFlight, while preserving normal
completion and rejection handling and allowing the deeplink flow to proceed
after the timeout.

---

Nitpick comments:
In `@src/apps/main/auth/close-user-session-resources.test.ts`:
- Around line 98-164: Add a test for executeCloseUserSessionResources where an
individual cleanup mock, such as shutdownMock, rejects or throws; assert that
subsequent cleanup steps, including createAuthWindowMock and final resource
cleanup, still execute. Keep the existing successful and
concurrent-deduplication coverage unchanged.
🪄 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: d4cca27b-d805-4c4b-a39b-166f2b9dae20

📥 Commits

Reviewing files that changed from the base of the PR and between 22b055b and b764241.

📒 Files selected for processing (7)
  • src/apps/main/auth/close-user-session-resources.test.ts
  • src/apps/main/auth/deeplink/handle-deeplink.test.ts
  • src/apps/main/auth/deeplink/handle-deeplink.ts
  • src/apps/main/auth/deeplink/initialize-current-user.test.ts
  • src/apps/main/auth/deeplink/initialize-current-user.ts
  • src/apps/main/remote-sync/service.test.ts
  • src/core/bootstrap/register-session-event-handlers.test.ts

Comment thread src/apps/main/auth/close-user-session-resources.test.ts
Comment thread src/apps/main/auth/deeplink/initialize-current-user.test.ts Outdated
Comment thread src/apps/main/remote-sync/service.test.ts Outdated
Comment thread src/apps/main/remote-sync/service.test.ts
Comment thread src/core/bootstrap/register-session-event-handlers.test.ts Outdated

@AlexisMora AlexisMora 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.

Please, stick to one relate set of changes per pr, this pr introduces not only "clean up on logout change" but rather product availability and so on


export async function handleDeeplink({ url }: Props) {
try {
await waitForLogoutToFinish();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This is not this function responsability, but rather on the callers, this way we have have the necessary logic separated and the function only does what it is described.

Also, why this scenario could happen? why the deeplink feature (which is for login) could be happening when logging out?

@@ -23,7 +24,7 @@ export async function initializeCurrentUser() {
mnemonic: currentUser.mnemonic,
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why not just ConfigStore.set('userData', updatedUser); ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The app has some important classes that cache configuration values like this one. If only the configuration is updated, the cached values remain, and the user may be shown the previous user's information when logging in. This function updates both the configuration and the cache to prevent this proble


let closeUserSessionResourcesInFlight: Promise<void> | undefined;

async function runCleanupStep({ step, task }: CleanupStepProps) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done

Comment thread src/apps/main/auth/close-user-session-resources.ts
@@ -0,0 +1,20 @@
import packageConfig from '../../../../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.

This could be moved into backend/features/auth no? since you are just creaqting it (And generate tests for it)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done

Comment on lines +46 to +51
eventBus.emit('USER_AVAILABLE_PRODUCTS_UPDATED', userProducts);
} catch (error) {
logger.error({
tag: 'PRODUCTS',
msg: 'Found difference in user products, storing and emitting update',
msg: 'Failed to resolve available user products',
error,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why? the reason i did this originally was because we want to emit and propagate the changes only when the products change

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I agree. You are right, the intended behavior is to propagate only real product changes. I have updated the service to emit USER_AVAILABLE_PRODUCTS_UPDATED only when fetched products differ from stored products, and adjusted the tests accordingly.

import { ValueObject } from './ValueObject';

export class BucketEntry extends ValueObject<number> {
static readonly MAX_SIZE = ABSOLUTE_UPLOAD_FILE_SIZE_LIMIT;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why not delete the property whatsoever and use ABSOLUTE_UPLOAD_FILE_SIZE_LIMIT

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Sorry about that—it was something the AI did that didn't make sense, and I forgot to remove it.

Comment thread src/context/shared/domain/value-objects/BucketEntry.ts Outdated
return left(new Error('logout request was not successful'));
}
return right(response.data);
return right(true);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This function returns a Boolean value, and previously it was returning data that wasn't needed, which caused a type error.

}

// Keep product flags in sync even if later UI steps fail.
await getUserAvailableProductsAndStore();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Woulndt it be better to leave it where originally was? so if changes have happened there the renderer already can act accordingly

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I moved it earlier on purpose so product flags are refreshed even if a later login/UI step fails.
The renderer won’t miss this, because on subscribe it also receives the current stored products immediately, so it can still react correctly even if the event fired before mount.

@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

Caution

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

⚠️ Outside diff range comments (2)
src/apps/main/auth/deeplink/handle-deeplink.test.ts (1)

89-96: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Await completed logout before deeplink login.

handleDeeplink() starts with processDeeplink() and updateCredentials() while logout() runs on the unmocked user-logged-out path and later calls resetConfig(), which resets mnemonic, newToken, and userData. Await a completion signal such as waitForLogoutToFinish() before processing the deeplink, or update this test to call that dependency through handleDeeplink().

🤖 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/apps/main/auth/deeplink/handle-deeplink.test.ts` around lines 89 - 96,
Update the handleDeeplink flow to await waitForLogoutToFinish before invoking
processDeeplink or updateCredentials, ensuring logout’s resetConfig cannot
overwrite the new credentials. Keep the existing dependency ordering and verify
the test exercises this completion signal through handleDeeplink rather than
only mocking it unused.
src/apps/main/auth/handlers.ts (1)

36-36: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Attach error handling to logout() before running it fire-and-forget.

closeUserSession() calls void logout(), which discards failures from closeUserSessionResources() or the server logout request. This can produce unhandled rejections during teardown. Use logout().catch(...) or await it if this path should report logout failures.

🤖 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/apps/main/auth/handlers.ts` at line 36, Update closeUserSession’s logout
invocation to handle the promise rejection before running it fire-and-forget.
Attach a catch handler to logout() that reports failures appropriately, or await
logout() if the teardown path supports awaiting, while preserving the existing
closeUserSession behavior.
🤖 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 `@src/backend/features/auth/logout.ts`:
- Around line 9-28: Make logout single-flight by adding a module-level in-flight
promise covering the entire operation in logout(), including configuration save,
resource cleanup, event emission, server logout, and resetConfig. Return the
existing promise to concurrent callers, clear the stored promise in finally, and
add a test verifying concurrent logout calls share one operation and do not
duplicate side effects.

In `@src/backend/features/config/reset-config.test.ts`:
- Line 62: Update the mock call assertions in
src/backend/features/config/reset-config.test.ts#L62-L62 and
src/backend/features/auth/logout.test.ts#L82-L82 to compare calls against
one-element argument arrays: ['availableUserProducts'] and ['USER_LOGGED_OUT'],
respectively.

---

Outside diff comments:
In `@src/apps/main/auth/deeplink/handle-deeplink.test.ts`:
- Around line 89-96: Update the handleDeeplink flow to await
waitForLogoutToFinish before invoking processDeeplink or updateCredentials,
ensuring logout’s resetConfig cannot overwrite the new credentials. Keep the
existing dependency ordering and verify the test exercises this completion
signal through handleDeeplink rather than only mocking it unused.

In `@src/apps/main/auth/handlers.ts`:
- Line 36: Update closeUserSession’s logout invocation to handle the promise
rejection before running it fire-and-forget. Attach a catch handler to logout()
that reports failures appropriately, or await logout() if the teardown path
supports awaiting, while preserving the existing closeUserSession behavior.
🪄 Autofix

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: fb83d3bd-a842-4deb-81b0-c7eb9f027b58

📥 Commits

Reviewing files that changed from the base of the PR and between b764241 and b04922f.

📒 Files selected for processing (41)
  • src/apps/main/auth/close-user-session-resources.test.ts
  • src/apps/main/auth/close-user-session-resources.ts
  • src/apps/main/auth/deeplink/handle-deeplink.test.ts
  • src/apps/main/auth/deeplink/handle-deeplink.ts
  • src/apps/main/auth/deeplink/initialize-current-user.test.ts
  • src/apps/main/auth/handlers.ts
  • src/apps/main/auth/service.test.ts
  • src/apps/main/interface.d.ts
  • src/apps/main/preload.d.ts
  • src/apps/main/realtime.ts
  • src/apps/main/remote-sync/service.test.ts
  • src/backend/features/auth/headers.test.ts
  • src/backend/features/auth/headers.ts
  • src/backend/features/auth/index.ts
  • src/backend/features/auth/logout.test.ts
  • src/backend/features/auth/logout.ts
  • src/backend/features/auth/user-session.test.ts
  • src/backend/features/auth/user-session.ts
  • src/backend/features/backup/download-backup.test.ts
  • src/backend/features/backup/download-backup.ts
  • src/backend/features/config/can-his-config-be-restore.test.ts
  • src/backend/features/config/can-his-config-be-restore.ts
  • src/backend/features/config/reset-config.test.ts
  • src/backend/features/config/reset-config.ts
  • src/backend/features/payments/services/get-user-available-products-and-store.test.ts
  • src/backend/features/payments/services/get-user-available-products-and-store.ts
  • src/context/shared/domain/value-objects/BucketEntry.ts
  • src/core/bootstrap/register-session-event-handlers.test.ts
  • src/infra/drive-server/client/drive-server.client.instance.test.ts
  • src/infra/drive-server/client/drive-server.client.instance.ts
  • src/infra/drive-server/services/auth/auth.service.test.ts
  • src/infra/drive-server/services/auth/auth.service.ts
  • src/infra/drive-server/services/backup/backup.service.test.ts
  • src/infra/drive-server/services/files/services/add-file-to-trash.test.ts
  • src/infra/drive-server/services/files/services/create-file.test.ts
  • src/infra/drive-server/services/files/services/create-thumbnail.test.ts
  • src/infra/drive-server/services/files/services/delete-file-content-from-bucket.test.ts
  • src/infra/drive-server/services/files/services/move-file.test.ts
  • src/infra/drive-server/services/files/services/override-file.test.ts
  • src/infra/drive-server/services/files/services/rename-file.test.ts
  • vitest.setup.main.ts
💤 Files with no reviewable changes (3)
  • src/apps/main/auth/service.test.ts
  • src/apps/main/auth/close-user-session-resources.test.ts
  • src/core/bootstrap/register-session-event-handlers.test.ts
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/infra/drive-server/services/auth/auth.service.ts
  • src/infra/drive-server/services/auth/auth.service.test.ts
  • src/infra/drive-server/client/drive-server.client.instance.test.ts
  • vitest.setup.main.ts
  • src/apps/main/auth/close-user-session-resources.ts
  • src/backend/features/payments/services/get-user-available-products-and-store.ts
  • src/infra/drive-server/client/drive-server.client.instance.ts
  • src/apps/main/auth/deeplink/initialize-current-user.test.ts
  • src/apps/main/remote-sync/service.test.ts

Comment on lines +9 to +28
export async function logout() {
logger.debug({ msg: 'Logging out' });

const user = getUser();

if (user) {
const { uuid } = user;

saveConfig({ uuid });
}

await closeUserSessionResources();
eventBus.emit('USER_LOGGED_OUT');

if (user) {
void driveServerModule.auth.logout();
}

resetConfig();
logger.debug({ msg: '[AUTH] User logged out' });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Make logout() single-flight.

closeUserSessionResources() only shares cleanup at Line 20. A concurrent caller still saves configuration, emits USER_LOGGED_OUT, starts another server logout, and resets configuration at Lines 12-28. A later reset can affect a session that starts after the first logout completes.

Store one in-flight promise for the complete logout operation. Clear it in finally. Add a concurrent logout test.

Proposed fix
+let logoutInFlight: Promise<void> | undefined;
+
 export async function logout() {
+  if (logoutInFlight) {
+    return logoutInFlight;
+  }
+
+  logoutInFlight = executeLogout();
+  try {
+    await logoutInFlight;
+  } finally {
+    logoutInFlight = undefined;
+  }
+}
+
+async function executeLogout() {
   logger.debug({ msg: 'Logging out' });
   // existing logout steps
 }
📝 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
export async function logout() {
logger.debug({ msg: 'Logging out' });
const user = getUser();
if (user) {
const { uuid } = user;
saveConfig({ uuid });
}
await closeUserSessionResources();
eventBus.emit('USER_LOGGED_OUT');
if (user) {
void driveServerModule.auth.logout();
}
resetConfig();
logger.debug({ msg: '[AUTH] User logged out' });
let logoutInFlight: Promise<void> | undefined;
export async function logout() {
if (logoutInFlight) {
return logoutInFlight;
}
logoutInFlight = executeLogout();
try {
await logoutInFlight;
} finally {
logoutInFlight = undefined;
}
}
async function executeLogout() {
logger.debug({ msg: 'Logging out' });
const user = getUser();
if (user) {
const { uuid } = user;
saveConfig({ uuid });
}
await closeUserSessionResources();
eventBus.emit('USER_LOGGED_OUT');
if (user) {
void driveServerModule.auth.logout();
}
resetConfig();
logger.debug({ msg: '[AUTH] User logged out' });
}
🤖 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/backend/features/auth/logout.ts` around lines 9 - 28, Make logout
single-flight by adding a module-level in-flight promise covering the entire
operation in logout(), including configuration save, resource cleanup, event
emission, server logout, and resetConfig. Return the existing promise to
concurrent callers, clear the stored promise in finally, and add a test
verifying concurrent logout calls share one operation and do not duplicate side
effects.

resetConfig();

// Then
calls(configDeleteMock).toContainEqual('availableUserProducts');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert one-argument mock call tuples.

calls(mock) returns argument arrays. Both assertions compare a bare string, so they cannot match the recorded calls.

  • src/backend/features/config/reset-config.test.ts#L62-L62: compare against ['availableUserProducts'].
  • src/backend/features/auth/logout.test.ts#L82-L82: compare against ['USER_LOGGED_OUT'].
📍 Affects 2 files
  • src/backend/features/config/reset-config.test.ts#L62-L62 (this comment)
  • src/backend/features/auth/logout.test.ts#L82-L82
🤖 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/backend/features/config/reset-config.test.ts` at line 62, Update the mock
call assertions in src/backend/features/config/reset-config.test.ts#L62-L62 and
src/backend/features/auth/logout.test.ts#L82-L82 to compare calls against
one-element argument arrays: ['availableUserProducts'] and ['USER_LOGGED_OUT'],
respectively.

@egalvis27
egalvis27 force-pushed the fix/app-clean-up-on-logout branch from 081319e to 1b1e6c5 Compare August 5, 2026 01:32
@egalvis27
egalvis27 requested a review from AlexisMora August 5, 2026 01:39

await Promise.all([
tryCatch(
() => DependencyInjectionUserProvider.clearUser(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

you could just do:

DependencyInjectionUserProvider.clearUser,

as well for the other methods

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done

Comment on lines 10 to +33
@@ -27,4 +27,8 @@ export class DependencyInjectionUserProvider {
DependencyInjectionUserProvider._user = user;
ConfigStore.set('userData', user);
}

static clearUser() {
DependencyInjectionUserProvider._user = null;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Add a todo comment as well that references that ticket

const userProducts = await PaymentsModule.getUserAvailableProducts({
paymentsClientConfig,
});
try {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why these changes? i dont recall these steps throwing an Exception

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

You're right, it's not necessary; I've removed it.

@egalvis27
egalvis27 requested a review from AlexisMora August 5, 2026 15:40
@sonarqubecloud

sonarqubecloud Bot commented Aug 5, 2026

Copy link
Copy Markdown

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