feat: add new route to check user access to the service or not
This commit is contained in:
@@ -20,5 +20,13 @@ export function getSwaggerDocument(app: NestFastifyApplication) {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
const swaggerDocument = SwaggerModule.createDocument(app, swaggerConfig);
|
const swaggerDocument = SwaggerModule.createDocument(app, swaggerConfig);
|
||||||
SwaggerModule.setup("api-docs", app, swaggerDocument);
|
SwaggerModule.setup("api-docs", app, swaggerDocument, {
|
||||||
|
customSiteTitle: "Danak dsc API",
|
||||||
|
swaggerOptions: {
|
||||||
|
persistAuthorization: true,
|
||||||
|
displayRequestDuration: true,
|
||||||
|
filter: true,
|
||||||
|
showExtensions: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,18 +12,25 @@ export class ApiKeyGuard implements CanActivate {
|
|||||||
const request = context.switchToHttp().getRequest<FastifyRequest>();
|
const request = context.switchToHttp().getRequest<FastifyRequest>();
|
||||||
const apiKey = request.headers["x-api-key"] as string;
|
const apiKey = request.headers["x-api-key"] as string;
|
||||||
|
|
||||||
|
console.log("apiKey", apiKey);
|
||||||
if (!apiKey) throw new UnauthorizedException(AuthMessage.UNAUTHORIZED_ACCESS);
|
if (!apiKey) throw new UnauthorizedException(AuthMessage.UNAUTHORIZED_ACCESS);
|
||||||
|
|
||||||
// Get allowed API keys from environment (comma-separated)
|
// Get allowed API keys from environment (comma-separated)
|
||||||
const allowedApiKeys = this.configService.getOrThrow<string>("EXTERNAL_API_KEYS");
|
const allowedApiKeys = this.configService.getOrThrow<string>("EXTERNAL_API_KEYS");
|
||||||
const validApiKeys = allowedApiKeys.split(",").filter((key) => key.trim().length > 0);
|
const validApiKeys = allowedApiKeys.split(",").filter((key) => key.trim().length > 0);
|
||||||
|
|
||||||
|
console.log("validApiKeys", validApiKeys);
|
||||||
|
|
||||||
if (validApiKeys.length === 0) throw new UnauthorizedException(AuthMessage.UNAUTHORIZED_ACCESS);
|
if (validApiKeys.length === 0) throw new UnauthorizedException(AuthMessage.UNAUTHORIZED_ACCESS);
|
||||||
|
|
||||||
const isValidApiKey = validApiKeys.includes(apiKey);
|
const isValidApiKey = validApiKeys.includes(apiKey);
|
||||||
|
|
||||||
|
console.log("isValidApiKey", isValidApiKey);
|
||||||
|
|
||||||
if (!isValidApiKey) throw new UnauthorizedException(AuthMessage.UNAUTHORIZED_ACCESS);
|
if (!isValidApiKey) throw new UnauthorizedException(AuthMessage.UNAUTHORIZED_ACCESS);
|
||||||
|
|
||||||
|
console.log("true");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ export class CreateServiceFeedbackDto {
|
|||||||
|
|
||||||
@IsNotEmpty({ message: ServiceMessage.RATING_REQUIRED })
|
@IsNotEmpty({ message: ServiceMessage.RATING_REQUIRED })
|
||||||
@IsInt({ message: ServiceMessage.RATING_INT })
|
@IsInt({ message: ServiceMessage.RATING_INT })
|
||||||
@IsIn([1, 2, 3, 4, 5], { message: ServiceMessage.RATING_RANGE })
|
@IsIn([1, 2, 3, 4, 5, 6], { message: ServiceMessage.RATING_RANGE })
|
||||||
@ApiProperty({ description: "Rating (1-5, only for reviews)", example: 5, required: false })
|
@ApiProperty({ description: "Rating (1-6, only for reviews)", example: 5, required: false })
|
||||||
rating: number;
|
rating: number;
|
||||||
|
|
||||||
@IsUUID("4", { message: ServiceMessage.SERVICE_ID_SHOULD_BE_A_UUID })
|
@IsUUID("4", { message: ServiceMessage.SERVICE_ID_SHOULD_BE_A_UUID })
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsNotEmpty, IsUUID } from "class-validator";
|
||||||
|
|
||||||
|
import { CommonMessage } from "../../../common/enums/message.enum";
|
||||||
|
|
||||||
|
export class SubscriptionAccessDto {
|
||||||
|
@IsNotEmpty({ message: CommonMessage.ID_REQUIRED })
|
||||||
|
@IsUUID("4", { message: CommonMessage.ID_SHOULD_BE_UUID })
|
||||||
|
@ApiProperty({ description: "Id of user", example: "8b1e8b1e-8b1e-8b1e-8b1e-8b1e8b1e8b1e" })
|
||||||
|
userId: string;
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: CommonMessage.ID_REQUIRED })
|
||||||
|
@IsUUID("4", { message: CommonMessage.ID_SHOULD_BE_UUID })
|
||||||
|
@ApiProperty({ description: "Id of danak subscription", example: "8b1e8b1e-8b1e-8b1e-8b1e-8b1e8b1e8b1e" })
|
||||||
|
danakSubscriptionId: string;
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ import { AddSubscriptionsToServiceDto } from "../DTO/create-subscription.dto";
|
|||||||
import { SearchUserSubsQueryDto } from "../DTO/search-user-subs-query.dto";
|
import { SearchUserSubsQueryDto } from "../DTO/search-user-subs-query.dto";
|
||||||
import { ServiceSubsQueryDto } from "../DTO/service-subs-query.dto";
|
import { ServiceSubsQueryDto } from "../DTO/service-subs-query.dto";
|
||||||
import { SubscribeServiceDto } from "../DTO/subscribe-service.dto";
|
import { SubscribeServiceDto } from "../DTO/subscribe-service.dto";
|
||||||
|
import { SubscriptionAccessDto } from "../DTO/subscription-access-dto";
|
||||||
import { UpdateSubscriptionPlanDto } from "../DTO/update-subscription.dto";
|
import { UpdateSubscriptionPlanDto } from "../DTO/update-subscription.dto";
|
||||||
import { SubscriptionPlan } from "../entities/subscription.entity";
|
import { SubscriptionPlan } from "../entities/subscription.entity";
|
||||||
import { UserSubscription } from "../entities/user-subscription.entity";
|
import { UserSubscription } from "../entities/user-subscription.entity";
|
||||||
@@ -360,6 +361,19 @@ export class SubscriptionsService {
|
|||||||
plan: { service: true },
|
plan: { service: true },
|
||||||
staff: true,
|
staff: true,
|
||||||
},
|
},
|
||||||
|
order: { endDate: "DESC" },
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
businessName: true,
|
||||||
|
businessPhone: true,
|
||||||
|
description: true,
|
||||||
|
slug: true,
|
||||||
|
status: true,
|
||||||
|
endDate: true,
|
||||||
|
startDate: true,
|
||||||
|
plan: { id: true, name: true, duration: true, service: { id: true, name: true, slug: true, title: true } },
|
||||||
|
staff: { id: true, firstName: true, lastName: true, email: true, phone: true },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!userSubscriptions.length) throw new ForbiddenException(SubscriptionMessage.USER_DOES_NOT_HAVE_ACCESS);
|
if (!userSubscriptions.length) throw new ForbiddenException(SubscriptionMessage.USER_DOES_NOT_HAVE_ACCESS);
|
||||||
@@ -368,15 +382,28 @@ export class SubscriptionsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//************************************ */
|
//************************************ */
|
||||||
async checkUserAccessToService(userId: string, serviceId: string) {
|
async checkUserAccessToService(serviceId: string, accessDto: SubscriptionAccessDto) {
|
||||||
const userSubscriptions = await this.userSubscriptionsRepository.findOne({
|
const userSubscriptions = await this.userSubscriptionsRepository.findOne({
|
||||||
where: { user: { id: userId }, plan: { service: { id: serviceId } }, status: SubscriptionStatus.ACTIVE },
|
where: [
|
||||||
|
{
|
||||||
|
id: accessDto.danakSubscriptionId,
|
||||||
|
user: { id: accessDto.userId },
|
||||||
|
plan: { service: { id: serviceId } },
|
||||||
|
status: SubscriptionStatus.ACTIVE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: accessDto.danakSubscriptionId,
|
||||||
|
staff: { id: accessDto.userId },
|
||||||
|
plan: { service: { id: serviceId } },
|
||||||
|
status: SubscriptionStatus.ACTIVE,
|
||||||
|
},
|
||||||
|
],
|
||||||
relations: {
|
relations: {
|
||||||
plan: { service: true },
|
plan: { service: true },
|
||||||
staff: true,
|
staff: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return userSubscriptions ? true : false;
|
return { hasAccess: !!userSubscriptions && userSubscriptions.status === SubscriptionStatus.ACTIVE };
|
||||||
}
|
}
|
||||||
|
|
||||||
//************************************ */
|
//************************************ */
|
||||||
|
|||||||
@@ -6,11 +6,14 @@ import { SearchUserSubsQueryDto } from "./DTO/search-user-subs-query.dto";
|
|||||||
import { ServiceIdParamDto } from "./DTO/service-id.param.dto";
|
import { ServiceIdParamDto } from "./DTO/service-id.param.dto";
|
||||||
import { ServiceSubsQueryDto } from "./DTO/service-subs-query.dto";
|
import { ServiceSubsQueryDto } from "./DTO/service-subs-query.dto";
|
||||||
import { SubscribeServiceDto } from "./DTO/subscribe-service.dto";
|
import { SubscribeServiceDto } from "./DTO/subscribe-service.dto";
|
||||||
|
import { SubscriptionAccessDto } from "./DTO/subscription-access-dto";
|
||||||
import { UpdateSubscriptionPlanDto } from "./DTO/update-subscription.dto";
|
import { UpdateSubscriptionPlanDto } from "./DTO/update-subscription.dto";
|
||||||
import { SubscriptionsService } from "./providers/subscriptions.service";
|
import { SubscriptionsService } from "./providers/subscriptions.service";
|
||||||
import { UserQuickAccessService } from "./providers/users-quick-access.service";
|
import { UserQuickAccessService } from "./providers/users-quick-access.service";
|
||||||
|
import { ApiKeyGuards } from "../../common/decorators/api-key-guard.decorator";
|
||||||
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
||||||
import { PermissionsDec } from "../../common/decorators/permission.decorator";
|
import { PermissionsDec } from "../../common/decorators/permission.decorator";
|
||||||
|
import { SkipAuth } from "../../common/decorators/skip-auth.decorator";
|
||||||
import { UserDec } from "../../common/decorators/user.decorator";
|
import { UserDec } from "../../common/decorators/user.decorator";
|
||||||
import { ParamDto } from "../../common/DTO/param.dto";
|
import { ParamDto } from "../../common/DTO/param.dto";
|
||||||
import { PermissionEnum } from "../users/enums/permission.enum";
|
import { PermissionEnum } from "../users/enums/permission.enum";
|
||||||
@@ -95,9 +98,11 @@ export class SubscriptionsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "check user access to a service" })
|
@ApiOperation({ summary: "check user access to a service" })
|
||||||
|
@SkipAuth()
|
||||||
|
@ApiKeyGuards()
|
||||||
@Get("check-access/:serviceId")
|
@Get("check-access/:serviceId")
|
||||||
checkUserAccessToService(@Param() serviceIdParamDto: ServiceIdParamDto, @UserDec("id") userId: string) {
|
checkUserAccessToService(@Param() serviceIdParamDto: ServiceIdParamDto, @Query() queryDto: SubscriptionAccessDto) {
|
||||||
return this.subscriptionService.checkUserAccessToService(userId, serviceIdParamDto.serviceId);
|
return this.subscriptionService.checkUserAccessToService(serviceIdParamDto.serviceId, queryDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "Check if user has access to a service and return all their workspaces for that service (internal use)" })
|
@ApiOperation({ summary: "Check if user has access to a service and return all their workspaces for that service (internal use)" })
|
||||||
|
|||||||
Reference in New Issue
Block a user