Compare commits

...

2 Commits

Author SHA1 Message Date
Leon Bösche
260b8b180e idle4000 2026-01-09 23:57:29 +01:00
Leon Bösche
4f67ead22d Add detailed logging for file uploads and handle upload errors in UI 2026-01-09 23:57:28 +01:00
5 changed files with 42 additions and 21 deletions

View File

@@ -24,10 +24,18 @@ class UploadBloc extends Bloc<UploadEvent, UploadState> {
for (final file in event.files) {
try {
print(
'[UploadBloc] Starting upload for ${file.name} to orgId=${event.orgId}, path=${file.path}',
);
print(
'[UploadBloc] File bytes: ${file.bytes?.length ?? 0} bytes, localPath: ${file.localPath}',
);
// Simulate upload
await _fileRepository.uploadFile(event.orgId, file);
print('[UploadBloc] Upload successful for ${file.name}');
add(UploadCompleted(file));
} catch (e) {
print('[UploadBloc] Upload failed for ${file.name}: $e');
add(UploadFailed(fileName: file.name, error: e.toString()));
}
}

View File

@@ -665,6 +665,14 @@ class _FileExplorerState extends State<FileExplorer> {
return BlocListener<UploadBloc, UploadState>(
listener: (context, uploadState) {
if (uploadState is UploadInProgress) {
// Show error if any upload failed
for (final upload in uploadState.uploads) {
if (upload.error != null && upload.error!.isNotEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Upload failed: ${upload.error}')),
);
}
}
final hasCompleted = uploadState.uploads.any((u) => u.isCompleted);
if (hasCompleted) {
final fbState = context.read<FileBrowserBloc>().state;

View File

@@ -52,18 +52,32 @@ class FileService {
// If bytes or localPath available, send multipart upload with field 'file'
final Map<String, dynamic> fields = {'path': file.path};
FormData formData;
print(
'[FileService] uploadFile: file=${file.name}, path=${file.path}, orgId=$orgId',
);
print(
'[FileService] bytes=${file.bytes?.length ?? 0}, localPath=${file.localPath}',
);
if (file.bytes != null) {
print(
'[FileService] Using bytes for upload (${file.bytes!.length} bytes)',
);
formData = FormData.fromMap({
...fields,
'file': MultipartFile.fromBytes(file.bytes!, filename: file.name),
});
} else if (file.localPath != null) {
print('[FileService] Using localPath for upload: ${file.localPath}');
formData = FormData.fromMap({
...fields,
'file': MultipartFile.fromFile(file.localPath!, filename: file.name),
});
} else {
// Fallback to metadata-only create (folders or client that can't send file content)
print(
'[FileService] No bytes or localPath; falling back to metadata-only',
);
final data = {
'name': file.name,
'path': file.path,
@@ -82,19 +96,10 @@ class FileService {
return;
}
if (orgId.isEmpty) {
await _apiClient.post(
'/user/files',
data: formData,
fromJson: (d) => null,
);
return;
}
await _apiClient.post(
'/orgs/$orgId/files',
data: formData,
fromJson: (d) => null,
);
final endpoint = orgId.isEmpty ? '/user/files' : '/orgs/$orgId/files';
print('[FileService] Uploading to endpoint: $endpoint');
await _apiClient.post(endpoint, data: formData, fromJson: (d) => null);
print('[FileService] Upload completed for ${file.name}');
}
Future<void> deleteFile(String orgId, String path) async {

Binary file not shown.

View File

@@ -968,17 +968,17 @@ func createOrgFileHandler(w http.ResponseWriter, r *http.Request, db *database.D
}
}
// Fallback: Save to disk under data/uploads/orgs/<orgId>/<parentPath>
baseDir := filepath.Join("data", "uploads", "orgs", orgID.String())
// Fallback: Save to temp directory (WebDAV should be the primary storage)
baseDir := filepath.Join("/tmp", "uploads", "orgs", orgID.String())
targetDir := filepath.Join(baseDir, parentPath)
if err = os.MkdirAll(targetDir, 0o755); err != nil {
errors.LogError(r, err, "Failed to create target dir")
errors.LogError(r, err, "Failed to create target dir in /tmp")
errors.WriteError(w, errors.CodeInternal, "Server error", http.StatusInternalServerError)
return
}
outPath := filepath.Join(targetDir, header.Filename)
if err = os.WriteFile(outPath, data, 0o644); err != nil {
errors.LogError(r, err, "Failed to write file")
errors.LogError(r, err, "Failed to write file to /tmp")
errors.WriteError(w, errors.CodeInternal, "Server error", http.StatusInternalServerError)
return
}
@@ -1148,17 +1148,17 @@ func createUserFileHandler(w http.ResponseWriter, r *http.Request, db *database.
}
}
// Fallback: write to disk
baseDir := filepath.Join("data", "uploads", "users", userID.String())
// Fallback: write to temp directory (WebDAV should be the primary storage)
baseDir := filepath.Join("/tmp", "uploads", "users", userID.String())
targetDir := filepath.Join(baseDir, parentPath)
if err = os.MkdirAll(targetDir, 0o755); err != nil {
errors.LogError(r, err, "Failed to create target dir")
errors.LogError(r, err, "Failed to create target dir in /tmp")
errors.WriteError(w, errors.CodeInternal, "Server error", http.StatusInternalServerError)
return
}
outPath := filepath.Join(targetDir, header.Filename)
if err = os.WriteFile(outPath, data, 0o644); err != nil {
errors.LogError(r, err, "Failed to create file")
errors.LogError(r, err, "Failed to write file to /tmp")
errors.WriteError(w, errors.CodeInternal, "Server error", http.StatusInternalServerError)
return
}