fix: persistent random Snowflake worker ID + clean reaction API
Two fixes addressing architectural review feedback: 1. Snowflake ID collisions: Replace process.pid-based worker ID with a cryptographically random value (0-1023) generated once at first boot and persisted to instance_settings.worker_id. Eliminates deterministic ID collisions between Docker instances that all run as PID 1. 2. Reaction API leak: Revert addReaction/removeReaction signatures to (messageId, emoji) — the store now resolves the channel internally by scanning its message cache, keeping routing logic out of the UI layer.
This commit is contained in:
@@ -60,9 +60,9 @@ export function Message({ message, isCompact, isFirstInGroup }: MessageProps) {
|
||||
const toggleReaction = (emoji: string) => {
|
||||
const hasReacted = message.reactions?.some(r => r.userId === currentUser?.id && r.emoji === emoji);
|
||||
if (hasReacted) {
|
||||
removeReaction(message.id, emoji, channelKey);
|
||||
removeReaction(message.id, emoji);
|
||||
} else {
|
||||
addReaction(message.id, emoji, channelKey);
|
||||
addReaction(message.id, emoji);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@ interface ChatState {
|
||||
addRealtimeMessage: (channelId: string, message: MessageWithUser) => void;
|
||||
updateMessage: (message: MessageWithUser) => void;
|
||||
removeMessage: (messageId: string, channelId: string) => void;
|
||||
addReaction: (messageId: string, emoji: string, channelId: string) => void;
|
||||
removeReaction: (messageId: string, emoji: string, channelId: string) => void;
|
||||
addReaction: (messageId: string, emoji: string) => void;
|
||||
removeReaction: (messageId: string, emoji: string) => void;
|
||||
onReactionAdded: (messageId: string, reaction: any) => void;
|
||||
onReactionRemoved: (messageId: string, userId: string, emoji: string) => void;
|
||||
setTyping: (channelId: string, userId: string, username: string) => void;
|
||||
@@ -57,6 +57,16 @@ interface ChatState {
|
||||
onChannelAck: (channelId: string, messageId: string) => void;
|
||||
}
|
||||
|
||||
/** Find which channel a message belongs to by scanning the message cache. */
|
||||
function findChannelForMessage(messages: Map<string, MessageWithUser[]>, messageId: string): string | null {
|
||||
for (const [channelId, msgs] of messages) {
|
||||
if (msgs.some(m => m.id === messageId)) {
|
||||
return channelId;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export const useChatStore = create<ChatState>((set, get) => ({
|
||||
messages: new Map(),
|
||||
currentChannelId: null,
|
||||
@@ -356,13 +366,16 @@ export const useChatStore = create<ChatState>((set, get) => ({
|
||||
});
|
||||
},
|
||||
|
||||
addReaction: (messageId: string, emoji: string, channelId: string) => {
|
||||
const origin = getChannelOrigin(channelId);
|
||||
addReaction: (messageId: string, emoji: string) => {
|
||||
// Resolve the channel from our message cache so the UI doesn't need to pass it
|
||||
const channelId = findChannelForMessage(get().messages, messageId);
|
||||
const origin = channelId ? getChannelOrigin(channelId) : '';
|
||||
wsSend({ type: 'reaction_add', messageId, emoji }, origin);
|
||||
},
|
||||
|
||||
removeReaction: (messageId: string, emoji: string, channelId: string) => {
|
||||
const origin = getChannelOrigin(channelId);
|
||||
removeReaction: (messageId: string, emoji: string) => {
|
||||
const channelId = findChannelForMessage(get().messages, messageId);
|
||||
const origin = channelId ? getChannelOrigin(channelId) : '';
|
||||
wsSend({ type: 'reaction_remove', messageId, emoji }, origin);
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user