Skip to content

Commit 420bed8

Browse files
committed
feat: add permissions, DTOs, logger, filtering, Swagger, and fix email bug
Implements 6 production-readiness features across the plugin ecosystem: 1. Email bug fix: use emitAsync() for reliable async event handling, mark notifications as failed when queue is null 2. Filtering & sorting: add FilterableDto, ValidSortBy decorator, search support in paginate(), query DTOs for audit-log/notifications 3. DTOs & validation: proper class-validator DTOs for organizations, notifications, device tokens, preferences, and items 4. Logger module: new @bbv/nestjs-logger wrapping nestjs-pino with correlation IDs and pretty-print support 5. Permissions system: role field on User model, PermissionsGuard with wildcard matching, OrgMemberGuard for org-level authorization, JWT enriched with role 6. Swagger decorators: @apitags, @apioperation, @apiresponse, @ApiBearerAuth on all controllers and @ApiProperty on all DTOs
1 parent 5a743f8 commit 420bed8

68 files changed

Lines changed: 2000 additions & 429 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/demo/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@
2626
"@bbv/nestjs-pagination": "*",
2727
"@bbv/nestjs-prisma": "*",
2828
"@bbv/nestjs-response": "*",
29+
"@bbv/nestjs-logger": "*",
2930
"@bbv/nestjs-storage": "*",
31+
"nestjs-pino": "^4.0.0",
32+
"pino-http": "^10.0.0",
33+
"pino-pretty": "^11.0.0",
3034
"@nestjs/bullmq": "^10.2.0",
3135
"@nestjs/event-emitter": "^2.1.0",
3236
"@nestjs/common": "^10.0.0",

apps/demo/prisma/schema/auth.prisma

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ model User {
44
passwordHash String?
55
emailVerified Boolean @default(false)
66
avatarUrl String?
7+
role String @default("user")
78
isActive Boolean @default(true)
89
lastLoginAt DateTime?
910
createdAt DateTime @default(now())

apps/demo/src/app.controller.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { Controller, Get } from '@nestjs/common';
2-
import { ApiTags } from '@nestjs/swagger';
2+
import { ApiTags, ApiOperation } from '@nestjs/swagger';
33
import { Public } from '@bbv/nestjs-auth';
44

55
@ApiTags('Health')
66
@Controller()
77
export class AppController {
88
@Public()
99
@Get('health')
10+
@ApiOperation({ summary: 'Health check' })
1011
health() {
1112
return {
1213
status: 'ok',

apps/demo/src/app.module.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import { APP_GUARD } from '@nestjs/core';
33
import { ConfigModule, ConfigService } from '@nestjs/config';
44
import { EventEmitterModule } from '@nestjs/event-emitter';
55
import { PrismaModule } from '@bbv/nestjs-prisma';
6-
import { AuthModule, JwtAuthGuard } from '@bbv/nestjs-auth';
6+
import { AuthModule, JwtAuthGuard, PermissionsGuard } from '@bbv/nestjs-auth';
77
import { OtpModule } from '@bbv/nestjs-otp';
88
import { StorageModule } from '@bbv/nestjs-storage';
99
import { NotificationModule, AuthNotificationModule } from '@bbv/nestjs-notifications';
1010
import { AuditLogModule } from '@bbv/nestjs-audit-log';
11+
import { LoggerModule } from '@bbv/nestjs-logger';
1112
import { AppController } from './app.controller';
1213
import { ItemsModule } from './items/items.module';
1314
import * as path from 'path';
@@ -18,6 +19,10 @@ import * as path from 'path';
1819

1920
EventEmitterModule.forRoot(),
2021

22+
LoggerModule.forRoot({
23+
prettyPrint: process.env.NODE_ENV !== 'production',
24+
}),
25+
2126
PrismaModule.forRoot({ isGlobal: true }),
2227

2328
AuthModule.forRootAsync({
@@ -53,6 +58,16 @@ import * as path from 'path';
5358
}
5459
: {}),
5560
},
61+
permissions: {
62+
rolePermissions: {
63+
admin: ['*'],
64+
owner: ['org:*', 'items:*', 'audit:*', 'notifications:*', 'storage:*'],
65+
member: ['items:read', 'items:create', 'notifications:read', 'notifications:manage'],
66+
user: ['items:read', 'items:create', 'notifications:read'],
67+
},
68+
superAdminRoles: ['admin'],
69+
},
70+
defaultAdminEmail: config.get('ADMIN_EMAIL'),
5671
}),
5772
inject: [ConfigService],
5873
}),
@@ -125,7 +140,14 @@ import * as path from 'path';
125140
},
126141
},
127142
inApp: { enabled: true },
128-
sms: { enabled: false },
143+
sms: {
144+
enabled: true,
145+
provider: 'log' as const,
146+
},
147+
push: {
148+
enabled: true,
149+
provider: 'log' as const,
150+
},
129151
},
130152
features: {
131153
preferences: true,
@@ -170,6 +192,10 @@ import * as path from 'path';
170192
provide: APP_GUARD,
171193
useClass: JwtAuthGuard,
172194
},
195+
{
196+
provide: APP_GUARD,
197+
useClass: PermissionsGuard,
198+
},
173199
],
174200
controllers: [AppController],
175201
})
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
2+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
3+
4+
export class CreateItemDto {
5+
@ApiProperty({ description: 'Item name' })
6+
@IsString()
7+
@IsNotEmpty()
8+
name!: string;
9+
10+
@ApiPropertyOptional({ description: 'Item description' })
11+
@IsString()
12+
@IsOptional()
13+
description?: string;
14+
}

apps/demo/src/items/items.controller.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
33
import { PaginationDto, ApiPaginatedResponse } from '@bbv/nestjs-pagination';
44
import { CurrentUser, Public } from '@bbv/nestjs-auth';
55
import { ItemsService } from './items.service';
6+
import { CreateItemDto } from './dto/create-item.dto';
67

78
@ApiTags('Items')
89
@ApiBearerAuth()
@@ -24,7 +25,7 @@ export class ItemsController {
2425

2526
@Post()
2627
create(
27-
@Body() body: { name: string; description?: string },
28+
@Body() body: CreateItemDto,
2829
@CurrentUser('id') userId: string,
2930
) {
3031
return this.itemsService.create({ ...body, createdBy: userId });

apps/demo/src/main.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { NestFactory, Reflector } from '@nestjs/core';
22
import { ClassSerializerInterceptor, ValidationPipe } from '@nestjs/common';
33
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
4+
import { Logger } from 'nestjs-pino';
45
import { AppModule } from './app.module';
56
import { TransformInterceptor, HttpExceptionFilter } from '@bbv/nestjs-response';
67

78
async function bootstrap() {
8-
const app = await NestFactory.create(AppModule);
9+
const app = await NestFactory.create(AppModule, { bufferLogs: true });
10+
app.useLogger(app.get(Logger));
911

1012
app.useGlobalPipes(
1113
new ValidationPipe({

0 commit comments

Comments
 (0)