-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #852 from emptyarrayhq/integration
EMP-17 int gmail and x integrations
- Loading branch information
Showing
11 changed files
with
337 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { TwitterApi } from "twitter-api-v2"; | ||
import { XQueue } from "../../loaders/bullmq.loader.js"; | ||
|
||
const twitterClient = new TwitterApi({ | ||
clientId: process.env.X_CLIENT_ID, | ||
clientSecret: process.env.X_CLIENT_SECRET | ||
}); | ||
|
||
const CALLBACK_URL = process.env.X_CALLBACK_URL; | ||
|
||
const tempOAuthStore = new Map(); | ||
|
||
export const redirectXOAuthLoginController = async (req, res) => { | ||
try { | ||
const { url, codeVerifier, state } = twitterClient.generateOAuth2AuthLink(CALLBACK_URL, { | ||
scope: ["tweet.read", "users.read", "bookmark.read", "offline.access"] | ||
}); | ||
|
||
tempOAuthStore.set(state, { | ||
codeVerifier, | ||
timestamp: Date.now() | ||
}); | ||
|
||
console.log("Redirect URL:", url); | ||
|
||
// Clean up expired entries | ||
cleanupTempStore(); | ||
res.redirect(url); | ||
} catch (error) { | ||
console.error("Twitter OAuth Error:", error); | ||
res.status(500).json({ | ||
message: "Error initiating Twitter OAuth", | ||
error: error.message | ||
}); | ||
} | ||
}; | ||
|
||
export const getXAccessTokenController = async (req, res) => { | ||
try { | ||
const { state, code } = req.query; | ||
const user = req.user; | ||
const storedData = tempOAuthStore.get(state); | ||
|
||
if (!storedData) { | ||
console.error("No stored data found for state:", state); | ||
throw new Error("Invalid or expired state parameter"); | ||
} | ||
|
||
// Clean up stored data immediately after retrieving it | ||
tempOAuthStore.delete(state); | ||
|
||
const { client: loggedClient, accessToken, refreshToken } = await twitterClient.loginWithOAuth2({ | ||
code, | ||
codeVerifier: storedData.codeVerifier, | ||
redirectUri: CALLBACK_URL | ||
}); | ||
user.integration.x.accessToken = accessToken; | ||
user.integration.x.refreshToken = refreshToken; | ||
user.integration.x.connected = true; | ||
await user.save() | ||
|
||
await XQueue.add( | ||
"XQueue", | ||
{ | ||
accessToken, | ||
userId: user._id | ||
} | ||
); | ||
|
||
res.json({ | ||
message: "Twitter authentication successful", | ||
accessToken, | ||
refreshToken | ||
}); | ||
} catch (error) { | ||
console.error("Error exchanging Twitter OAuth token:", error); | ||
res.status(500).json({ | ||
message: "Error getting access token", | ||
error: error.message | ||
}); | ||
} | ||
}; | ||
|
||
// cleanup function to remove expired entries (older than 5 minutes) | ||
function cleanupTempStore () { | ||
const fiveMinutes = 5 * 60 * 1000; | ||
const now = Date.now(); | ||
|
||
for (const [state, data] of tempOAuthStore.entries()) { | ||
if (now - data.timestamp > fiveMinutes) { | ||
console.log("Removing expired state:", state); | ||
tempOAuthStore.delete(state); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { XQueue } from '../loaders/bullmq.loader.js'; | ||
import { Worker } from "bullmq"; | ||
import { redisConnection } from "../loaders/redis.loader.js"; | ||
import { syncXBookmarks } from "../services/integration/x.service.js"; | ||
|
||
const processXJob = async (job) => { | ||
const { accessToken, userId } = job.data; | ||
console.log(`Processing x job with ${accessToken}`); | ||
try { | ||
await syncXBookmarks(accessToken, userId) | ||
} catch (error) { | ||
console.error('Error processing X job:', error); | ||
throw error; | ||
} | ||
}; | ||
|
||
/** | ||
* Worker setup and event handling | ||
*/ | ||
const XWorker = new Worker('XQueue', async (job) => { | ||
console.log(`Processing job with id ${job.id}`); | ||
await processXJob(job); | ||
}, { | ||
connection: redisConnection, | ||
concurrency: 5 | ||
}); | ||
|
||
XWorker.on('active', (job) => { | ||
console.log(`Processing job: ${job.id}`); | ||
}); | ||
|
||
/** | ||
* Event listener for job completion. | ||
* Logs the completion and removes the job from the queue. | ||
* | ||
* @param {Object} job - The completed job object. | ||
*/ | ||
XWorker.on('completed', async (job) => { | ||
console.log(`Job with id ${job.id} has been completed`); | ||
await job.remove(); | ||
}); | ||
|
||
/** | ||
* Event listener for job failure. | ||
* Logs the error message. | ||
* | ||
* @param {Object} job - The failed job object. | ||
* @param {Error} err - The error that caused the job to fail. | ||
*/ | ||
XWorker.on('failed', (job, err) => { | ||
console.error(`Job with id ${job.id} failed with error: ${err.message}`); | ||
}); | ||
|
||
/** | ||
* Event listener for worker errors. | ||
* Logs Redis connection errors. | ||
* | ||
* @param {Error} err - The error object. | ||
*/ | ||
XWorker.on('error', (err) => { | ||
console.error('Redis connection error in XWorker:', err); | ||
}); | ||
|
||
export { | ||
XQueue, | ||
XWorker | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { Router } from "express"; | ||
import { redirectXOAuthLoginController, getXAccessTokenController } from "../../controllers/integration/x.controller.js"; | ||
|
||
const router = Router(); | ||
|
||
router.route("/connect/").get(redirectXOAuthLoginController); | ||
router.route("/getAccessToken/").get(getXAccessTokenController); | ||
|
||
export default router; |
Oops, something went wrong.