Skip to content

feat(settings): add sync button to apply default permissions to existing users#3056

Open
swiizyy wants to merge 1 commit into
seerr-team:developfrom
swiizyy:feat/sync-default-permissions-to-existing-users
Open

feat(settings): add sync button to apply default permissions to existing users#3056
swiizyy wants to merge 1 commit into
seerr-team:developfrom
swiizyy:feat/sync-default-permissions-to-existing-users

Conversation

@swiizyy

@swiizyy swiizyy commented May 20, 2026

Copy link
Copy Markdown

Summary

  • Adds a "Sync to Existing Users" button on the User Settings page (Settings > Users)
  • When clicked, applies the current default permissions to all non-admin users in the database
  • The existing Save button behaviour is unchanged — it still only affects new users
  • Backend: POST /api/v1/settings/main now accepts an applyToExistingUsers flag; when true, a bulk UPDATE excludes any user with the ADMIN permission bit set

Motivation

Previously, changing the default permissions in settings had no effect on existing users. Admins had to update each user manually. This adds a one-click way to propagate the change without altering the default "save-only" flow.

Test plan

  • Change default permissions and click Save → existing users are unaffected
  • Change default permissions and click Sync to Existing Users → all non-admin users receive the new permissions
  • Admin users are not modified by the sync
  • Toast messages appear correctly for both success and error cases

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a "Sync to Existing Users" action in Users Settings to apply updated default permissions to all existing non-admin users in one operation. The UI shows a progress/saving indicator, temporarily disables related controls during the operation, and displays success/error notifications. Settings and public views refresh automatically after sync.

Review Change Stack

@swiizyy swiizyy requested a review from a team as a code owner May 20, 2026 09:45
Copilot AI review requested due to automatic review settings May 20, 2026 09:45
@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds a backend flag to POST /settings/main to apply updated defaultPermissions to existing non-admin users, and a frontend "Sync to Existing Users" button with an async handler that calls the API, revalidates SWR caches, and shows success/error toasts.

Changes

Default Permissions Sync for Existing Users

Layer / File(s) Summary
Backend sync API implementation
server/routes/settings/index.ts
The POST /main endpoint extracts applyToExistingUsers from the request body, merges remaining fields into main settings, and performs a conditional bulk permissions update for non-admin users when the flag is enabled and defaultPermissions is provided.
Frontend sync handler and UI
src/components/Settings/SettingsUsers/index.tsx
Component imports ArrowPathIcon and useState; new i18n entries define sync success/error messages; isSyncing state controls button state during requests; handleSyncPermissions handler posts to the updated API with sync flag, triggers SWR revalidation, and shows toasts; a new "Sync to Existing Users" button is rendered alongside the primary submit button with independent disabled/saving states.

Sequence Diagram

sequenceDiagram
  participant SettingsUsers as SettingsUsers Component
  participant API as POST /api/v1/settings/main
  participant UserRepo as User Repository
  participant SWR as SWR Cache
  SettingsUsers->>API: POST with defaultPermissions + applyToExistingUsers: true
  API->>UserRepo: Update non-admin users' permissions
  UserRepo->>API: Bulk update complete
  API->>SettingsUsers: Return updated settings
  SettingsUsers->>SWR: Mutate /api/v1/settings/public + revalidate()
  SettingsUsers->>SettingsUsers: Show success toast
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A sync button hops into view,
Permissions march to their new cue,
Non-admins catch the default tune,
Toasts pop up to say it's true,
Hooray — the rabbit synced for you!

🚥 Pre-merge checks | ✅ 4
✅ 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 accurately and concisely describes the main feature: adding a sync button to apply default permissions to existing users, which is the primary purpose of the PR.
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.


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.

@github-actions

Copy link
Copy Markdown

Hey @swiizyy, thanks for submitting this PR! However, it looks like the PR template hasn't been fully filled out.

Issues found:

  • Description section is empty or only contains placeholder text.
  • How Has This Been Tested? section is empty.
  • Checklist section is missing or has been removed.
  • The contribution guidelines checkbox has not been checked.
  • The AI disclosure checkbox has not been checked.

Please update your PR description to follow the PR template.
Incomplete or missing PR descriptions may indicate insufficient review of the changes, and PRs that do not follow the template may be closed without review.
See our Contributing Guide for more details.

This check will automatically re-run when you edit your PR description.

…ing users

Adds a "Sync to Existing Users" button in the User Settings page that
propagates the current default permissions to all non-admin users.
Previously, changing default permissions only affected newly created users.

Copilot AI 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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds a UI control and backend support to apply the configured “default permissions” to all existing non-admin users.

