Skip to content

fix(realtime): broadcast drive role mutations and wire RolesManager socket listener - #1328

Merged
2witstudios merged 2 commits into
masterfrom
pu/socket-drive-roles
May 13, 2026
Merged

2witstudios merged 2 commits into
masterfrom
pu/socket-drive-roles

Conversation

@2witstudios

@2witstudios 2witstudios commented May 13, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Add broadcastDriveEvent calls to all four drive role mutation routes (create, update, delete, reorder) so peer admins receive a drive:updated socket event immediately
  • Wire a drive:updated socket listener in RolesManager.tsx that calls fetchRoles() when an event arrives for the current drive, keeping the roles list live for all admins viewing the panel simultaneously

Test plan

  • Open Roles settings panel as two admin users in the same drive
  • Admin A creates a role — Admin B's list updates without refresh
  • Admin A edits a role — Admin B sees the updated name/color/permissions
  • Admin A deletes a role — Admin B's list removes it live
  • Admin A reorders roles — Admin B sees the new order
  • Verify socket listener is cleaned up on component unmount (no listener leak)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Role management (create, edit, reorder, delete) now broadcasts updates via realtime events so changes propagate instantly to drive members.
    • Settings UI now listens for drive update events and refreshes role lists automatically when the current drive is affected.

Review Change Stack

…ocket listener

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 13, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3fa3cce1-1858-40b1-9650-a17f8f086b0f

📥 Commits

Reviewing files that changed from the base of the PR and between 70f4c65 and 7acfe49.

📒 Files selected for processing (4)
  • apps/web/src/app/api/drives/[driveId]/roles/[roleId]/route.ts
  • apps/web/src/app/api/drives/[driveId]/roles/reorder/route.ts
  • apps/web/src/app/api/drives/[driveId]/roles/route.ts
  • apps/web/src/components/settings/RolesManager.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/web/src/app/api/drives/[driveId]/roles/[roleId]/route.ts
  • apps/web/src/components/settings/RolesManager.tsx
  • apps/web/src/app/api/drives/[driveId]/roles/route.ts
  • apps/web/src/app/api/drives/[driveId]/roles/reorder/route.ts

📝 Walkthrough

Walkthrough

Adds best-effort WebSocket broadcasts to role mutation endpoints (create, update, delete, reorder) that resolve drive recipient user IDs and emit a drive:updated event, and subscribes RolesManager to drive:updated to refresh roles in real time.

Changes

Drive Role Real-Time Synchronization

Layer / File(s) Summary
Role creation broadcast
apps/web/src/app/api/drives/[driveId]/roles/route.ts
POST create-role now imports recipient resolution and broadcast utilities and sends a drive:updated WebSocket event to drive recipients after persisting the new role.
Role update and delete broadcasts
apps/web/src/app/api/drives/[driveId]/roles/[roleId]/route.ts
PATCH and DELETE handlers import broadcast helpers, resolve drive recipient user IDs, and perform a best-effort drive:updated broadcast immediately after the mutation and before audit logging.
Role reorder broadcast
apps/web/src/app/api/drives/[driveId]/roles/reorder/route.ts
PATCH reorder route resolves drive recipients and emits a best-effort drive:updated WebSocket event after successfully reordering roles; broadcast failures are logged only.
Real-time role refresh in UI
apps/web/src/components/settings/RolesManager.tsx
RolesManager adds useSocket and DriveEventPayload, creates a socket, and registers a drive:updated listener that calls fetchRoles() when the event's driveId matches the current drive.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

codex

Poem

🐰 I hopped through sockets, loud and spry,
Sent role-updates racing by,
A ping, a fetch, the list renews,
Admins grin at live-updated views,
Tiny rabbit, big realtime sky!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main changes: adding real-time broadcast of drive role mutations and connecting a socket listener in RolesManager.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pu/socket-drive-roles

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
Contributor

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 (1)
apps/web/src/components/settings/RolesManager.tsx (1)

10-10: ⚡ Quick win

Use a type-only import for DriveEventPayload.

This symbol is only used in type position (parameter type annotation on line 71); import type avoids accidental runtime coupling and improves tree-shaking.

