mirror of
https://github.com/open-webui/open-webui
synced 2025-06-15 19:05:04 +00:00
Merge pull request #12904 from hurxxxx/feat/onedrive-orgs
feat: support Organization and School accounts in OneDrive
This commit is contained in:
commit
fa8132903e
@ -1763,6 +1763,13 @@ ONEDRIVE_CLIENT_ID = PersistentConfig(
|
|||||||
os.environ.get("ONEDRIVE_CLIENT_ID", ""),
|
os.environ.get("ONEDRIVE_CLIENT_ID", ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ONEDRIVE_SHAREPOINT_URL = PersistentConfig(
|
||||||
|
"ONEDRIVE_SHAREPOINT_URL",
|
||||||
|
"onedrive.sharepoint_url",
|
||||||
|
os.environ.get("ONEDRIVE_SHAREPOINT_URL", ""),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# RAG Content Extraction
|
# RAG Content Extraction
|
||||||
CONTENT_EXTRACTION_ENGINE = PersistentConfig(
|
CONTENT_EXTRACTION_ENGINE = PersistentConfig(
|
||||||
"CONTENT_EXTRACTION_ENGINE",
|
"CONTENT_EXTRACTION_ENGINE",
|
||||||
|
@ -100,6 +100,7 @@ from open_webui.config import (
|
|||||||
# OpenAI
|
# OpenAI
|
||||||
ENABLE_OPENAI_API,
|
ENABLE_OPENAI_API,
|
||||||
ONEDRIVE_CLIENT_ID,
|
ONEDRIVE_CLIENT_ID,
|
||||||
|
ONEDRIVE_SHAREPOINT_URL,
|
||||||
OPENAI_API_BASE_URLS,
|
OPENAI_API_BASE_URLS,
|
||||||
OPENAI_API_KEYS,
|
OPENAI_API_KEYS,
|
||||||
OPENAI_API_CONFIGS,
|
OPENAI_API_CONFIGS,
|
||||||
@ -240,6 +241,7 @@ from open_webui.config import (
|
|||||||
GOOGLE_DRIVE_CLIENT_ID,
|
GOOGLE_DRIVE_CLIENT_ID,
|
||||||
GOOGLE_DRIVE_API_KEY,
|
GOOGLE_DRIVE_API_KEY,
|
||||||
ONEDRIVE_CLIENT_ID,
|
ONEDRIVE_CLIENT_ID,
|
||||||
|
ONEDRIVE_SHAREPOINT_URL,
|
||||||
ENABLE_RAG_HYBRID_SEARCH,
|
ENABLE_RAG_HYBRID_SEARCH,
|
||||||
ENABLE_RAG_LOCAL_WEB_FETCH,
|
ENABLE_RAG_LOCAL_WEB_FETCH,
|
||||||
ENABLE_WEB_LOADER_SSL_VERIFICATION,
|
ENABLE_WEB_LOADER_SSL_VERIFICATION,
|
||||||
@ -1327,7 +1329,10 @@ async def get_app_config(request: Request):
|
|||||||
"client_id": GOOGLE_DRIVE_CLIENT_ID.value,
|
"client_id": GOOGLE_DRIVE_CLIENT_ID.value,
|
||||||
"api_key": GOOGLE_DRIVE_API_KEY.value,
|
"api_key": GOOGLE_DRIVE_API_KEY.value,
|
||||||
},
|
},
|
||||||
"onedrive": {"client_id": ONEDRIVE_CLIENT_ID.value},
|
"onedrive": {
|
||||||
|
"client_id": ONEDRIVE_CLIENT_ID.value,
|
||||||
|
"sharepoint_url": ONEDRIVE_SHAREPOINT_URL.value,
|
||||||
|
},
|
||||||
"license_metadata": app.state.LICENSE_METADATA,
|
"license_metadata": app.state.LICENSE_METADATA,
|
||||||
**(
|
**(
|
||||||
{
|
{
|
||||||
|
@ -1063,9 +1063,9 @@
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
uploadOneDriveHandler={async () => {
|
uploadOneDriveHandler={async (authorityType) => {
|
||||||
try {
|
try {
|
||||||
const fileData = await pickAndDownloadFile();
|
const fileData = await pickAndDownloadFile(authorityType);
|
||||||
if (fileData) {
|
if (fileData) {
|
||||||
const file = new File([fileData.blob], fileData.name, {
|
const file = new File([fileData.blob], fileData.name, {
|
||||||
type: fileData.blob.type || 'application/octet-stream'
|
type: fileData.blob.type || 'application/octet-stream'
|
||||||
|
@ -229,94 +229,66 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if $config?.features?.enable_onedrive_integration}
|
{#if $config?.features?.enable_onedrive_integration}
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Sub>
|
||||||
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl"
|
<DropdownMenu.SubTrigger class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full">
|
||||||
on:click={() => {
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" class="w-5 h-5" fill="none">
|
||||||
uploadOneDriveHandler();
|
<mask id="mask0_87_7796" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="6" width="32" height="20">
|
||||||
}}
|
<path d="M7.82979 26C3.50549 26 0 22.5675 0 18.3333C0 14.1921 3.35322 10.8179 7.54613 10.6716C9.27535 7.87166 12.4144 6 16 6C20.6308 6 24.5169 9.12183 25.5829 13.3335C29.1316 13.3603 32 16.1855 32 19.6667C32 23.0527 29 26 25.8723 25.9914L7.82979 26Z" fill="#C4C4C4"/>
|
||||||
>
|
</mask>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" class="w-5 h-5" fill="none">
|
<g mask="url(#mask0_87_7796)">
|
||||||
<mask
|
<path d="M7.83017 26.0001C5.37824 26.0001 3.18957 24.8966 1.75391 23.1691L18.0429 16.3335L30.7089 23.4647C29.5926 24.9211 27.9066 26.0001 26.0004 25.9915C23.1254 26.0001 12.0629 26.0001 7.83017 26.0001Z" fill="url(#paint0_linear_87_7796)"/>
|
||||||
id="mask0_87_7796"
|
<path d="M25.5785 13.3149L18.043 16.3334L30.709 23.4647C31.5199 22.4065 32.0004 21.0916 32.0004 19.6669C32.0004 16.1857 29.1321 13.3605 25.5833 13.3337C25.5817 13.3274 25.5801 13.3212 25.5785 13.3149Z" fill="url(#paint1_linear_87_7796)"/>
|
||||||
style="mask-type:alpha"
|
<path d="M7.06445 10.7028L18.0423 16.3333L25.5779 13.3148C24.5051 9.11261 20.6237 6 15.9997 6C12.4141 6 9.27508 7.87166 7.54586 10.6716C7.3841 10.6773 7.22358 10.6877 7.06445 10.7028Z" fill="url(#paint2_linear_87_7796)"/>
|
||||||
maskUnits="userSpaceOnUse"
|
<path d="M1.7535 23.1687L18.0425 16.3331L7.06471 10.7026C3.09947 11.0792 0 14.3517 0 18.3331C0 20.1665 0.657197 21.8495 1.7535 23.1687Z" fill="url(#paint3_linear_87_7796)"/>
|
||||||
x="0"
|
</g>
|
||||||
y="6"
|
<defs>
|
||||||
width="32"
|
<linearGradient id="paint0_linear_87_7796" x1="4.42591" y1="24.6668" x2="27.2309" y2="23.2764" gradientUnits="userSpaceOnUse">
|
||||||
height="20"
|
<stop stop-color="#2086B8"/>
|
||||||
|
<stop offset="1" stop-color="#46D3F6"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_87_7796" x1="23.8302" y1="19.6668" x2="30.2108" y2="15.2082" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#1694DB"/>
|
||||||
|
<stop offset="1" stop-color="#62C3FE"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint2_linear_87_7796" x1="8.51037" y1="7.33333" x2="23.3335" y2="15.9348" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#0D3D78"/>
|
||||||
|
<stop offset="1" stop-color="#063B83"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint3_linear_87_7796" x1="-0.340429" y1="19.9998" x2="14.5634" y2="14.4649" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#16589B"/>
|
||||||
|
<stop offset="1" stop-color="#1464B7"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
<div class="line-clamp-1">{$i18n.t('Microsoft OneDrive')}</div>
|
||||||
|
</DropdownMenu.SubTrigger>
|
||||||
|
<DropdownMenu.SubContent
|
||||||
|
class="w-[calc(100vw-2rem)] max-w-[280px] rounded-xl px-1 py-1 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-sm"
|
||||||
|
side={$mobile ? "bottom" : "right"}
|
||||||
|
sideOffset={$mobile ? 5 : 0}
|
||||||
|
alignOffset={$mobile ? 0 : -8}
|
||||||
|
>
|
||||||
|
<DropdownMenu.Item
|
||||||
|
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl"
|
||||||
|
on:click={() => {
|
||||||
|
uploadOneDriveHandler('personal');
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<path
|
<div class="line-clamp-1">{$i18n.t('Microsoft OneDrive (personal)')}</div>
|
||||||
d="M7.82979 26C3.50549 26 0 22.5675 0 18.3333C0 14.1921 3.35322 10.8179 7.54613 10.6716C9.27535 7.87166 12.4144 6 16 6C20.6308 6 24.5169 9.12183 25.5829 13.3335C29.1316 13.3603 32 16.1855 32 19.6667C32 23.0527 29 26 25.8723 25.9914L7.82979 26Z"
|
</DropdownMenu.Item>
|
||||||
fill="#C4C4C4"
|
<DropdownMenu.Item
|
||||||
/>
|
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl"
|
||||||
</mask>
|
on:click={() => {
|
||||||
<g mask="url(#mask0_87_7796)">
|
uploadOneDriveHandler('organizations');
|
||||||
<path
|
}}
|
||||||
d="M7.83017 26.0001C5.37824 26.0001 3.18957 24.8966 1.75391 23.1691L18.0429 16.3335L30.7089 23.4647C29.5926 24.9211 27.9066 26.0001 26.0004 25.9915C23.1254 26.0001 12.0629 26.0001 7.83017 26.0001Z"
|
>
|
||||||
fill="url(#paint0_linear_87_7796)"
|
<div class="flex flex-col">
|
||||||
/>
|
<div class="line-clamp-1">{$i18n.t('Microsoft OneDrive (work/school)')}</div>
|
||||||
<path
|
<div class="text-xs text-gray-500">Includes SharePoint</div>
|
||||||
d="M25.5785 13.3149L18.043 16.3334L30.709 23.4647C31.5199 22.4065 32.0004 21.0916 32.0004 19.6669C32.0004 16.1857 29.1321 13.3605 25.5833 13.3337C25.5817 13.3274 25.5801 13.3212 25.5785 13.3149Z"
|
</div>
|
||||||
fill="url(#paint1_linear_87_7796)"
|
</DropdownMenu.Item>
|
||||||
/>
|
</DropdownMenu.SubContent>
|
||||||
<path
|
</DropdownMenu.Sub>
|
||||||
d="M7.06445 10.7028L18.0423 16.3333L25.5779 13.3148C24.5051 9.11261 20.6237 6 15.9997 6C12.4141 6 9.27508 7.87166 7.54586 10.6716C7.3841 10.6773 7.22358 10.6877 7.06445 10.7028Z"
|
|
||||||
fill="url(#paint2_linear_87_7796)"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M1.7535 23.1687L18.0425 16.3331L7.06471 10.7026C3.09947 11.0792 0 14.3517 0 18.3331C0 20.1665 0.657197 21.8495 1.7535 23.1687Z"
|
|
||||||
fill="url(#paint3_linear_87_7796)"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<linearGradient
|
|
||||||
id="paint0_linear_87_7796"
|
|
||||||
x1="4.42591"
|
|
||||||
y1="24.6668"
|
|
||||||
x2="27.2309"
|
|
||||||
y2="23.2764"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
>
|
|
||||||
<stop stop-color="#2086B8" />
|
|
||||||
<stop offset="1" stop-color="#46D3F6" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="paint1_linear_87_7796"
|
|
||||||
x1="23.8302"
|
|
||||||
y1="19.6668"
|
|
||||||
x2="30.2108"
|
|
||||||
y2="15.2082"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
>
|
|
||||||
<stop stop-color="#1694DB" />
|
|
||||||
<stop offset="1" stop-color="#62C3FE" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="paint2_linear_87_7796"
|
|
||||||
x1="8.51037"
|
|
||||||
y1="7.33333"
|
|
||||||
x2="23.3335"
|
|
||||||
y2="15.9348"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
>
|
|
||||||
<stop stop-color="#0D3D78" />
|
|
||||||
<stop offset="1" stop-color="#063B83" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="paint3_linear_87_7796"
|
|
||||||
x1="-0.340429"
|
|
||||||
y1="19.9998"
|
|
||||||
x2="14.5634"
|
|
||||||
y2="14.4649"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
>
|
|
||||||
<stop stop-color="#16589B" />
|
|
||||||
<stop offset="1" stop-color="#1464B7" />
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
||||||
<div class="line-clamp-1">{$i18n.t('OneDrive')}</div>
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
{/if}
|
{/if}
|
||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,70 +2,128 @@ import { PublicClientApplication } from '@azure/msal-browser';
|
|||||||
import type { PopupRequest } from '@azure/msal-browser';
|
import type { PopupRequest } from '@azure/msal-browser';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
let CLIENT_ID = '';
|
class OneDriveConfig {
|
||||||
|
private static instance: OneDriveConfig;
|
||||||
|
private clientId: string = '';
|
||||||
|
private sharepointUrl: string = '';
|
||||||
|
private msalInstance: PublicClientApplication | null = null;
|
||||||
|
private currentAuthorityType: 'personal' | 'organizations' = 'personal';
|
||||||
|
|
||||||
async function getCredentials() {
|
private constructor() {}
|
||||||
if (CLIENT_ID) return;
|
|
||||||
|
|
||||||
const response = await fetch('/api/config');
|
public static getInstance(): OneDriveConfig {
|
||||||
if (!response.ok) {
|
if (!OneDriveConfig.instance) {
|
||||||
throw new Error('Failed to fetch OneDrive credentials');
|
OneDriveConfig.instance = new OneDriveConfig();
|
||||||
}
|
|
||||||
const config = await response.json();
|
|
||||||
CLIENT_ID = config.onedrive?.client_id;
|
|
||||||
if (!CLIENT_ID) {
|
|
||||||
throw new Error('OneDrive client ID not configured');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let msalInstance: PublicClientApplication | null = null;
|
|
||||||
|
|
||||||
// Initialize MSAL authentication
|
|
||||||
async function initializeMsal() {
|
|
||||||
try {
|
|
||||||
if (!CLIENT_ID) {
|
|
||||||
await getCredentials();
|
|
||||||
}
|
}
|
||||||
|
return OneDriveConfig.instance;
|
||||||
|
}
|
||||||
|
|
||||||
const msalParams = {
|
public async initialize(authorityType?: 'personal' | 'organizations'): Promise<void> {
|
||||||
auth: {
|
if (authorityType && this.currentAuthorityType !== authorityType) {
|
||||||
authority: 'https://login.microsoftonline.com/consumers',
|
this.currentAuthorityType = authorityType;
|
||||||
clientId: CLIENT_ID
|
this.msalInstance = null;
|
||||||
}
|
}
|
||||||
|
await this.getCredentials();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ensureInitialized(authorityType?: 'personal' | 'organizations'): Promise<void> {
|
||||||
|
await this.initialize(authorityType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getCredentials(): Promise<void> {
|
||||||
|
|
||||||
|
const headers: HeadersInit = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!msalInstance) {
|
const response = await fetch('/api/config', {
|
||||||
msalInstance = new PublicClientApplication(msalParams);
|
headers,
|
||||||
if (msalInstance.initialize) {
|
credentials: 'include'
|
||||||
await msalInstance.initialize();
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to fetch OneDrive credentials');
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = await response.json();
|
||||||
|
|
||||||
|
const newClientId = config.onedrive?.client_id;
|
||||||
|
const newSharepointUrl = config.onedrive?.sharepoint_url;
|
||||||
|
|
||||||
|
if (!newClientId) {
|
||||||
|
throw new Error('OneDrive configuration is incomplete');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clientId = newClientId;
|
||||||
|
this.sharepointUrl = newSharepointUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getMsalInstance(authorityType?: 'personal' | 'organizations'): Promise<PublicClientApplication> {
|
||||||
|
await this.ensureInitialized(authorityType);
|
||||||
|
|
||||||
|
if (!this.msalInstance) {
|
||||||
|
const authorityEndpoint = this.currentAuthorityType === 'organizations' ? 'common' : 'consumers';
|
||||||
|
const msalParams = {
|
||||||
|
auth: {
|
||||||
|
authority: `https://login.microsoftonline.com/${authorityEndpoint}`,
|
||||||
|
clientId: this.clientId
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.msalInstance = new PublicClientApplication(msalParams);
|
||||||
|
if (this.msalInstance.initialize) {
|
||||||
|
await this.msalInstance.initialize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return msalInstance;
|
return this.msalInstance;
|
||||||
} catch (error) {
|
}
|
||||||
throw new Error(
|
|
||||||
'MSAL initialization failed: ' + (error instanceof Error ? error.message : String(error))
|
public getAuthorityType(): 'personal' | 'organizations' {
|
||||||
);
|
return this.currentAuthorityType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSharepointUrl(): string {
|
||||||
|
return this.sharepointUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getBaseUrl(): string {
|
||||||
|
if (this.currentAuthorityType === 'organizations') {
|
||||||
|
if (!this.sharepointUrl || this.sharepointUrl === '') {
|
||||||
|
throw new Error('Sharepoint URL not configured');
|
||||||
|
}
|
||||||
|
|
||||||
|
let sharePointBaseUrl = this.sharepointUrl.replace(/^https?:\/\//, '');
|
||||||
|
sharePointBaseUrl = sharePointBaseUrl.replace(/\/$/, '');
|
||||||
|
|
||||||
|
return `https://${sharePointBaseUrl}`;
|
||||||
|
} else {
|
||||||
|
return 'https://onedrive.live.com/picker';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve OneDrive access token
|
|
||||||
async function getToken(): Promise<string> {
|
|
||||||
const authParams: PopupRequest = { scopes: ['OneDrive.ReadWrite'] };
|
|
||||||
let accessToken = '';
|
|
||||||
try {
|
|
||||||
msalInstance = await initializeMsal();
|
|
||||||
if (!msalInstance) {
|
|
||||||
throw new Error('MSAL not initialized');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Retrieve OneDrive access token
|
||||||
|
async function getToken(resource?: string, authorityType?: 'personal' | 'organizations'): Promise<string> {
|
||||||
|
const config = OneDriveConfig.getInstance();
|
||||||
|
await config.ensureInitialized(authorityType);
|
||||||
|
|
||||||
|
const currentAuthorityType = config.getAuthorityType();
|
||||||
|
|
||||||
|
const scopes = currentAuthorityType === 'organizations'
|
||||||
|
? [`${resource || config.getBaseUrl()}/.default`]
|
||||||
|
: ['OneDrive.ReadWrite'];
|
||||||
|
|
||||||
|
const authParams: PopupRequest = { scopes };
|
||||||
|
let accessToken = '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const msalInstance = await config.getMsalInstance(authorityType);
|
||||||
const resp = await msalInstance.acquireTokenSilent(authParams);
|
const resp = await msalInstance.acquireTokenSilent(authParams);
|
||||||
accessToken = resp.accessToken;
|
accessToken = resp.accessToken;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!msalInstance) {
|
const msalInstance = await config.getMsalInstance(authorityType);
|
||||||
throw new Error('MSAL not initialized');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resp = await msalInstance.loginPopup(authParams);
|
const resp = await msalInstance.loginPopup(authParams);
|
||||||
msalInstance.setActiveAccount(resp.account);
|
msalInstance.setActiveAccount(resp.account);
|
||||||
@ -88,60 +146,121 @@ async function getToken(): Promise<string> {
|
|||||||
return accessToken;
|
return accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseUrl = 'https://onedrive.live.com/picker';
|
interface PickerParams {
|
||||||
const params = {
|
sdk: string;
|
||||||
sdk: '8.0',
|
|
||||||
entry: {
|
entry: {
|
||||||
oneDrive: {
|
oneDrive: Record<string, unknown>;
|
||||||
files: {}
|
};
|
||||||
}
|
authentication: Record<string, unknown>;
|
||||||
},
|
|
||||||
authentication: {},
|
|
||||||
messaging: {
|
messaging: {
|
||||||
origin: window?.location?.origin,
|
origin: string;
|
||||||
channelId: uuidv4()
|
channelId: string;
|
||||||
},
|
};
|
||||||
typesAndSources: {
|
typesAndSources: {
|
||||||
mode: 'files',
|
mode: string;
|
||||||
pivots: {
|
pivots: Record<string, boolean>;
|
||||||
oneDrive: true,
|
};
|
||||||
recent: true
|
}
|
||||||
|
|
||||||
|
interface PickerResult {
|
||||||
|
command?: string;
|
||||||
|
items?: OneDriveFileInfo[];
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get picker parameters based on account type
|
||||||
|
function getPickerParams(): PickerParams {
|
||||||
|
const channelId = uuidv4();
|
||||||
|
const config = OneDriveConfig.getInstance();
|
||||||
|
|
||||||
|
const params: PickerParams = {
|
||||||
|
sdk: '8.0',
|
||||||
|
entry: {
|
||||||
|
oneDrive: {}
|
||||||
|
},
|
||||||
|
authentication: {},
|
||||||
|
messaging: {
|
||||||
|
origin: window?.location?.origin || '',
|
||||||
|
channelId
|
||||||
|
},
|
||||||
|
typesAndSources: {
|
||||||
|
mode: 'files',
|
||||||
|
pivots: {
|
||||||
|
oneDrive: true,
|
||||||
|
recent: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// For personal accounts, set files object in oneDrive
|
||||||
|
if (config.getAuthorityType() !== 'organizations') {
|
||||||
|
params.entry.oneDrive = { files: {} };
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OneDriveFileInfo {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
parentReference: {
|
||||||
|
driveId: string;
|
||||||
|
};
|
||||||
|
'@sharePoint.endpoint': string;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
// Download file from OneDrive
|
// Download file from OneDrive
|
||||||
async function downloadOneDriveFile(fileInfo: any): Promise<Blob> {
|
async function downloadOneDriveFile(fileInfo: OneDriveFileInfo, authorityType?: 'personal' | 'organizations'): Promise<Blob> {
|
||||||
const accessToken = await getToken();
|
const accessToken = await getToken(undefined, authorityType);
|
||||||
if (!accessToken) {
|
if (!accessToken) {
|
||||||
throw new Error('Unable to retrieve OneDrive access token.');
|
throw new Error('Unable to retrieve OneDrive access token.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The endpoint URL is provided in the file info
|
||||||
const fileInfoUrl = `${fileInfo['@sharePoint.endpoint']}/drives/${fileInfo.parentReference.driveId}/items/${fileInfo.id}`;
|
const fileInfoUrl = `${fileInfo['@sharePoint.endpoint']}/drives/${fileInfo.parentReference.driveId}/items/${fileInfo.id}`;
|
||||||
|
|
||||||
const response = await fetch(fileInfoUrl, {
|
const response = await fetch(fileInfoUrl, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Failed to fetch file information.');
|
throw new Error(`Failed to fetch file information: ${response.status} ${response.statusText}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileData = await response.json();
|
const fileData = await response.json();
|
||||||
const downloadUrl = fileData['@content.downloadUrl'];
|
const downloadUrl = fileData['@content.downloadUrl'];
|
||||||
const downloadResponse = await fetch(downloadUrl);
|
|
||||||
if (!downloadResponse.ok) {
|
if (!downloadUrl) {
|
||||||
throw new Error('Failed to download file.');
|
throw new Error('Download URL not found in file data');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const downloadResponse = await fetch(downloadUrl);
|
||||||
|
|
||||||
|
if (!downloadResponse.ok) {
|
||||||
|
throw new Error(`Failed to download file: ${downloadResponse.status} ${downloadResponse.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
return await downloadResponse.blob();
|
return await downloadResponse.blob();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open OneDrive file picker and return selected file metadata
|
// Open OneDrive file picker and return selected file metadata
|
||||||
export async function openOneDrivePicker(): Promise<any | null> {
|
export async function openOneDrivePicker(authorityType?: 'personal' | 'organizations'): Promise<PickerResult | null> {
|
||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
throw new Error('Not in browser environment');
|
throw new Error('Not in browser environment');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize OneDrive config with the specified authority type
|
||||||
|
const config = OneDriveConfig.getInstance();
|
||||||
|
await config.initialize(authorityType);
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let pickerWindow: Window | null = null;
|
let pickerWindow: Window | null = null;
|
||||||
let channelPort: MessagePort | null = null;
|
let channelPort: MessagePort | null = null;
|
||||||
|
const params = getPickerParams();
|
||||||
|
const baseUrl = config.getBaseUrl();
|
||||||
|
|
||||||
const handleWindowMessage = (event: MessageEvent) => {
|
const handleWindowMessage = (event: MessageEvent) => {
|
||||||
if (event.source !== pickerWindow) return;
|
if (event.source !== pickerWindow) return;
|
||||||
@ -166,7 +285,9 @@ export async function openOneDrivePicker(): Promise<any | null> {
|
|||||||
switch (command.command) {
|
switch (command.command) {
|
||||||
case 'authenticate': {
|
case 'authenticate': {
|
||||||
try {
|
try {
|
||||||
const newToken = await getToken();
|
// Pass the resource from the command for org accounts
|
||||||
|
const resource = config.getAuthorityType() === 'organizations' ? command.resource : undefined;
|
||||||
|
const newToken = await getToken(resource, authorityType);
|
||||||
if (newToken) {
|
if (newToken) {
|
||||||
channelPort?.postMessage({
|
channelPort?.postMessage({
|
||||||
type: 'result',
|
type: 'result',
|
||||||
@ -178,9 +299,12 @@ export async function openOneDrivePicker(): Promise<any | null> {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
channelPort?.postMessage({
|
channelPort?.postMessage({
|
||||||
result: 'error',
|
type: 'result',
|
||||||
error: { code: 'tokenError', message: 'Failed to get token' },
|
id: portData.id,
|
||||||
isExpected: true
|
data: {
|
||||||
|
result: 'error',
|
||||||
|
error: { code: 'tokenError', message: 'Failed to get token' }
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -227,7 +351,7 @@ export async function openOneDrivePicker(): Promise<any | null> {
|
|||||||
|
|
||||||
const initializePicker = async () => {
|
const initializePicker = async () => {
|
||||||
try {
|
try {
|
||||||
const authToken = await getToken();
|
const authToken = await getToken(undefined, authorityType);
|
||||||
if (!authToken) {
|
if (!authToken) {
|
||||||
return reject(new Error('Failed to acquire access token'));
|
return reject(new Error('Failed to acquire access token'));
|
||||||
}
|
}
|
||||||
@ -240,7 +364,13 @@ export async function openOneDrivePicker(): Promise<any | null> {
|
|||||||
const queryString = new URLSearchParams({
|
const queryString = new URLSearchParams({
|
||||||
filePicker: JSON.stringify(params)
|
filePicker: JSON.stringify(params)
|
||||||
});
|
});
|
||||||
const url = `${baseUrl}?${queryString.toString()}`;
|
|
||||||
|
let url = '';
|
||||||
|
if(config.getAuthorityType() === 'organizations') {
|
||||||
|
url = baseUrl + `/_layouts/15/FilePicker.aspx?${queryString}`;
|
||||||
|
} else {
|
||||||
|
url = baseUrl + `?${queryString}`;
|
||||||
|
}
|
||||||
|
|
||||||
const form = pickerWindow.document.createElement('form');
|
const form = pickerWindow.document.createElement('form');
|
||||||
form.setAttribute('action', url);
|
form.setAttribute('action', url);
|
||||||
@ -268,15 +398,15 @@ export async function openOneDrivePicker(): Promise<any | null> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pick and download file from OneDrive
|
// Pick and download file from OneDrive
|
||||||
export async function pickAndDownloadFile(): Promise<{ blob: Blob; name: string } | null> {
|
export async function pickAndDownloadFile(authorityType?: 'personal' | 'organizations'): Promise<{ blob: Blob; name: string } | null> {
|
||||||
const pickerResult = await openOneDrivePicker();
|
const pickerResult = await openOneDrivePicker(authorityType);
|
||||||
|
|
||||||
if (!pickerResult || !pickerResult.items || pickerResult.items.length === 0) {
|
if (!pickerResult || !pickerResult.items || pickerResult.items.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedFile = pickerResult.items[0];
|
const selectedFile = pickerResult.items[0];
|
||||||
const blob = await downloadOneDriveFile(selectedFile);
|
const blob = await downloadOneDriveFile(selectedFile, authorityType);
|
||||||
|
|
||||||
return { blob, name: selectedFile.name };
|
return { blob, name: selectedFile.name };
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user