Skip to content
Open
2 changes: 2 additions & 0 deletions collector/extensions/resync/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ async function resyncConfluence({ chunkSource }, response) {
spaceKey: source.searchParams.get('spaceKey'),
accessToken: source.searchParams.get('token'),
username: source.searchParams.get('username'),
cloud: source.searchParams.get('cloud') === 'true',
bypassSSL: source.searchParams.get('bypassSSL') === 'true',
});

if (!success) throw new Error(`Failed to sync Confluence page content. ${reason}`);
Expand Down
25 changes: 23 additions & 2 deletions collector/utils/extensions/Confluence/ConfluenceLoader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class ConfluencePagesLoader {
expand = "body.storage,version",
personalAccessToken,
cloud = true,
bypassSSL = false,
}) {
this.baseUrl = baseUrl;
this.spaceKey = spaceKey;
Expand All @@ -23,6 +24,7 @@ class ConfluencePagesLoader {
this.expand = expand;
this.personalAccessToken = personalAccessToken;
this.cloud = cloud;
this.bypassSSL = bypassSSL;
}

get authorizationHeader() {
Expand Down Expand Up @@ -60,16 +62,35 @@ class ConfluencePagesLoader {
if (authHeader) {
initialHeaders.Authorization = authHeader;
}
const response = await fetch(url, {

// Configure fetch options with SSL bypass if enabled
const fetchOptions = {
headers: initialHeaders,
});
};

// If SSL bypass is enabled, set the NODE_TLS_REJECT_UNAUTHORIZED environment variable
if (this.bypassSSL) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
}

const response = await fetch(url, fetchOptions);

// Reset the environment variable after the request
if (this.bypassSSL) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1';
}

if (!response.ok) {
throw new Error(
`Failed to fetch ${url} from Confluence: ${response.status}`
);
}
return await response.json();
} catch (error) {
// Reset the environment variable in case of error
if (this.bypassSSL) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1';
}
throw new Error(`Failed to fetch ${url} from Confluence: ${error}`);
}
}
Expand Down
9 changes: 7 additions & 2 deletions collector/utils/extensions/Confluence/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ async function loadConfluence(
accessToken = null,
cloud = true,
personalAccessToken = null,
bypassSSL = false,
},
response
) {
Expand Down Expand Up @@ -54,6 +55,7 @@ async function loadConfluence(
accessToken,
cloud,
personalAccessToken,
bypassSSL,
});

const { docs, error } = await loader
Expand Down Expand Up @@ -100,7 +102,7 @@ async function loadConfluence(
description: doc.metadata.title,
docSource: `${origin} Confluence`,
chunkSource: generateChunkSource(
{ doc, baseUrl: origin, spaceKey, accessToken, username, cloud },
{ doc, baseUrl: origin, spaceKey, accessToken, username, cloud, bypassSSL },
response.locals.encryptionWorker
),
published: new Date().toLocaleString(),
Expand Down Expand Up @@ -144,6 +146,7 @@ async function fetchConfluencePage({
username,
accessToken,
cloud = true,
bypassSSL = false,
}) {
if (!pageUrl || !baseUrl || !spaceKey || !username || !accessToken) {
return {
Expand Down Expand Up @@ -177,6 +180,7 @@ async function fetchConfluencePage({
username,
accessToken,
cloud,
bypassSSL,
});

const { docs, error } = await loader
Expand Down Expand Up @@ -240,7 +244,7 @@ function validBaseUrl(baseUrl) {
* @returns {string}
*/
function generateChunkSource(
{ doc, baseUrl, spaceKey, accessToken, username, cloud },
{ doc, baseUrl, spaceKey, accessToken, username, cloud, bypassSSL },
encryptionWorker
) {
const payload = {
Expand All @@ -249,6 +253,7 @@ function generateChunkSource(
token: accessToken,
username,
cloud,
bypassSSL,
};
return `confluence://${doc.metadata.url}?payload=${encryptionWorker.encrypt(
JSON.stringify(payload)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default function ConfluenceOptions() {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [accessType, setAccessType] = useState("username");
const [isCloud, setIsCloud] = useState(true);

const handleSubmit = async (e) => {
e.preventDefault();
Expand All @@ -31,6 +32,7 @@ export default function ConfluenceOptions() {
accessToken: form.get("accessToken"),
cloud: form.get("isCloud") === "true",
personalAccessToken: form.get("personalAccessToken"),
bypassSSL: form.get("bypassSSL") === "true",
});

if (!!error) {
Expand Down Expand Up @@ -77,6 +79,7 @@ export default function ConfluenceOptions() {
autoComplete="off"
spellCheck={false}
defaultValue="true"
onChange={(e) => setIsCloud(e.target.value === "true")}
>
<option value="true">Atlassian Cloud</option>
<option value="false">Self-hosted</option>
Expand Down Expand Up @@ -198,7 +201,7 @@ export default function ConfluenceOptions() {
<p className="text-sm">
{t("connectors.confluence.token_explained_start")}
<a
href="https://id.atlassian.com/manage-profile/security/api-tokens"
href="https://id.atlassian.com/manage-profile/security/api-token"
target="_blank"
rel="noopener noreferrer"
className="underline"
Expand Down Expand Up @@ -250,6 +253,31 @@ export default function ConfluenceOptions() {
</div>
</div>

{!isCloud && (
<div className="w-full flex flex-col py-2">
<div className="w-full flex flex-col gap-4">
<div className="flex flex-col pr-10">
<div className="flex flex-col gap-y-1 mb-4">
<label className="text-white text-sm font-bold flex gap-x-2 items-center">
<input
type="checkbox"
name="bypassSSL"
className="mr-2"
defaultChecked={false}
/>
<p className="font-bold text-theme-text-primary">
{t("connectors.confluence.bypass_ssl")}
</p>
</label>
<p className="text-xs font-normal text-theme-text-secondary">
{t("connectors.confluence.bypass_ssl_explained")}
</p>
</div>
</div>
</div>
</div>
)}

<div className="flex flex-col gap-y-2 w-full pr-10">
<button
type="submit"
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/locales/en/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,9 @@ const TRANSLATIONS = {
token_desc: "Access token for authentication",
pat_token: "Confluence Personal Access Token",
pat_token_explained: "Your Confluence personal access token.",
bypass_ssl: "Bypass SSL Certificate Validation",
bypass_ssl_explained:
"Enable this option to bypass SSL certificate validation for self-hosted confluence instances with self-signed certificate",
task_explained:
"Once complete, the page content will be available for embedding into workspaces in the document picker.",
},
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/models/dataConnector.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ const DataConnector = {
accessToken,
cloud,
personalAccessToken,
bypassSSL,
}) {
return await fetch(`${API_BASE}/ext/confluence`, {
method: "POST",
Expand All @@ -151,6 +152,7 @@ const DataConnector = {
accessToken,
cloud,
personalAccessToken,
bypassSSL,
}),
})
.then((res) => res.json())
Expand Down
Loading