@devstroupe/devkit-cli 1.1.14 → 1.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/boilerplates/nest-template/src/modules/storage/infra/http/storage.controller.ts +49 -0
- package/dist/boilerplates/nest-template/src/modules/storage/storage.module.ts +2 -2
- package/dist/templates/nest/crud.templates.js +16 -5
- package/package.json +2 -2
- package/dist/boilerplates/nest-template/src/modules/storage/infra/http/storage-download.controller.ts +0 -38
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Controller, Get, Post, Param, Res, NotFoundException, UseInterceptors, UploadedFile, Body } from '@nestjs/common';
|
|
2
|
+
import { FileInterceptor } from '@nestjs/platform-express';
|
|
3
|
+
import { Response } from 'express';
|
|
4
|
+
import { StorageService } from '@devstroupe/devkit-nest';
|
|
5
|
+
|
|
6
|
+
@Controller('storage')
|
|
7
|
+
export class StorageController {
|
|
8
|
+
constructor(
|
|
9
|
+
private readonly storageService: StorageService,
|
|
10
|
+
) {}
|
|
11
|
+
|
|
12
|
+
@Post('upload')
|
|
13
|
+
@UseInterceptors(FileInterceptor('file'))
|
|
14
|
+
async upload(
|
|
15
|
+
@UploadedFile() file: any,
|
|
16
|
+
@Body('path') path?: string,
|
|
17
|
+
) {
|
|
18
|
+
if (!file) {
|
|
19
|
+
throw new NotFoundException('Nenhum arquivo enviado.');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const result = await this.storageService.upload(
|
|
23
|
+
{
|
|
24
|
+
originalname: file.originalname,
|
|
25
|
+
mimetype: file.mimetype,
|
|
26
|
+
buffer: file.buffer,
|
|
27
|
+
size: file.size,
|
|
28
|
+
},
|
|
29
|
+
{ path }
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@Get('download/*key')
|
|
36
|
+
async download(@Param('key') key: string, @Res() res: Response) {
|
|
37
|
+
try {
|
|
38
|
+
const result = await this.storageService.download(key);
|
|
39
|
+
|
|
40
|
+
if (result.type === 'file') {
|
|
41
|
+
return res.sendFile(result.filePath!);
|
|
42
|
+
} else {
|
|
43
|
+
return res.redirect(result.url!);
|
|
44
|
+
}
|
|
45
|
+
} catch (err: any) {
|
|
46
|
+
throw new NotFoundException(err.message || 'Arquivo não encontrado.');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Module } from '@nestjs/common';
|
|
2
|
-
import {
|
|
2
|
+
import { StorageController } from './infra/http/storage.controller';
|
|
3
3
|
|
|
4
4
|
@Module({
|
|
5
|
-
controllers: [
|
|
5
|
+
controllers: [StorageController],
|
|
6
6
|
})
|
|
7
7
|
export class StorageDownloadModule {}
|
|
@@ -25,12 +25,15 @@ function nestDomainEntityTemplate(name, properties, isTenantScoped, automaticFie
|
|
|
25
25
|
return ` ${relation.foreignKey}!: number;\n ${relation.relationName}?: any;`;
|
|
26
26
|
}
|
|
27
27
|
let typeStr = 'string';
|
|
28
|
-
if (p.type === 'number' || p.type === 'decimal' || p.type === 'currency') {
|
|
28
|
+
if (p.type === 'number' || p.type === 'decimal' || p.type === 'currency' || p.type === 'integer') {
|
|
29
29
|
typeStr = 'number';
|
|
30
30
|
}
|
|
31
31
|
else if (p.type === 'boolean') {
|
|
32
32
|
typeStr = 'boolean';
|
|
33
33
|
}
|
|
34
|
+
else if (p.type === 'datepicker' || p.type === 'datetime' || p.type === 'date') {
|
|
35
|
+
typeStr = 'string | Date';
|
|
36
|
+
}
|
|
34
37
|
else if (p.type === 'enum' && p.enumOptions) {
|
|
35
38
|
typeStr = p.enumOptions.map(o => `'${o}'`).join(' | ');
|
|
36
39
|
}
|
|
@@ -195,11 +198,16 @@ function nestOrmEntityTemplate(name, properties, tableName, isTenantScoped, inde
|
|
|
195
198
|
}
|
|
196
199
|
const indexDecorators = indexes && indexes.length > 0
|
|
197
200
|
? indexes.map(idx => {
|
|
198
|
-
const nameOption = idx.name ? `name: '${idx.name}'` : '';
|
|
199
201
|
const uniqueOption = idx.unique ? `unique: true` : '';
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
202
|
+
const nameOption = idx.name ? `'${idx.name}'` : '';
|
|
203
|
+
if (nameOption) {
|
|
204
|
+
const optStr = uniqueOption ? `, { ${uniqueOption} }` : '';
|
|
205
|
+
return `@Index(${nameOption}, ${JSON.stringify(idx.columns)}${optStr})`;
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
const optStr = uniqueOption ? `, { ${uniqueOption} }` : '';
|
|
209
|
+
return `@Index(${JSON.stringify(idx.columns)}${optStr})`;
|
|
210
|
+
}
|
|
203
211
|
}).join('\n') + '\n'
|
|
204
212
|
: '';
|
|
205
213
|
return `import { ${typeormImports.join(', ')} } from 'typeorm';${relImportsStr}
|
|
@@ -245,6 +253,9 @@ function nestRepositoryAdapterTemplate(name, properties, isTenantScoped) {
|
|
|
245
253
|
.join('\n');
|
|
246
254
|
const mapToOrmProperties = persistedProperties.map((property) => {
|
|
247
255
|
if (property.type !== 'relationship') {
|
|
256
|
+
if (property.type === 'datepicker' || property.type === 'datetime' || property.type === 'date') {
|
|
257
|
+
return ` if (domain.${property.name} !== undefined) orm.${property.name} = typeof domain.${property.name} === 'string' ? new Date(domain.${property.name}) : domain.${property.name};`;
|
|
258
|
+
}
|
|
248
259
|
return ` if (domain.${property.name} !== undefined) orm.${property.name} = domain.${property.name};`;
|
|
249
260
|
}
|
|
250
261
|
const relation = (0, relationships_1.getRelationships)([property])[0];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devstroupe/devkit-cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.16",
|
|
4
4
|
"description": "DevsTroupe Development Kit CLI — scaffold NestJS+Angular projects, inject Spartan UI, generate CRUDs and audit architectural governance",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"commander": "^12.0.0",
|
|
38
38
|
"fs-extra": "^11.2.0",
|
|
39
|
-
"@devstroupe/devkit-core": "1.1.
|
|
39
|
+
"@devstroupe/devkit-core": "1.1.16"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/fs-extra": "^11.0.4",
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { Controller, Get, Param, Res, NotFoundException, Inject } from '@nestjs/common';
|
|
2
|
-
import { Response } from 'express';
|
|
3
|
-
import * as path from 'path';
|
|
4
|
-
import * as fs from 'fs';
|
|
5
|
-
import { STORAGE_OPTIONS_TOKEN, StorageService } from '@devstroupe/devkit-nest';
|
|
6
|
-
|
|
7
|
-
@Controller('storage')
|
|
8
|
-
export class StorageDownloadController {
|
|
9
|
-
constructor(
|
|
10
|
-
@Inject(STORAGE_OPTIONS_TOKEN)
|
|
11
|
-
private readonly storageOptions: any,
|
|
12
|
-
private readonly storageService: StorageService,
|
|
13
|
-
) {}
|
|
14
|
-
|
|
15
|
-
@Get('download/*key')
|
|
16
|
-
async download(@Param('key') key: string, @Res() res: Response) {
|
|
17
|
-
if (this.storageOptions.driver === 'local') {
|
|
18
|
-
const storagePath = path.resolve(this.storageOptions.local?.storagePath || './storage');
|
|
19
|
-
const targetFile = path.resolve(storagePath, key);
|
|
20
|
-
const rootPrefix = storagePath.endsWith(path.sep) ? storagePath : storagePath + path.sep;
|
|
21
|
-
|
|
22
|
-
// Mitigação Directory Traversal
|
|
23
|
-
if (!targetFile.startsWith(rootPrefix)) {
|
|
24
|
-
throw new NotFoundException('Arquivo não encontrado ou acesso inválido.');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (!fs.existsSync(targetFile)) {
|
|
28
|
-
throw new NotFoundException('Arquivo não encontrado.');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return res.sendFile(targetFile);
|
|
32
|
-
} else {
|
|
33
|
-
// S3 / R2
|
|
34
|
-
const url = await this.storageService.getSignedUrl(key, 300);
|
|
35
|
-
return res.redirect(url);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|