@devstroupe/devkit-cli 1.1.16 → 1.2.0-beta.2
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/README.md +18 -0
- package/dist/boilerplates/angular-template/src/app/core/services/auth.service.ts +1 -1
- package/dist/boilerplates/angular-template/src/app/services/role.service.ts +1 -1
- package/dist/boilerplates/angular-template/src/app/services/user.service.ts +1 -1
- package/dist/boilerplates/nest-template/.dockerignore +8 -0
- package/dist/boilerplates/nest-template/package.json +13 -15
- package/dist/boilerplates/nest-template/src/app.module.ts +7 -10
- package/dist/boilerplates/nest-template/src/database/data-source.ts +4 -1
- package/dist/boilerplates/nest-template/src/database/migrations/1600000000000-create-roles.ts +2 -1
- package/dist/boilerplates/nest-template/src/database/migrations/1700000000000-create-users.ts +2 -2
- package/dist/boilerplates/nest-template/src/modules/auth/auth.controller.ts +1 -1
- package/dist/boilerplates/nest-template/src/modules/auth/auth.module.ts +2 -7
- package/dist/boilerplates/nest-template/src/modules/auth/auth.service.ts +13 -11
- package/dist/boilerplates/nest-template/src/modules/auth/jwt-auth.guard.ts +5 -6
- package/dist/boilerplates/nest-template/src/modules/core/core.module.ts +9 -0
- package/dist/boilerplates/nest-template/src/modules/role/infra/database/role.orm-entity.ts +4 -1
- package/dist/boilerplates/nest-template/src/modules/role/infra/database/role.repository.adapter.ts +2 -2
- package/dist/boilerplates/nest-template/src/modules/role/infra/http/role.controller.ts +1 -1
- package/dist/boilerplates/nest-template/src/modules/storage/infra/http/storage.controller.ts +1 -1
- package/dist/boilerplates/nest-template/src/modules/user/infra/database/user.orm-entity.ts +4 -1
- package/dist/boilerplates/nest-template/src/modules/user/infra/database/user.repository.adapter.ts +2 -2
- package/dist/boilerplates/nest-template/src/modules/user/infra/http/user.controller.ts +1 -1
- package/dist/boilerplates/nest-template/src/modules/user/service/user.service.ts +6 -3
- package/dist/boilerplates/nest-template/src/platform/security/security.module.ts +20 -0
- package/dist/boilerplates/nest-template/src/platform/security/security.tokens.ts +4 -0
- package/dist/boilerplates/nest-template/src/platform/security/token-verification.module.ts +22 -0
- package/dist/boilerplates/nest-template/tsconfig.json +1 -1
- package/dist/generation/backend-generator.integration.test.d.ts +1 -0
- package/dist/generation/backend-generator.integration.test.js +251 -0
- package/dist/generation/backend-layout.d.ts +15 -0
- package/dist/generation/backend-layout.js +77 -0
- package/dist/generation/backend-layout.test.d.ts +1 -0
- package/dist/generation/backend-layout.test.js +48 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +389 -210
- package/dist/migrations/entity-module-move.d.ts +16 -0
- package/dist/migrations/entity-module-move.js +98 -0
- package/dist/migrations/entity-module-move.test.d.ts +1 -0
- package/dist/migrations/entity-module-move.test.js +30 -0
- package/dist/rules/linter-rules.js +252 -22
- package/dist/rules/linter-rules.test.js +125 -0
- package/dist/templates/angular/service.template.d.ts +1 -1
- package/dist/templates/angular/service.template.js +2 -2
- package/dist/templates/generator-characterization.test.d.ts +1 -0
- package/dist/templates/generator-characterization.test.js +42 -0
- package/dist/templates/nest/crud.templates.d.ts +3 -3
- package/dist/templates/nest/crud.templates.js +20 -13
- package/dist/templates/nest/functional-module.templates.d.ts +10 -0
- package/dist/templates/nest/functional-module.templates.js +261 -0
- package/dist/templates/nest/index.d.ts +1 -1
- package/dist/templates/nest/index.js +1 -1
- package/dist/templates/nest/migration.template.js +6 -3
- package/dist/templates/relationship-templates.test.js +0 -4
- package/package.json +4 -4
- package/dist/boilerplates/angular-template/node_modules/.bin/acorn +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/browserslist +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/eslint +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/jiti +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/js-yaml +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/lessc +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/ng +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/ng-xi18n +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/ngc +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/nx +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/nx-cloud +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/sass +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/terser +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/tsc +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/tsserver +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/vite +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/vitest +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/webpack +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/yaml +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/acorn +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/nest +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/prettier +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/ts-node +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/ts-node-cwd +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/ts-node-esm +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/ts-node-script +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/ts-node-transpile-only +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/ts-script +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/tsc +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/tsserver +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/typeorm +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/typeorm-ts-node-commonjs +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/typeorm-ts-node-esm +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/webpack +0 -21
- package/dist/templates/nest/microservice.templates.d.ts +0 -4
- package/dist/templates/nest/microservice.templates.js +0 -157
package/README.md
CHANGED
|
@@ -19,6 +19,8 @@ npm install -g @devstroupe/devkit-cli
|
|
|
19
19
|
| `devstroupe init` | Cria `devstroupe.config.ts` na raiz do projeto |
|
|
20
20
|
| `devstroupe init-ui [--style <style>]` | Injeta Design System Spartan NG, wrappers devkit-*, ThemeService e ProfileComponent |
|
|
21
21
|
| `devstroupe generate crud <entity>` | Gera CRUD completo (backend NestJS + frontend Angular) para a entidade |
|
|
22
|
+
| `devstroupe entity:set-module <entity> <module> --dry-run` | Planeja mudança de ownership sem alterar arquivos |
|
|
23
|
+
| `devstroupe entity:set-module <entity> <module> --apply` | Aplica mudança monolítica sem conflito e preserva backup |
|
|
22
24
|
| `devstroupe lint:rules` | Auditoria estática de governança arquitetural |
|
|
23
25
|
| `devstroupe ui:set-style <style>` | Troca o style Spartan e regenera `libs/ui/` |
|
|
24
26
|
|
|
@@ -28,6 +30,22 @@ O `init-ui` requer que o projeto Angular tenha o `@spartan-ng/cli` instalado. O
|
|
|
28
30
|
|
|
29
31
|
Styles disponíveis: `nova`, `vega`, `lyra`, `maia`, `mira`, `luma`.
|
|
30
32
|
|
|
33
|
+
## Backend gerado
|
|
34
|
+
|
|
35
|
+
`entity.module` agrupa entidades por capacidade; ausência resolve para `core`.
|
|
36
|
+
No monólito existe um Nest module por grupo. Em microsserviços existe uma
|
|
37
|
+
aplicação, banco, porta, container e rota HTTP de gateway por grupo — nunca por
|
|
38
|
+
entidade. O gateway é stateless, não possui TypeORM e encaminha
|
|
39
|
+
`/api/<module>/*`; controllers e DTOs existem somente na aplicação funcional.
|
|
40
|
+
User, Role e Auth pertencem ao Core e ao banco `devstroupe_core`. O CRUD usa use
|
|
41
|
+
cases tipados, TypeORM 1.x, soft delete obrigatório e tenant obtido
|
|
42
|
+
do contexto autenticado. No modo distribuído, `backend/packages/platform` é um
|
|
43
|
+
package local reutilizável, ligado ao gateway e a cada aplicação funcional via
|
|
44
|
+
`local_packages`; edite a origem central, não as cópias sincronizadas.
|
|
45
|
+
|
|
46
|
+
Veja [a arquitetura backend](../../docs/backend-architecture.md) e
|
|
47
|
+
[a migração TypeORM 1.x](../../docs/typeorm-1-migration.md).
|
|
48
|
+
|
|
31
49
|
## Pré-requisitos do `init-ui`
|
|
32
50
|
|
|
33
51
|
```bash
|
|
@@ -22,7 +22,7 @@ export interface AuthResponse {
|
|
|
22
22
|
})
|
|
23
23
|
export class AuthService {
|
|
24
24
|
private http = inject(HttpClient);
|
|
25
|
-
private apiUrl = '/api/auth';
|
|
25
|
+
private apiUrl = '/api/core/auth';
|
|
26
26
|
|
|
27
27
|
private currentUserSubject = new BehaviorSubject<User | null>(null);
|
|
28
28
|
public currentUser$ = this.currentUserSubject.asObservable();
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"version": "1.0.1",
|
|
4
4
|
"description": "Boilerplate NestJS integrado ao DT-DevKit",
|
|
5
5
|
"private": true,
|
|
6
|
+
"engines": { "node": ">=20" },
|
|
6
7
|
"scripts": {
|
|
7
8
|
"build": "nest build",
|
|
8
9
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
@@ -15,34 +16,31 @@
|
|
|
15
16
|
"migration:generate": "typeorm-ts-node-commonjs -d src/database/data-source.ts migration:generate src/database/migrations/schema"
|
|
16
17
|
},
|
|
17
18
|
"dependencies": {
|
|
19
|
+
"@devstroupe/devkit-core": "workspace:*",
|
|
20
|
+
"@devstroupe/devkit-nest": "workspace:*",
|
|
18
21
|
"@nestjs/common": "^11.0.0",
|
|
19
22
|
"@nestjs/core": "^11.0.0",
|
|
23
|
+
"@nestjs/microservices": "^11.0.0",
|
|
20
24
|
"@nestjs/platform-express": "^11.0.0",
|
|
25
|
+
"@nestjs/platform-socket.io": "^11.0.0",
|
|
21
26
|
"@nestjs/swagger": "^11.0.0",
|
|
22
27
|
"@nestjs/typeorm": "^11.0.0",
|
|
23
|
-
"@nestjs/
|
|
24
|
-
"
|
|
25
|
-
"
|
|
28
|
+
"@nestjs/websockets": "^11.0.0",
|
|
29
|
+
"class-transformer": "^0.5.1",
|
|
30
|
+
"class-validator": "^0.14.1",
|
|
31
|
+
"dotenv": "^16.4.5",
|
|
26
32
|
"mysql2": "^3.9.0",
|
|
27
33
|
"reflect-metadata": "^0.2.0",
|
|
28
34
|
"rxjs": "^7.8.1",
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"@devstroupe/devkit-core": "workspace:*",
|
|
32
|
-
"@devstroupe/devkit-nest": "workspace:*",
|
|
33
|
-
"class-validator": "^0.14.1",
|
|
34
|
-
"class-transformer": "^0.5.1",
|
|
35
|
-
"@nestjs/websockets": "^11.0.0",
|
|
36
|
-
"@nestjs/platform-socket.io": "^11.0.0",
|
|
37
|
-
"socket.io": "^4.7.5"
|
|
35
|
+
"socket.io": "^4.7.5",
|
|
36
|
+
"typeorm": "^1.0.0"
|
|
38
37
|
},
|
|
39
38
|
"devDependencies": {
|
|
40
39
|
"@nestjs/cli": "^11.0.0",
|
|
41
40
|
"@nestjs/schematics": "^11.0.0",
|
|
42
41
|
"@types/express": "^5.0.0",
|
|
43
42
|
"@types/node": "^20.11.0",
|
|
44
|
-
"
|
|
45
|
-
"typescript": "^5.4.5"
|
|
46
|
-
"ts-node": "^10.9.2"
|
|
43
|
+
"ts-node": "^10.9.2",
|
|
44
|
+
"typescript": "^5.4.5"
|
|
47
45
|
}
|
|
48
46
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { Module } from '@nestjs/common';
|
|
2
|
+
import { APP_INTERCEPTOR } from '@nestjs/core';
|
|
2
3
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
3
|
-
import { StorageModule } from '@devstroupe/devkit-nest';
|
|
4
|
-
import {
|
|
5
|
-
import { AuthModule } from './modules/auth/auth.module';
|
|
6
|
-
import { RoleModule } from './modules/role/role.module';
|
|
7
|
-
import { StorageDownloadModule } from './modules/storage/storage.module';
|
|
4
|
+
import { ExecutionContextInterceptor, StorageModule } from '@devstroupe/devkit-nest';
|
|
5
|
+
import { CoreModule } from './modules/core/core.module';
|
|
8
6
|
import * as path from 'path';
|
|
7
|
+
import { SecurityModule } from './platform/security/security.module';
|
|
9
8
|
|
|
10
9
|
@Module({
|
|
11
10
|
imports: [
|
|
11
|
+
SecurityModule,
|
|
12
12
|
TypeOrmModule.forRoot({
|
|
13
13
|
type: 'mysql',
|
|
14
14
|
host: process.env.DB_HOST || 'localhost',
|
|
@@ -41,12 +41,9 @@ import * as path from 'path';
|
|
|
41
41
|
}
|
|
42
42
|
})
|
|
43
43
|
}),
|
|
44
|
-
|
|
45
|
-
AuthModule,
|
|
46
|
-
RoleModule,
|
|
47
|
-
StorageDownloadModule,
|
|
44
|
+
CoreModule,
|
|
48
45
|
],
|
|
49
46
|
controllers: [],
|
|
50
|
-
providers: [],
|
|
47
|
+
providers: [{ provide: APP_INTERCEPTOR, useClass: ExecutionContextInterceptor }],
|
|
51
48
|
})
|
|
52
49
|
export class AppModule {}
|
|
@@ -10,7 +10,10 @@ export default new DataSource({
|
|
|
10
10
|
username: process.env.DB_USER || 'root',
|
|
11
11
|
password: process.env.DB_PASSWORD || 'root',
|
|
12
12
|
database: process.env.DB_NAME || 'devstroupe',
|
|
13
|
-
entities: [
|
|
13
|
+
entities: [
|
|
14
|
+
path.join(__dirname, '../modules/**/infra/database/*.orm-entity{.ts,.js}'),
|
|
15
|
+
path.join(__dirname, '../modules/**/infrastructure/persistence/typeorm/*.orm-entity{.ts,.js}'),
|
|
16
|
+
],
|
|
14
17
|
migrations: [path.join(__dirname, './migrations/*{.ts,.js}')],
|
|
15
18
|
synchronize: false,
|
|
16
19
|
});
|
package/dist/boilerplates/nest-template/src/database/migrations/1600000000000-create-roles.ts
CHANGED
|
@@ -9,7 +9,8 @@ export class CreateRoles1600000000000 implements MigrationInterface {
|
|
|
9
9
|
columns: [
|
|
10
10
|
{ name: 'id', type: 'int', isPrimary: true, isGenerated: true, generationStrategy: 'increment' },
|
|
11
11
|
{ name: 'name', type: 'varchar', length: '255', isUnique: true },
|
|
12
|
-
{ name: 'permissions', type: 'text', isNullable: true }
|
|
12
|
+
{ name: 'permissions', type: 'text', isNullable: true },
|
|
13
|
+
{ name: 'deleted_at', type: 'datetime', isNullable: true }
|
|
13
14
|
]
|
|
14
15
|
}), true);
|
|
15
16
|
}
|
package/dist/boilerplates/nest-template/src/database/migrations/1700000000000-create-users.ts
CHANGED
|
@@ -16,7 +16,8 @@ export class CreateUsers1700000000000 implements MigrationInterface {
|
|
|
16
16
|
{ name: 'tenant_id', type: 'varchar', length: '255', isNullable: true },
|
|
17
17
|
{ name: 'is_active', type: 'boolean', default: "'1'" },
|
|
18
18
|
{ name: 'created_at', type: 'datetime', default: 'CURRENT_TIMESTAMP' },
|
|
19
|
-
{ name: 'updated_at', type: 'datetime', default: 'CURRENT_TIMESTAMP', onUpdate: 'CURRENT_TIMESTAMP' }
|
|
19
|
+
{ name: 'updated_at', type: 'datetime', default: 'CURRENT_TIMESTAMP', onUpdate: 'CURRENT_TIMESTAMP' },
|
|
20
|
+
{ name: 'deleted_at', type: 'datetime', isNullable: true }
|
|
20
21
|
]
|
|
21
22
|
}), true);
|
|
22
23
|
|
|
@@ -34,4 +35,3 @@ export class CreateUsers1700000000000 implements MigrationInterface {
|
|
|
34
35
|
await queryRunner.dropTable('users', true);
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
|
-
|
|
@@ -5,7 +5,7 @@ import { JwtAuthGuard } from './jwt-auth.guard';
|
|
|
5
5
|
import { CurrentUser } from './current-user.decorator';
|
|
6
6
|
|
|
7
7
|
@ApiTags('auth')
|
|
8
|
-
@Controller('auth')
|
|
8
|
+
@Controller('core/auth')
|
|
9
9
|
export class AuthController {
|
|
10
10
|
constructor(private readonly authService: AuthService) {}
|
|
11
11
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { Module } from '@nestjs/common';
|
|
2
|
-
import { JwtModule } from '@nestjs/jwt';
|
|
1
|
+
import { Global, Module } from '@nestjs/common';
|
|
3
2
|
import { UserModule } from '../user/user.module';
|
|
4
3
|
import { RoleModule } from '../role/role.module';
|
|
5
4
|
import { AuthService } from './auth.service';
|
|
@@ -7,15 +6,11 @@ import { AuthController } from './auth.controller';
|
|
|
7
6
|
import { JwtAuthGuard } from './jwt-auth.guard';
|
|
8
7
|
import { RolesGuard } from './roles.guard';
|
|
9
8
|
|
|
9
|
+
@Global()
|
|
10
10
|
@Module({
|
|
11
11
|
imports: [
|
|
12
12
|
UserModule,
|
|
13
13
|
RoleModule,
|
|
14
|
-
JwtModule.register({
|
|
15
|
-
global: true,
|
|
16
|
-
secret: process.env.JWT_SECRET || 'devstroupe-secret-key-12345',
|
|
17
|
-
signOptions: { expiresIn: '1d' },
|
|
18
|
-
}),
|
|
19
14
|
],
|
|
20
15
|
providers: [AuthService, JwtAuthGuard, RolesGuard],
|
|
21
16
|
controllers: [AuthController],
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
|
2
|
-
import {
|
|
3
|
-
import * as bcrypt from 'bcryptjs';
|
|
1
|
+
import { Inject, Injectable, UnauthorizedException } from '@nestjs/common';
|
|
2
|
+
import type { PasswordHasher, TokenIssuer, TokenVerifier } from '@devstroupe/devkit-nest';
|
|
4
3
|
import { UserService } from '../user/service/user.service';
|
|
5
4
|
import { User } from '../user/domain/user';
|
|
6
5
|
import { RoleService } from '../role/service/role.service';
|
|
6
|
+
import { ACCESS_TOKEN_ISSUER, PASSWORD_HASHER, REFRESH_TOKEN_ISSUER, TOKEN_VERIFIER } from '../../platform/security/security.tokens';
|
|
7
7
|
|
|
8
8
|
@Injectable()
|
|
9
9
|
export class AuthService {
|
|
10
10
|
constructor(
|
|
11
11
|
private readonly userService: UserService,
|
|
12
|
-
private readonly jwtService: JwtService,
|
|
13
12
|
private readonly roleService: RoleService,
|
|
13
|
+
@Inject(PASSWORD_HASHER) private readonly passwordHasher: PasswordHasher,
|
|
14
|
+
@Inject(ACCESS_TOKEN_ISSUER) private readonly accessTokenIssuer: TokenIssuer,
|
|
15
|
+
@Inject(REFRESH_TOKEN_ISSUER) private readonly refreshTokenIssuer: TokenIssuer,
|
|
16
|
+
@Inject(TOKEN_VERIFIER) private readonly tokenVerifier: TokenVerifier,
|
|
14
17
|
) {}
|
|
15
18
|
|
|
16
19
|
async register(data: Omit<User, 'id' | 'role' | 'isActive'>): Promise<User> {
|
|
@@ -31,7 +34,7 @@ export class AuthService {
|
|
|
31
34
|
throw new UnauthorizedException('Invalid credentials');
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
const isMatch = await
|
|
37
|
+
const isMatch = await this.passwordHasher.verify(pass, user.password);
|
|
35
38
|
if (!isMatch) {
|
|
36
39
|
throw new UnauthorizedException('Invalid credentials');
|
|
37
40
|
}
|
|
@@ -45,7 +48,7 @@ export class AuthService {
|
|
|
45
48
|
|
|
46
49
|
async refresh(email: string, refreshToken: string): Promise<{ accessToken: string; refreshToken: string; user: Omit<User, 'password' | 'currentRefreshToken'> }> {
|
|
47
50
|
try {
|
|
48
|
-
const payload = await this.
|
|
51
|
+
const payload = await this.tokenVerifier.verify(refreshToken);
|
|
49
52
|
if (payload.email !== email) {
|
|
50
53
|
throw new UnauthorizedException('Invalid refresh token');
|
|
51
54
|
}
|
|
@@ -55,7 +58,7 @@ export class AuthService {
|
|
|
55
58
|
throw new UnauthorizedException('Session expired or user inactive');
|
|
56
59
|
}
|
|
57
60
|
|
|
58
|
-
const isMatch = await
|
|
61
|
+
const isMatch = await this.passwordHasher.verify(refreshToken, user.currentRefreshToken);
|
|
59
62
|
if (!isMatch) {
|
|
60
63
|
throw new UnauthorizedException('Invalid session');
|
|
61
64
|
}
|
|
@@ -79,11 +82,10 @@ export class AuthService {
|
|
|
79
82
|
permissions: user.role?.permissions || []
|
|
80
83
|
};
|
|
81
84
|
|
|
82
|
-
const accessToken = await this.
|
|
83
|
-
const refreshToken = await this.
|
|
85
|
+
const accessToken = await this.accessTokenIssuer.issue(payload);
|
|
86
|
+
const refreshToken = await this.refreshTokenIssuer.issue({ email: user.email });
|
|
84
87
|
|
|
85
|
-
const
|
|
86
|
-
const hashedRefreshToken = await bcrypt.hash(refreshToken, salt);
|
|
88
|
+
const hashedRefreshToken = await this.passwordHasher.hash(refreshToken);
|
|
87
89
|
await this.userService.update(user.id, { currentRefreshToken: hashedRefreshToken } as any);
|
|
88
90
|
|
|
89
91
|
const { password, currentRefreshToken, ...result } = user;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
|
|
2
|
-
import {
|
|
1
|
+
import { CanActivate, ExecutionContext, Inject, Injectable, UnauthorizedException } from '@nestjs/common';
|
|
2
|
+
import type { TokenVerifier } from '@devstroupe/devkit-nest';
|
|
3
3
|
import { Request } from 'express';
|
|
4
|
+
import { TOKEN_VERIFIER } from '../../platform/security/security.tokens';
|
|
4
5
|
|
|
5
6
|
@Injectable()
|
|
6
7
|
export class JwtAuthGuard implements CanActivate {
|
|
7
|
-
constructor(private readonly
|
|
8
|
+
constructor(@Inject(TOKEN_VERIFIER) private readonly tokenVerifier: TokenVerifier) {}
|
|
8
9
|
|
|
9
10
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
10
11
|
const request = context.switchToHttp().getRequest<Request>();
|
|
@@ -13,9 +14,7 @@ export class JwtAuthGuard implements CanActivate {
|
|
|
13
14
|
throw new UnauthorizedException('Token not found');
|
|
14
15
|
}
|
|
15
16
|
try {
|
|
16
|
-
const payload = await this.
|
|
17
|
-
secret: process.env.JWT_SECRET || 'devstroupe-secret-key-12345',
|
|
18
|
-
});
|
|
17
|
+
const payload = await this.tokenVerifier.verify(token);
|
|
19
18
|
// Injeta o payload decodificado (contendo sub, email, role, tenantId) na request
|
|
20
19
|
request['user'] = payload;
|
|
21
20
|
} catch {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Module } from '@nestjs/common';
|
|
2
|
+
import { AuthModule } from '../auth/auth.module';
|
|
3
|
+
import { StorageDownloadModule } from '../storage/storage.module';
|
|
4
|
+
|
|
5
|
+
@Module({
|
|
6
|
+
imports: [AuthModule, StorageDownloadModule],
|
|
7
|
+
exports: [AuthModule, StorageDownloadModule],
|
|
8
|
+
})
|
|
9
|
+
export class CoreModule {}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
|
|
1
|
+
import { Entity, PrimaryGeneratedColumn, Column, DeleteDateColumn } from 'typeorm';
|
|
2
2
|
|
|
3
3
|
@Entity('roles')
|
|
4
4
|
export class RoleOrmEntity {
|
|
@@ -10,4 +10,7 @@ export class RoleOrmEntity {
|
|
|
10
10
|
|
|
11
11
|
@Column({ type: 'simple-array', nullable: true })
|
|
12
12
|
permissions!: string[];
|
|
13
|
+
|
|
14
|
+
@DeleteDateColumn({ name: 'deleted_at', nullable: true })
|
|
15
|
+
deletedAt?: Date;
|
|
13
16
|
}
|
package/dist/boilerplates/nest-template/src/modules/role/infra/database/role.repository.adapter.ts
CHANGED
|
@@ -48,7 +48,7 @@ export class RoleRepositoryAdapter extends BaseRepository<RoleOrmEntity> impleme
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
async findAll(): Promise<Role[]> {
|
|
51
|
-
const found = await this.repo.find();
|
|
51
|
+
const found = await this.repo.find({ take: 100 });
|
|
52
52
|
return found.map(orm => this.toDomain(orm));
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -66,6 +66,6 @@ export class RoleRepositoryAdapter extends BaseRepository<RoleOrmEntity> impleme
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
async delete(id: number): Promise<void> {
|
|
69
|
-
await
|
|
69
|
+
await super.delete(id);
|
|
70
70
|
}
|
|
71
71
|
}
|
|
@@ -7,7 +7,7 @@ import { CreateRoleDto, UpdateRoleDto } from './role.dto';
|
|
|
7
7
|
import { JwtAuthGuard } from '../../../auth/jwt-auth.guard';
|
|
8
8
|
|
|
9
9
|
@UseGuards(JwtAuthGuard, PermissionsGuard)
|
|
10
|
-
@Controller('role')
|
|
10
|
+
@Controller('core/role')
|
|
11
11
|
@ApiTags('role')
|
|
12
12
|
export class RoleController extends BaseCrudController<Role> {
|
|
13
13
|
constructor(private readonly roleService: RoleService) {
|
package/dist/boilerplates/nest-template/src/modules/storage/infra/http/storage.controller.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { FileInterceptor } from '@nestjs/platform-express';
|
|
|
3
3
|
import { Response } from 'express';
|
|
4
4
|
import { StorageService } from '@devstroupe/devkit-nest';
|
|
5
5
|
|
|
6
|
-
@Controller('storage')
|
|
6
|
+
@Controller('core/storage')
|
|
7
7
|
export class StorageController {
|
|
8
8
|
constructor(
|
|
9
9
|
private readonly storageService: StorageService,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn } from 'typeorm';
|
|
1
|
+
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, DeleteDateColumn, ManyToOne, JoinColumn } from 'typeorm';
|
|
2
2
|
import { RoleOrmEntity } from '../../../role/infra/database/role.orm-entity';
|
|
3
3
|
|
|
4
4
|
@Entity('users')
|
|
@@ -33,4 +33,7 @@ export class UserOrmEntity {
|
|
|
33
33
|
|
|
34
34
|
@UpdateDateColumn({ name: 'updated_at' })
|
|
35
35
|
updatedAt!: Date;
|
|
36
|
+
|
|
37
|
+
@DeleteDateColumn({ name: 'deleted_at', nullable: true })
|
|
38
|
+
deletedAt?: Date;
|
|
36
39
|
}
|
package/dist/boilerplates/nest-template/src/modules/user/infra/database/user.repository.adapter.ts
CHANGED
|
@@ -55,7 +55,7 @@ export class UserRepositoryAdapter extends BaseRepository<UserOrmEntity> impleme
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
async findAll(): Promise<User[]> {
|
|
58
|
-
const found = await this.repo.find({ relations:
|
|
58
|
+
const found = await this.repo.find({ relations: { role: true }, take: 100 });
|
|
59
59
|
return found.map((u) => this.toDomain(u));
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -76,6 +76,6 @@ export class UserRepositoryAdapter extends BaseRepository<UserOrmEntity> impleme
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
async delete(id: number): Promise<void> {
|
|
79
|
-
await
|
|
79
|
+
await super.delete(id);
|
|
80
80
|
}
|
|
81
81
|
}
|
|
@@ -6,7 +6,7 @@ import { BaseCrudController, Permissions, PermissionsGuard } from '@devstroupe/d
|
|
|
6
6
|
import { User } from '../../domain/user';
|
|
7
7
|
|
|
8
8
|
@ApiTags('users')
|
|
9
|
-
@Controller('user')
|
|
9
|
+
@Controller('core/user')
|
|
10
10
|
@UseGuards(JwtAuthGuard, PermissionsGuard)
|
|
11
11
|
@ApiBearerAuth()
|
|
12
12
|
export class UserController extends BaseCrudController<User> {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Injectable, Inject } from '@nestjs/common';
|
|
2
|
-
import
|
|
2
|
+
import type { PasswordHasher } from '@devstroupe/devkit-nest';
|
|
3
|
+
import { PASSWORD_HASHER } from '../../../platform/security/security.tokens';
|
|
3
4
|
import { IPaginator } from '@devstroupe/devkit-core';
|
|
4
5
|
import { IUserRepository } from '../domain/user.repository';
|
|
5
6
|
import { User } from '../domain/user';
|
|
@@ -9,6 +10,8 @@ export class UserService {
|
|
|
9
10
|
constructor(
|
|
10
11
|
@Inject('IUserRepository')
|
|
11
12
|
private readonly userRepo: IUserRepository,
|
|
13
|
+
@Inject(PASSWORD_HASHER)
|
|
14
|
+
private readonly passwordHasher: PasswordHasher,
|
|
12
15
|
) {}
|
|
13
16
|
|
|
14
17
|
async create(user: Omit<User, 'id'>): Promise<User> {
|
|
@@ -17,7 +20,7 @@ export class UserService {
|
|
|
17
20
|
throw new Error('Email already registered');
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
const hashedPassword = await
|
|
23
|
+
const hashedPassword = await this.passwordHasher.hash(user.password);
|
|
21
24
|
return this.userRepo.create({
|
|
22
25
|
...user,
|
|
23
26
|
password: hashedPassword,
|
|
@@ -38,7 +41,7 @@ export class UserService {
|
|
|
38
41
|
|
|
39
42
|
async update(id: number, user: Partial<User>): Promise<User> {
|
|
40
43
|
if (user.password) {
|
|
41
|
-
user.password = await
|
|
44
|
+
user.password = await this.passwordHasher.hash(user.password);
|
|
42
45
|
}
|
|
43
46
|
return this.userRepo.update(id, user);
|
|
44
47
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Global, Module } from '@nestjs/common';
|
|
2
|
+
import { BcryptPasswordHasher, JwtTokenIssuer } from '@devstroupe/devkit-nest';
|
|
3
|
+
import { ACCESS_TOKEN_ISSUER, PASSWORD_HASHER, REFRESH_TOKEN_ISSUER } from './security.tokens';
|
|
4
|
+
import { TokenVerificationModule } from './token-verification.module';
|
|
5
|
+
|
|
6
|
+
function signingKey(): string {
|
|
7
|
+
return process.env.JWT_PRIVATE_KEY || process.env.JWT_SECRET || 'development-secret-with-at-least-32-characters';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
@Global()
|
|
11
|
+
@Module({
|
|
12
|
+
imports: [TokenVerificationModule],
|
|
13
|
+
providers: [
|
|
14
|
+
{ provide: PASSWORD_HASHER, useFactory: () => new BcryptPasswordHasher(12) },
|
|
15
|
+
{ provide: ACCESS_TOKEN_ISSUER, useFactory: () => new JwtTokenIssuer(signingKey(), { algorithm: process.env.JWT_PRIVATE_KEY ? 'RS256' : 'HS256', expiresIn: '15m' }) },
|
|
16
|
+
{ provide: REFRESH_TOKEN_ISSUER, useFactory: () => new JwtTokenIssuer(signingKey(), { algorithm: process.env.JWT_PRIVATE_KEY ? 'RS256' : 'HS256', expiresIn: '7d' }) },
|
|
17
|
+
],
|
|
18
|
+
exports: [PASSWORD_HASHER, ACCESS_TOKEN_ISSUER, REFRESH_TOKEN_ISSUER, TokenVerificationModule],
|
|
19
|
+
})
|
|
20
|
+
export class SecurityModule {}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Global, Module } from '@nestjs/common';
|
|
2
|
+
import { JwtTokenVerifier, staticVerificationKey } from '@devstroupe/devkit-nest';
|
|
3
|
+
import { TOKEN_VERIFIER } from './security.tokens';
|
|
4
|
+
|
|
5
|
+
function verificationKey(): string {
|
|
6
|
+
return process.env.JWT_PUBLIC_KEY || process.env.JWT_SECRET || 'development-secret-with-at-least-32-characters';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
@Global()
|
|
10
|
+
@Module({
|
|
11
|
+
providers: [
|
|
12
|
+
{
|
|
13
|
+
provide: TOKEN_VERIFIER,
|
|
14
|
+
useFactory: () => new JwtTokenVerifier(
|
|
15
|
+
staticVerificationKey(verificationKey()),
|
|
16
|
+
[process.env.JWT_PUBLIC_KEY ? 'RS256' : 'HS256'],
|
|
17
|
+
),
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
exports: [TOKEN_VERIFIER],
|
|
21
|
+
})
|
|
22
|
+
export class TokenVerificationModule {}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|