@urbansolv/create-nestjs-app 1.0.2 → 1.2.0

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.
Files changed (51) hide show
  1. package/dist/templates/nestjs-app/.editorconfig +12 -12
  2. package/dist/templates/nestjs-app/.env.example +24 -24
  3. package/dist/templates/nestjs-app/.eslintrc.js +25 -25
  4. package/dist/templates/nestjs-app/package.json +40 -30
  5. package/dist/templates/nestjs-app/prisma/schema.prisma +79 -79
  6. package/dist/templates/nestjs-app/prisma/seed.ts +153 -154
  7. package/dist/templates/nestjs-app/src/app.module.ts +68 -68
  8. package/dist/templates/nestjs-app/src/common/constants/permissions.constant.ts +27 -27
  9. package/dist/templates/nestjs-app/src/common/decorators/api-response.decorator.ts +44 -44
  10. package/dist/templates/nestjs-app/src/common/decorators/get-user.decorator.ts +11 -11
  11. package/dist/templates/nestjs-app/src/common/decorators/permissions.decorator.ts +5 -5
  12. package/dist/templates/nestjs-app/src/common/decorators/public.decorator.ts +4 -4
  13. package/dist/templates/nestjs-app/src/common/dto/api-response.dto.ts +21 -21
  14. package/dist/templates/nestjs-app/src/common/dto/pagination.dto.ts +33 -33
  15. package/dist/templates/nestjs-app/src/common/filters/http-exception.filter.ts +56 -56
  16. package/dist/templates/nestjs-app/src/common/guards/jwt-auth.guard.ts +32 -32
  17. package/dist/templates/nestjs-app/src/common/guards/permissions.guard.ts +53 -53
  18. package/dist/templates/nestjs-app/src/common/interceptors/logging.interceptor.ts +37 -37
  19. package/dist/templates/nestjs-app/src/common/interceptors/transform.interceptor.ts +55 -55
  20. package/dist/templates/nestjs-app/src/common/prisma/prisma.module.ts +9 -9
  21. package/dist/templates/nestjs-app/src/common/prisma/prisma.service.ts +46 -46
  22. package/dist/templates/nestjs-app/src/common/utils/password.util.ts +13 -13
  23. package/dist/templates/nestjs-app/src/config/app.config.ts +10 -10
  24. package/dist/templates/nestjs-app/src/config/database.config.ts +5 -5
  25. package/dist/templates/nestjs-app/src/config/env.validation.ts +30 -30
  26. package/dist/templates/nestjs-app/src/config/jwt.config.ts +8 -8
  27. package/dist/templates/nestjs-app/src/config/swagger.config.ts +6 -6
  28. package/dist/templates/nestjs-app/src/main.ts +94 -91
  29. package/dist/templates/nestjs-app/src/modules/auth/auth.module.ts +28 -27
  30. package/dist/templates/nestjs-app/src/modules/auth/auth.service.ts +180 -54
  31. package/dist/templates/nestjs-app/src/modules/auth/controllers/v1/auth.controller.ts +33 -33
  32. package/dist/templates/nestjs-app/src/modules/auth/{dto → core/dto}/auth-response.dto.ts +19 -19
  33. package/dist/templates/nestjs-app/src/modules/auth/core/dto/login-response.dto.ts +10 -0
  34. package/dist/templates/nestjs-app/src/modules/auth/{dto → core/dto}/login.dto.ts +15 -15
  35. package/dist/templates/nestjs-app/src/modules/auth/{dto → core/dto}/register.dto.ts +30 -30
  36. package/dist/templates/nestjs-app/src/modules/auth/{interfaces → core/interfaces}/jwt-payload.interface.ts +7 -7
  37. package/dist/templates/nestjs-app/src/modules/auth/{strategies → core/strategies}/jwt.strategy.ts +54 -54
  38. package/dist/templates/nestjs-app/src/modules/health/health.controller.ts +29 -29
  39. package/dist/templates/nestjs-app/src/modules/health/health.module.ts +7 -7
  40. package/dist/templates/nestjs-app/src/modules/users/controllers/v1/users.controller.ts +118 -114
  41. package/dist/templates/nestjs-app/src/modules/users/core/dto/change-position.dto.ts +9 -9
  42. package/dist/templates/nestjs-app/src/modules/users/core/dto/create-user.dto.ts +35 -35
  43. package/dist/templates/nestjs-app/src/modules/users/core/dto/manage-permissions.dto.ts +13 -13
  44. package/dist/templates/nestjs-app/src/modules/users/core/dto/update-user.dto.ts +30 -30
  45. package/dist/templates/nestjs-app/src/modules/users/core/dto/user-query.dto.ts +22 -22
  46. package/dist/templates/nestjs-app/src/modules/users/core/dto/user-response.dto.ts +32 -0
  47. package/dist/templates/nestjs-app/src/modules/users/core/entities/user.entity.ts +45 -45
  48. package/dist/templates/nestjs-app/src/modules/users/core/helpers/user-transform.helper.ts +31 -31
  49. package/dist/templates/nestjs-app/src/modules/users/users.module.ts +10 -10
  50. package/dist/templates/nestjs-app/src/modules/users/users.service.ts +340 -340
  51. package/package.json +2 -2
