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();
|
||||
|
||||
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 apiKey = request.headers["x-api-key"] as string;
|
||||
|
||||
console.log("apiKey", apiKey);
|
||||
if (!apiKey) throw new UnauthorizedException(AuthMessage.UNAUTHORIZED_ACCESS);
|
||||
|
||||
// Get allowed API keys from environment (comma-separated)
|
||||
const allowedApiKeys = this.configService.getOrThrow<string>("EXTERNAL_API_KEYS");
|
||||
const validApiKeys = allowedApiKeys.split(",").filter((key) => key.trim().length > 0);
|
||||
|
||||
console.log("validApiKeys", validApiKeys);
|
||||
|
||||
if (validApiKeys.length === 0) throw new UnauthorizedException(AuthMessage.UNAUTHORIZED_ACCESS);
|
||||
|
||||
const isValidApiKey = validApiKeys.includes(apiKey);
|
||||
|
||||
console.log("isValidApiKey", isValidApiKey);
|
||||
|
||||
if (!isValidApiKey) throw new UnauthorizedException(AuthMessage.UNAUTHORIZED_ACCESS);
|
||||
|
||||
console.log("true");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ export class CreateServiceFeedbackDto {
|
||||
|
||||
@IsNotEmpty({ message: ServiceMessage.RATING_REQUIRED })
|
||||
@IsInt({ message: ServiceMessage.RATING_INT })
|
||||
@IsIn([1, 2, 3, 4, 5], { message: ServiceMessage.RATING_RANGE })
|
||||
@ApiProperty({ description: "Rating (1-5, only for reviews)", example: 5, required: false })
|
||||
@IsIn([1, 2, 3, 4, 5, 6], { message: ServiceMessage.RATING_RANGE })
|
||||
@ApiProperty({ description: "Rating (1-6, only for reviews)", example: 5, required: false })
|
||||
rating: number;
|
||||
|
||||
@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 { ServiceSubsQueryDto } from "../DTO/service-subs-query.dto";
|
||||
import { SubscribeServiceDto } from "../DTO/subscribe-service.dto";
|
||||
import { SubscriptionAccessDto } from "../DTO/subscription-access-dto";
|
||||
import { UpdateSubscriptionPlanDto } from "../DTO/update-subscription.dto";
|
||||
import { SubscriptionPlan } from "../entities/subscription.entity";
|
||||
import { UserSubscription } from "../entities/user-subscription.entity";
|
||||
@@ -360,6 +361,19 @@ export class SubscriptionsService {
|
||||
plan: { service: 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);
|
||||
@@ -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({
|
||||
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: {
|
||||
plan: { service: 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 { ServiceSubsQueryDto } from "./DTO/service-subs-query.dto";
|
||||
import { SubscribeServiceDto } from "./DTO/subscribe-service.dto";
|
||||
import { SubscriptionAccessDto } from "./DTO/subscription-access-dto";
|
||||
import { UpdateSubscriptionPlanDto } from "./DTO/update-subscription.dto";
|
||||
import { SubscriptionsService } from "./providers/subscriptions.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 { PermissionsDec } from "../../common/decorators/permission.decorator";
|
||||
import { SkipAuth } from "../../common/decorators/skip-auth.decorator";
|
||||
import { UserDec } from "../../common/decorators/user.decorator";
|
||||
import { ParamDto } from "../../common/DTO/param.dto";
|
||||
import { PermissionEnum } from "../users/enums/permission.enum";
|
||||
@@ -95,9 +98,11 @@ export class SubscriptionsController {
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "check user access to a service" })
|
||||
@SkipAuth()
|
||||
@ApiKeyGuards()
|
||||
@Get("check-access/:serviceId")
|
||||
checkUserAccessToService(@Param() serviceIdParamDto: ServiceIdParamDto, @UserDec("id") userId: string) {
|
||||
return this.subscriptionService.checkUserAccessToService(userId, serviceIdParamDto.serviceId);
|
||||
checkUserAccessToService(@Param() serviceIdParamDto: ServiceIdParamDto, @Query() queryDto: SubscriptionAccessDto) {
|
||||
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)" })
|
||||
|
||||
Reference in New Issue
Block a user