forked from twentyhq/twenty
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of https://github.com/synapsenet-arena/lead360
- Loading branch information
Showing
678 changed files
with
14,816 additions
and
6,238 deletions.
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
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,22 @@ | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<link rel="icon" href="/icons/android/android-launchericon-48-48.png" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Twenty</title> | ||
<style> | ||
/* Reset margin and padding */ | ||
html, body { | ||
margin: 0; | ||
padding: 0; | ||
height: 100%; /* Ensure body takes full viewport height */ | ||
overflow: hidden; /* Prevents scrollbars from appearing */ | ||
} | ||
</style> | ||
|
||
</head> | ||
<body> | ||
<div id="app"></div> | ||
<script type="module" src="/src/options/index.tsx"></script> | ||
</body> | ||
</html> |
168 changes: 69 additions & 99 deletions
168
packages/twenty-chrome-extension/src/background/index.ts
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 |
---|---|---|
@@ -1,130 +1,100 @@ | ||
import Crypto from 'crypto-js'; | ||
|
||
import { openOptionsPage } from '~/background/utils/openOptionsPage'; | ||
import { exchangeAuthorizationCode } from '~/db/auth.db'; | ||
import { isDefined } from '~/utils/isDefined'; | ||
|
||
// Open options page programmatically in a new tab. | ||
chrome.runtime.onInstalled.addListener((details) => { | ||
if (details.reason === 'install') { | ||
openOptionsPage(); | ||
} | ||
}); | ||
// chrome.runtime.onInstalled.addListener((details) => { | ||
// if (details.reason === 'install') { | ||
// openOptionsPage(); | ||
// } | ||
// }); | ||
|
||
// Open options page when extension icon is clicked. | ||
chrome.action.onClicked.addListener((tab) => { | ||
chrome.tabs.sendMessage(tab.id ?? 0, { action: 'TOGGLE' }); | ||
}); | ||
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }); | ||
|
||
// This listens for an event from other parts of the extension, such as the content script, and performs the required tasks. | ||
// The cases themselves are labelled such that their operations are reflected by their names. | ||
chrome.runtime.onMessage.addListener((message, _, sendResponse) => { | ||
switch (message.action) { | ||
case 'getActiveTab': // e.g. "https://linkedin.com/company/twenty/" | ||
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { | ||
if (isDefined(tabs) && isDefined(tabs[0])) { | ||
sendResponse({ tab: tabs[0] }); | ||
case 'getActiveTab': { | ||
// e.g. "https://linkedin.com/company/twenty/" | ||
chrome.tabs.query({ active: true, currentWindow: true }, ([tab]) => { | ||
if (isDefined(tab) && isDefined(tab.id)) { | ||
sendResponse({ tab }); | ||
} | ||
}); | ||
break; | ||
case 'openOptionsPage': | ||
openOptionsPage(); | ||
} | ||
case 'openSidepanel': { | ||
chrome.tabs.query({ active: true, currentWindow: true }, ([tab]) => { | ||
if (isDefined(tab) && isDefined(tab.id)) { | ||
chrome.sidePanel.open({ tabId: tab.id }); | ||
} | ||
}); | ||
break; | ||
case 'CONNECT': | ||
launchOAuth(({ status, message }) => { | ||
sendResponse({ status, message }); | ||
} | ||
case 'changeSidepanelUrl': { | ||
chrome.tabs.query({ active: true, currentWindow: true }, ([tab]) => { | ||
if (isDefined(tab) && isDefined(tab.id)) { | ||
chrome.tabs.sendMessage(tab.id, { | ||
action: 'changeSidepanelUrl', | ||
message, | ||
}); | ||
} | ||
}); | ||
break; | ||
} | ||
default: | ||
break; | ||
} | ||
|
||
return true; | ||
}); | ||
|
||
const generateRandomString = (length: number) => { | ||
const charset = | ||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'; | ||
let result = ''; | ||
for (let i = 0; i < length; i++) { | ||
result += charset.charAt(Math.floor(Math.random() * charset.length)); | ||
} | ||
return result; | ||
}; | ||
|
||
const generateCodeVerifierAndChallenge = () => { | ||
const codeVerifier = generateRandomString(32); | ||
const hash = Crypto.SHA256(codeVerifier); | ||
const codeChallenge = hash | ||
.toString(Crypto.enc.Base64) | ||
.replace(/\+/g, '-') | ||
.replace(/\//g, '_') | ||
.replace(/=+$/, ''); | ||
|
||
return { codeVerifier, codeChallenge }; | ||
}; | ||
|
||
const launchOAuth = ( | ||
callback: ({ status, message }: { status: boolean; message: string }) => void, | ||
) => { | ||
const { codeVerifier, codeChallenge } = generateCodeVerifierAndChallenge(); | ||
const redirectUrl = chrome.identity.getRedirectURL(); | ||
chrome.identity | ||
.launchWebAuthFlow({ | ||
url: `${ | ||
import.meta.env.VITE_FRONT_BASE_URL | ||
}/authorize?clientId=chrome&codeChallenge=${codeChallenge}&redirectUrl=${redirectUrl}`, | ||
interactive: true, | ||
}) | ||
.then((responseUrl) => { | ||
if (typeof responseUrl === 'string') { | ||
const url = new URL(responseUrl); | ||
const authorizationCode = url.searchParams.get( | ||
'authorizationCode', | ||
) as string; | ||
exchangeAuthorizationCode({ | ||
authorizationCode, | ||
codeVerifier, | ||
}).then((tokens) => { | ||
if (isDefined(tokens)) { | ||
chrome.storage.local.set({ | ||
loginToken: tokens.loginToken, | ||
}); | ||
|
||
chrome.storage.local.set({ | ||
accessToken: tokens.accessToken, | ||
}); | ||
|
||
chrome.storage.local.set({ | ||
refreshToken: tokens.refreshToken, | ||
}); | ||
|
||
callback({ status: true, message: '' }); | ||
|
||
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { | ||
if (isDefined(tabs) && isDefined(tabs[0])) { | ||
chrome.tabs.sendMessage(tabs[0].id ?? 0, { | ||
action: 'AUTHENTICATED', | ||
}); | ||
} | ||
}); | ||
} | ||
}); | ||
} | ||
}) | ||
.catch((error) => { | ||
callback({ status: false, message: error.message }); | ||
}); | ||
}; | ||
|
||
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { | ||
chrome.tabs.onUpdated.addListener(async (tabId, _, tab) => { | ||
const isDesiredRoute = | ||
tab.url?.match(/^https?:\/\/(?:www\.)?linkedin\.com\/company(?:\/\S+)?/) || | ||
tab.url?.match(/^https?:\/\/(?:www\.)?linkedin\.com\/in(?:\/\S+)?/); | ||
|
||
if (changeInfo.status === 'complete' && tab.active) { | ||
if (tab.active === true) { | ||
if (isDefined(isDesiredRoute)) { | ||
chrome.tabs.sendMessage(tabId, { action: 'executeContentScript' }); | ||
} | ||
} | ||
|
||
await chrome.sidePanel.setOptions({ | ||
tabId, | ||
path: tab.url?.match(/^https?:\/\/(?:www\.)?linkedin\.com/) | ||
? 'sidepanel.html' | ||
: 'page-inaccessible.html', | ||
enabled: true, | ||
}); | ||
}); | ||
|
||
const setTokenStateFromCookie = (cookie: string) => { | ||
const decodedValue = decodeURIComponent(cookie); | ||
const tokenPair = JSON.parse(decodedValue); | ||
if (isDefined(tokenPair)) { | ||
chrome.storage.local.set({ | ||
isAuthenticated: true, | ||
accessToken: tokenPair.accessToken, | ||
refreshToken: tokenPair.refreshToken, | ||
}); | ||
} | ||
}; | ||
|
||
chrome.cookies.onChanged.addListener(async ({ cookie }) => { | ||
if (cookie.name === 'tokenPair') { | ||
setTokenStateFromCookie(cookie.value); | ||
} | ||
}); | ||
|
||
// This will only run the very first time the extension loads, after we have stored the | ||
// cookiesRead variable to true, this will not allow to change the token state everytime background script runs | ||
chrome.cookies.get( | ||
{ name: 'tokenPair', url: `${import.meta.env.VITE_FRONT_BASE_URL}` }, | ||
async (cookie) => { | ||
const store = await chrome.storage.local.get(['cookiesRead']); | ||
if (isDefined(cookie) && !isDefined(store.cookiesRead)) { | ||
setTokenStateFromCookie(cookie.value); | ||
chrome.storage.local.set({ cookiesRead: true }); | ||
} | ||
}, | ||
); |
5 changes: 0 additions & 5 deletions
5
packages/twenty-chrome-extension/src/background/utils/openOptionsPage.ts
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.