@lenne.tech/nest-server 9.2.7 → 9.3.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.
- package/dist/core/common/decorators/current-user.decorator.d.ts +1 -0
- package/dist/core/common/decorators/current-user.decorator.js +7 -0
- package/dist/core/common/decorators/current-user.decorator.js.map +1 -0
- package/dist/core/common/decorators/graphql-populate.decorator.d.ts +4 -0
- package/dist/core/common/decorators/graphql-populate.decorator.js +7 -0
- package/dist/core/common/decorators/graphql-populate.decorator.js.map +1 -0
- package/dist/core/common/decorators/graphql-service-options.decorator.d.ts +4 -0
- package/dist/core/common/decorators/graphql-service-options.decorator.js +12 -0
- package/dist/core/common/decorators/graphql-service-options.decorator.js.map +1 -0
- package/dist/core/common/filters/http-exception-log.filter.js.map +1 -1
- package/dist/core/common/helpers/decorator.helper.d.ts +7 -0
- package/dist/core/common/helpers/decorator.helper.js +32 -0
- package/dist/core/common/helpers/decorator.helper.js.map +1 -0
- package/dist/core/common/scalars/date.scalar.d.ts +2 -2
- package/dist/core/common/scalars/date.scalar.js +18 -4
- package/dist/core/common/scalars/date.scalar.js.map +1 -1
- package/dist/core/common/types/populate-config.type.d.ts +2 -0
- package/dist/core/common/types/populate-config.type.js +3 -0
- package/dist/core/common/types/populate-config.type.js.map +1 -0
- package/dist/core/modules/auth/core-auth.controller.d.ts +2 -3
- package/dist/core/modules/auth/core-auth.controller.js +11 -14
- package/dist/core/modules/auth/core-auth.controller.js.map +1 -1
- package/dist/core/modules/auth/core-auth.resolver.d.ts +3 -3
- package/dist/core/modules/auth/core-auth.resolver.js +10 -9
- package/dist/core/modules/auth/core-auth.resolver.js.map +1 -1
- package/dist/core/modules/file/core-file.controller.js +2 -2
- package/dist/core/modules/file/core-file.controller.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/server/modules/auth/auth.resolver.d.ts +3 -3
- package/dist/server/modules/auth/auth.resolver.js +7 -8
- package/dist/server/modules/auth/auth.resolver.js.map +1 -1
- package/dist/server/modules/user/avatar.controller.js +2 -2
- package/dist/server/modules/user/avatar.controller.js.map +1 -1
- package/dist/server/modules/user/user.resolver.d.ts +7 -7
- package/dist/server/modules/user/user.resolver.js +28 -37
- package/dist/server/modules/user/user.resolver.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/core/common/decorators/current-user.decorator.ts +7 -0
- package/src/core/common/decorators/graphql-populate.decorator.ts +15 -0
- package/src/core/common/decorators/graphql-service-options.decorator.ts +29 -0
- package/src/core/common/enums/role.enum.ts +2 -2
- package/src/core/common/filters/http-exception-log.filter.ts +3 -2
- package/src/core/common/helpers/decorator.helper.ts +54 -0
- package/src/core/common/scalars/date.scalar.ts +35 -6
- package/src/core/common/types/populate-config.type.ts +6 -0
- package/src/core/modules/auth/core-auth.controller.ts +6 -16
- package/src/core/modules/auth/core-auth.resolver.ts +10 -9
- package/src/core/modules/file/core-file.controller.ts +2 -2
- package/src/index.ts +5 -0
- package/src/server/modules/auth/auth.resolver.ts +7 -8
- package/src/server/modules/user/avatar.controller.ts +2 -2
- package/src/server/modules/user/user.resolver.ts +20 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.3.0",
|
|
4
4
|
"description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"node",
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createParamDecorator } from '@nestjs/common';
|
|
2
|
+
import { currentUserDec } from '../helpers/decorator.helper';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Decorator to get current user for Controller (http context) and Resolver (graphql context)
|
|
6
|
+
*/
|
|
7
|
+
export const CurrentUser = createParamDecorator(currentUserDec);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createParamDecorator } from '@nestjs/common';
|
|
2
|
+
import { graphqlPopulateDec } from '../helpers/decorator.helper';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get Mongoose populate configuration for result type of GraphQL request
|
|
6
|
+
*
|
|
7
|
+
* gqlPath (string, default: name of the resolver method):
|
|
8
|
+
* Dot separated path to select specific fields in GraphQL request
|
|
9
|
+
* (usually name of the query or mutation (resolver) method, e.g 'getUser')
|
|
10
|
+
*
|
|
11
|
+
* ignoreSelections (boolean, default: true):
|
|
12
|
+
* Whether to ignore selections in population options
|
|
13
|
+
* to avoid problems with missing properties (if not requested) in the checkSecurity method of models
|
|
14
|
+
*/
|
|
15
|
+
export const GraphQLPopulate = createParamDecorator(graphqlPopulateDec);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
|
2
|
+
import { currentUserDec, graphqlPopulateDec } from '../helpers/decorator.helper';
|
|
3
|
+
import { ServiceOptions } from '../interfaces/service-options.interface';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Get standard ServiceOptions for GraphQL-Requests
|
|
7
|
+
*
|
|
8
|
+
* Includes following properties of ServiceOptions:
|
|
9
|
+
* - currentUser
|
|
10
|
+
* - populate
|
|
11
|
+
*
|
|
12
|
+
* Configuration via Decorator data:
|
|
13
|
+
*
|
|
14
|
+
* gqlPath (string, default: name of the resolver method):
|
|
15
|
+
* Dot separated path to select specific fields in GraphQL request
|
|
16
|
+
* (usually name of the query or mutation (resolver) method, e.g 'getUser')
|
|
17
|
+
*
|
|
18
|
+
* ignoreSelections (boolean, default: true):
|
|
19
|
+
* Whether to ignore selections in population options
|
|
20
|
+
* to avoid problems with missing properties (if not requested) in the checkSecurity method of models
|
|
21
|
+
*/
|
|
22
|
+
export const GraphQLServiceOptions = createParamDecorator(
|
|
23
|
+
(data: { gqlPath?: string; ignoreSelections?: boolean }, ctx: ExecutionContext): ServiceOptions => {
|
|
24
|
+
return {
|
|
25
|
+
currentUser: currentUserDec(null, ctx),
|
|
26
|
+
populate: graphqlPopulateDec(data, ctx),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
);
|
|
@@ -36,13 +36,13 @@ export enum RoleEnum {
|
|
|
36
36
|
// into user.roles!
|
|
37
37
|
// ===================================================================================================================
|
|
38
38
|
|
|
39
|
-
// Everyone, including users who are not logged in, can access (see context user, e.g. @
|
|
39
|
+
// Everyone, including users who are not logged in, can access (see context user, e.g. @CurrentUser)
|
|
40
40
|
S_EVERYONE = 's_everyone',
|
|
41
41
|
|
|
42
42
|
// No one has access, not even administrators
|
|
43
43
|
S_NO_ONE = 's_no_one',
|
|
44
44
|
|
|
45
|
-
// User must be logged in (see context user, e.g. @
|
|
45
|
+
// User must be logged in (see context user, e.g. @CurrentUser)
|
|
46
46
|
S_USER = 's_user',
|
|
47
47
|
|
|
48
48
|
// ===================================================================================================================
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { ArgumentsHost, Catch,
|
|
1
|
+
import { ArgumentsHost, Catch, ExceptionFilter, HttpException } from '@nestjs/common';
|
|
2
|
+
import { GqlContextType } from '@nestjs/graphql';
|
|
2
3
|
import { Response } from 'express';
|
|
3
4
|
|
|
4
5
|
@Catch(HttpException)
|
|
@@ -11,7 +12,7 @@ export class HttpExceptionLogFilter implements ExceptionFilter {
|
|
|
11
12
|
console.debug(exception.stack);
|
|
12
13
|
|
|
13
14
|
// GraphQL
|
|
14
|
-
const type = host.getType()
|
|
15
|
+
const type = host.getType<GqlContextType>();
|
|
15
16
|
if (type === 'graphql') {
|
|
16
17
|
return exception;
|
|
17
18
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ExecutionContext } from '@nestjs/common';
|
|
2
|
+
import { GqlContextType, GqlExecutionContext } from '@nestjs/graphql';
|
|
3
|
+
import { PopulateOptions } from 'mongoose';
|
|
4
|
+
import { getPopulateOptions } from './db.helper';
|
|
5
|
+
import { removePropertiesDeep } from './input.helper';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Decorator function to get current user for Controller (http context) and Resolver (graphql context)
|
|
9
|
+
*/
|
|
10
|
+
export function currentUserDec(data, ctx: ExecutionContext) {
|
|
11
|
+
if (ctx.getType<GqlContextType>() === 'graphql') {
|
|
12
|
+
const gqlContext = GqlExecutionContext.create(ctx);
|
|
13
|
+
return gqlContext.getContext().req.user;
|
|
14
|
+
}
|
|
15
|
+
return ctx.switchToHttp().getRequest().user;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Decorator function to get Mongoose populate configuration for result type of GraphQL request
|
|
20
|
+
*
|
|
21
|
+
* gqlPath (string, default: name of the resolver method):
|
|
22
|
+
* Dot separated path to select specific fields in GraphQL request
|
|
23
|
+
* (usually name of the query or mutation (resolver) method, e.g 'getUser')
|
|
24
|
+
*
|
|
25
|
+
* ignoreSelections (boolean, default: true):
|
|
26
|
+
* Whether to ignore selections in population options
|
|
27
|
+
* to avoid problems with missing properties (if not requested) in the checkSecurity method of models
|
|
28
|
+
*/
|
|
29
|
+
export function graphqlPopulateDec(
|
|
30
|
+
data: { gqlPath?: string; ignoreSelections?: boolean },
|
|
31
|
+
ctx: ExecutionContext
|
|
32
|
+
): PopulateOptions[] {
|
|
33
|
+
// Check context type
|
|
34
|
+
if (ctx.getType<GqlContextType>() !== 'graphql') {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Init data
|
|
39
|
+
const gqlContext = GqlExecutionContext.create(ctx);
|
|
40
|
+
const { gqlPath, ignoreSelections } = {
|
|
41
|
+
gqlPath: gqlContext.getHandler().name,
|
|
42
|
+
ignoreSelections: true,
|
|
43
|
+
...data,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Get and prepare populate options
|
|
47
|
+
const populateOptions = getPopulateOptions(gqlContext.getInfo(), gqlPath);
|
|
48
|
+
if (ignoreSelections) {
|
|
49
|
+
removePropertiesDeep(populateOptions, ['select']);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Return prepared populate options
|
|
53
|
+
return populateOptions;
|
|
54
|
+
}
|
|
@@ -5,7 +5,7 @@ import { Kind } from 'graphql';
|
|
|
5
5
|
* Date scalar to convert string into date
|
|
6
6
|
*/
|
|
7
7
|
@Scalar('Date', (type) => Date)
|
|
8
|
-
export class DateScalar implements CustomScalar<
|
|
8
|
+
export class DateScalar implements CustomScalar<string, Date> {
|
|
9
9
|
description = 'Date custom scalar type';
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -18,17 +18,46 @@ export class DateScalar implements CustomScalar<number, Date> {
|
|
|
18
18
|
/**
|
|
19
19
|
* Serialize value to send to the client
|
|
20
20
|
*/
|
|
21
|
-
serialize(value: Date):
|
|
22
|
-
return value.
|
|
21
|
+
serialize(value: Date): string {
|
|
22
|
+
return value.toISOString(); // value sent to the client
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Parse value from the client query
|
|
27
27
|
*/
|
|
28
28
|
parseLiteral(ast: any): Date {
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
// Check value
|
|
30
|
+
if (ast.value === undefined || ast.value === null) {
|
|
31
|
+
return ast.value;
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
+
|
|
34
|
+
// Check nullable
|
|
35
|
+
if (!ast.value) {
|
|
36
|
+
throw new Error('Invalid value for date');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Check value type
|
|
40
|
+
if (ast.kind !== Kind.INT && ast.kind !== Kind.STRING) {
|
|
41
|
+
throw new Error('Invalid value type for date');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Check format if value is a string
|
|
45
|
+
if (ast.kind === Kind.STRING && isNaN(Date.parse(ast.value))) {
|
|
46
|
+
throw new Error('Invalid ISO 8601 format for date');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Create date from value
|
|
50
|
+
const date = new Date(ast.value);
|
|
51
|
+
|
|
52
|
+
// Check value
|
|
53
|
+
if (date.toString() === 'Invalid Date') {
|
|
54
|
+
throw new Error('Invalid value for date');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check if range is valid
|
|
58
|
+
date.toISOString();
|
|
59
|
+
|
|
60
|
+
// Return date if everything is fine
|
|
61
|
+
return date;
|
|
33
62
|
}
|
|
34
63
|
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { Body, Controller, Get, Param, ParseBoolPipe, Post, Res, UseGuards } from '@nestjs/common';
|
|
2
|
-
import { Args
|
|
2
|
+
import { Args } from '@nestjs/graphql';
|
|
3
3
|
import { Response as ResponseType } from 'express';
|
|
4
|
-
import {
|
|
5
|
-
import { GraphQLUser } from '../../common/decorators/graphql-user.decorator';
|
|
6
|
-
import { RESTUser } from '../../common/decorators/rest-user.decorator';
|
|
4
|
+
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
|
7
5
|
import { ConfigService } from '../../common/services/config.service';
|
|
8
6
|
import { AuthGuardStrategy } from './auth-guard-strategy.enum';
|
|
9
7
|
import { CoreAuthModel } from './core-auth.model';
|
|
@@ -27,7 +25,7 @@ export class CoreAuthController {
|
|
|
27
25
|
@UseGuards(AuthGuard(AuthGuardStrategy.JWT))
|
|
28
26
|
@Get()
|
|
29
27
|
async logout(
|
|
30
|
-
@
|
|
28
|
+
@CurrentUser() currentUser: ICoreAuthUser,
|
|
31
29
|
@Tokens('token') token: string,
|
|
32
30
|
@Res() res: ResponseType,
|
|
33
31
|
@Param('allDevices', ParseBoolPipe) allDevices?: boolean
|
|
@@ -42,7 +40,7 @@ export class CoreAuthController {
|
|
|
42
40
|
@UseGuards(AuthGuard(AuthGuardStrategy.JWT_REFRESH))
|
|
43
41
|
@Get()
|
|
44
42
|
async refreshToken(
|
|
45
|
-
@
|
|
43
|
+
@CurrentUser() user: ICoreAuthUser,
|
|
46
44
|
@Tokens('refreshToken') refreshToken: string,
|
|
47
45
|
@Res() res: ResponseType
|
|
48
46
|
): Promise<CoreAuthModel> {
|
|
@@ -54,11 +52,7 @@ export class CoreAuthController {
|
|
|
54
52
|
* Sign in user via email and password (on specific device)
|
|
55
53
|
*/
|
|
56
54
|
@Post()
|
|
57
|
-
async signIn(
|
|
58
|
-
@Info() info: GraphQLResolveInfo,
|
|
59
|
-
@Res() res: ResponseType,
|
|
60
|
-
@Body('input') input: CoreAuthSignInInput
|
|
61
|
-
): Promise<CoreAuthModel> {
|
|
55
|
+
async signIn(@Res() res: ResponseType, @Body('input') input: CoreAuthSignInInput): Promise<CoreAuthModel> {
|
|
62
56
|
const result = await this.authService.signIn(input);
|
|
63
57
|
return this.processCookies(res, result);
|
|
64
58
|
}
|
|
@@ -67,11 +61,7 @@ export class CoreAuthController {
|
|
|
67
61
|
* Register a new user account (on specific device)
|
|
68
62
|
*/
|
|
69
63
|
@Post()
|
|
70
|
-
async signUp(
|
|
71
|
-
@Info() info: GraphQLResolveInfo,
|
|
72
|
-
@Res() res: ResponseType,
|
|
73
|
-
@Args('input') input: CoreAuthSignUpInput
|
|
74
|
-
): Promise<CoreAuthModel> {
|
|
64
|
+
async signUp(@Res() res: ResponseType, @Args('input') input: CoreAuthSignUpInput): Promise<CoreAuthModel> {
|
|
75
65
|
const result = await this.authService.signUp(input);
|
|
76
66
|
return this.processCookies(res, result);
|
|
77
67
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { UseGuards } from '@nestjs/common';
|
|
2
|
-
import { Args, Context,
|
|
2
|
+
import { Args, Context, Mutation, Resolver } from '@nestjs/graphql';
|
|
3
3
|
import { Response as ResponseType } from 'express';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
|
5
|
+
import { GraphQLServiceOptions } from '../../common/decorators/graphql-service-options.decorator';
|
|
6
|
+
import { ServiceOptions } from '../../common/interfaces/service-options.interface';
|
|
6
7
|
import { ConfigService } from '../../common/services/config.service';
|
|
7
8
|
import { AuthGuardStrategy } from './auth-guard-strategy.enum';
|
|
8
9
|
import { CoreAuthModel } from './core-auth.model';
|
|
@@ -33,7 +34,7 @@ export class CoreAuthResolver {
|
|
|
33
34
|
@UseGuards(AuthGuard(AuthGuardStrategy.JWT))
|
|
34
35
|
@Mutation((returns) => Boolean, { description: 'Logout user (from specific device)' })
|
|
35
36
|
async logout(
|
|
36
|
-
@
|
|
37
|
+
@CurrentUser() currentUser: ICoreAuthUser,
|
|
37
38
|
@Context() ctx: { res: ResponseType },
|
|
38
39
|
@Tokens('token') token: string,
|
|
39
40
|
@Args('allDevices', { nullable: true }) allDevices?: boolean
|
|
@@ -48,7 +49,7 @@ export class CoreAuthResolver {
|
|
|
48
49
|
@UseGuards(AuthGuard(AuthGuardStrategy.JWT_REFRESH))
|
|
49
50
|
@Mutation((returns) => CoreAuthModel, { description: 'Refresh tokens (for specific device)' })
|
|
50
51
|
async refreshToken(
|
|
51
|
-
@
|
|
52
|
+
@CurrentUser() user: ICoreAuthUser,
|
|
52
53
|
@Tokens('refreshToken') refreshToken: string,
|
|
53
54
|
@Context() ctx: { res: ResponseType }
|
|
54
55
|
): Promise<CoreAuthModel> {
|
|
@@ -63,11 +64,11 @@ export class CoreAuthResolver {
|
|
|
63
64
|
description: 'Sign in user via email and password and get JWT tokens (for specific device)',
|
|
64
65
|
})
|
|
65
66
|
async signIn(
|
|
66
|
-
@
|
|
67
|
+
@GraphQLServiceOptions({ gqlPath: 'signIn.user' }) serviceOptions: ServiceOptions,
|
|
67
68
|
@Context() ctx: { res: ResponseType },
|
|
68
69
|
@Args('input') input: CoreAuthSignInInput
|
|
69
70
|
): Promise<CoreAuthModel> {
|
|
70
|
-
const result = await this.authService.signIn(input,
|
|
71
|
+
const result = await this.authService.signIn(input, serviceOptions);
|
|
71
72
|
return this.processCookies(ctx, result);
|
|
72
73
|
}
|
|
73
74
|
|
|
@@ -76,11 +77,11 @@ export class CoreAuthResolver {
|
|
|
76
77
|
*/
|
|
77
78
|
@Mutation((returns) => CoreAuthModel, { description: 'Register a new user account (on specific device)' })
|
|
78
79
|
async signUp(
|
|
79
|
-
@
|
|
80
|
+
@GraphQLServiceOptions({ gqlPath: 'signUp.user' }) serviceOptions: ServiceOptions,
|
|
80
81
|
@Context() ctx: { res: ResponseType },
|
|
81
82
|
@Args('input') input: CoreAuthSignUpInput
|
|
82
83
|
): Promise<CoreAuthModel> {
|
|
83
|
-
const result = await this.authService.signUp(input,
|
|
84
|
+
const result = await this.authService.signUp(input, serviceOptions);
|
|
84
85
|
return this.processCookies(ctx, result);
|
|
85
86
|
}
|
|
86
87
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BadRequestException, Controller, Get, NotFoundException, Param, Res } from '@nestjs/common';
|
|
2
|
-
import {
|
|
2
|
+
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
|
3
3
|
import { CoreUserModel } from '../user/core-user.model';
|
|
4
4
|
import { CoreFileService } from './core-file.service';
|
|
5
5
|
|
|
@@ -17,7 +17,7 @@ export abstract class CoreFileController {
|
|
|
17
17
|
* Download file
|
|
18
18
|
*/
|
|
19
19
|
@Get(':filename')
|
|
20
|
-
async getFile(@Param('filename') filename: string, @Res() res, @
|
|
20
|
+
async getFile(@Param('filename') filename: string, @Res() res, @CurrentUser() user: CoreUserModel) {
|
|
21
21
|
if (!filename) {
|
|
22
22
|
throw new BadRequestException('Missing filename for download');
|
|
23
23
|
}
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,9 @@ export * from './core.module';
|
|
|
9
9
|
// =====================================================================================================================
|
|
10
10
|
export * from './core/common/args/filter.args';
|
|
11
11
|
export * from './core/common/args/pagination.args';
|
|
12
|
+
export * from './core/common/decorators/current-user.decorator';
|
|
13
|
+
export * from './core/common/decorators/graphql-populate.decorator';
|
|
14
|
+
export * from './core/common/decorators/graphql-service-options.decorator';
|
|
12
15
|
export * from './core/common/decorators/graphql-user.decorator';
|
|
13
16
|
export * from './core/common/decorators/rest-user.decorator';
|
|
14
17
|
export * from './core/common/decorators/restricted.decorator';
|
|
@@ -23,6 +26,7 @@ export * from './core/common/helpers/common.helper';
|
|
|
23
26
|
export * from './core/common/helpers/config.helper';
|
|
24
27
|
export * from './core/common/helpers/context.helper';
|
|
25
28
|
export * from './core/common/helpers/db.helper';
|
|
29
|
+
export * from './core/common/helpers/decorator.helper';
|
|
26
30
|
export * from './core/common/helpers/file.helper';
|
|
27
31
|
export * from './core/common/helpers/filter.helper';
|
|
28
32
|
export * from './core/common/helpers/graphql.helper';
|
|
@@ -68,6 +72,7 @@ export * from './core/common/types/is-one-of.type';
|
|
|
68
72
|
export * from './core/common/types/maybe-promise.type';
|
|
69
73
|
export * from './core/common/types/plain-input.type';
|
|
70
74
|
export * from './core/common/types/plain-object.type';
|
|
75
|
+
export * from './core/common/types/populate-config.type';
|
|
71
76
|
export * from './core/common/types/remove-methods.type';
|
|
72
77
|
export * from './core/common/types/require-only-one.type';
|
|
73
78
|
export * from './core/common/types/required-at-least-one.type';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Args, Context,
|
|
1
|
+
import { Args, Context, Mutation, Resolver } from '@nestjs/graphql';
|
|
2
2
|
import { Response as ResponseType } from 'express';
|
|
3
|
-
import {
|
|
3
|
+
import { GraphQLServiceOptions } from '../../../core/common/decorators/graphql-service-options.decorator';
|
|
4
|
+
import { ServiceOptions } from '../../../core/common/interfaces/service-options.interface';
|
|
4
5
|
import { ConfigService } from '../../../core/common/services/config.service';
|
|
5
6
|
import { CoreAuthResolver } from '../../../core/modules/auth/core-auth.resolver';
|
|
6
7
|
import { Auth } from './auth.model';
|
|
@@ -28,12 +29,12 @@ export class AuthResolver extends CoreAuthResolver {
|
|
|
28
29
|
*/
|
|
29
30
|
@Mutation(() => Auth, { description: 'Sign in and get JWT token' })
|
|
30
31
|
override async signIn(
|
|
31
|
-
@
|
|
32
|
+
@GraphQLServiceOptions({ gqlPath: 'signIn.user' }) serviceOptions: ServiceOptions,
|
|
32
33
|
@Context() ctx: { res: ResponseType },
|
|
33
34
|
@Args('input') input: AuthSignInInput
|
|
34
35
|
): Promise<Auth> {
|
|
35
36
|
const result = await this.authService.signIn(input, {
|
|
36
|
-
|
|
37
|
+
...serviceOptions,
|
|
37
38
|
inputType: AuthSignInInput,
|
|
38
39
|
});
|
|
39
40
|
return this.processCookies(ctx, result);
|
|
@@ -46,13 +47,11 @@ export class AuthResolver extends CoreAuthResolver {
|
|
|
46
47
|
description: 'Sign up user and get JWT token',
|
|
47
48
|
})
|
|
48
49
|
override async signUp(
|
|
49
|
-
@
|
|
50
|
+
@GraphQLServiceOptions({ gqlPath: 'signUp.user' }) serviceOptions: ServiceOptions,
|
|
50
51
|
@Context() ctx: { res: ResponseType },
|
|
51
52
|
@Args('input') input: AuthSignUpInput
|
|
52
53
|
): Promise<Auth> {
|
|
53
|
-
const result = await this.authService.signUp(input,
|
|
54
|
-
fieldSelection: { info, select: 'signUp' },
|
|
55
|
-
});
|
|
54
|
+
const result = await this.authService.signUp(input, serviceOptions);
|
|
56
55
|
return this.processCookies(ctx, result);
|
|
57
56
|
}
|
|
58
57
|
}
|
|
@@ -2,7 +2,7 @@ import { Post, UploadedFile, UseInterceptors } from '@nestjs/common';
|
|
|
2
2
|
import { Controller } from '@nestjs/common/decorators/core/controller.decorator';
|
|
3
3
|
import { FileInterceptor } from '@nestjs/platform-express';
|
|
4
4
|
import envConfig from '../../../config.env';
|
|
5
|
-
import {
|
|
5
|
+
import { CurrentUser } from '../../../core/common/decorators/current-user.decorator';
|
|
6
6
|
import { Roles } from '../../../core/common/decorators/roles.decorator';
|
|
7
7
|
import { RoleEnum } from '../../../core/common/enums/role.enum';
|
|
8
8
|
import { multerOptionsForImageUpload } from '../../../core/common/helpers/file.helper';
|
|
@@ -32,7 +32,7 @@ export class AvatarController {
|
|
|
32
32
|
})
|
|
33
33
|
)
|
|
34
34
|
)
|
|
35
|
-
uploadFile(@UploadedFile() file: Express.Multer.File, @
|
|
35
|
+
uploadFile(@UploadedFile() file: Express.Multer.File, @CurrentUser() user: User): Promise<string> {
|
|
36
36
|
return this.usersService.setAvatar(file, user);
|
|
37
37
|
}
|
|
38
38
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Inject } from '@nestjs/common';
|
|
2
|
-
import { Args,
|
|
3
|
-
import { GraphQLResolveInfo } from 'graphql';
|
|
2
|
+
import { Args, Mutation, Query, Resolver, Subscription } from '@nestjs/graphql';
|
|
4
3
|
import { PubSub } from 'graphql-subscriptions';
|
|
5
4
|
import { FilterArgs } from '../../../core/common/args/filter.args';
|
|
6
|
-
import {
|
|
5
|
+
import { GraphQLServiceOptions } from '../../../core/common/decorators/graphql-service-options.decorator';
|
|
7
6
|
import { Roles } from '../../../core/common/decorators/roles.decorator';
|
|
8
7
|
import { RoleEnum } from '../../../core/common/enums/role.enum';
|
|
8
|
+
import { ServiceOptions } from '../../../core/common/interfaces/service-options.interface';
|
|
9
9
|
import { UserCreateInput } from './inputs/user-create.input';
|
|
10
10
|
import { UserInput } from './inputs/user.input';
|
|
11
11
|
import { FindAndCountUsersResult } from './outputs/find-and-count-users-result.output';
|
|
@@ -32,9 +32,9 @@ export class UserResolver {
|
|
|
32
32
|
*/
|
|
33
33
|
@Roles(RoleEnum.ADMIN)
|
|
34
34
|
@Query(() => [User], { description: 'Find users (via filter)' })
|
|
35
|
-
async findUsers(@
|
|
35
|
+
async findUsers(@GraphQLServiceOptions() serviceOptions: ServiceOptions, @Args() args?: FilterArgs) {
|
|
36
36
|
return await this.userService.find(args, {
|
|
37
|
-
|
|
37
|
+
...serviceOptions,
|
|
38
38
|
inputType: FilterArgs,
|
|
39
39
|
});
|
|
40
40
|
}
|
|
@@ -44,9 +44,12 @@ export class UserResolver {
|
|
|
44
44
|
*/
|
|
45
45
|
@Roles(RoleEnum.ADMIN)
|
|
46
46
|
@Query(() => FindAndCountUsersResult, { description: 'Find users (via filter)' })
|
|
47
|
-
async findAndCountUsers(
|
|
47
|
+
async findAndCountUsers(
|
|
48
|
+
@GraphQLServiceOptions({ gqlPath: 'findAndCountUsers.items' }) serviceOptions: ServiceOptions,
|
|
49
|
+
@Args() args?: FilterArgs
|
|
50
|
+
) {
|
|
48
51
|
return await this.userService.findAndCount(args, {
|
|
49
|
-
|
|
52
|
+
...serviceOptions,
|
|
50
53
|
inputType: FilterArgs,
|
|
51
54
|
});
|
|
52
55
|
}
|
|
@@ -56,10 +59,9 @@ export class UserResolver {
|
|
|
56
59
|
*/
|
|
57
60
|
@Roles(RoleEnum.S_USER)
|
|
58
61
|
@Query(() => User, { description: 'Get user with specified ID' })
|
|
59
|
-
async getUser(@
|
|
62
|
+
async getUser(@GraphQLServiceOptions() serviceOptions: ServiceOptions, @Args('id') id: string): Promise<User> {
|
|
60
63
|
return await this.userService.get(id, {
|
|
61
|
-
|
|
62
|
-
fieldSelection: { info, select: 'getUser' },
|
|
64
|
+
...serviceOptions,
|
|
63
65
|
roles: [RoleEnum.ADMIN, RoleEnum.S_CREATOR],
|
|
64
66
|
});
|
|
65
67
|
}
|
|
@@ -92,13 +94,11 @@ export class UserResolver {
|
|
|
92
94
|
@Roles(RoleEnum.ADMIN)
|
|
93
95
|
@Mutation(() => User, { description: 'Create a new user' })
|
|
94
96
|
async createUser(
|
|
95
|
-
@
|
|
96
|
-
@
|
|
97
|
-
@Info() info: GraphQLResolveInfo
|
|
97
|
+
@GraphQLServiceOptions() serviceOptions: ServiceOptions,
|
|
98
|
+
@Args('input') input: UserCreateInput
|
|
98
99
|
): Promise<User> {
|
|
99
100
|
return await this.userService.create(input, {
|
|
100
|
-
|
|
101
|
-
fieldSelection: { info, select: 'createUser' },
|
|
101
|
+
...serviceOptions,
|
|
102
102
|
inputType: UserCreateInput,
|
|
103
103
|
});
|
|
104
104
|
}
|
|
@@ -108,10 +108,9 @@ export class UserResolver {
|
|
|
108
108
|
*/
|
|
109
109
|
@Roles(RoleEnum.S_USER)
|
|
110
110
|
@Mutation(() => User, { description: 'Delete existing user' })
|
|
111
|
-
async deleteUser(@
|
|
111
|
+
async deleteUser(@GraphQLServiceOptions() serviceOptions: ServiceOptions, @Args('id') id: string): Promise<User> {
|
|
112
112
|
return await this.userService.delete(id, {
|
|
113
|
-
|
|
114
|
-
fieldSelection: { info, select: 'deleteUser' },
|
|
113
|
+
...serviceOptions,
|
|
115
114
|
roles: [RoleEnum.ADMIN, RoleEnum.S_CREATOR],
|
|
116
115
|
});
|
|
117
116
|
}
|
|
@@ -131,15 +130,13 @@ export class UserResolver {
|
|
|
131
130
|
@Roles(RoleEnum.S_USER)
|
|
132
131
|
@Mutation(() => User, { description: 'Update existing user' })
|
|
133
132
|
async updateUser(
|
|
133
|
+
@GraphQLServiceOptions() serviceOptions: ServiceOptions,
|
|
134
134
|
@Args('input') input: UserInput,
|
|
135
|
-
@Args('id') id: string
|
|
136
|
-
@GraphQLUser() user: User,
|
|
137
|
-
@Info() info: GraphQLResolveInfo
|
|
135
|
+
@Args('id') id: string
|
|
138
136
|
): Promise<User> {
|
|
139
137
|
// Update user
|
|
140
138
|
return await this.userService.update(id, input, {
|
|
141
|
-
|
|
142
|
-
fieldSelection: { info, select: 'updateUser' },
|
|
139
|
+
...serviceOptions,
|
|
143
140
|
inputType: UserInput,
|
|
144
141
|
roles: [RoleEnum.ADMIN, RoleEnum.S_CREATOR],
|
|
145
142
|
});
|