Skip to content

Commit 80c7f47

Browse files
authored
Merge pull request #11 from muneebhashone/notifications-chauffeur
Notifications chauffeur
2 parents bb3102b + 810af8d commit 80c7f47

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1845
-129
lines changed

.env.sample

+9
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,12 @@ SESSION_EXPIRES_IN=""
3333
PASSWORD_RESET_TOKEN_EXPIRES_IN=""
3434

3535
SET_PASSWORD_TOKEN_EXPIRES_IN=""
36+
37+
# false in development
38+
SET_SESSION=
39+
40+
GOOGLE_CLIENT_ID=""
41+
42+
GOOGLE_CLIENT_SECRET=''
43+
44+
REDIRECT_URI = ''
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"type": "service_account",
3+
"project_id": "city-link-application",
4+
"private_key_id": "e379ddb0654b4fbe7e0315310e0ccc60aa1ce9a8",
5+
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDApQ7ViezYSedR\nFMeEHwS7SEA5stGaSI/xdw8IUcbZ47mB5LEEsNdUu95JaHSdSZnaGfm+ZFN/k8SM\n06G09EWLnbCAZvukGh1YnbrGMIE2IV5iRX54vPYNCrGziXkOhBjybw40ppFzBHBH\nlcLJKGUCDd0yk8IXnFEvjlmajygGHJj/xtFX0j9kHJn/UAly/fXgW1YF66DcNBiP\n8CXhFhKW1NXv2wISHZItY6PGgenNW/jJ4sJM/5/CvZ/GvI8FRF7KBa6Ol5lEdmrk\ntxH1N4nkIBu80CMaiObvUasA5fFxPVoRQKsouEUm4ZluI8MFSeCF4/qCh+AiBChx\n1Qt4g5cXAgMBAAECggEAGsAw3aUv7t7gUKmGhQm7ofgH9xsmMPrnhhvGyhx09PfC\n4E8t551Y4WrPFLl9DARW6WrW+8G5uIr/7aBLobGT4K3Ofx4Nzn1T2uTYNUIyBx6+\nGP5WcBSpRClagLhTx5i/UBbszZHdu69/mekpab6CvDTJpQbQmm+59FUbizNa1vIu\nmTvhFAtOZ0SPp9ULVKYDtBw8JEaVvrl0MXyaFoI254tbt527Qs5HlrbKJT5pMW5c\nez+V6Z+8hjXt26GilIBF7nk0W8+9Lz1Q2c9W2UCeeUU8ISjrMd5SltWFAvPfRtAw\n7CXvBPZFMB6jxJvUuM1Ap3w1zZrAfgLcGwZIxygReQKBgQD4l2kvJ3j4fI6m+mRz\n9lDTBx4kuMaOZpc5XsNOj9JTqU1MoyEa+nQhBPwPgFnrLDOrNKjcLk06oJgPk3Y3\nLwd0Rot3Z2ElcIUJF5r1pYInhnStvJR3m3bswaVU7+4pVK6woRWhB0IZgepKhTvV\nDF983tfHToJq+kahy9tAkBY9SQKBgQDGYs+GQfKPgbzfcYo0zDSA3uUcBTzB23ss\njVmUjAjQsu00vZ1W/mntPakVqJUn+LKNTx+e41ZyaZIokQTvJWMBpNJzb54OeZaa\nWosxWarmOto9ydsWqjGKaYgNa6Y2HCZ5fppf6sSiw+2mDPAplz9xSf8RsO62KDHQ\nPZK6iakRXwKBgQCqpWVzFSmZigdy4Fesst1wv2YV1zGV9x7AJyjy+Q9slr+VaDYF\niv/E2uMc0QLoyQL+97siyjYBlNRNfPCdmQ8I5pQ+dxzc0ozAXQ/mqKLb9xSJXvab\n1ICirZuM3tciUBtK7IkobQ8W63OljCgya7fwxBhjnKUyQnPSBVqQy1g44QKBgQCT\nVxvOGPivwKmoQkllQSzVcbYzg2hGil4HDswzWXXKmrk17IYI8Fd3zc3guh5dW4Yx\nEUzo0uuHegUFf/9sJhZMSD/NYF7BXWiPJyQt+OzGpXslXOANC0ZUCGD1prgBhBLH\nNVIuXszAHkhIhV9xZFXILC720rPYMi4c/T4WsNsSfQKBgQCdDjWcvf3yMGoEu/4+\ntrUrOiNVM/WIiYERDcxHzEdGlMxKvfwfABNK74w9WXBkUY9bfYxqX/C2iUdhvi3P\nL0F28DAn19oqv2oPSkvsYbFwIxWIzUYcBOqzMAKxn5L5pJaaH1pnwl6WRi3rEGQJ\nnP2DptDdPnDF7HWjqQSBxJ1hsA==\n-----END PRIVATE KEY-----\n",
6+
"client_email": "firebase-adminsdk-npczq@city-link-application.iam.gserviceaccount.com",
7+
"client_id": "112652304365380801917",
8+
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
9+
"token_uri": "https://oauth2.googleapis.com/token",
10+
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
11+
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-npczq%40city-link-application.iam.gserviceaccount.com",
12+
"universe_domain": "googleapis.com"
13+
}

