Refactor: Move avatar sync to event-driven processor

- Remove isSync property from UploadUserAvatarTask (always check)
- Create AvatarSyncProcessor that listens for users.signin events
- Remove avatar sync scheduling from userProvisioner
- Simplify architecture with event-driven approach
This commit is contained in:
codegen-sh[bot]
2025-11-24 13:18:22 +00:00
parent 41198c8287
commit bdab6997c1
3 changed files with 45 additions and 23 deletions
+1 -11
View File
@@ -8,8 +8,7 @@ import {
InviteRequiredError,
} from "@server/errors";
import Logger from "@server/logging/Logger";
import { Team, User, UserAuthentication, UserFlag } from "@server/models";
import UploadUserAvatarTask from "@server/queues/tasks/UploadUserAvatarTask";
import { Team, User, UserAuthentication } from "@server/models";
import { sequelize } from "@server/storage/database";
import { APIContext } from "@server/types";
@@ -162,15 +161,6 @@ export default async function userProvisioner(
);
});
// Schedule avatar sync task if user has an avatar URL and hasn't manually changed it
if (avatarUrl && !existingUser.getFlag(UserFlag.AvatarChanged)) {
await new UploadUserAvatarTask().schedule({
userId: existingUser.id,
avatarUrl,
isSync: true,
});
}
if (isInvite) {
const inviter = await existingUser.$get("invitedBy");
if (inviter) {
@@ -0,0 +1,37 @@
import { User } from "@server/models";
import { UserEvent } from "@server/types";
import UploadUserAvatarTask from "../tasks/UploadUserAvatarTask";
import BaseProcessor from "./BaseProcessor";
/**
* Processor to handle avatar synchronization on user signin events.
* This checks if the user's avatar from their identity provider has changed
* and updates it if necessary, unless the user has manually changed their avatar.
*/
export default class AvatarSyncProcessor extends BaseProcessor {
static applicableEvents: UserEvent["name"][] = ["users.signin"];
async perform(event: UserEvent) {
switch (event.name) {
case "users.signin": {
const user = await User.findByPk(event.userId, {
rejectOnEmpty: true,
});
// Only sync if user has an avatar URL
if (!user.avatarUrl) {
return;
}
// Schedule the avatar upload task which will check if update is needed
await new UploadUserAvatarTask().schedule({
userId: user.id,
avatarUrl: user.avatarUrl,
});
break;
}
default:
}
}
}
+7 -12
View File
@@ -12,8 +12,6 @@ type Props = {
userId: string;
/** The original avatarUrl from the SSO provider */
avatarUrl: string;
/** Whether this is a sync operation (should check for changes) */
isSync?: boolean;
};
/**
@@ -26,17 +24,14 @@ export default class UploadUserAvatarTask extends BaseTask<Props> {
rejectOnEmpty: true,
});
// If this is a sync operation, check if we need to update
if (props.isSync) {
// Check if user has manually changed their avatar
if (user.getFlag(UserFlag.AvatarChanged)) {
return; // Don't override user's manual avatar choice
}
// Check if user has manually changed their avatar
if (user.getFlag(UserFlag.AvatarChanged)) {
return; // Don't override user's manual avatar choice
}
// Check if the avatar has actually changed
if (!shouldUpdateAvatar(user.avatarUrl, props.avatarUrl)) {
return; // No change needed
}
// Check if the avatar has actually changed
if (!shouldUpdateAvatar(user.avatarUrl, props.avatarUrl)) {
return; // No change needed
}
// Use deterministic filename for change detection