Add Google OAuth authentication system (#4)
Some checks failed
Deploy Apartment API / deploy (push) Failing after 1s
Some checks failed
Deploy Apartment API / deploy (push) Failing after 1s
Co-authored-by: Stephen Minakian <stephenminakian@gmail.com> Co-committed-by: Stephen Minakian <stephenminakian@gmail.com>
This commit is contained in:
128
models/user.js
Normal file
128
models/user.js
Normal file
@ -0,0 +1,128 @@
|
||||
const { ObjectId } = require('mongodb');
|
||||
|
||||
const USER_COLLECTION = 'users';
|
||||
|
||||
/**
|
||||
* Find or create a user based on Google OAuth profile
|
||||
* Uses upsert pattern to handle both new and returning users
|
||||
*
|
||||
* @param {Db} db - MongoDB database instance
|
||||
* @param {Object} profile - Google OAuth profile
|
||||
* @param {string} profile.id - Google's unique user ID
|
||||
* @param {string} profile.displayName - User's display name
|
||||
* @param {Array} profile.emails - Array of email objects
|
||||
* @param {Array} profile.photos - Array of photo objects
|
||||
* @returns {Promise<Object>} User document
|
||||
*/
|
||||
async function findOrCreateUser(db, profile) {
|
||||
const now = new Date();
|
||||
|
||||
const result = await db.collection(USER_COLLECTION).findOneAndUpdate(
|
||||
{ googleId: profile.googleId },
|
||||
{
|
||||
$set: {
|
||||
email: profile.email,
|
||||
name: profile.name,
|
||||
picture: profile.picture || null,
|
||||
lastLoginAt: now
|
||||
},
|
||||
$inc: { loginCount: 1 },
|
||||
$setOnInsert: {
|
||||
googleId: profile.googleId,
|
||||
isActive: true,
|
||||
role: 'user',
|
||||
createdAt: now
|
||||
}
|
||||
},
|
||||
{
|
||||
upsert: true,
|
||||
returnDocument: 'after'
|
||||
}
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a user by Google ID
|
||||
*
|
||||
* @param {Db} db - MongoDB database instance
|
||||
* @param {string} googleId - Google's unique user ID
|
||||
* @returns {Promise<Object|null>} User document or null
|
||||
*/
|
||||
async function findByGoogleId(db, googleId) {
|
||||
return await db.collection(USER_COLLECTION).findOne({ googleId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a user by MongoDB ObjectId
|
||||
*
|
||||
* @param {Db} db - MongoDB database instance
|
||||
* @param {string|ObjectId} id - User's MongoDB _id
|
||||
* @returns {Promise<Object|null>} User document or null
|
||||
*/
|
||||
async function findById(db, id) {
|
||||
// Convert string to ObjectId if needed
|
||||
const objectId = typeof id === 'string' ? new ObjectId(id) : id;
|
||||
return await db.collection(USER_COLLECTION).findOne({ _id: objectId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable a user account
|
||||
*
|
||||
* @param {Db} db - MongoDB database instance
|
||||
* @param {string|ObjectId} id - User's MongoDB _id
|
||||
* @param {boolean} isActive - New active status
|
||||
* @returns {Promise<Object>} Update result
|
||||
*/
|
||||
async function setUserActive(db, id, isActive) {
|
||||
// Convert string to ObjectId if needed
|
||||
const objectId = typeof id === 'string' ? new ObjectId(id) : id;
|
||||
|
||||
const result = await db.collection(USER_COLLECTION).findOneAndUpdate(
|
||||
{ _id: objectId },
|
||||
{ $set: { isActive } },
|
||||
{ returnDocument: 'after' }
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create required indexes for the users collection
|
||||
* Should be called once during application startup
|
||||
*
|
||||
* @param {Db} db - MongoDB database instance
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function createIndexes(db) {
|
||||
const collection = db.collection(USER_COLLECTION);
|
||||
|
||||
// Unique index on googleId for OAuth lookups
|
||||
await collection.createIndex(
|
||||
{ googleId: 1 },
|
||||
{ unique: true }
|
||||
);
|
||||
|
||||
// Unique index on email for user identification
|
||||
await collection.createIndex(
|
||||
{ email: 1 },
|
||||
{ unique: true }
|
||||
);
|
||||
|
||||
// Compound index for querying active users sorted by last login
|
||||
await collection.createIndex(
|
||||
{ isActive: 1, lastLoginAt: -1 }
|
||||
);
|
||||
|
||||
console.log('User collection indexes created successfully');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
USER_COLLECTION,
|
||||
findOrCreateUser,
|
||||
findByGoogleId,
|
||||
findById,
|
||||
setUserActive,
|
||||
createIndexes
|
||||
};
|
||||
Reference in New Issue
Block a user