@@ -1,46 +1,46 @@
1
- import { Injectable, OnModuleInit, OnModuleDestroy, Logger } from '@nestjs/common';
2
- import { PrismaClient } from '@prisma/client';
3
-
4
- @Injectable()
5
- export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
6
- private readonly logger = new Logger(PrismaService.name);
7
-
8
- constructor() {
9
- super({
10
- log: [
11
- { level: 'query', emit: 'event' },
12
- { level: 'error', emit: 'stdout' },
13
- { level: 'warn', emit: 'stdout' },
14
- ],
15
- });
16
- }
17
-
18
- async onModuleInit() {
19
- await this.$connect();
20
- this.logger.log('✅ Database connected successfully');
21
-
22
- // Log queries in development
23
- if (process.env.NODE_ENV === 'development') {
24
- // @ts-ignore
25
- this.$on('query', (e) => {
26
- this.logger.debug(`Query: ${e.query}`);
27
- this.logger.debug(`Duration: ${e.duration}ms`);
28
- });
29
- }
30
- }
31
-
32
- async onModuleDestroy() {
33
- await this.$disconnect();
34
- this.logger.log('Database disconnected');
35
- }
36
-
37
- async cleanDatabase() {
38
- if (process.env.NODE_ENV === 'production') {
39
- throw new Error('Cannot clean database in production');
40
- }
41
-
42
- const models = Reflect.ownKeys(this).filter((key) => key[0] !== '_');
43
-
44
- return Promise.all(models.map((modelKey) => (this as any)[modelKey].deleteMany()));
45
- }
46
- }
1
+ import { Injectable, OnModuleInit, OnModuleDestroy, Logger } from '@nestjs/common';
2
+ import { PrismaClient } from '@prisma/client';
3
+
4
+ @Injectable()
5
+ export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
6
+ private readonly logger = new Logger(PrismaService.name);
7
+
8
+ constructor() {
9
+ super({
10
+ log: [
11
+ { level: 'query', emit: 'event' },
12
+ { level: 'error', emit: 'stdout' },
13
+ { level: 'warn', emit: 'stdout' },
14
+ ],
15
+ });
16
+ }
17
+
18
+ async onModuleInit() {
19
+ await this.$connect();
20
+ this.logger.log('✅ Database connected successfully');
21
+
22
+ // Log queries in development
23
+ if (process.env.NODE_ENV === 'development') {
24
+ // @ts-ignore
25
+ this.$on('query', (e) => {
26
+ this.logger.debug(`Query: ${e.query}`);
27
+ this.logger.debug(`Duration: ${e.duration}ms`);
28
+ });
29
+ }
30
+ }
31
+
32
+ async onModuleDestroy() {
33
+ await this.$disconnect();
34
+ this.logger.log('Database disconnected');
35
+ }
36
+
37
+ async cleanDatabase() {
38
+ if (process.env.NODE_ENV === 'production') {
39
+ throw new Error('Cannot clean database in production');
40
+ }
41
+
42
+ const models = Reflect.ownKeys(this).filter((key) => key[0] !== '_');
43
+
44
+ return Promise.all(models.map((modelKey) => (this as any)[modelKey].deleteMany()));
45
+ }
46
+ }
@@ -1,13 +1,13 @@
1
- import * as bcrypt from 'bcrypt';
2
-
3
- export class PasswordUtil {
4
- private static readonly SALT_ROUNDS = 10;
5
-
6
- static async hash(password: string): Promise<string> {
7
- return bcrypt.hash(password, this.SALT_ROUNDS);
8
- }
9
-
10
- static async compare(password: string, hashedPassword: string): Promise<boolean> {
11
- return bcrypt.compare(password, hashedPassword);
12
- }
13
- }
1
+ import * as bcrypt from 'bcryptjs';
2
+
3
+ export class PasswordUtil {
4
+ private static readonly SALT_ROUNDS = 10;
5
+
6
+ static async hash(password: string): Promise<string> {
7
+ return bcrypt.hash(password, this.SALT_ROUNDS);
8
+ }
9
+
10
+ static async compare(password: string, hashedPassword: string): Promise<boolean> {
11
+ return bcrypt.compare(password, hashedPassword);
12
+ }
13
+ }
@@ -1,10 +1,10 @@
1
- import { registerAs } from '@nestjs/config';
2
-
3
- export default registerAs('app', () => ({
4
- env: process.env.NODE_ENV || 'development',
5
- port: parseInt(process.env.PORT, 10) || 3000,
6
- name: process.env.APP_NAME,
7
- apiPrefix: process.env.API_PREFIX || 'api',
8
- apiVersion: process.env.API_VERSION || 'v1',
9
- corsOrigin: process.env.CORS_ORIGIN || '*',
10
- }));
1
+ import { registerAs } from '@nestjs/config';
2
+
3
+ export default registerAs('app', () => ({
4
+ env: process.env.NODE_ENV || 'development',
5
+ port: parseInt(process.env.PORT, 10) || 3000,
6
+ name: process.env.APP_NAME,
7
+ apiPrefix: process.env.API_PREFIX || 'api',
8
+ apiVersion: process.env.API_VERSION || 'v1',
9
+ corsOrigin: process.env.CORS_ORIGIN || '*',
10
+ }));
@@ -1,5 +1,5 @@
1
- import { registerAs } from '@nestjs/config';
2
-
3
- export default registerAs('database', () => ({
4
- url: process.env.DATABASE_URL,
5
- }));
1
+ import { registerAs } from '@nestjs/config';
2
+
3
+ export default registerAs('database', () => ({
4
+ url: process.env.DATABASE_URL,
5
+ }));
@@ -1,30 +1,30 @@
1
- import * as Joi from 'joi';
2
-
3
- export const validationSchema = Joi.object({
4
- // Application
5
- NODE_ENV: Joi.string()
6
- .valid('development', 'production', 'test', 'staging')
7
- .default('development'),
8
- PORT: Joi.number().default(3000),
9
- APP_NAME: Joi.string().required(),
10
-
11
- // Database
12
- DATABASE_URL: Joi.string().required(),
13
-
14
- // JWT
15
- JWT_SECRET: Joi.string().required(),
16
- JWT_EXPIRATION: Joi.string().default('7d'),
17
- JWT_REFRESH_SECRET: Joi.string().required(),
18
- JWT_REFRESH_EXPIRATION: Joi.string().default('30d'),
19
-
20
- // CORS
21
- CORS_ORIGIN: Joi.string().default('*'),
22
-
23
- // API
24
- API_PREFIX: Joi.string().default('api'),
25
- API_VERSION: Joi.string().default('v1'),
26
-
27
- // Swagger
28
- SWAGGER_ENABLED: Joi.boolean().default(true),
29
- SWAGGER_PATH: Joi.string().default('api-docs'),
30
- });
1
+ import * as Joi from 'joi';
2
+
3
+ export const validationSchema = Joi.object({
4
+ // Application
5
+ NODE_ENV: Joi.string()
6
+ .valid('development', 'production', 'test', 'staging')
7
+ .default('development'),
8
+ PORT: Joi.number().default(3000),
9
+ APP_NAME: Joi.string().required(),
10
+
11
+ // Database
12
+ DATABASE_URL: Joi.string().required(),
13
+
14
+ // JWT
15
+ JWT_SECRET: Joi.string().required(),
16
+ JWT_EXPIRATION: Joi.string().default('7d'),
17
+ JWT_REFRESH_SECRET: Joi.string().required(),
18
+ JWT_REFRESH_EXPIRATION: Joi.string().default('30d'),
19
+
20
+ // CORS
21
+ CORS_ORIGIN: Joi.string().default('*'),
22
+
23
+ // API
24
+ API_PREFIX: Joi.string().default('api'),
25
+ API_VERSION: Joi.string().default('v1'),
26
+
27
+ // Swagger
28
+ SWAGGER_ENABLED: Joi.boolean().default(true),
29
+ SWAGGER_PATH: Joi.string().default('api-docs'),
30
+ });
@@ -1,8 +1,8 @@
1
- import { registerAs } from '@nestjs/config';
2
-
3
- export default registerAs('jwt', () => ({
4
- secret: process.env.JWT_SECRET,
5
- expiresIn: process.env.JWT_EXPIRATION || '7d',
6
- refreshSecret: process.env.JWT_REFRESH_SECRET,
7
- refreshExpiresIn: process.env.JWT_REFRESH_EXPIRATION || '30d',
8
- }));
1
+ import { registerAs } from '@nestjs/config';
2
+
3
+ export default registerAs('jwt', () => ({
4
+ secret: process.env.JWT_SECRET,
5
+ expiresIn: process.env.JWT_EXPIRATION || '7d',
6
+ refreshSecret: process.env.JWT_REFRESH_SECRET,
7
+ refreshExpiresIn: process.env.JWT_REFRESH_EXPIRATION || '30d',
8
+ }));
@@ -1,6 +1,6 @@
1
- import { registerAs } from '@nestjs/config';
2
-
3
- export default registerAs('swagger', () => ({
4
- enabled: process.env.SWAGGER_ENABLED === 'true',
5
- path: process.env.SWAGGER_PATH || 'api-docs',
6
- }));
1
+ import { registerAs } from '@nestjs/config';
2
+
3
+ export default registerAs('swagger', () => ({
4
+ enabled: process.env.SWAGGER_ENABLED === 'true',
5
+ path: process.env.SWAGGER_PATH || 'docs',
6
+ }));
@@ -1,91 +1,94 @@
1
- import { NestFactory, Reflector } from '@nestjs/core';
2
- import { ValidationPipe, VersioningType, ClassSerializerInterceptor } from '@nestjs/common';
3
- import { ConfigService } from '@nestjs/config';
4
- import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
5
- import { AppModule } from './app.module';
6
-
7
- async function bootstrap() {
8
- const app = await NestFactory.create(AppModule);
9
-
10
- const configService = app.get(ConfigService);
11
-
12
- // Get configurations
13
- const port = configService.get<number>('app.port');
14
- const apiPrefix = configService.get<string>('app.apiPrefix');
15
- const corsOrigin = configService.get<string>('app.corsOrigin');
16
- const swaggerEnabled = configService.get<boolean>('swagger.enabled');
17
- const swaggerPath = configService.get<string>('swagger.path');
18
-
19
- // Enable CORS
20
- app.enableCors({
21
- origin: corsOrigin,
22
- credentials: true,
23
- });
24
-
25
- // Global prefix
26
- app.setGlobalPrefix(apiPrefix);
27
-
28
- // API Versioning
29
- app.enableVersioning({
30
- type: VersioningType.URI,
31
- defaultVersion: '1',
32
- });
33
-
34
- // Global validation pipe
35
- app.useGlobalPipes(
36
- new ValidationPipe({
37
- whitelist: true,
38
- forbidNonWhitelisted: true,
39
- transform: true,
40
- transformOptions: {
41
- enableImplicitConversion: true,
42
- },
43
- }),
44
- );
45
-
46
- // Class serializer for excluding fields (like password)
47
- app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
48
-
49
- // Swagger Documentation
50
- if (swaggerEnabled) {
51
- const config = new DocumentBuilder()
52
- .setTitle('Urbansolv API')
53
- .setDescription('Urbansolv NestJS Backend API Documentation')
54
- .setVersion('1.0')
55
- .addBearerAuth(
56
- {
57
- type: 'http',
58
- scheme: 'bearer',
59
- bearerFormat: 'JWT',
60
- name: 'JWT',
61
- description: 'Enter JWT token',
62
- in: 'header',
63
- },
64
- 'JWT-auth',
65
- )
66
- .addTag('Authentication', 'Authentication endpoints')
67
- .addTag('Users', 'User management endpoints')
68
- .addTag('Health', 'Health check endpoints')
69
- .build();
70
-
71
- const document = SwaggerModule.createDocument(app, config);
72
- SwaggerModule.setup(swaggerPath, app, document, {
73
- swaggerOptions: {
74
- persistAuthorization: true,
75
- tagsSorter: 'alpha',
76
- operationsSorter: 'alpha',
77
- },
78
- });
79
-
80
- console.log(`\n📚 Swagger documentation available at: http://localhost:${port}/${swaggerPath}\n`);
81
- }
82
-
83
- await app.listen(port);
84
-
85
- console.log(`\n🚀 Application is running on: http://localhost:${port}/${apiPrefix}`);
86
- console.log(`🌍 Environment: ${configService.get<string>('app.env')}`);
87
- console.log(`📊 Health check: http://localhost:${port}/health`);
88
- console.log(`🏓 Ping endpoint: http://localhost:${port}/ping\n`);
89
- }
90
-
91
- bootstrap();
1
+ import { NestFactory, Reflector } from '@nestjs/core';
2
+ import { ValidationPipe, VersioningType, ClassSerializerInterceptor } from '@nestjs/common';
3
+ import { ConfigService } from '@nestjs/config';
4
+ import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
5
+ import { AppModule } from './app.module';
6
+
7
+ async function bootstrap() {
8
+ const app = await NestFactory.create(AppModule);
9
+
10
+ const configService = app.get(ConfigService);
11
+
12
+ // Get configurations
13
+ const port = configService.get<number>('app.port');
14
+ const apiPrefix = configService.get<string>('app.apiPrefix');
15
+ const corsOrigin = configService.get<string>('app.corsOrigin');
16
+ const swaggerEnabled = configService.get<boolean>('swagger.enabled');
17
+ const swaggerPath = configService.get<string>('swagger.path');
18
+
19
+ // Enable CORS
20
+ app.enableCors({
21
+ origin: corsOrigin,
22
+ credentials: true,
23
+ });
24
+
25
+ // Global prefix
26
+ app.setGlobalPrefix(apiPrefix);
27
+
28
+ // API Versioning
29
+ app.enableVersioning({
30
+ type: VersioningType.URI,
31
+ defaultVersion: '1',
32
+ });
33
+
34
+ // Global validation pipe
35
+ app.useGlobalPipes(
36
+ new ValidationPipe({
37
+ whitelist: true,
38
+ forbidNonWhitelisted: true,
39
+ transform: true,
40
+ transformOptions: {
41
+ enableImplicitConversion: true,
42
+ },
43
+ }),
44
+ );
45
+
46
+ // Class serializer for excluding fields (like password)
47
+ app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
48
+
49
+ // Swagger Documentation
50
+ if (swaggerEnabled) {
51
+ const config = new DocumentBuilder()
52
+ .setTitle('Urbansolv API')
53
+ .setDescription('Urbansolv NestJS Backend API Documentation')
54
+ .setVersion('1.0')
55
+ .addBearerAuth(
56
+ {
57
+ type: 'http',
58
+ scheme: 'bearer',
59
+ bearerFormat: 'JWT',
60
+ name: 'JWT',
61
+ description: 'Enter JWT token',
62
+ in: 'header',
63
+ },
64
+ 'JWT-auth',
65
+ )
66
+ .addTag('Authentication', 'Authentication endpoints')
67
+ .addTag('Users', 'User management endpoints')
68
+ .addTag('Health', 'Health check endpoints')
69
+ .build();
70
+
71
+ const document = SwaggerModule.createDocument(app, config);
72
+ SwaggerModule.setup(swaggerPath, app, document, {
73
+ useGlobalPrefix: true,
74
+ swaggerOptions: {
75
+ persistAuthorization: true,
76
+ tagsSorter: 'alpha',
77
+ operationsSorter: 'alpha',
78
+ },
79
+ });
80
+
81
+ console.log(`\n📚 Swagger documentation available at: http://localhost:${port}/${swaggerPath}\n`);
82
+ }
83
+
84
+ await app.listen(port);
85
+
86
+ console.log(`\n🚀 Application is running on: http://localhost:${port}/${apiPrefix}`);
87
+ console.log(`🌍 Environment: ${configService.get<string>('app.env')}`);
88
+ console.log(`📊 Health check: http://localhost:${port}/health`);
89
+ console.log(`🏓 Ping endpoint: http://localhost:${port}/ping\n`);
90
+ console.log(apiPrefix, swaggerPath, swaggerEnabled);
91
+
92
+ }
93
+
94
+ bootstrap();
@@ -1,27 +1,28 @@
1
- import { Module } from '@nestjs/common';
2
- import { JwtModule } from '@nestjs/jwt';
3
- import { PassportModule } from '@nestjs/passport';
4
- import { ConfigModule, ConfigService } from '@nestjs/config';
5
- import { AuthService } from './auth.service';
6
- import { AuthController } from './controllers/v1/auth.controller';
7
- import { JwtStrategy } from './strategies/jwt.strategy';
8
-
9
- @Module({
10
- imports: [
11
- PassportModule.register({ defaultStrategy: 'jwt' }),
12
- JwtModule.registerAsync({
13
- imports: [ConfigModule],
14
- useFactory: async (configService: ConfigService) => ({
15
- secret: configService.get<string>('jwt.secret'),
16
- signOptions: {
17
- expiresIn: configService.get<string>('jwt.expiresIn'),
18
- },
19
- }),
20
- inject: [ConfigService],
21
- }),
22
- ],
23
- controllers: [AuthController],
24
- providers: [AuthService, JwtStrategy],
25
- exports: [JwtStrategy, PassportModule, JwtModule],
26
- })
27
- export class AuthModule {}
1
+ import { Module } from '@nestjs/common';
2
+ import { JwtModule } from '@nestjs/jwt';
3
+ import { PassportModule } from '@nestjs/passport';
4
+ import { ConfigModule, ConfigService } from '@nestjs/config';
5
+ import { AuthService } from './auth.service';
6
+ import { AuthController } from './controllers/v1/auth.controller';
7
+ import { JwtStrategy } from './core/strategies/jwt.strategy';
8
+ import type { StringValue } from 'ms';
9
+
10
+ @Module({
11
+ imports: [
12
+ PassportModule.register({ defaultStrategy: 'jwt' }),
13
+ JwtModule.registerAsync({
14
+ imports: [ConfigModule],
15
+ inject: [ConfigService],
16
+ useFactory: (configService: ConfigService) => ({
17
+ secret: configService.get<string>('jwt.secret'),
18
+ signOptions: {
19
+ expiresIn: configService.get('jwt.expiresIn', '7d') as StringValue,
20
+ },
21
+ }),
22
+ }),
23
+ ],
24
+ controllers: [AuthController],
25
+ providers: [AuthService, JwtStrategy],
26
+ exports: [JwtStrategy, PassportModule, JwtModule],
27
+ })
28
+ export class AuthModule {}