docker-compose.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ services:
1010
MONGO_INITDB_ROOT_USERNAME: root
1111
MONGO_INITDB_ROOT_PASSWORD: example
1212
volumes:
13-
- mongodb_city_link:/data/db
13+
- ./.database/mongodb_city_link:/data/db
1414

1515
redis:
1616
image: redis:latest
1717
ports:
1818
- 6379:6379
1919
volumes:
20-
- redis:/data
20+
- ./.databse/redis:/data
2121

2222
volumes:
2323
mongodb_city_link:

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"@types/compression": "^1.7.2",
5757
"argon2": "^0.30.3",
5858
"axios": "^1.4.0",
59+
"batch": "^0.6.1",
5960
"bullmq": "^5.7.6",
6061
"compression": "^1.7.4",
6162
"connect-redis": "^7.1.1",
@@ -70,6 +71,7 @@
7071
"express": "^4.19.2",
7172
"express-async-handler": "^1.2.0",
7273
"express-session": "^1.18.0",
74+
"firebase-admin": "^12.3.0",
7375
"geolib": "^3.3.4",
7476
"helmet": "^6.0.1",
7577
"http-status-codes": "^2.3.0",

pnpm-lock.yaml

+838-42
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/apartment/apartment-booking/apartment-booking.service.ts

+16-4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ import {
1616
ConfirmApartmentBookingSchema,
1717
MyApartmentBookingsSchema,
1818
} from './apartment-booking.schema';
19+
import { addNotificationJob } from '../../queues/notification.queue';
20+
import {
21+
NOTIFICATION_MESSAGES,
22+
NOTIFICATION_TITLE,
23+
} from '../../notification/notification.constants';
1924

