Files
twenty/packages/twenty-server/src/engine/workspace-manager/workspace-manager.service.ts
T
Marie 0001c2e7d0 [Apps] Apps marketplace (first draft) (#17562)
https://github.com/user-attachments/assets/c4e63edb-6e98-44bc-841f-ee110ae712d4

How it works
- `manifest.json` files are now committed when apps are published to our
repo
- to display available apps, from the server, we read into our github
repo, using a pod-scoped cache
- feature flagged
- app installation will be behind permission gate MARKETPLACE_APPS

Limitations and what is yet to develop
- content and settings tabs
- installed apps tab
- app installation
- test and potentially fix reading from .manifest.json once [Reshape
manifest
structure](https://github.com/twentyhq/core-team-issues/issues/2183) is
done. additional work is expected on assets notably. (couldnt properly
do it here as manifest.json will only be committed after this pr)
- we only read in community/ folder for now - we may want tochange that
- the cache is rather artisanal for now and scoped by pod - we may want
to change that
2026-01-30 17:32:32 +00:00

132 lines
4.3 KiB
TypeScript

import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ApplicationService } from 'src/engine/core-modules/application/services/application.service';
import { FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { ADMIN_ROLE } from 'src/engine/metadata-modules/role/constants/admin-role';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { RoleService } from 'src/engine/metadata-modules/role/role.service';
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { TwentyStandardApplicationService } from 'src/engine/workspace-manager/twenty-standard-application/services/twenty-standard-application.service';
@Injectable()
export class WorkspaceManagerService {
private readonly logger = new Logger(WorkspaceManagerService.name);
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
private readonly dataSourceService: DataSourceService,
@InjectRepository(UserWorkspaceEntity)
private readonly userWorkspaceRepository: Repository<UserWorkspaceEntity>,
private readonly roleService: RoleService,
private readonly userRoleService: UserRoleService,
private readonly twentyStandardApplicationService: TwentyStandardApplicationService,
@InjectRepository(WorkspaceEntity)
private readonly workspaceRepository: Repository<WorkspaceEntity>,
@InjectRepository(RoleEntity)
private readonly roleRepository: Repository<RoleEntity>,
private readonly applicationService: ApplicationService,
) {}
public async init({
workspace,
userId,
}: {
workspace: WorkspaceEntity;
userId: string;
}): Promise<void> {
const workspaceId = workspace.id;
const schemaCreationStart = performance.now();
const schemaName =
await this.workspaceDataSourceService.createWorkspaceDBSchema(
workspaceId,
);
const schemaCreationEnd = performance.now();
this.logger.log(
`Schema creation took ${schemaCreationEnd - schemaCreationStart}ms`,
);
const dataSourceMetadataCreationStart = performance.now();
await this.dataSourceService.createDataSourceMetadata(
workspaceId,
schemaName,
);
await this.applicationService.createTwentyStandardApplication({
workspaceId,
});
await this.twentyStandardApplicationService.synchronizeTwentyStandardApplicationOrThrow(
{
workspaceId,
},
);
const dataSourceMetadataCreationEnd = performance.now();
this.logger.log(
`Metadata creation took ${dataSourceMetadataCreationEnd - dataSourceMetadataCreationStart}ms`,
);
const { workspaceCustomFlatApplication } =
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
{
workspaceId,
},
);
await this.setupDefaultRoles({
workspaceId,
userId,
workspaceCustomFlatApplication,
});
}
private async setupDefaultRoles({
userId,
workspaceId,
workspaceCustomFlatApplication,
}: {
workspaceId: string;
userId: string;
workspaceCustomFlatApplication: FlatApplication;
}): Promise<void> {
const adminRole = await this.roleRepository.findOne({
where: {
standardId: ADMIN_ROLE.standardId,
workspaceId,
},
});
if (adminRole) {
const userWorkspace = await this.userWorkspaceRepository.findOneOrFail({
where: { workspaceId, userId },
});
await this.userRoleService.assignRoleToManyUserWorkspace({
workspaceId,
userWorkspaceIds: [userWorkspace.id],
roleId: adminRole.id,
});
}
const memberRole = await this.roleService.createMemberRole({
workspaceId,
applicationId: workspaceCustomFlatApplication.id,
});
await this.workspaceRepository.update(workspaceId, {
defaultRoleId: memberRole.id,
});
}
}