Skip to content

Commit a39a64a

Browse files
committed
chore: merge main into release for new releases
2 parents 6ec8ba1 + 7f79351 commit a39a64a

63 files changed

Lines changed: 2833 additions & 537 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/api/src/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { CloudSecurityModule } from './cloud-security/cloud-security.module';
3333
import { BrowserbaseModule } from './browserbase/browserbase.module';
3434
import { TaskManagementModule } from './task-management/task-management.module';
3535
import { AssistantChatModule } from './assistant-chat/assistant-chat.module';
36+
import { OrgChartModule } from './org-chart/org-chart.module';
3637
import { TrainingModule } from './training/training.module';
3738

3839
@Module({
@@ -80,6 +81,7 @@ import { TrainingModule } from './training/training.module';
8081
TaskManagementModule,
8182
AssistantChatModule,
8283
TrainingModule,
84+
OrgChartModule,
8385
],
8486
controllers: [AppController],
8587
providers: [
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { IsNotEmpty, IsString, Matches } from 'class-validator';
2+
import { ApiProperty } from '@nestjs/swagger';
3+
4+
export class UploadOrgChartDto {
5+
@ApiProperty({ description: 'Original file name' })
6+
@IsString()
7+
@IsNotEmpty()
8+
fileName: string;
9+
10+
@ApiProperty({ description: 'MIME type of the file (e.g. image/png)' })
11+
@IsString()
12+
@IsNotEmpty()
13+
@Matches(/^[a-zA-Z0-9\-]+\/[a-zA-Z0-9\-\+\.]+$/, {
14+
message: 'Invalid MIME type format',
15+
})
16+
fileType: string;
17+
18+
@ApiProperty({ description: 'Base64-encoded file data' })
19+
@IsString()
20+
@IsNotEmpty()
21+
fileData: string;
22+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { IsArray, IsOptional, IsString } from 'class-validator';
2+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
3+
4+
export class UpsertOrgChartDto {
5+
@ApiPropertyOptional({ description: 'Name of the org chart' })
6+
@IsOptional()
7+
@IsString()
8+
name?: string;
9+
10+
@ApiProperty({ description: 'React Flow nodes array', type: [Object] })
11+
@IsArray()
12+
nodes: any[];
13+
14+
@ApiProperty({ description: 'React Flow edges array', type: [Object] })
15+
@IsArray()
16+
edges: any[];
17+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import {
2+
Controller,
3+
Get,
4+
Put,
5+
Post,
6+
Delete,
7+
Body,
8+
UseGuards,
9+
UsePipes,
10+
ValidationPipe,
11+
} from '@nestjs/common';
12+
import {
13+
ApiHeader,
14+
ApiOperation,
15+
ApiResponse,
16+
ApiSecurity,
17+
ApiTags,
18+
} from '@nestjs/swagger';
19+
import { OrganizationId } from '../auth/auth-context.decorator';
20+
import { HybridAuthGuard } from '../auth/hybrid-auth.guard';
21+
import { OrgChartService } from './org-chart.service';
22+
import { UpsertOrgChartDto } from './dto/upsert-org-chart.dto';
23+
import { UploadOrgChartDto } from './dto/upload-org-chart.dto';
24+
25+
@ApiTags('Org Chart')
26+
@Controller({ path: 'org-chart', version: '1' })
27+
@UseGuards(HybridAuthGuard)
28+
@ApiSecurity('apikey')
29+
@ApiHeader({
30+
name: 'X-Organization-Id',
31+
description:
32+
'Organization ID (required for session auth, optional for API key auth)',
33+
required: false,
34+
})
35+
export class OrgChartController {
36+
constructor(private readonly orgChartService: OrgChartService) {}
37+
38+
@Get()
39+
@ApiOperation({ summary: 'Get the organization chart' })
40+
@ApiResponse({ status: 200, description: 'The organization chart' })
41+
async getOrgChart(@OrganizationId() organizationId: string) {
42+
return await this.orgChartService.findByOrganization(organizationId);
43+
}
44+
45+
@Put()
46+
@ApiOperation({ summary: 'Create or update an interactive organization chart' })
47+
@ApiResponse({ status: 200, description: 'The saved organization chart' })
48+
@UsePipes(new ValidationPipe({ whitelist: false, transform: false }))
49+
async upsertOrgChart(
50+
@OrganizationId() organizationId: string,
51+
@Body() body: Record<string, unknown>,
52+
) {
53+
const dto: UpsertOrgChartDto = {
54+
name: typeof body?.name === 'string' ? body.name : undefined,
55+
nodes: Array.isArray(body?.nodes) ? body.nodes : [],
56+
edges: Array.isArray(body?.edges) ? body.edges : [],
57+
};
58+
return await this.orgChartService.upsertInteractive(organizationId, dto);
59+
}
60+
61+
@Post('upload')
62+
@ApiOperation({ summary: 'Upload an image as the organization chart' })
63+
@ApiResponse({ status: 201, description: 'The uploaded organization chart' })
64+
@UsePipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true }))
65+
async uploadOrgChart(
66+
@OrganizationId() organizationId: string,
67+
@Body() dto: UploadOrgChartDto,
68+
) {
69+
return await this.orgChartService.uploadImage(organizationId, dto);
70+
}
71+
72+
@Delete()
73+
@ApiOperation({ summary: 'Delete the organization chart' })
74+
@ApiResponse({ status: 200, description: 'Deletion confirmation' })
75+
async deleteOrgChart(@OrganizationId() organizationId: string) {
76+
return await this.orgChartService.delete(organizationId);
77+
}
78+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Module } from '@nestjs/common';
2+
import { AuthModule } from '../auth/auth.module';
3+
import { OrgChartController } from './org-chart.controller';
4+
import { OrgChartService } from './org-chart.service';
5+
6+
@Module({
7+
imports: [AuthModule],
8+
controllers: [OrgChartController],
9+
providers: [OrgChartService],
10+
exports: [OrgChartService],
11+
})
12+
export class OrgChartModule {}

0 commit comments

Comments
 (0)