diff --git a/serverless/functions/user.yml b/serverless/functions/user.yml index 93377eb..1a4cde9 100644 --- a/serverless/functions/user.yml +++ b/serverless/functions/user.yml @@ -301,4 +301,19 @@ viewMoreActivitiesByInterest: events: - httpApi: path: /user/activities/view-more-activities + method: get + +getRandomActiveActivity: + handler: src/modules/user/handlers/activities/getRandomActiveActivity.handler + memorySize: 384 + package: + patterns: + - 'src/modules/user/handlers/activities/**' + - ${file(./serverless/patterns/base.yml):pattern1} + - ${file(./serverless/patterns/base.yml):pattern2} + - ${file(./serverless/patterns/base.yml):pattern3} + - ${file(./serverless/patterns/base.yml):pattern4} + events: + - httpApi: + path: /user/activities/get-random-active-activity method: get \ No newline at end of file diff --git a/src/modules/user/handlers/activities/getRandomActiveActivity.ts b/src/modules/user/handlers/activities/getRandomActiveActivity.ts new file mode 100644 index 0000000..3778d8d --- /dev/null +++ b/src/modules/user/handlers/activities/getRandomActiveActivity.ts @@ -0,0 +1,59 @@ +import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda'; +import { safeHandler } from '../../../../common/utils/handlers/safeHandler'; +import { prismaClient } from '../../../../common/database/prisma.lambda.service'; +import ApiError from '../../../../common/utils/helper/ApiError'; +import { UserService } from '../../services/user.service'; +import { verifyUserToken } from '../../../../common/middlewares/jwt/authForUser'; + +const userService = new UserService(prismaClient); + +export const handler = safeHandler(async ( + event: APIGatewayProxyEvent, + context?: Context +): Promise => { + // Extract token from headers + const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token']; + if (!token) { + throw new ApiError(400, 'This is a protected route. Please provide a valid token.'); + } + + // Verify token and get user info + const userInfo = await verifyUserToken(token); + const userId = Number(userInfo.id); + + if (!userId || Number.isNaN(userId)) { + throw new ApiError(400, 'Invalid user ID'); + } + + // Fetch 50 random active activities + const result = await userService.getRandomActiveActivity(); + + if (!result || result.length === 0) { + return { + statusCode: 404, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, + body: JSON.stringify({ + success: false, + message: 'No active activities found', + data: [], + }), + }; + } + + return { + statusCode: 200, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, + body: JSON.stringify({ + success: true, + message: 'Random active activities retrieved successfully', + data: result, + count: result.length, + }), + }; +}); diff --git a/src/modules/user/services/user.service.ts b/src/modules/user/services/user.service.ts index 21b96c2..4cb7d67 100644 --- a/src/modules/user/services/user.service.ts +++ b/src/modules/user/services/user.service.ts @@ -2616,4 +2616,94 @@ export class UserService { return true; } + async getRandomActiveActivity() { + return await this.prisma.$transaction(async (tx) => { + // Get count of active activities + const count = await tx.activities.count({ + where: { + isActive: true, + activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED, + amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED, + deletedAt: null, + }, + }); + + if (count === 0) { + return []; + } + + // Determine how many activities to fetch (50 or less if count is smaller) + const takeCount = Math.min(50, count); + + // Fetch random activities - using ORDER BY RANDOM() equivalent approach + // Get all IDs first, shuffle, then take 50 + const allActivityIds = await tx.activities.findMany({ + where: { + isActive: true, + activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED, + amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED, + deletedAt: null, + }, + select: { id: true }, + }); + + // Shuffle array and take first 50 + const shuffled = allActivityIds.sort(() => Math.random() - 0.5); + const selectedIds = shuffled.slice(0, takeCount).map(a => a.id); + + // Fetch activities with only activityTitle and ActivitiesMedia + const activities = await tx.activities.findMany({ + where: { + id: { in: selectedIds }, + isActive: true, + activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED, + amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED, + deletedAt: null, + }, + select: { + id: true, + activityTitle: true, + ActivitiesMedia: { + where: { + isActive: true, + }, + select: { + id: true, + mediaFileName: true, + mediaType: true, + }, + orderBy: { + displayOrder: 'asc', // Get the first image by display order + }, + take: 1, // Get only the first image + }, + }, + }); + + // Process activities to attach presigned URLs and format response + const result = await Promise.all( + activities.map(async (activity) => { + let activityImage = null; + let activityImagePresignedUrl = null; + + // Get the first image and attach presigned URL + if (Array.isArray(activity.ActivitiesMedia) && activity.ActivitiesMedia.length > 0) { + const firstImage = activity.ActivitiesMedia[0]; + activityImage = firstImage.mediaFileName; + activityImagePresignedUrl = await attachPresignedUrl(firstImage.mediaFileName); + } + + return { + id: activity.id, + activityName: activity.activityTitle, + activityImage: activityImage, + activityImagePresignedUrl: activityImagePresignedUrl, + }; + }) + ); + + return result; + }); + } + } \ No newline at end of file