mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-20 15:38:23 +00:00
521 lines
16 KiB
TypeScript
521 lines
16 KiB
TypeScript
import {
|
|
Controller,
|
|
ForbiddenException,
|
|
Get,
|
|
Param,
|
|
Post,
|
|
Put,
|
|
Delete,
|
|
Query,
|
|
UseGuards,
|
|
Body,
|
|
BadRequestException,
|
|
UseInterceptors,
|
|
} from '@nestjs/common';
|
|
import { JwtAuthGuard } from '../../src/modules/auth/jwt-auth.guard';
|
|
import { AppAuthGuard } from 'src/modules/auth/app-auth.guard';
|
|
import { AppsService } from '../services/apps.service';
|
|
import { camelizeKeys, decamelizeKeys } from 'humps';
|
|
import { AppsAbilityFactory } from 'src/modules/casl/abilities/apps-ability.factory';
|
|
|
|
import { App } from 'src/entities/app.entity';
|
|
import { User } from 'src/decorators/user.decorator';
|
|
|
|
import { CreatePageDto, DeletePageDto } from '@dto/pages.dto';
|
|
import { CreateComponentDto, DeleteComponentDto, UpdateComponentDto, LayoutUpdateDto } from '@dto/component.dto';
|
|
|
|
import { ValidAppInterceptor } from 'src/interceptors/valid.app.interceptor';
|
|
import { AppDecorator } from 'src/decorators/app.decorator';
|
|
|
|
import { ComponentsService } from '@services/components.service';
|
|
import { PageService } from '@services/page.service';
|
|
import { EventsService } from '@services/events_handler.service';
|
|
import { AppVersionUpdateDto } from '@dto/app-version-update.dto';
|
|
import { CreateEventHandlerDto, UpdateEventHandlerDto } from '@dto/event-handler.dto';
|
|
import { VersionReleaseDto } from '@dto/version-release.dto';
|
|
|
|
@Controller({
|
|
path: 'apps',
|
|
version: '2',
|
|
})
|
|
export class AppsControllerV2 {
|
|
constructor(
|
|
private appsService: AppsService,
|
|
private componentsService: ComponentsService,
|
|
private pageService: PageService,
|
|
private eventsService: EventsService,
|
|
private eventService: EventsService,
|
|
private appsAbilityFactory: AppsAbilityFactory
|
|
) {}
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
@UseInterceptors(ValidAppInterceptor)
|
|
@Get(':id')
|
|
async show(@User() user, @AppDecorator() app: App, @Query('access_type') accessType: string) {
|
|
const ability = await this.appsAbilityFactory.appsActions(user, app.id);
|
|
if (!ability.can('viewApp', app)) {
|
|
throw new ForbiddenException(
|
|
JSON.stringify({
|
|
organizationId: app.organizationId,
|
|
})
|
|
);
|
|
}
|
|
|
|
if (accessType === 'edit' && !ability.can('editApp', app)) {
|
|
throw new ForbiddenException(
|
|
JSON.stringify({
|
|
organizationId: app.organizationId,
|
|
})
|
|
);
|
|
}
|
|
|
|
const response = decamelizeKeys(app);
|
|
|
|
const seralizedQueries = [];
|
|
const dataQueriesForVersion = app.editingVersion
|
|
? await this.appsService.findDataQueriesForVersion(app.editingVersion.id)
|
|
: [];
|
|
|
|
const pagesForVersion = app.editingVersion ? await this.pageService.findPagesForVersion(app.editingVersion.id) : [];
|
|
const eventsForVersion = app.editingVersion
|
|
? await this.eventsService.findEventsForVersion(app.editingVersion.id)
|
|
: [];
|
|
|
|
// serialize queries
|
|
for (const query of dataQueriesForVersion) {
|
|
const decamelizedQuery = decamelizeKeys(query);
|
|
decamelizedQuery['options'] = query.options;
|
|
seralizedQueries.push(decamelizedQuery);
|
|
}
|
|
|
|
response['data_queries'] = seralizedQueries;
|
|
response['definition'] = app.editingVersion?.definition;
|
|
response['pages'] = pagesForVersion;
|
|
response['events'] = eventsForVersion;
|
|
|
|
console.log({ app });
|
|
|
|
//! if editing version exists, camelize the definition
|
|
if (app.editingVersion && app.editingVersion.definition) {
|
|
response['editing_version'] = {
|
|
...response['editing_version'],
|
|
definition: camelizeKeys(app.editingVersion.definition),
|
|
};
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
@UseGuards(AppAuthGuard) // This guard will allow access for unauthenticated user if the app is public
|
|
@Get('slugs/:slug')
|
|
async appFromSlug(@User() user, @AppDecorator() app: App) {
|
|
if (user) {
|
|
const ability = await this.appsAbilityFactory.appsActions(user, app.id);
|
|
|
|
if (!ability.can('viewApp', app)) {
|
|
throw new ForbiddenException(
|
|
JSON.stringify({
|
|
organizationId: app.organizationId,
|
|
})
|
|
);
|
|
}
|
|
}
|
|
|
|
const versionToLoad = app.currentVersionId
|
|
? await this.appsService.findVersion(app.currentVersionId)
|
|
: await this.appsService.findVersion(app.editingVersion?.id);
|
|
|
|
const pagesForVersion = app.editingVersion ? await this.pageService.findPagesForVersion(versionToLoad.id) : [];
|
|
const eventsForVersion = app.editingVersion ? await this.eventsService.findEventsForVersion(versionToLoad.id) : [];
|
|
|
|
// serialize
|
|
return {
|
|
current_version_id: app['currentVersionId'],
|
|
data_queries: versionToLoad?.dataQueries,
|
|
definition: versionToLoad?.definition,
|
|
is_public: app.isPublic,
|
|
is_maintenance_on: app.isMaintenanceOn,
|
|
name: app.name,
|
|
slug: app.slug,
|
|
events: eventsForVersion,
|
|
pages: pagesForVersion,
|
|
homePageId: versionToLoad.homePageId,
|
|
globalSettings: versionToLoad.globalSettings,
|
|
showViewerNavigation: versionToLoad.showViewerNavigation,
|
|
};
|
|
}
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
@UseInterceptors(ValidAppInterceptor)
|
|
@Get(':id/versions/:versionId')
|
|
async version(@User() user, @Param('id') id, @Param('versionId') versionId) {
|
|
const appVersion = await this.appsService.findVersion(versionId);
|
|
const app = appVersion.app;
|
|
|
|
if (app.id !== id) {
|
|
throw new BadRequestException();
|
|
}
|
|
const ability = await this.appsAbilityFactory.appsActions(user, app.id);
|
|
|
|
if (!ability.can('fetchVersions', app)) {
|
|
throw new ForbiddenException(
|
|
JSON.stringify({
|
|
organizationId: app.organizationId,
|
|
})
|
|
);
|
|
}
|
|
|
|
const pagesForVersion = await this.pageService.findPagesForVersion(versionId);
|
|
const eventsForVersion = await this.eventsService.findEventsForVersion(versionId);
|
|
|
|
const appCurrentEditingVersion = JSON.parse(JSON.stringify(appVersion));
|
|
|
|
delete appCurrentEditingVersion['app'];
|
|
|
|
const appData = {
|
|
...app,
|
|
};
|
|
|
|
delete appData['editingVersion'];
|
|
|
|
return {
|
|
...appData,
|
|
editing_version: camelizeKeys(appCurrentEditingVersion),
|
|
pages: pagesForVersion,
|
|
events: eventsForVersion,
|
|
};
|
|
}
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
@UseInterceptors(ValidAppInterceptor)
|
|
@Put(':id/versions/:versionId')
|
|
async updateVersion(
|
|
@User() user,
|
|
@Param('id') id,
|
|
@Param('versionId') versionId,
|
|
@Body() appVersionUpdateDto: AppVersionUpdateDto
|
|
) {
|
|
const version = await this.appsService.findVersion(versionId);
|
|
const app = version.app;
|
|
|
|
if (app.id !== id) {
|
|
throw new BadRequestException();
|
|
}
|
|
const ability = await this.appsAbilityFactory.appsActions(user, id);
|
|
|
|
if (!ability.can('updateVersions', app)) {
|
|
throw new ForbiddenException('You do not have permissions to perform this action');
|
|
}
|
|
|
|
return await this.appsService.updateAppVersion(version, appVersionUpdateDto);
|
|
}
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
@UseInterceptors(ValidAppInterceptor)
|
|
@Put(':id/versions/:versionId/global_settings')
|
|
async updateGlobalSettings(
|
|
@User() user,
|
|
@Param('id') id,
|
|
@Param('versionId') versionId,
|
|
@Body() appVersionUpdateDto: AppVersionUpdateDto
|
|
) {
|
|
const version = await this.appsService.findVersion(versionId);
|
|
const app = version.app;
|
|
|
|
if (app.id !== id) {
|
|
throw new BadRequestException();
|
|
}
|
|
const ability = await this.appsAbilityFactory.appsActions(user, id);
|
|
|
|
if (!ability.can('updateVersions', app)) {
|
|
throw new ForbiddenException('You do not have permissions to perform this action');
|
|
}
|
|
|
|
return await this.appsService.updateAppVersion(version, appVersionUpdateDto);
|
|
}
|
|
|
|
//components api
|
|
@UseGuards(JwtAuthGuard)
|
|
@UseInterceptors(ValidAppInterceptor)
|
|
@Post(':id/versions/:versionId/components')
|
|
async createComponent(
|
|
@User() user,
|
|
@Param('id') id,
|
|
@Param('versionId') versionId,
|
|
@Body() createComponentDto: CreateComponentDto
|
|
) {
|
|
const version = await this.appsService.findVersion(versionId);
|
|
const app = version.app;
|
|
|
|
if (app.id !== id) {
|
|
throw new BadRequestException();
|
|
}
|
|
const ability = await this.appsAbilityFactory.appsActions(user, id);
|
|
|
|
if (!ability.can('updateVersions', app)) {
|
|
throw new ForbiddenException('You do not have permissions to perform this action');
|
|
}
|
|
|
|
await this.componentsService.create(createComponentDto.diff, createComponentDto.pageId, versionId);
|
|
}
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
@UseInterceptors(ValidAppInterceptor)
|
|
@Put(':id/versions/:versionId/components')
|
|
async updateComponent(
|
|
@User() user,
|
|
@Param('id') id,
|
|
@Param('versionId') versionId,
|
|
@Body() updateComponentDto: UpdateComponentDto
|
|
) {
|
|
const version = await this.appsService.findVersion(versionId);
|
|
const app = version.app;
|
|
|
|
if (app.id !== id) {
|
|
throw new BadRequestException();
|
|
}
|
|
const ability = await this.appsAbilityFactory.appsActions(user, id);
|
|
|
|
if (!ability.can('updateVersions', app)) {
|
|
throw new ForbiddenException('You do not have permissions to perform this action');
|
|
}
|
|
|
|
await this.componentsService.update(updateComponentDto.diff, versionId);
|
|
}
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
@UseInterceptors(ValidAppInterceptor)
|
|
@Delete(':id/versions/:versionId/components')
|
|
async deleteComponents(
|
|
@User() user,
|
|
@Param('id') id,
|
|
@Param('versionId') versionId,
|
|
@Body() deleteComponentDto: DeleteComponentDto
|
|
) {
|
|
const version = await this.appsService.findVersion(versionId);
|
|
const app = version.app;
|
|
|
|
if (app.id !== id) {
|
|
throw new BadRequestException();
|
|
}
|
|
const ability = await this.appsAbilityFactory.appsActions(user, id);
|
|
|
|
if (!ability.can('updateVersions', app)) {
|
|
throw new ForbiddenException('You do not have permissions to perform this action');
|
|
}
|
|
|
|
await this.componentsService.delete(deleteComponentDto.diff, versionId, deleteComponentDto.is_component_cut);
|
|
}
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
@UseInterceptors(ValidAppInterceptor)
|
|
@Put(':id/versions/:versionId/components/layout')
|
|
async updateComponentLayout(
|
|
@User() user,
|
|
@Param('id') id,
|
|
@Param('versionId') versionId,
|
|
@Body() updateComponentLayout: LayoutUpdateDto
|
|
) {
|
|
const version = await this.appsService.findVersion(versionId);
|
|
const app = version.app;
|
|
|
|
if (app.id !== id) {
|
|
throw new BadRequestException();
|
|
}
|
|
const ability = await this.appsAbilityFactory.appsActions(user, id);
|
|
|
|
if (!ability.can('updateVersions', app)) {
|
|
throw new ForbiddenException('You do not have permissions to perform this action');
|
|
}
|
|
|
|
await this.componentsService.componentLayoutChange(updateComponentLayout.diff, versionId);
|
|
}
|
|
|
|
// pages api
|
|
@UseGuards(JwtAuthGuard)
|
|
@UseInterceptors(ValidAppInterceptor)
|
|
@Post(':id/versions/:versionId/pages')
|
|
async createPages(
|
|
@User() user,
|
|
@Param('id') id,
|
|
@Param('versionId') versionId,
|
|
@Body() createPageDto: CreatePageDto
|
|
) {
|
|
const version = await this.appsService.findVersion(versionId);
|
|
const app = version.app;
|
|
|
|
if (app.id !== id) {
|
|
throw new BadRequestException();
|
|
}
|
|
const ability = await this.appsAbilityFactory.appsActions(user, id);
|
|
|
|
if (!ability.can('updateVersions', app)) {
|
|
throw new ForbiddenException('You do not have permissions to perform this action');
|
|
}
|
|
|
|
await this.pageService.createPage(createPageDto, versionId);
|
|
}
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
@UseInterceptors(ValidAppInterceptor)
|
|
@Post(':id/versions/:versionId/pages/:pageId/clone')
|
|
async clonePage(@User() user, @Param('id') id, @Param('versionId') versionId, @Param('pageId') pageId) {
|
|
const version = await this.appsService.findVersion(versionId);
|
|
const app = version.app;
|
|
const ability = await this.appsAbilityFactory.appsActions(user, id);
|
|
|
|
if (!ability.can('updateVersions', app)) {
|
|
throw new ForbiddenException('You do not have permissions to perform this action');
|
|
}
|
|
|
|
return await this.pageService.clonePage(pageId, versionId);
|
|
}
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
@UseInterceptors(ValidAppInterceptor)
|
|
@Put(':id/versions/:versionId/pages')
|
|
async updatePages(@User() user, @Param('id') id, @Param('versionId') versionId, @Body() updatePageDto) {
|
|
const version = await this.appsService.findVersion(versionId);
|
|
const app = version.app;
|
|
|
|
if (app.id !== id) {
|
|
throw new BadRequestException();
|
|
}
|
|
const ability = await this.appsAbilityFactory.appsActions(user, id);
|
|
|
|
if (!ability.can('updateVersions', app)) {
|
|
throw new ForbiddenException('You do not have permissions to perform this action');
|
|
}
|
|
|
|
await this.pageService.updatePage(updatePageDto, versionId);
|
|
}
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
@UseInterceptors(ValidAppInterceptor)
|
|
@Delete(':id/versions/:versionId/pages')
|
|
async deletePage(@User() user, @Param('id') id, @Param('versionId') versionId, @Body() deletePageDto: DeletePageDto) {
|
|
const version = await this.appsService.findVersion(versionId);
|
|
const app = version.app;
|
|
|
|
if (app.id !== id) {
|
|
throw new BadRequestException();
|
|
}
|
|
const ability = await this.appsAbilityFactory.appsActions(user, id);
|
|
|
|
if (!ability.can('updateVersions', app)) {
|
|
throw new ForbiddenException('You do not have permissions to perform this action');
|
|
}
|
|
|
|
await this.pageService.deletePage(deletePageDto.pageId, versionId);
|
|
}
|
|
|
|
// events api
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
@UseInterceptors(ValidAppInterceptor)
|
|
@Get(':id/versions/:versionId/events')
|
|
async getEvents(@User() user, @Param('id') id, @Param('versionId') versionId, @Query('sourceId') sourceId) {
|
|
const version = await this.appsService.findVersion(versionId);
|
|
const app = version.app;
|
|
|
|
if (app.id !== id) {
|
|
throw new BadRequestException();
|
|
}
|
|
|
|
const ability = await this.appsAbilityFactory.appsActions(user, id);
|
|
|
|
if (!ability.can('viewApp', app)) {
|
|
throw new ForbiddenException('You do not have permissions to perform this action');
|
|
}
|
|
|
|
if (!sourceId) {
|
|
return this.eventService.findEventsForVersion(versionId);
|
|
}
|
|
|
|
return this.eventService.findAllEventsWithSourceId(sourceId);
|
|
}
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
@UseInterceptors(ValidAppInterceptor)
|
|
@Post(':id/versions/:versionId/events')
|
|
async createEvent(
|
|
@User() user,
|
|
@Param('id') id,
|
|
@Param('versionId') versionId,
|
|
@Body() createEventHandlerDto: CreateEventHandlerDto
|
|
) {
|
|
const version = await this.appsService.findVersion(versionId);
|
|
const app = version.app;
|
|
|
|
if (app.id !== id) {
|
|
throw new BadRequestException();
|
|
}
|
|
const ability = await this.appsAbilityFactory.appsActions(user, id);
|
|
|
|
if (!ability.can('updateVersions', app)) {
|
|
throw new ForbiddenException('You do not have permissions to perform this action');
|
|
}
|
|
|
|
return this.eventService.createEvent(createEventHandlerDto, versionId);
|
|
}
|
|
@UseGuards(JwtAuthGuard)
|
|
@UseInterceptors(ValidAppInterceptor)
|
|
@Put(':id/versions/:versionId/events')
|
|
async updateEvents(
|
|
@User() user,
|
|
@Param('id') id,
|
|
@Param('versionId') versionId,
|
|
@Body() updateEventHandlerDto: UpdateEventHandlerDto
|
|
) {
|
|
const version = await this.appsService.findVersion(versionId);
|
|
const app = version.app;
|
|
|
|
if (app.id !== id) {
|
|
throw new BadRequestException();
|
|
}
|
|
const ability = await this.appsAbilityFactory.appsActions(user, id);
|
|
|
|
if (!ability.can('updateVersions', app)) {
|
|
throw new ForbiddenException('You do not have permissions to perform this action');
|
|
}
|
|
|
|
const { events, updateType } = updateEventHandlerDto;
|
|
|
|
return await this.eventService.updateEvent(events, updateType, versionId);
|
|
}
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
@UseInterceptors(ValidAppInterceptor)
|
|
@Delete(':id/versions/:versionId/events/:eventId')
|
|
async deleteEvents(@User() user, @Param('id') id, @Param('versionId') versionId, @Param('eventId') eventId) {
|
|
const version = await this.appsService.findVersion(versionId);
|
|
const app = version.app;
|
|
|
|
if (app.id !== id) {
|
|
throw new BadRequestException();
|
|
}
|
|
const ability = await this.appsAbilityFactory.appsActions(user, id);
|
|
|
|
if (!ability.can('updateVersions', app)) {
|
|
throw new ForbiddenException('You do not have permissions to perform this action');
|
|
}
|
|
|
|
return await this.eventService.deleteEvent(eventId, versionId);
|
|
}
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
@UseInterceptors(ValidAppInterceptor)
|
|
@Put(':id/release')
|
|
async releaseVersion(
|
|
@User() user,
|
|
@Param('id') id,
|
|
@AppDecorator() app: App,
|
|
@Body() versionReleaseDto: VersionReleaseDto
|
|
) {
|
|
const ability = await this.appsAbilityFactory.appsActions(user, app.id);
|
|
if (!ability.can('updateParams', app)) {
|
|
throw new ForbiddenException('You do not have permissions to perform this action');
|
|
}
|
|
return await this.appsService.releaseVersion(app.id, versionReleaseDto);
|
|
}
|
|
}
|