Skip to content

Commit 18d43ec

Browse files
committed
fix(login-count): fixes organization logins count
1 parent a871830 commit 18d43ec

File tree

2 files changed

+73
-59
lines changed

2 files changed

+73
-59
lines changed

src/resolvers/2fa.resolvers.ts

Lines changed: 48 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { sendEmail } from '../utils/sendEmail';
77
import { verifyOtpToken } from '../utils/2WayAuthentication';
88
import { GraphQLError } from 'graphql';
99
import { User } from '../models/user';
10+
import { logGeoActivity, loginsCount } from './userResolver';
1011

1112
interface Enable2FAInput {
1213
email: string
@@ -23,7 +24,7 @@ const resolvers = {
2324
try {
2425
const UserModel = mongoose.model('User')
2526
const user = await UserModel.findOne({ email })
26-
27+
2728
if (!user) {
2829
throw new Error('User not found')
2930
}
@@ -77,47 +78,52 @@ const resolvers = {
7778

7879

7980
loginWithTwoFactorAuthentication: async (
80-
_: any,
81-
{ id, email, otp, TwoWayVerificationToken }: { id?: string; email?: string; otp: string; TwoWayVerificationToken: string }
82-
) => {
83-
84-
// Verify OTP
85-
const isValidOtp = verifyOtpToken(TwoWayVerificationToken, otp);
86-
87-
if (!isValidOtp) {
88-
throw new GraphQLError('Invalid OTP. Please try again.');
89-
}
90-
91-
// Fetch user by either ID or email
92-
let user: any;
93-
if (id) {
94-
user = await User.findById(id);
95-
} else if (email) {
96-
user = await User.findOne({ email });
97-
}
98-
99-
// Check if user was found
100-
if (!user) {
101-
throw new GraphQLError('User not found.');
102-
}
103-
104-
// Generate JWT token
105-
const token = jwt.sign(
106-
{ userId: user._id, role: user._doc?.role || 'user' },
107-
SECRET,
108-
{ expiresIn: '2h' }
109-
);
110-
111-
112-
113-
return {
114-
token,
115-
user: user.toJSON(),
116-
117-
message: 'Logged in successfully',
118-
};
119-
},
120-
81+
_: any,
82+
{ id, email, otp, TwoWayVerificationToken }: { id?: string; email?: string; otp: string; TwoWayVerificationToken: string }, context: any
83+
) => {
84+
const { clientIpAdress } = context;
85+
// Verify OTP
86+
const isValidOtp = verifyOtpToken(TwoWayVerificationToken, otp);
87+
88+
if (!isValidOtp) {
89+
throw new GraphQLError('Invalid OTP. Please try again.');
90+
}
91+
92+
// Fetch user by either ID or email
93+
let user: any;
94+
if (id) {
95+
user = await User.findById(id);
96+
} else if (email) {
97+
user = await User.findOne({ email });
98+
}
99+
100+
// Check if user was found
101+
if (!user) {
102+
throw new GraphQLError('User not found.');
103+
}
104+
105+
// Generate JWT token
106+
const token = jwt.sign(
107+
{ userId: user._id, role: user._doc?.role || 'user' },
108+
SECRET,
109+
{ expiresIn: '2h' }
110+
);
111+
112+
const geoData = await logGeoActivity(user, clientIpAdress)
113+
const organizationName = user.organizations[0];
114+
if (organizationName) {
115+
const location = geoData.city && geoData.country_name ? `${geoData.city}-${geoData.country_name}` : null;
116+
await loginsCount(organizationName, location);
117+
}
118+
119+
return {
120+
token,
121+
user: user.toJSON(),
122+
123+
message: 'Logged in successfully',
124+
};
125+
},
126+
121127
},
122128
};
123129

src/resolvers/userResolver.ts

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import { EmailPattern } from '../utils/validation.utils'
3737
import { Context } from './../context'
3838
import { UserInputError } from 'apollo-server'
3939
import { encodeOtpToToken, generateOtp } from '../utils/2WayAuthentication'
40-
import jwt from 'jsonwebtoken'
40+
import jwt from 'jsonwebtoken'
4141
const octokit = new Octokit({ auth: `${process.env.Org_Repo_Access}` })
4242

4343
const SECRET = (process.env.SECRET as string) || 'mysq_unique_secret'
@@ -50,7 +50,7 @@ enum Status {
5050
rejected = 'rejected',
5151
}
5252

53-
export async function logGeoActivity(user: any, clientIpAdress: string) {
53+
export async function logGeoActivity(user: any, clientIpAdress: string) {
5454
const response = await fetch(`https://ipapi.co/${clientIpAdress}/json/`)
5555

5656
const geoData = await response.json()
@@ -86,7 +86,7 @@ enum Status {
8686
return geoData
8787
}
8888

89-
async function loginscount(organizationName: any, recentLocation: any) {
89+
export async function loginsCount(organizationName: any, recentLocation: any) {
9090
if (!organizationName) return;
9191

9292

@@ -103,7 +103,7 @@ async function loginscount(organizationName: any, recentLocation: any) {
103103
loginEntry.recentLocation = recentLocation;
104104
} else {
105105

106-
organization.logins.push({ date: new Date(), loginsCount: 1, recentLocation});
106+
organization.logins.push({ date: new Date(), loginsCount: 1, recentLocation });
107107
}
108108

109109

@@ -264,9 +264,9 @@ const resolvers: any = {
264264
},
265265
Login: {
266266
user: async (parent: any) => {
267-
267+
268268
const user = await User.findById(parent.user.id)
269-
269+
270270
return user
271271
},
272272
},
@@ -378,7 +378,7 @@ const resolvers: any = {
378378
if (!org) {
379379
throw new GraphQLError('Organization not found', { extensions: { code: 'InvalidOrganization' } });
380380
}
381-
381+
382382
// Find user with populated fields
383383
const user: any = await User.findOne({ email }).populate({
384384
path: 'cohort',
@@ -391,24 +391,24 @@ const resolvers: any = {
391391
populate: { path: 'organization', model: Organization, strictPopulate: false }
392392
}
393393
});
394-
394+
395395
// Check if user exists
396396
if (!user) {
397397
throw new GraphQLError('Invalid credentials', { extensions: { code: 'AccountNotFound' } });
398398
}
399-
399+
400400
// Check if account is active
401401
if (user.status?.status !== 'active') {
402402
throw new GraphQLError(`Account is ${user.status?.status}. Contact admin.`, {
403403
extensions: { code: 'AccountInactive' }
404404
});
405405
}
406-
406+
407407
// Check if two-factor authentication is enabled
408408
if (user.twoFactorAuth) {
409409
const otp = generateOtp(); // Generate OTP
410410
const TwoWayVerificationToken = encodeOtpToToken(otp, email); // Encode OTP
411-
411+
412412
// Send email with OTP
413413
await sendEmail(
414414
email,
@@ -418,7 +418,7 @@ const resolvers: any = {
418418
process.env.ADMIN_EMAIL,
419419
process.env.ADMIN_PASS
420420
);
421-
421+
422422
// Return response with encoded OTP token and message
423423
return {
424424
message: 'Check your email for the OTP code.',
@@ -432,19 +432,27 @@ const resolvers: any = {
432432
if (!passwordMatch) {
433433
throw new GraphQLError('Invalid credentials', { extensions: { code: 'InvalidCredential' } });
434434
}
435-
435+
436436
// Generate token for authenticated user
437437
const token = jwt.sign(
438438
{ userId: user._id, role: user._doc?.role || 'user' },
439439
SECRET,
440440
{ expiresIn: '2h' }
441441
);
442-
442+
443443
const geoData = await logGeoActivity(user, clientIpAdress) // Log activity
444-
444+
445+
const organizationName = user.organizations[0];
446+
if (organizationName) {
447+
const location = geoData.city && geoData.country_name ? `${geoData.city}-${geoData.country_name}` : null;
448+
await loginsCount(organizationName, location);
449+
}
450+
445451
// Return token and user data
446-
return { token, user: user.toJSON(),
447-
geoData, otpRequired: false, };
452+
return {
453+
token, user: user.toJSON(),
454+
geoData, otpRequired: false,
455+
};
448456
}
449457
}
450458
,

0 commit comments

Comments
 (0)