diff --git a/packages/cli/src/services/user.service.ts b/packages/cli/src/services/user.service.ts index 41037f26c27c1..02fab23e4b9e5 100644 --- a/packages/cli/src/services/user.service.ts +++ b/packages/cli/src/services/user.service.ts @@ -17,6 +17,7 @@ import { RoleService } from '@/services/role.service'; import { ApplicationError, ErrorReporterProxy as ErrorReporter } from 'n8n-workflow'; import type { UserRequest } from '@/requests'; import { InternalServerError } from '@/errors/response-errors/internal-server.error'; +import { RoleRepository } from '@/databases/repositories/role.repository'; @Service() export class UserService { @@ -307,4 +308,22 @@ export class UserService { return { usersInvited, usersCreated: toCreateUsers.map(({ email }) => email) }; } + + /** + * Counts the number of users in each role, e.g. `{ admin: 2, member: 6, owner: 1 }` + */ + async countUsersByRole() { + const result: Array<{ role_name: string; count: number }> = await Container.get(RoleRepository) + .createQueryBuilder('role') + .select('role.name') + .addSelect('COUNT(user.id)', 'count') + .innerJoin('user', 'user', 'role.id = user.globalRoleId') + .groupBy('role.name') + .getRawMany(); + + return result.reduce>((acc, item) => { + acc[item.role_name] = item.count; + return acc; + }, {}); + } } diff --git a/packages/cli/src/telemetry/index.ts b/packages/cli/src/telemetry/index.ts index bbd8cfcbeb8e8..2836d119f6257 100644 --- a/packages/cli/src/telemetry/index.ts +++ b/packages/cli/src/telemetry/index.ts @@ -10,6 +10,7 @@ import { LicenseService } from '@/license/License.service'; import { N8N_VERSION } from '@/constants'; import { SourceControlPreferencesService } from '../environments/sourceControl/sourceControlPreferences.service.ee'; import { InstanceSettings } from 'n8n-core'; +import { UserService } from '@/services/user.service'; type ExecutionTrackDataKey = 'manual_error' | 'manual_success' | 'prod_error' | 'prod_success'; @@ -108,6 +109,7 @@ export class Telemetry { plan_name_current: this.license.getPlanName(), quota: this.license.getTriggerLimit(), usage: await LicenseService.getActiveTriggerCount(), + role_count: await Container.get(UserService).countUsersByRole(), source_control_set_up: Container.get(SourceControlPreferencesService).isSourceControlSetup(), branchName: sourceControlPreferences.branchName, read_only_instance: sourceControlPreferences.branchReadOnly, diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index 37beb90ebfe33..cfbe7ae5200a6 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -1766,6 +1766,7 @@ export interface ExternalSecretsProviderWithProperties extends ExternalSecretsPr } export type CloudUpdateLinkSourceType = + | 'advanced-permissions' | 'canvas-nav' | 'custom-data-filter' | 'workflow_sharing' diff --git a/packages/editor-ui/src/components/InviteUsersModal.vue b/packages/editor-ui/src/components/InviteUsersModal.vue index 3145a91d1bdda..d8844697e949e 100644 --- a/packages/editor-ui/src/components/InviteUsersModal.vue +++ b/packages/editor-ui/src/components/InviteUsersModal.vue @@ -333,7 +333,7 @@ export default defineComponent({ } }, goToUpgradeAdvancedPermissions() { - void this.uiStore.goToUpgrade('settings-users', 'upgrade-advanced-permissions'); + void this.uiStore.goToUpgrade('advanced-permissions', 'upgrade-advanced-permissions'); }, }, });