mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-07-13 09:27:28 +00:00
export
This commit is contained in:
@ -43,6 +43,11 @@ export class ControllerRoute {
|
||||
'Get user by uuid in project';
|
||||
public static readonly GET_USER_BY_UUID_IN_PROJECT_DESCRIPTION =
|
||||
'This endpoint retrieves a user by their unique identifier (UUID) associated with a specific project.';
|
||||
|
||||
public static readonly EXPORT_STRUCTURE_CSV_SUMMARY =
|
||||
'Export project with their full structure to a CSV file';
|
||||
public static readonly EXPORT_STRUCTURE_CSV_DESCRIPTION =
|
||||
'This endpoint exports project along with their associated communities, spaces, and nested space hierarchy into a downloadable CSV file. Useful for backups, reports, or audits';
|
||||
};
|
||||
};
|
||||
static PROJECT_USER = class {
|
||||
|
39
package-lock.json
generated
39
package-lock.json
generated
@ -9,6 +9,7 @@
|
||||
"version": "0.0.1",
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"@fast-csv/format": "^5.0.2",
|
||||
"@nestjs/common": "^10.0.0",
|
||||
"@nestjs/config": "^3.2.0",
|
||||
"@nestjs/core": "^10.0.0",
|
||||
@ -900,6 +901,19 @@
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fast-csv/format": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-5.0.2.tgz",
|
||||
"integrity": "sha512-fRYcWvI8vs0Zxa/8fXd/QlmQYWWkJqKZPAXM+vksnplb3owQFKTPPh9JqOtD0L3flQw/AZjjXdPkD7Kp/uHm8g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash.escaperegexp": "^4.1.2",
|
||||
"lodash.isboolean": "^3.0.3",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"lodash.isfunction": "^3.0.9",
|
||||
"lodash.isnil": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@firebase/analytics": {
|
||||
"version": "0.10.8",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.8.tgz",
|
||||
@ -9354,6 +9368,12 @@
|
||||
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.escaperegexp": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
|
||||
"integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.includes": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||
@ -9372,12 +9392,31 @@
|
||||
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isequal": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
|
||||
"deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isfunction": {
|
||||
"version": "3.0.9",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz",
|
||||
"integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isinteger": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isnil": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz",
|
||||
"integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isnumber": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
||||
|
@ -20,6 +20,7 @@
|
||||
"test:e2e": "jest --config ./apps/backend/test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fast-csv/format": "^5.0.2",
|
||||
"@nestjs/common": "^10.0.0",
|
||||
"@nestjs/config": "^3.2.0",
|
||||
"@nestjs/core": "^10.0.0",
|
||||
|
@ -4,13 +4,20 @@ import {
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Header,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import {
|
||||
ApiBearerAuth,
|
||||
ApiOperation,
|
||||
ApiProduces,
|
||||
ApiResponse,
|
||||
ApiTags,
|
||||
} from '@nestjs/swagger';
|
||||
import { ProjectService } from '../services';
|
||||
import { CreateProjectDto, GetProjectParam } from '../dto';
|
||||
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||
@ -86,4 +93,24 @@ export class ProjectController {
|
||||
async findOne(@Param() params: GetProjectParam): Promise<BaseResponseDto> {
|
||||
return this.projectService.getProject(params.projectUuid);
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiOperation({
|
||||
summary: ControllerRoute.PROJECT.ACTIONS.EXPORT_STRUCTURE_CSV_SUMMARY,
|
||||
description:
|
||||
ControllerRoute.PROJECT.ACTIONS.EXPORT_STRUCTURE_CSV_DESCRIPTION,
|
||||
})
|
||||
@Get(':projectUuid/structure/export-csv')
|
||||
@ApiProduces('text/csv')
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description:
|
||||
'A CSV file containing project details and their structural hierarchy (Project, Community, Space, Parent Space).',
|
||||
})
|
||||
@Header('Content-Type', 'text/csv')
|
||||
@Header('Content-Disposition', 'attachment; filename=project-structure.csv')
|
||||
async exportStructures(@Param() params: GetProjectParam) {
|
||||
return this.projectService.exportToCsv(params);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ProjectRepository } from '@app/common/modules/project/repositiories';
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { CreateProjectDto } from '../dto';
|
||||
import { CreateProjectDto, GetProjectParam } from '../dto';
|
||||
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
||||
import { ProjectEntity } from '@app/common/modules/project/entities';
|
||||
@ -13,6 +13,8 @@ import { PageResponse } from '@app/common/dto/pagination.response.dto';
|
||||
import { CommandBus } from '@nestjs/cqrs';
|
||||
import { CreateOrphanSpaceCommand } from '../command/create-orphan-space-command';
|
||||
import { UserRepository } from '@app/common/modules/user/repositories';
|
||||
import { format } from '@fast-csv/format';
|
||||
import { PassThrough } from 'stream';
|
||||
|
||||
@Injectable()
|
||||
export class ProjectService {
|
||||
@ -213,4 +215,62 @@ export class ProjectService {
|
||||
async validate(name: string): Promise<boolean> {
|
||||
return await this.projectRepository.exists({ where: { name } });
|
||||
}
|
||||
|
||||
async exportToCsv(param: GetProjectParam): Promise<PassThrough> {
|
||||
try {
|
||||
const project = await this.projectRepository.findOne({
|
||||
where: { uuid: param.projectUuid },
|
||||
relations: [
|
||||
'communities',
|
||||
'communities.spaces',
|
||||
'communities.spaces.parent',
|
||||
'communities.spaces.tags',
|
||||
],
|
||||
});
|
||||
|
||||
if (!project) {
|
||||
throw new HttpException(
|
||||
`Project with UUID ${param.projectUuid} not found`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const stream = new PassThrough();
|
||||
const csvStream = format({ headers: true });
|
||||
|
||||
csvStream.pipe(stream);
|
||||
|
||||
for (const community of project.communities || []) {
|
||||
for (const space of community.spaces || []) {
|
||||
const tagNames = space.tags?.map((tag) => tag.tag).join(', ') || '';
|
||||
|
||||
csvStream.write({
|
||||
'Project Name': project.name,
|
||||
'Project UUID': project.uuid,
|
||||
'Community Name': community.name,
|
||||
'Community UUID': community.uuid,
|
||||
'Space Name': space.spaceName,
|
||||
'Space UUID': space.uuid,
|
||||
'Parent Space UUID': space.parent?.uuid || '',
|
||||
'Space Tuya UUID': space.spaceTuyaUuid || '',
|
||||
X: space.x,
|
||||
Y: space.y,
|
||||
Tags: tagNames,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
csvStream.end();
|
||||
return stream;
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new HttpException(
|
||||
`Failed to export project structure for UUID ${param.projectUuid}: ${error.message}`,
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user