merge: personal user filesystem minio integration

This commit is contained in:
2026-06-03 16:32:29 +08:00
56 changed files with 4780 additions and 115 deletions

View File

@ -1425,3 +1425,112 @@ export async function createWorkspaceDir(path: string): Promise<WorkspaceItem> {
method: 'POST',
});
}
// ---------------------------------------------------------------------------
// User File System
// ---------------------------------------------------------------------------
export interface UserFileItem {
name: string;
path: string;
type: 'file' | 'directory';
size: number | null;
content_type?: string | null;
modified?: string | null;
}
export interface UserFileBrowseResult {
path: string;
items: UserFileItem[];
}
export interface UserFileContent {
name: string;
path: string;
size: number;
content_type: string;
modified: string | null;
is_binary: boolean;
is_truncated: boolean;
content: string | null;
}
export interface UserFilesStatus {
configured: boolean;
storage_mode: string;
roots: string[];
workspace_visible: boolean;
}
export async function getUserFilesStatus(): Promise<UserFilesStatus> {
return fetchJSON('/api/user-files/status');
}
export async function browseUserFiles(path: string = ''): Promise<UserFileBrowseResult> {
const params = path ? `?path=${encodeURIComponent(path)}` : '';
return fetchJSON(`/api/user-files/browse${params}`);
}
export async function getUserFile(path: string): Promise<UserFileContent> {
return fetchJSON(`/api/user-files/preview?path=${encodeURIComponent(path)}`);
}
export function getUserFileDownloadUrl(path: string): string {
return buildApiUrl(`/api/user-files/download?path=${encodeURIComponent(path)}`);
}
export async function uploadUserFile(
file: File,
dirPath: string = 'uploads',
onProgress?: (percent: number) => void
): Promise<UserFileItem> {
const locale = getCurrentAppLocale();
const formData = new FormData();
formData.append('file', file);
formData.append('path', dirPath);
return new Promise<UserFileItem>((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', buildApiUrl('/api/user-files/upload'));
const token = getAccessToken();
if (token) {
xhr.setRequestHeader('Authorization', `Bearer ${token}`);
}
xhr.upload.onprogress = (e) => {
if (e.lengthComputable && onProgress) {
onProgress(Math.round((e.loaded / e.total) * 100));
}
};
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText));
} else {
let detail = '';
try {
const data = JSON.parse(xhr.responseText);
detail = typeof data?.detail === 'string' ? data.detail : '';
} catch {
detail = '';
}
reject(new Error(detail || `${pickAppText(locale, '上传失败', 'Upload failed')}: ${xhr.status}`));
}
};
xhr.onerror = () => reject(new Error(pickAppText(locale, '上传失败', 'Upload failed')));
xhr.send(formData);
});
}
export async function deleteUserFile(path: string): Promise<void> {
await fetchJSON(`/api/user-files/delete?path=${encodeURIComponent(path)}`, {
method: 'DELETE',
});
}
export async function createUserFileDir(path: string): Promise<UserFileItem> {
return fetchJSON(`/api/user-files/mkdir?path=${encodeURIComponent(path)}`, {
method: 'POST',
});
}