mirror of
https://github.com/open-webui/open-webui.git
synced 2026-08-28 05:27:35 +00:00
Merge 6392e33dce into b3591e60b1
This commit is contained in:
commit
02660c5678
2 changed files with 161 additions and 17 deletions
|
|
@ -16,7 +16,7 @@
|
||||||
import { onMount, tick, getContext, createEventDispatcher } from 'svelte';
|
import { onMount, tick, getContext, createEventDispatcher } from 'svelte';
|
||||||
|
|
||||||
import { createPicker, getAuthToken } from '$lib/utils/google-drive-picker';
|
import { createPicker, getAuthToken } from '$lib/utils/google-drive-picker';
|
||||||
import { pickAndDownloadFile } from '$lib/utils/onedrive-file-picker';
|
import { pickAndDownloadFiles } from '$lib/utils/onedrive-file-picker';
|
||||||
import { KokoroWorker } from '$lib/workers/KokoroWorker';
|
import { KokoroWorker } from '$lib/workers/KokoroWorker';
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
@ -2216,17 +2216,47 @@
|
||||||
}}
|
}}
|
||||||
uploadOneDriveHandler={async (authorityType) => {
|
uploadOneDriveHandler={async (authorityType) => {
|
||||||
try {
|
try {
|
||||||
const fileData = await pickAndDownloadFile(authorityType);
|
const maxFileCount = $config?.file?.max_count;
|
||||||
if (fileData) {
|
const filesData = await pickAndDownloadFiles(
|
||||||
const file = new File([fileData.blob], fileData.name, {
|
authorityType,
|
||||||
type: fileData.blob.type || 'application/octet-stream'
|
maxFileCount
|
||||||
});
|
);
|
||||||
await uploadFileHandler(file);
|
if (filesData && filesData.length > 0) {
|
||||||
|
if (filesData.length > 1) {
|
||||||
|
toast.success(
|
||||||
|
$i18n.t('Uploading {{count}} files from OneDrive...', {
|
||||||
|
count: filesData.length
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const fileData of filesData) {
|
||||||
|
const file = new File([fileData.blob], fileData.name, {
|
||||||
|
type: fileData.blob.type || 'application/octet-stream'
|
||||||
|
});
|
||||||
|
await uploadFileHandler(file);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('No file was selected from OneDrive');
|
console.log('No files were selected from OneDrive');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('OneDrive Error:', error);
|
console.error('OneDrive Error:', error);
|
||||||
|
// Handle specific max file count error
|
||||||
|
if (error.message?.startsWith('MAX_FILE_COUNT_EXCEEDED:')) {
|
||||||
|
const [, count, max] = error.message.split(':');
|
||||||
|
toast.error(
|
||||||
|
$i18n.t('Selected items contain {{count}} files, but maximum is {{max}}.', {
|
||||||
|
count,
|
||||||
|
max
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
toast.error(
|
||||||
|
$i18n.t('OneDrive Error: {{error}}', {
|
||||||
|
error: error.message
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
{onUpload}
|
{onUpload}
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,9 @@ interface PickerParams {
|
||||||
search: {
|
search: {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
};
|
};
|
||||||
|
selection?: {
|
||||||
|
mode?: 'single' | 'multiple' | 'pick';
|
||||||
|
};
|
||||||
typesAndSources: {
|
typesAndSources: {
|
||||||
mode: string;
|
mode: string;
|
||||||
pivots: Record<string, boolean>;
|
pivots: Record<string, boolean>;
|
||||||
|
|
@ -211,12 +214,18 @@ function getPickerParams(): PickerParams {
|
||||||
search: {
|
search: {
|
||||||
enabled: true
|
enabled: true
|
||||||
},
|
},
|
||||||
|
selection: {
|
||||||
|
mode: 'multiple'
|
||||||
|
},
|
||||||
typesAndSources: {
|
typesAndSources: {
|
||||||
mode: 'files',
|
mode: 'all',
|
||||||
pivots: {
|
pivots: {
|
||||||
oneDrive: true,
|
oneDrive: true,
|
||||||
recent: true,
|
recent: true,
|
||||||
myOrganization: config.getAuthorityType() === 'organizations'
|
shared: true,
|
||||||
|
sharedLibraries: true,
|
||||||
|
myOrganization: config.getAuthorityType() === 'organizations',
|
||||||
|
site: config.getAuthorityType() === 'organizations'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -236,10 +245,66 @@ interface OneDriveFileInfo {
|
||||||
driveId: string;
|
driveId: string;
|
||||||
};
|
};
|
||||||
'@sharePoint.endpoint': string;
|
'@sharePoint.endpoint': string;
|
||||||
|
folder?: {
|
||||||
|
childCount?: number;
|
||||||
|
};
|
||||||
|
file?: {
|
||||||
|
mimeType?: string;
|
||||||
|
};
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// List all items in a folder recursively
|
||||||
|
async function listFolderContentsRecursive(
|
||||||
|
folderInfo: OneDriveFileInfo,
|
||||||
|
authorityType?: 'personal' | 'organizations',
|
||||||
|
currentPath: string = ''
|
||||||
|
): Promise<OneDriveFileInfo[]> {
|
||||||
|
const accessToken = await getToken(undefined, authorityType);
|
||||||
|
if (!accessToken) {
|
||||||
|
throw new Error('Unable to retrieve OneDrive access token.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const childrenUrl = `${folderInfo['@sharePoint.endpoint']}/drives/${folderInfo.parentReference.driveId}/items/${folderInfo.id}/children`;
|
||||||
|
|
||||||
|
const response = await fetch(childrenUrl, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to list folder contents: ${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const items: OneDriveFileInfo[] = data.value || [];
|
||||||
|
|
||||||
|
const allFiles: OneDriveFileInfo[] = [];
|
||||||
|
|
||||||
|
// Process all items
|
||||||
|
for (const item of items) {
|
||||||
|
// Inherit the @sharePoint.endpoint from parent folder if not present
|
||||||
|
if (!item['@sharePoint.endpoint']) {
|
||||||
|
item['@sharePoint.endpoint'] = folderInfo['@sharePoint.endpoint'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.folder) {
|
||||||
|
// Recursively get files from subfolder
|
||||||
|
const subPath = currentPath ? `${currentPath}/${item.name}` : item.name;
|
||||||
|
const subfolderFiles = await listFolderContentsRecursive(item, authorityType, subPath);
|
||||||
|
allFiles.push(...subfolderFiles);
|
||||||
|
} else if (item.file) {
|
||||||
|
// It's a file, add it to the list
|
||||||
|
allFiles.push(item);
|
||||||
|
}
|
||||||
|
// Skip items that are neither files nor folders (e.g., packages, OneNote notebooks)
|
||||||
|
}
|
||||||
|
|
||||||
|
return allFiles;
|
||||||
|
}
|
||||||
|
|
||||||
// Download file from OneDrive
|
// Download file from OneDrive
|
||||||
async function downloadOneDriveFile(
|
async function downloadOneDriveFile(
|
||||||
fileInfo: OneDriveFileInfo,
|
fileInfo: OneDriveFileInfo,
|
||||||
|
|
@ -435,20 +500,69 @@ export async function openOneDrivePicker(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pick and download file from OneDrive
|
// Pick and download multiple files from OneDrive (with folder support)
|
||||||
export async function pickAndDownloadFile(
|
export async function pickAndDownloadFiles(
|
||||||
authorityType?: 'personal' | 'organizations'
|
authorityType?: 'personal' | 'organizations',
|
||||||
): Promise<{ blob: Blob; name: string } | null> {
|
maxFileCount?: number
|
||||||
|
): Promise<{ blob: Blob; name: string }[] | null> {
|
||||||
const pickerResult = await openOneDrivePicker(authorityType);
|
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 allFiles: OneDriveFileInfo[] = [];
|
||||||
const blob = await downloadOneDriveFile(selectedFile, authorityType);
|
|
||||||
|
|
||||||
return { blob, name: selectedFile.name };
|
// First, expand folders to get all files
|
||||||
|
for (const item of pickerResult.items) {
|
||||||
|
if (item.folder) {
|
||||||
|
// It's a folder, get all files recursively
|
||||||
|
const folderFiles = await listFolderContentsRecursive(item, authorityType, item.name);
|
||||||
|
allFiles.push(...folderFiles);
|
||||||
|
} else {
|
||||||
|
// It's a file
|
||||||
|
allFiles.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any files were actually found
|
||||||
|
if (allFiles.length === 0) {
|
||||||
|
throw new Error('No files found in the selected items.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the number of files exceeds the configured maximum
|
||||||
|
if (maxFileCount && allFiles.length > maxFileCount) {
|
||||||
|
throw new Error(
|
||||||
|
`MAX_FILE_COUNT_EXCEEDED:${allFiles.length}:${maxFileCount}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download all files with error handling
|
||||||
|
const downloadPromises = allFiles.map(async (fileInfo) => {
|
||||||
|
const blob = await downloadOneDriveFile(fileInfo, authorityType);
|
||||||
|
return {
|
||||||
|
blob,
|
||||||
|
name: fileInfo.name
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const results = await Promise.allSettled(downloadPromises);
|
||||||
|
const successful = results
|
||||||
|
.filter((r): r is PromiseFulfilledResult<{ blob: Blob; name: string }> => r.status === 'fulfilled')
|
||||||
|
.map((r) => r.value);
|
||||||
|
|
||||||
|
const failed = results.filter((r) => r.status === 'rejected');
|
||||||
|
|
||||||
|
if (failed.length > 0) {
|
||||||
|
console.warn(`Failed to download ${failed.length} out of ${allFiles.length} files from OneDrive`);
|
||||||
|
// If all downloads failed, throw error
|
||||||
|
if (successful.length === 0) {
|
||||||
|
throw new Error('Failed to download any files from OneDrive');
|
||||||
|
}
|
||||||
|
// If some succeeded, log warning but continue with successful ones
|
||||||
|
}
|
||||||
|
|
||||||
|
return successful.length > 0 ? successful : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { downloadOneDriveFile };
|
export { downloadOneDriveFile };
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue