feat(editor): Show avatars for users currently working on the same workflow (#7763)
This PR introduces the following changes: - New Vue stores: `collaborationStore` and `pushConnectionStore` - Front-end push connection handling overhaul: Keep only a singe connection open and handle it from the new store - Add user avatars in the editor header when there are multiple users working on the same workflow - Sending a heartbeat event to back-end service periodically to confirm user is still active - Back-end overhauls (authored by @tomi): - Implementing a cleanup procedure that removes inactive users - Refactoring collaboration service current implementation --------- Co-authored-by: Tomi Turtiainen <10324676+tomi@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
99a9ea497a
commit
77bc8ecd4b
@@ -1,5 +1,6 @@
|
||||
import type { Workflow } from 'n8n-workflow';
|
||||
import { Service } from 'typedi';
|
||||
import config from '@/config';
|
||||
import { Push } from '../push';
|
||||
import { Logger } from '@/Logger';
|
||||
import type { WorkflowClosedMessage, WorkflowOpenedMessage } from './collaboration.message';
|
||||
@@ -8,6 +9,13 @@ import { UserService } from '../services/user.service';
|
||||
import type { IActiveWorkflowUsersChanged } from '../Interfaces';
|
||||
import type { OnPushMessageEvent } from '@/push/types';
|
||||
import { CollaborationState } from '@/collaboration/collaboration.state';
|
||||
import { TIME } from '@/constants';
|
||||
|
||||
/**
|
||||
* After how many minutes of inactivity a user should be removed
|
||||
* as being an active user of a workflow.
|
||||
*/
|
||||
const INACTIVITY_CLEAN_UP_TIME_IN_MS = 15 * TIME.MINUTE;
|
||||
|
||||
/**
|
||||
* Service for managing collaboration feature between users. E.g. keeping
|
||||
@@ -28,6 +36,14 @@ export class CollaborationService {
|
||||
return;
|
||||
}
|
||||
|
||||
const isMultiMainSetup = config.get('multiMainSetup.enabled');
|
||||
if (isMultiMainSetup) {
|
||||
// TODO: We should support collaboration in multi-main setup as well
|
||||
// This requires using redis as the state store instead of in-memory
|
||||
logger.warn('Collaboration features are disabled because multi-main setup is enabled.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.push.on('message', async (event: OnPushMessageEvent) => {
|
||||
try {
|
||||
await this.handleUserMessage(event.userId, event.msg);
|
||||
@@ -53,6 +69,7 @@ export class CollaborationService {
|
||||
const { workflowId } = msg;
|
||||
|
||||
this.state.addActiveWorkflowUser(workflowId, userId);
|
||||
this.state.cleanInactiveUsers(workflowId, INACTIVITY_CLEAN_UP_TIME_IN_MS);
|
||||
|
||||
await this.sendWorkflowUsersChangedMessage(workflowId);
|
||||
}
|
||||
|
||||
@@ -59,4 +59,21 @@ export class CollaborationState {
|
||||
|
||||
return [...workflowState.values()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all users that have not been seen in a given time
|
||||
*/
|
||||
cleanInactiveUsers(workflowId: Workflow['id'], inactivityCleanUpTimeInMs: number) {
|
||||
const activeUsers = this.state.activeUsersByWorkflowId.get(workflowId);
|
||||
if (!activeUsers) {
|
||||
return;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
for (const user of activeUsers.values()) {
|
||||
if (now - user.lastSeen.getTime() > inactivityCleanUpTimeInMs) {
|
||||
activeUsers.delete(user.userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user