Suggested change
-import { DriveEventPayload } from '@/lib/websocket';
+import type { DriveEventPayload } from '@/lib/websocket';
🤖 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 `@apps/web/src/components/settings/RolesManager.tsx` at line 10, Change the
import of DriveEventPayload to a type-only import to avoid runtime coupling:
replace the current import of DriveEventPayload used only as a parameter type
(see usage in RolesManager component/function where DriveEventPayload is
annotated on the event handler parameter) with an "import type" form so it is
erased at runtime and improves tree-shaking.
🤖 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 `@apps/web/src/app/api/drives/`[driveId]/roles/[roleId]/route.ts:
- Around line 118-121: The broadcast side-effect (calls to
getDriveRecipientUserIds and broadcastDriveEvent using createDriveEventPayload)
must not cause a 500 when the underlying mutation already succeeded; wrap the
broadcast logic in a try/catch in the route handler so any errors are caught,
logged (with context and the error), and ignored for the HTTP response, ensuring
the handler still returns success for the update/delete; apply this same change
to both broadcast call sites in this file that invoke
getDriveRecipientUserIds/createDriveEventPayload/broadcastDriveEvent.

In `@apps/web/src/app/api/drives/`[driveId]/roles/reorder/route.ts:
- Around line 54-57: The broadcast call is causing API failures when it errors
even though the role reorder has already been persisted; update the code around
broadcastDriveEvent (and its preparation with
getDriveRecipientUserIds/createDriveEventPayload) to treat broadcasting as
best-effort by wrapping the broadcast in a try/catch (or performing a non-await
fire-and-forget) and swallowing/logging errors instead of allowing them to
propagate to the route handler so the HTTP response remains a success after
persistence.

In `@apps/web/src/app/api/drives/`[driveId]/roles/route.ts:
- Around line 92-95: The websocket broadcast is a non-critical side effect
causing the route to return 500 if it fails: wrap the broadcast logic (calls to
getDriveRecipientUserIds, createDriveEventPayload, and broadcastDriveEvent) in a
try/catch (or use Promise.allSettled if broadcasting multiple targets) inside
the roles route handler so any exceptions are caught, logged, and suppressed
instead of rethrown; ensure the route still returns the successful create
response even when broadcastDriveEvent fails and include a clear log
message/error context referencing driveId and the payload for debugging.

---

Nitpick comments:
In `@apps/web/src/components/settings/RolesManager.tsx`:
- Line 10: Change the import of DriveEventPayload to a type-only import to avoid
runtime coupling: replace the current import of DriveEventPayload used only as a
parameter type (see usage in RolesManager component/function where
DriveEventPayload is annotated on the event handler parameter) with an "import
type" form so it is erased at runtime and improves tree-shaking.
🪄 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: ec0115a9-c757-4901-8368-55c1b1484a00

📥 Commits

Reviewing files that changed from the base of the PR and between a13be78 and 70f4c65.

📒 Files selected for processing (4)
  • apps/web/src/app/api/drives/[driveId]/roles/[roleId]/route.ts
  • apps/web/src/app/api/drives/[driveId]/roles/reorder/route.ts
  • apps/web/src/app/api/drives/[driveId]/roles/route.ts
  • apps/web/src/components/settings/RolesManager.tsx

Comment thread apps/web/src/app/api/drives/[driveId]/roles/[roleId]/route.ts Outdated
Comment thread apps/web/src/app/api/drives/[driveId]/roles/reorder/route.ts Outdated
Comment thread apps/web/src/app/api/drives/[driveId]/roles/route.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 70f4c65c26

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +93 to +94
const recipientUserIds = await getDriveRecipientUserIds(driveId);
await broadcastDriveEvent(createDriveEventPayload(driveId, 'updated', {}), recipientUserIds);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid failing successful role creates on broadcast lookup errors

After createDriveRole succeeds, the new getDriveRecipientUserIds call runs inside the main request try block; if that lookup throws (e.g., transient DB/read failure), this handler returns 500 even though the role was already committed. That creates a false failure signal and can trigger client retries that then hit duplicate-name conflicts. Make the realtime recipient lookup/broadcast path best-effort (isolated try/catch) so post-commit notification issues don’t change API success semantics.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 7acfe49 — same fix as the CodeRabbit thread above.

- Broadcast errors no longer cause 500 responses when the
  underlying DB mutation already succeeded
- DriveEventPayload import changed to type-only import

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@2witstudios
2witstudios merged commit e27fc10 into master May 13, 2026
10 checks passed
@2witstudios
2witstudios deleted the pu/socket-drive-roles branch May 13, 2026 15:46
2witstudios added a commit that referenced this pull request May 15, 2026
…ocket listener (#1328)

* fix(realtime): broadcast drive role mutations and wire RolesManager socket listener

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(realtime): wrap broadcasts in try/catch; use import type

- Broadcast errors no longer cause 500 responses when the
  underlying DB mutation already succeeded
- DriveEventPayload import changed to type-only import

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

1 participant