From 0fb275002af9db6a63f2e6bd140c4a23c8bc9271 Mon Sep 17 00:00:00 2001 From: aelassas Date: Sat, 18 May 2024 18:23:59 +0100 Subject: [PATCH] Add MI_DB_SERVER_SIDE_JAVASCRIPT --- api/.env.example | 1 + api/src/config/env.config.ts | 261 +++++++++++----------- api/src/controllers/propertyController.ts | 73 +++--- 3 files changed, 175 insertions(+), 160 deletions(-) diff --git a/api/.env.example b/api/.env.example index e91de51c..dd82e056 100644 --- a/api/.env.example +++ b/api/.env.example @@ -8,6 +8,7 @@ MI_DB_SSL=false MI_DB_SSL_CERT=/etc/ssl/movinin.pem MI_DB_SSL_CA=/etc/ssl/movinin.pem MI_DB_DEBUG=true +MI_DB_SERVER_SIDE_JAVASCRIPT=false MI_COOKIE_SECRET=COOKIE_SECRET MI_AUTH_COOKIE_DOMAIN=localhost MI_JWT_SECRET=JWT_SECRET diff --git a/api/src/config/env.config.ts b/api/src/config/env.config.ts index 28dee482..98438438 100644 --- a/api/src/config/env.config.ts +++ b/api/src/config/env.config.ts @@ -13,14 +13,14 @@ import * as helper from '../common/helper' * @returns {string} */ export const __env__ = (name: string, required?: boolean, defaultValue?: string): string => { - const value = process.env[name] - if (required && !value) { - throw new Error(`'${name} not found`) - } - if (!value) { - return defaultValue || '' - } - return String(value) + const value = process.env[name] + if (required && !value) { + throw new Error(`'${name} not found`) + } + if (!value) { + return defaultValue || '' + } + return String(value) } /** @@ -86,6 +86,13 @@ export const DB_SSL_CA = __env__('MI_DB_SSL_CA', DB_SSL) */ export const DB_DEBUG = helper.StringToBoolean(__env__('MI_DB_DEBUG', false, 'false')) +/** + * Indicate whether server-side JavaScript, such as $where, $function, $accumulator and map-reduce are supported. + * + * @type {boolean} + */ +export const DB_SERVER_SIDE_JAVASCRIPT = helper.StringToBoolean(__env__('MI_DB_SERVER_SIDE_JAVASCRIPT', false, 'false')) + /** * Cookie secret. It should at least be 32 characters long, but the longer the better. * @@ -297,24 +304,24 @@ export const ADMIN_EMAIL = __env__('MI_ADMIN_EMAIL', false) * @extends {Document} */ export interface User extends Document { - agency?: Types.ObjectId - fullName: string - email: string - phone?: string - password?: string - birthDate?: Date - verified?: boolean - verifiedAt?: Date - active?: boolean - language: string - enableEmailNotifications?: boolean - avatar?: string - bio?: string - location?: string - type?: movininTypes.UserType - blacklisted?: boolean - payLater?: boolean - customerId?: string + agency?: Types.ObjectId + fullName: string + email: string + phone?: string + password?: string + birthDate?: Date + verified?: boolean + verifiedAt?: Date + active?: boolean + language: string + enableEmailNotifications?: boolean + avatar?: string + bio?: string + location?: string + type?: movininTypes.UserType + blacklisted?: boolean + payLater?: boolean + customerId?: string } /** @@ -325,24 +332,24 @@ export interface User extends Document { * @typedef {UserInfo} */ export interface UserInfo { - _id?: Types.ObjectId - agency?: Types.ObjectId - fullName: string - email?: string - phone?: string - password?: string - birthDate?: Date - verified?: boolean - verifiedAt?: Date - active?: boolean - language?: string - enableEmailNotifications?: boolean - avatar?: string - bio?: string - location?: string - type?: string - blacklisted?: boolean - payLater?: boolean + _id?: Types.ObjectId + agency?: Types.ObjectId + fullName: string + email?: string + phone?: string + password?: string + birthDate?: Date + verified?: boolean + verifiedAt?: Date + active?: boolean + language?: string + enableEmailNotifications?: boolean + avatar?: string + bio?: string + location?: string + type?: string + blacklisted?: boolean + payLater?: boolean } /** @@ -354,20 +361,20 @@ export interface UserInfo { * @extends {Document} */ export interface Booking extends Document { - agency: Types.ObjectId - location: Types.ObjectId - property: Types.ObjectId - renter: Types.ObjectId - from: Date - to: Date - status: movininTypes.BookingStatus - cancellation?: boolean - cancelRequest?: boolean - price: number - sessionId?: string - paymentIntentId?: string - customerId?: string - expireAt?: Date + agency: Types.ObjectId + location: Types.ObjectId + property: Types.ObjectId + renter: Types.ObjectId + from: Date + to: Date + status: movininTypes.BookingStatus + cancellation?: boolean + cancelRequest?: boolean + price: number + sessionId?: string + paymentIntentId?: string + customerId?: string + expireAt?: Date } /** @@ -378,16 +385,16 @@ export interface Booking extends Document { * @typedef {BookingInfo} */ export interface BookingInfo { - _id?: Types.ObjectId - agency: UserInfo - property: Types.ObjectId - renter: UserInfo - from: Date - to: Date - status: movininTypes.BookingStatus - cancellation?: boolean - cancelRequest?: boolean - price: number + _id?: Types.ObjectId + agency: UserInfo + property: Types.ObjectId + renter: UserInfo + from: Date + to: Date + status: movininTypes.BookingStatus + cancellation?: boolean + cancelRequest?: boolean + price: number } /** @@ -399,7 +406,7 @@ export interface BookingInfo { * @extends {Document} */ export interface Location extends Document { - values: Types.ObjectId[] + values: Types.ObjectId[] } /** @@ -411,8 +418,8 @@ export interface Location extends Document { * @extends {Document} */ export interface LocationValue extends Document { - language: string - value: string + language: string + value: string } /** @@ -424,9 +431,9 @@ export interface LocationValue extends Document { * @extends {Document} */ export interface LocationInfo extends Document { - _id?: Types.ObjectId - name?: string - values: LocationValue[] + _id?: Types.ObjectId + name?: string + values: LocationValue[] } /** @@ -438,10 +445,10 @@ export interface LocationInfo extends Document { * @extends {Document} */ export interface Notification extends Document { - user: Types.ObjectId - message: string - booking: Types.ObjectId - isRead?: boolean + user: Types.ObjectId + message: string + booking: Types.ObjectId + isRead?: boolean } /** @@ -453,8 +460,8 @@ export interface Notification extends Document { * @extends {Document} */ export interface NotificationCounter extends Document { - user: Types.ObjectId - count?: number + user: Types.ObjectId + count?: number } /** @@ -466,28 +473,28 @@ export interface NotificationCounter extends Document { * @extends {Document} */ export interface Property extends Document { - name: string - type: movininTypes.PropertyType - agency: Types.ObjectId - description: string - image: string - images?: string[] - bedrooms: number - bathrooms: number - kitchens?: number - parkingSpaces?: number, - size?: number - petsAllowed: boolean - furnished: boolean - minimumAge: number - location: Types.ObjectId - address?: string - price: number - hidden?: boolean - cancellation?: number - aircon?: boolean - available?: boolean - rentalTerm: movininTypes.RentalTerm + name: string + type: movininTypes.PropertyType + agency: Types.ObjectId + description: string + image: string + images?: string[] + bedrooms: number + bathrooms: number + kitchens?: number + parkingSpaces?: number, + size?: number + petsAllowed: boolean + furnished: boolean + minimumAge: number + location: Types.ObjectId + address?: string + price: number + hidden?: boolean + cancellation?: number + aircon?: boolean + available?: boolean + rentalTerm: movininTypes.RentalTerm } /** @@ -499,26 +506,26 @@ export interface Property extends Document { * @extends {Document} */ export interface PropertyInfo extends Document { - name: string - type: movininTypes.PropertyType - agency: UserInfo - description: string - image: string - images?: string[] - bedrooms: number - bathrooms: number - kitchens?: number - parkingSpaces?: number, - size: number - petsAllowed: boolean - furnished: boolean - minimumAge: number - location: Types.ObjectId - address?: string - price: number - hidden?: boolean - cancellation?: boolean - rentalTerm: movininTypes.RentalTerm + name: string + type: movininTypes.PropertyType + agency: UserInfo + description: string + image: string + images?: string[] + bedrooms: number + bathrooms: number + kitchens?: number + parkingSpaces?: number, + size: number + petsAllowed: boolean + furnished: boolean + minimumAge: number + location: Types.ObjectId + address?: string + price: number + hidden?: boolean + cancellation?: boolean + rentalTerm: movininTypes.RentalTerm } /** @@ -530,8 +537,8 @@ export interface PropertyInfo extends Document { * @extends {Document} */ export interface PushToken extends Document { - user: Types.ObjectId - token: string + user: Types.ObjectId + token: string } /** @@ -543,7 +550,7 @@ export interface PushToken extends Document { * @extends {Document} */ export interface Token extends Document { - user: Types.ObjectId - token: string - expireAt?: Date + user: Types.ObjectId + token: string + expireAt?: Date } diff --git a/api/src/controllers/propertyController.ts b/api/src/controllers/propertyController.ts index 7925be5a..a9f1d841 100644 --- a/api/src/controllers/propertyController.ts +++ b/api/src/controllers/propertyController.ts @@ -673,6 +673,44 @@ export const getFrontendProperties = async (req: Request, res: Response) => { ], } + let $addFields = {} + let $sort: Record = { name: 1 } + if (env.DB_SERVER_SIDE_JAVASCRIPT) { + $addFields = { + dailyPrice: + { + $function: + { + // eslint-disable-next-line func-names, object-shorthand + body: function (price, rentalTerm) { + let dailyPrice = 0 + const now = new Date() + if (rentalTerm === 'MONTHLY') { + dailyPrice = price / new Date(now.getFullYear(), now.getMonth(), 0).getDate() + } else if (rentalTerm === 'WEEKLY') { + dailyPrice = price / 7 + } else if (rentalTerm === 'DAILY') { + dailyPrice = price + } else if (rentalTerm === 'YEARLY') { + const year = now.getFullYear() + dailyPrice = price / (((year % 4 === 0 && year % 100 > 0) || year % 400 === 0) ? 366 : 365) + } + + dailyPrice = Number((Math.trunc(dailyPrice * 100) / 100).toFixed(2)) + if (dailyPrice % 1 === 0) { + return Math.round(dailyPrice) + } + return dailyPrice + }, + args: ['$price', '$rentalTerm'], + lang: 'js', + }, + }, + } + + $sort = { dailyPrice: 1 } + } + const data = await Property.aggregate( [ { $match }, @@ -706,42 +744,11 @@ export const getFrontendProperties = async (req: Request, res: Response) => { }, }, { - $addFields: - { - dailyPrice: - { - $function: - { - // eslint-disable-next-line func-names, object-shorthand - body: function (price, rentalTerm) { - let dailyPrice = 0 - const now = new Date() - if (rentalTerm === 'MONTHLY') { - dailyPrice = price / new Date(now.getFullYear(), now.getMonth(), 0).getDate() - } else if (rentalTerm === 'WEEKLY') { - dailyPrice = price / 7 - } else if (rentalTerm === 'DAILY') { - dailyPrice = price - } else if (rentalTerm === 'YEARLY') { - const year = now.getFullYear() - dailyPrice = price / (((year % 4 === 0 && year % 100 > 0) || year % 400 === 0) ? 366 : 365) - } - - dailyPrice = Number((Math.trunc(dailyPrice * 100) / 100).toFixed(2)) - if (dailyPrice % 1 === 0) { - return Math.round(dailyPrice) - } - return dailyPrice - }, - args: ['$price', '$rentalTerm'], - lang: 'js', - }, - }, - }, + $addFields, }, { $facet: { - resultData: [{ $sort: { dailyPrice: 1 } }, { $skip: (page - 1) * size }, { $limit: size }], + resultData: [{ $sort }, { $skip: (page - 1) * size }, { $limit: size }], pageInfo: [ { $count: 'totalRecords',