2025
export const getApartmentBooking = async (): Promise<
2126
ApartmentBookingsType[]
@@ -64,6 +69,15 @@ export const confirmApartmentBooking = async (
6469
if (!apartmentBookingConfirmed) {
6570
throw new Error('Apartment Booking Failed');
6671
}
72+
73+
await addNotificationJob({
74+
title: NOTIFICATION_TITLE.YOUR_PURCHASE_IS_DONE,
75+
message: NOTIFICATION_MESSAGES.YOUR_PURCHASE_IS_DONE,
76+
notificationType: 'BOOKING_NOTIFICATION',
77+
businessType: 'apartment',
78+
bookingId: apartmentBookingConfirmed.id,
79+
});
80+
6781
return {
6882
status: 200,
6983
message: 'Apartment Successfully Booked',
@@ -144,7 +158,7 @@ export const createApartmentBookingSummary = async (
144158
user: JwtPayload,
145159
): Promise<ApartmentBookingsType> => {
146160
const apartment = await Apartment.findById(payload.apartment).select(
147-
'name address propertyPrice coverPhotoUrl ratingCount totalRating userId',
161+
'name address propertyPrice coverPhotoUrl ratingCount totalRating owner',
148162
);
149163

150164
if (!apartment)
@@ -155,12 +169,10 @@ export const createApartmentBookingSummary = async (
155169
if (!discount)
156170
throw new Error('Discount selected for booking does not exist');
157171

158-
console.log({ apartment, discount });
159-
160172
const booking = await ApartmentBooking.create({
161173
...payload,
162174
confirmed: false,
163-
apartmentOwner: apartment.userId,
175+
apartmentOwner: apartment.owner,
164176
status: 'pending',
165177
paymentStatus: 'unpaid',
166178
user: user.sub,

src/apartment/apartment.controller.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
deleteApartments,
77
getApartment,
88
getApartments,
9+
getMyApartments,
910
updateApartment,
1011
} from './apartment.service';
1112
import {
@@ -22,6 +23,15 @@ export const handleGetApartments = async (req: Request, res: Response) => {
2223
return errorResponse(res, (err as Error).message);
2324
}
2425
};
26+
export const handleGetMyApartments = async (req: Request, res: Response) => {
27+
try {
28+
const result = await getMyApartments({ id: req.user.sub });
29+
30+
return successResponse(res, undefined, result);
31+
} catch (err) {
32+
return errorResponse(res, (err as Error).message);
33+
}
34+
};
2535

2636
export const handleCreateApartment = async (
2737
req: Request<never, never, ApartmentCreateOrUpdateSchemaType>,
@@ -75,7 +85,8 @@ export const handleDeleteApartment = async (
7585
res: Response,
7686
) => {
7787
try {
78-
await deleteApartment({ id: req.params.id });
88+
const userId = req.user?.sub;
89+
await deleteApartment({ id: req.params.id }, { id: userId });
7990

8091
return successResponse(res, 'Apartment deleted successfully');
8192
} catch (err) {

src/apartment/apartment.model.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export interface IApartment {
2626
propertySizeUnit: string;
2727
bookingType: mongoose.Types.ObjectId;
2828
businessId?: number;
29-
userId?: number;
29+
owner?: mongoose.Schema.Types.ObjectId;
3030
propertyType?: mongoose.Types.ObjectId;
3131
typeOfPlace?: mongoose.Types.ObjectId;
3232
cancellationPolicies?: mongoose.Types.ObjectId[];
@@ -174,7 +174,7 @@ const ApartmentSchema = new Schema<IApartment>(
174174
ref: 'Business',
175175
required: true,
176176
},
177-
userId: {
177+
owner: {
178178
type: mongoose.Schema.Types.ObjectId,
179179
ref: 'User',
180180
required: true,

src/apartment/apartment.router.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
handleDeleteApartments,
88
handleGetApartment,
99
handleGetApartments,
10+
handleGetMyApartments,
1011
handleUpdateApartment,
1112
} from './apartment.controller';
1213
import {
@@ -24,6 +25,11 @@ apartmentRouter.get(
2425
validateZodSchema({ query: apartmentListQueryParamsSchema }),
2526
handleGetApartments,
2627
);
28+
apartmentRouter.get(
29+
'/my',
30+
canAccess('roles', ['SUPER_ADMIN', 'VENDOR']),
31+
handleGetMyApartments,
32+
);
2733

2834
apartmentRouter.post(
2935
'/',
@@ -34,7 +40,7 @@ apartmentRouter.post(
3440

3541
apartmentRouter.delete(
3642
'/',
37-
canAccess('roles', ['VENDOR', 'SUPER_ADMIN']),
43+
canAccess('roles', ['SUPER_ADMIN']),
3844
handleDeleteApartments,
3945
);
4046

src/apartment/apartment.service.ts

+44-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
import { FilterQuery } from 'mongoose';
22
import { ApartmentType } from '../types';
3+
import { UserIdSchemaType } from '../user/user.schema';
34
import { JwtPayload } from '../utils/auth.utils';
45
import { checkRecordForEmptyArrays } from '../utils/common.utils';
56
import { getPaginator, GetPaginatorReturnType } from '../utils/getPaginator';
6-
import { Apartment } from './apartment.model';
7+
import { Apartment, IApartment } from './apartment.model';
78
import {
89
ApartmentCreateOrUpdateSchemaType,
910
ApartmentIdSchemaType,
1011
ApartmentListQueryParamsType,
1112
} from './apartment.schema';
13+
import { addNotificationJob } from '../queues/notification.queue';
14+
import {
15+
NOTIFICATION_MESSAGES,
16+
NOTIFICATION_TITLE,
17+
} from '../notification/notification.constants';
1218

1319
export interface IGetApartment {
1420
results: ApartmentType[];
@@ -108,14 +114,42 @@ export const getApartment = async (
108114

109115
return result;
110116
};
117+
export const getMyApartments = async (
118+
userId: UserIdSchemaType,
119+
): Promise<IApartment[]> => {
120+
const result = await Apartment.find({
121+
owner: userId.id,
122+
}).populate([
123+
'propertyType',
124+
'typeOfPlace',
125+
'cancellationPolicies',
126+
'facilities',
127+
'houseRules',
128+
'discounts',
129+
'bookingType',
130+
]);
131+
132+
if (!result) {
133+
throw new Error('Apartment not found');
134+
}
135+
136+
return result;
137+
};
111138

112139
export const createApartment = async (
113140
body: ApartmentCreateOrUpdateSchemaType,
114141
user: JwtPayload,
115142
): Promise<ApartmentType> => {
116143
const apartment = await Apartment.create({
117144
...body,
118-
userId: user.sub,
145+
owner: user.sub,
146+
});
147+
148+
await addNotificationJob({
149+
title: NOTIFICATION_TITLE.NEW_LISTING,
150+
message: NOTIFICATION_MESSAGES.NEW_LISTING,
151+
notificationType: 'SYSTEM_NOTIFICATION',
152+
businessType: 'apartment',
119153
});
120154

121155
return apartment;
@@ -147,14 +181,18 @@ export const updateApartment = async (
147181

148182
export const deleteApartment = async (
149183
apartmentId: ApartmentIdSchemaType,
184+
userId: UserIdSchemaType,
150185
): Promise<void | Error> => {
151186
const { id } = apartmentId;
152-
const deleted = await Apartment.deleteOne({
153-
_id: id,
154-
});
155187

156-
if (deleted.deletedCount < 1) {
188+
const apartment = await Apartment.findById(id);
189+
190+
if (!apartment) {
157191
throw new Error('Apartment does not Exist');
192+
} else if (apartment?.owner?.toString() !== userId.id.toString()) {
193+
throw new Error('You do not have permission to delete this apartment.');
194+
} else {
195+
apartment.deleteOne();
158196
}
159197
};
160198

src/auth/auth.controller.ts

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { Request, Response } from 'express';
22
import { StatusCodes } from 'http-status-codes';
3+
import config from '../config/config.service';
4+
import { RoleType } from '../enums';
35
import {
46
ConflictError,
57
InvalidCredentialseError,
68
NotFoundError,
79
} from '../errors/errors.service';
10+
import { GoogleCallbackQuery } from '../types';
811
import { errorResponse, successResponse } from '../utils/api.utils';
912
import { JwtPayload, signToken } from '../utils/auth.utils';
1013
import { AUTH_COOKIE_KEY, COOKIE_CONFIG } from './auth.constants';
@@ -35,10 +38,6 @@ import {
3538
validateLoginOtp,
3639
verifyOtp,
3740
} from './auth.service';
38-
import { RoleType } from '../enums';
39-
import { sign } from 'jsonwebtoken';
40-
import { GoogleCallbackQuery } from '../types';
41-
import { ISocialAccountInfo } from '../models/users';
4241

4342
export const handleSetPassword = async (
4443
req: Request<never, never, SetPasswordSchemaType>,
@@ -180,9 +179,10 @@ export const handleLoginByEmail = async (
180179
) => {
181180
try {
182181
const token = await loginUserByEmail(req.body);
183-
184-
res.cookie(AUTH_COOKIE_KEY, token, COOKIE_CONFIG);
185-
182+
console.log(config.SET_SESSION, typeof config.SET_SESSION);
183+
if (config.SET_SESSION) {
184+
res.cookie(AUTH_COOKIE_KEY, token, COOKIE_CONFIG);
185+
}
186186
return res.json({ token: token });
187187
} catch (err) {
188188
if (err instanceof InvalidCredentialseError) {
@@ -268,7 +268,7 @@ export const handleGetCurrentUser = async (req: Request, res: Response) => {
268268
);
269269
}
270270
};
271-
export const handleGoogleLogin = async (req: Request, res: Response) => {
271+
export const handleGoogleLogin = async (_: Request, res: Response) => {
272272
try {
273273
const googleAuthURL = `https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=${process.env.GOOGLE_CLIENT_ID}&redirect_uri=${process.env.REDIRECT_URI}&scope=email profile`;
274274
res.redirect(googleAuthURL);
@@ -285,7 +285,7 @@ export const handleGoogleLogin = async (req: Request, res: Response) => {
285285
}
286286
};
287287
export const handleGoogleCallback = async (
288-
req: Request<{}, {}, {}, GoogleCallbackQuery>,
288+
req: Request<never, never, never, GoogleCallbackQuery>,
289289
res: Response,
290290
) => {
291291
try {

src/auth/auth.service.ts

+3-6
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,17 @@ import {
33
InvalidCredentialseError,
44
NotFoundError,
55
} from '../errors/errors.service';
6-
import User, { IUser } from '../models/users';
6+
import User from '../models/users';
77
import { SendOtpEmailQueue } from '../queues/email.queue';
88
import { GoogleCallbackQuery, UserType } from '../types';
99
import { createUser } from '../user/user.services';
1010
import {
11-
GoogleUserInfo,
12-
JwtPayload,
1311
compareHash,
1412
fetchGoogleTokens,
1513
getUserInfo,
1614
hashPassword,
17-
signToken,
15+
JwtPayload,
16+
signToken
1817
} from '../utils/auth.utils';
1918
import { generateRandomNumbers } from '../utils/common.utils';
2019
import {
@@ -477,7 +476,6 @@ export const googleLogin = async (
477476
payload: GoogleCallbackQuery,
478477
): Promise<UserType> => {
479478
const { code, error } = payload;
480-
console.log({ code, error });
481479
if (error) {
482480
throw new Error(error);
483481
}
@@ -490,7 +488,6 @@ export const googleLogin = async (
490488
const { access_token, refresh_token, expires_in } = tokenResponse;
491489

492490
const userInfoResponse = await getUserInfo(access_token);
493-
console.log({ userInfoResponse });
494491

495492
// const userInfo = (await userInfoResponse.) as GoogleUserInfo;
496493
const { id, email, name, picture } = userInfoResponse;

src/car/car-booking/car-booking-types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ export interface ICarBooking {
2525
bookingType: CarBookingTypesUnion;
2626
carId: mongoose.Types.ObjectId;
2727
userId: mongoose.Types.ObjectId;
28+
chauffeurId: mongoose.Types.ObjectId;
29+
owner: mongoose.Types.ObjectId;
2830
bookingStatus: BookingStatusUnion;
2931
paymentStatus: BookingPaymentStatusUnion;
3032
totalMiles: number;

src/car/car-booking/car-booking.model.ts

+5
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ const CarBookingSchema = new mongoose.Schema<ICarBooking>(
7373
required: true,
7474
ref: 'User',
7575
},
76+
owner: {
77+
type: mongoose.Schema.Types.ObjectId,
78+
required: true,
79+
ref: 'User',
80+
},
7681
bookingStatus: {
7782
type: String,
7883
enum: Object.keys(BookingStatus) as BookingStatusUnion[],

0 commit comments

Comments
 (0)