Changes:

  • Add “Sync to Existing Users” button in Settings → Users to trigger a bulk permissions sync.
  • Extend POST /api/v1/settings/main with an applyToExistingUsers flag that bulk-updates non-admin users’ permissions.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/components/Settings/SettingsUsers/index.tsx Adds sync button + client-side request/toasts/state handling for bulk permission application
server/routes/settings/index.ts Adds server-side handling for applyToExistingUsers and performs a bulk update of user permissions

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 81 to +95
await settings.save();

if (
applyToExistingUsers &&
bodyWithoutFlag.defaultPermissions !== undefined
) {
const userRepository = getRepository(User);
await userRepository
.createQueryBuilder()
.update(User)
.set({ permissions: bodyWithoutFlag.defaultPermissions })
.where('(permissions & :adminFlag) = 0', {
adminFlag: Permission.ADMIN,
})
.execute();
await settings.save();

if (
applyToExistingUsers &&
Comment on lines +166 to +188
const handleSyncPermissions = async () => {
setIsSyncing(true);
try {
await axios.post('/api/v1/settings/main', {
defaultPermissions: values.defaultPermissions,
applyToExistingUsers: true,
});
mutate('/api/v1/settings/public');

addToast(intl.formatMessage(messages.syncPermissionsSuccess), {
autoDismiss: true,
appearance: 'success',
});
} catch {
addToast(intl.formatMessage(messages.syncPermissionsFailure), {
autoDismiss: true,
appearance: 'error',
});
} finally {
revalidate();
setIsSyncing(false);
}
};
@swiizyy swiizyy force-pushed the feat/sync-default-permissions-to-existing-users branch from af9e646 to 2e603fa Compare May 20, 2026 09:47

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

🤖 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/components/Settings/SettingsUsers/index.tsx`:
- Around line 165-188: handleSyncPermissions currently calls revalidate() which
causes Formik (with enableReinitialize) to reload the entire form and clobber
any unsaved edits; instead, remove the revalidate() call and after a successful
POST only update the permissions in the form state and cache: call
setFieldValue('defaultPermissions', values.defaultPermissions) (or set the
returned permissions from the API if returned) and
mutate('/api/v1/settings/public') as you already do so the permissions are
refreshed without reinitializing the whole form; keep revalidate out of the
catch/finally to avoid resetting other fields on failure.
- Around line 352-355: The primary save Button currently disables only on
isSubmitting || !isValid but not when a sync is happening, so add the sync guard
by including isSyncing in the disabled condition (e.g., disabled={isSubmitting
|| !isValid || isSyncing}) and also ensure the form submit handler (the function
handling POST /api/v1/settings/main, e.g., onSubmit or handleSubmit)
short-circuits when isSyncing is true to avoid race conditions; update
references to isSyncing where the submit is triggered so both the UI and the
submit logic prevent concurrent saves.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 74348492-a7fa-48e9-85a7-5d203f5f0d8a

📥 Commits

Reviewing files that changed from the base of the PR and between 4fa2c71 and af9e646.

📒 Files selected for processing (2)
  • server/routes/settings/index.ts
  • src/components/Settings/SettingsUsers/index.tsx

Comment on lines 165 to +188
{({ isSubmitting, isValid, values, errors, setFieldValue }) => {
const handleSyncPermissions = async () => {
setIsSyncing(true);
try {
await axios.post('/api/v1/settings/main', {
defaultPermissions: values.defaultPermissions,
applyToExistingUsers: true,
});
mutate('/api/v1/settings/public');

addToast(intl.formatMessage(messages.syncPermissionsSuccess), {
autoDismiss: true,
appearance: 'success',
});
} catch {
addToast(intl.formatMessage(messages.syncPermissionsFailure), {
autoDismiss: true,
appearance: 'error',
});
} finally {
revalidate();
setIsSyncing(false);
}
};

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't reinitialize the whole form after a permission-only sync.

revalidate() will repopulate Formik while enableReinitialize is on, but this request only persists defaultPermissions. If the admin has unsaved edits in login or quota fields, those changes get reset back to the server values after the sync call, even on failure. Keep the current form state intact here and only refresh the permission value you actually changed.

🤖 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/components/Settings/SettingsUsers/index.tsx` around lines 165 - 188,
handleSyncPermissions currently calls revalidate() which causes Formik (with
enableReinitialize) to reload the entire form and clobber any unsaved edits;
instead, remove the revalidate() call and after a successful POST only update
the permissions in the form state and cache: call
setFieldValue('defaultPermissions', values.defaultPermissions) (or set the
returned permissions from the API if returned) and
mutate('/api/v1/settings/public') as you already do so the permissions are
refreshed without reinitializing the whole form; keep revalidate out of the
catch/finally to avoid resetting other fields on failure.

Comment on lines +352 to +355
<Button
buttonType="primary"
type="submit"
disabled={isSubmitting || !isValid}

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Disable the primary save while a sync is running.

The sync flow sets isSyncing, but the submit button can still fire another POST /api/v1/settings/main in parallel. That makes the final state order-dependent and can produce misleading toasts.

🤖 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/components/Settings/SettingsUsers/index.tsx` around lines 352 - 355, The
primary save Button currently disables only on isSubmitting || !isValid but not
when a sync is happening, so add the sync guard by including isSyncing in the
disabled condition (e.g., disabled={isSubmitting || !isValid || isSyncing}) and
also ensure the form submit handler (the function handling POST
/api/v1/settings/main, e.g., onSubmit or handleSubmit) short-circuits when
isSyncing is true to avoid race conditions; update references to isSyncing where
the submit is triggered so both the UI and the submit logic prevent concurrent
saves.

@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: 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 `@server/routes/settings/index.ts`:
- Around line 80-95: The current flow saves settings.main (settings.save())
before running the bulk user update
(userRepository.createQueryBuilder().update(User)...), which can leave settings
persisted if the UPDATE fails; wrap both operations in a single database
transaction (e.g., using TypeORM's getManager().transaction or
connection.transaction) so you perform settings.save() and the
userRepository.update inside the same transactional callback and let the
transaction roll back on error, or alternatively reverse the order and update
users first then save settings and return an explicit partial-success response
when only one step succeeds; target the settings.save() and the bulk update call
in your change.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 16364607-f629-491e-857e-500e7652cf5e

📥 Commits

Reviewing files that changed from the base of the PR and between af9e646 and 2e603fa.

📒 Files selected for processing (2)
  • server/routes/settings/index.ts
  • src/components/Settings/SettingsUsers/index.tsx

Comment on lines +80 to +95
settings.main = merge(settings.main, bodyWithoutFlag);
await settings.save();

if (
applyToExistingUsers &&
bodyWithoutFlag.defaultPermissions !== undefined
) {
const userRepository = getRepository(User);
await userRepository
.createQueryBuilder()
.update(User)
.set({ permissions: bodyWithoutFlag.defaultPermissions })
.where('(permissions & :adminFlag) = 0', {
adminFlag: Permission.ADMIN,
})
.execute();

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.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Avoid returning an error after partially applying the sync.

When applyToExistingUsers is enabled, this persists settings.main before the bulk user update. If the UPDATE fails, the request still returns an error even though the new defaultPermissions are already saved for future users. Please make these writes transactional/compensating, or return an explicit partial-success response instead of a generic failure.

🤖 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 `@server/routes/settings/index.ts` around lines 80 - 95, The current flow saves
settings.main (settings.save()) before running the bulk user update
(userRepository.createQueryBuilder().update(User)...), which can leave settings
persisted if the UPDATE fails; wrap both operations in a single database
transaction (e.g., using TypeORM's getManager().transaction or
connection.transaction) so you perform settings.save() and the
userRepository.update inside the same transactional callback and let the
transaction roll back on error, or alternatively reverse the order and update
users first then save settings and return an explicit partial-success response
when only one step succeeds; target the settings.save() and the bulk update call
in your change.

@fallenbagel

fallenbagel commented May 20, 2026

Copy link
Copy Markdown
Member

Admins had to update each user manually.

This is not true. Bulk edit button in Users page exists precisely for this..

Screenshot_20260520_180327_Chrome.jpg

@swiizyy

swiizyy commented May 20, 2026

Copy link
Copy Markdown
Author

There's a beug that when you change these paramit their block in the bulk edit and you can't change them

@swiizyy

swiizyy commented May 20, 2026

Copy link
Copy Markdown
Author

Or there should be a button in the bulk edit feature to add default permissions; plus, if you just want to add a setting, it doesn't work. If you just want to give everyone “View Request” permission, you can't because it deletes all the other settings.

@fallenbagel

Copy link
Copy Markdown
Member

There's a beug that when you change these paramit their block in the bulk edit and you can't change them

Then that should be handled first (it seems to work propely for me so far. So that should be troubleshooted).

@fallenbagel

Copy link
Copy Markdown
Member

Or there should be a button in the bulk edit feature to add default permissions; plus, if you just want to add a setting, it doesn't work. If you just want to give everyone “View Request” permission, you can't because it deletes all the other settings.

Because bulk edit works by applying the full perms you need. Not just one. And it applies to everyone you selected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants