Skip to content

Commit

Permalink
Merge pull request #27 from lowdefy/fix-mdb-auth-adapter-timeout
Browse files Browse the repository at this point in the history
Fix mdb auth adapter timeout
  • Loading branch information
Gervwyk authored Nov 14, 2023
2 parents 121fcb1 + 97281cc commit 31ee8bf
Show file tree
Hide file tree
Showing 11 changed files with 120 additions and 243 deletions.
2 changes: 1 addition & 1 deletion .changeset/cyan-swans-serve.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
'@lowdefy/community-plugin-nodemailer': patch
---

Escape host on email template.
Fix default email template to display the app host name.
5 changes: 5 additions & 0 deletions .changeset/poor-ladybugs-drive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@lowdefy/community-plugin-mongodb': minor
---

Add option to configure MultiAppMongoDBAdapter collection names.
5 changes: 5 additions & 0 deletions .changeset/wise-rules-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@lowdefy/community-plugin-mongodb': patch
---

Fix connection timeouts in MultiAppMongoDBAdapter.
Original file line number Diff line number Diff line change
Expand Up @@ -13,59 +13,53 @@ function to({ id, ...data }) {
return { _id: id, ...data };
}

function MultipleAppMongoDBAdapter({ properties }) {
const { appName, databaseUri, mongoDBClientOptions } = properties;
const dbPromise = (async () => {
const _db = (await new MongoClient(databaseUri, mongoDBClientOptions).connect()).db();
return {
accounts: _db.collection('user_accounts'),
contacts: _db.collection('user_contacts'),
sessions: _db.collection('user_sessions'),
verificationTokens: _db.collection('user_verification_tokens'),
};
})();
function MultiAppMongoDBAdapter({ properties }) {
const { appName, collections, databaseUri, mongoDBClientOptions } = properties;
const mongoClient = new MongoClient(databaseUri, mongoDBClientOptions);
const collectionNames = {
accounts: collections?.accounts ?? 'user_accounts',
contacts: collections?.contacts ?? 'user_contacts',
sessions: collections?.sessions ?? 'user_sessions',
verificationTokens: collections?.verificationTokens ?? 'user_verification_tokens',
};

return {
async createUser(adapterUserData) {
const db = await dbPromise;
return createDatabaseUser({
adapterUserData,
appName,
db,
collectionNames,
inviteRequired: properties.invite?.required,
mongoClient,
});
},

async getUser(userId) {
const db = await dbPromise;
return getUserFromDbById({ appName, db, userId });
return getUserFromDbById({ appName, collectionNames, mongoClient, userId });
},

async getUserByEmail(email) {
const db = await dbPromise;
return getUserFromDbByEmail({ appName, db, email });
return getUserFromDbByEmail({ appName, collectionNames, mongoClient, email });
},

async getUserByAccount(provider_providerAccountId) {
const db = await dbPromise;
const account = await db.accounts.findOne(provider_providerAccountId);
const account = await mongoClient
.db()
.collection(collectionNames.accounts)
.findOne(provider_providerAccountId);
if (!account) return null;

return getUserFromDbById({ appName, db, userId: account.userId });
return getUserFromDbById({ appName, collectionNames, mongoClient, userId: account.userId });
},

async updateUser(adapterUserData) {
const db = await dbPromise;
await updateDatabaseUser({ adapterUserData, db });
await updateDatabaseUser({ adapterUserData, collectionNames, mongoClient });
return adapterUserData;
},

// This is not yet implemented by Auth.js
// and we want to set a disabled flag, not delete users
// async deleteUser(userId) {
// console.log('deleteUser', userId);
// const db = await dbPromise;

// await Promise.all([
// db.accounts.deleteMany({ userId }),
// db.sessions.deleteMany({ userId }),
Expand All @@ -74,25 +68,32 @@ function MultipleAppMongoDBAdapter({ properties }) {
// },

async linkAccount(account) {
await (await dbPromise).accounts.insertOne(to(account));
await mongoClient.db().collection(collectionNames.accounts).insertOne(to(account));
return from(account);
},

async unlinkAccount(provider_providerAccountId) {
const { value: account } = await (
await dbPromise
).accounts.findOneAndDelete(provider_providerAccountId);
const { value: account } = await mongoClient
.db()
.collection(collectionNames.accounts)
.findOneAndDelete(provider_providerAccountId);
return from(account);
},

async getSessionAndUser(sessionToken) {
const db = await dbPromise;

// eslint-disable-next-line no-unused-vars
const session = await db.sessions.findOne({ sessionToken });
const session = await mongoClient
.db()
.collection(collectionNames.sessions)
.findOne({ sessionToken });
if (!session) return null;

const user = await getUserFromDbById({ appName, db, userId: session.userId });
const user = await getUserFromDbById({
appName,
collectionNames,
mongoClient,
userId: session.userId,
});

return {
user,
Expand All @@ -101,47 +102,50 @@ function MultipleAppMongoDBAdapter({ properties }) {
},

async createSession(session) {
await (await dbPromise).sessions.insertOne(to(session));
await mongoClient.db().collection(collectionNames.sessions).insertOne(to(session));
return session;
},

async updateSession(data) {
// eslint-disable-next-line no-unused-vars
const { _id, ...session } = to(data);

const result = await (
await dbPromise
).sessions.findOneAndUpdate(
{ sessionToken: session.sessionToken },
{ $set: session },
{ returnDocument: 'after' }
);
const result = await mongoClient
.db()
.collection(collectionNames.sessions)
.findOneAndUpdate(
{ sessionToken: session.sessionToken },
{ $set: session },
{ returnDocument: 'after' }
);
return from(result.value);
},

async deleteSession(sessionToken) {
const { value: session } = await (
await dbPromise
).sessions.findOneAndDelete({
sessionToken,
});
const { value: session } = await mongoClient
.db()
.collection(collectionNames.sessions)
.findOneAndDelete({
sessionToken,
});
return from(session);
},

async createVerificationToken(data) {
await (await dbPromise).verificationTokens.insertOne(to(data));
await mongoClient.db().collection(collectionNames.verificationTokens).insertOne(to(data));
return data;
},

async useVerificationToken(identifier_token) {
const { value: verificationToken } = await (
await dbPromise
).verificationTokens.findOneAndDelete(identifier_token);
const { value: verificationToken } = await mongoClient
.db()
.collection(collectionNames.verificationTokens)
.findOneAndDelete(identifier_token);

if (!verificationToken) return null;
return from(verificationToken);
},
};
}

export default MultipleAppMongoDBAdapter;
export default MultiAppMongoDBAdapter;
Original file line number Diff line number Diff line change
@@ -1,20 +1,34 @@
import createDatabaseUserFromContact from './createDatabaseUserFromContact.js';
import createDatabaseUserWithoutContact from './createDatabaseUserWithoutContact.js';

async function createDatabaseUser({ adapterUserData, appName, db, inviteRequired }) {
const contact = await db.contacts.findOne({
async function createDatabaseUser({
adapterUserData,
appName,
collectionNames,
inviteRequired,
mongoClient,
}) {
const contact = await mongoClient.db().collection(collectionNames.contacts).findOne({
lowercase_email: adapterUserData.email.toLowerCase(),
});

if (contact) {
return createDatabaseUserFromContact({ adapterUserData, appName, contact, db, inviteRequired });
return createDatabaseUserFromContact({
adapterUserData,
appName,
collectionNames,
contact,
inviteRequired,
mongoClient,
});
}

return createDatabaseUserWithoutContact({
adapterUserData,
appName,
db,
collectionNames,
inviteRequired,
mongoClient,
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import transformContactToAdapterUser from './transformContactToAdapterUser.js';
async function createDatabaseUserFromContact({
adapterUserData,
appName,
collectionNames,
contact,
db,
inviteRequired,
mongoClient,
}) {
const invite = contact.apps?.[appName]?.invite;
if (inviteRequired && (!invite || !invite.open)) {
Expand Down Expand Up @@ -42,11 +43,10 @@ async function createDatabaseUserFromContact({
};
}

const { value: updatedContact } = await db.contacts.findOneAndUpdate(
{ _id: contact._id },
{ $set: update },
{ returnDocument: 'after' }
);
const { value: updatedContact } = await mongoClient
.db()
.collection(collectionNames.contacts)
.findOneAndUpdate({ _id: contact._id }, { $set: update }, { returnDocument: 'after' });
return transformContactToAdapterUser({ appName, contact: updatedContact });
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import { v4 as uuid } from 'uuid';

import transformContactToAdapterUser from './transformContactToAdapterUser.js';

async function createDatabaseUserWithoutContact({ adapterUserData, appName, db, inviteRequired }) {
async function createDatabaseUserWithoutContact({
adapterUserData,
appName,
collectionNames,
inviteRequired,
mongoClient,
}) {
if (inviteRequired) {
throw new Error('Access denied.');
}
Expand All @@ -29,7 +35,7 @@ async function createDatabaseUserWithoutContact({ adapterUserData, appName, db,
contact.created = { timestamp: new Date(), user: { id: contact._id } };
contact.updated = { timestamp: new Date(), user: { id: contact._id } };

await db.contacts.insertOne(contact);
await mongoClient.db().collection(collectionNames.contacts).insertOne(contact);
return transformContactToAdapterUser({ appName, contact });
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import transformContactToAdapterUser from './transformContactToAdapterUser.js';

async function getUserFromDbByEmail({ appName, db, email }) {
const contact = await db.contacts.findOne({ lowercase_email: email.toLowerCase() });
async function getUserFromDbByEmail({ appName, collectionNames, email, mongoClient }) {
const contact = await mongoClient
.db()
.collection(collectionNames.contacts)
.findOne({ lowercase_email: email.toLowerCase() });
if (
!contact ||
contact.disabled ||
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import transformContactToAdapterUser from './transformContactToAdapterUser.js';

async function getUserFromDbById({ appName, db, userId }) {
const contact = await db.contacts.findOne({ _id: userId });
async function getUserFromDbById({ appName, collectionNames, mongoClient, userId }) {
const contact = await mongoClient
.db()
.collection(collectionNames.contacts)
.findOne({ _id: userId });
if (
!contact ||
contact.disabled ||
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
async function updateDatabaseUser({ adapterUserData, db }) {
async function updateDatabaseUser({ adapterUserData, collectionNames, mongoClient }) {
const { emailVerified: email_verified, id, image } = adapterUserData;
await db.contacts.updateOne({ _id: id }, { $set: { email_verified, image } });
await mongoClient
.db()
.collection(collectionNames.contacts)
.updateOne({ _id: id }, { $set: { email_verified, image } });
}

export default updateDatabaseUser;
Loading

0 comments on commit 31ee8bf

Please sign in to comment.