Skip to content

Commit d86d7a5

Browse files
jochem25claude
andcommitted
fix: upload IFC files to models/ instead of validation/
Add category parameter to cloud upload endpoint so IFC files go to models/ (category=bim) and validation results to validation/ (category=output). This fixes cloud-saved projects not appearing in the Open dialog. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 10b7778 commit d86d7a5

6 files changed

Lines changed: 38 additions & 18 deletions

File tree

server/nextcloud_client.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -334,20 +334,25 @@ async def list_validation_files(
334334
project_name, LEGACY_OUTPUT_SUBDIR
335335
)
336336

337-
async def upload_to_validation(
338-
self, project_name: str, filename: str, content: bytes
337+
async def upload_to(
338+
self,
339+
project_name: str,
340+
filename: str,
341+
content: bytes,
342+
subdir: str = DIR_VALIDATION,
339343
) -> None:
340-
"""Upload a file to the project's validation/ directory.
344+
"""Upload a file to a project subdirectory.
341345
342-
Always writes to the new path. Creates directories as needed.
346+
Creates directories as needed.
343347
344348
Args:
345349
project_name: Name of the project folder.
346350
filename: Target filename.
347351
content: File content as bytes.
352+
subdir: Target subdirectory (default: validation/).
348353
"""
349354
safe_project = quote(project_name, safe="")
350-
path = f"{PROJECTS_ROOT}/{safe_project}/{DIR_VALIDATION}"
355+
path = f"{PROJECTS_ROOT}/{safe_project}/{subdir}"
351356
await self.ensure_directory(path)
352357

353358
url = (
@@ -364,6 +369,15 @@ async def upload_to_validation(
364369
status_code=resp.status_code,
365370
)
366371

372+
async def upload_to_validation(
373+
self, project_name: str, filename: str, content: bytes
374+
) -> None:
375+
"""Upload a file to the project's validation/ directory.
376+
377+
Convenience wrapper around upload_to().
378+
"""
379+
await self.upload_to(project_name, filename, content, DIR_VALIDATION)
380+
367381
async def download_from(
368382
self,
369383
project_name: str,

server/routers/cloud.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -289,20 +289,24 @@ async def cloud_upload_file(
289289
project: str,
290290
filename: str,
291291
file: UploadFile = File(...),
292+
category: str = Query("output", pattern="^(bim|output)$"),
292293
tenant: str | None = Query(None),
293294
):
294-
"""Upload a file to the project's validation/ directory via WebDAV.
295+
"""Upload a file to a project subdirectory via WebDAV.
295296
296-
Always writes to the new validation/ path.
297+
Args:
298+
project: Project folder name.
299+
filename: Target filename.
300+
category: 'bim' writes to models/, 'output' writes to validation/.
301+
tenant: Tenant slug (optional).
297302
"""
298303
config = _resolve_tenant(tenant)
299304
client = get_nc_client(config)
300305

306+
subdir = DIR_MODELS if category == "bim" else DIR_VALIDATION
301307
content = await file.read()
302308
try:
303-
await client.upload_to_validation(
304-
project, filename, content
305-
)
309+
await client.upload_to(project, filename, content, subdir)
306310
except Exception as exc:
307311
raise _nc_error_to_http(exc) from exc
308312

viewer/src/api/cloudApi.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,14 +141,15 @@ export async function cloudUploadFile(
141141
project: string,
142142
filename: string,
143143
blob: Blob,
144+
category: "bim" | "output" = "output",
144145
): Promise<void> {
145146
const formData = new FormData();
146147
formData.append("file", blob, filename);
147148

148149
let response: Response;
149150
try {
150151
response = await fetch(
151-
`${CLOUD_BASE}/projects/${encodeURIComponent(project)}/files/${encodeURIComponent(filename)}`,
152+
`${CLOUD_BASE}/projects/${encodeURIComponent(project)}/files/${encodeURIComponent(filename)}?category=${category}`,
152153
{ method: "PUT", body: formData },
153154
);
154155
} catch (error) {

viewer/src/components/layout/AppShell.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ export function AppShell() {
280280
const bytes = await getModelBytes(model.fileName);
281281
if (bytes) {
282282
const blob = new Blob([bytes], { type: "application/octet-stream" });
283-
await cloudUploadFn(projectSaveInfo.cloudProject!, model.fileName, blob);
283+
await cloudUploadFn(projectSaveInfo.cloudProject!, model.fileName, blob, "bim");
284284
}
285285
}
286286
markClean();

viewer/src/components/projects/SaveAsDialog.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,11 @@ export default function SaveAsDialog({
156156
const blob = new Blob([bytes], {
157157
type: "application/octet-stream",
158158
});
159-
await cloudUpload(selectedCloudProject, model.fileName, blob);
159+
await cloudUpload(selectedCloudProject, model.fileName, blob, "bim");
160160
}
161161
}
162162

163-
// Upload validation results if available
163+
// Upload validation results to validation/ directory
164164
if (validationResult) {
165165
const resultBlob = new Blob(
166166
[JSON.stringify(validationResult, null, 2)],
@@ -169,7 +169,8 @@ export default function SaveAsDialog({
169169
await cloudUpload(
170170
selectedCloudProject,
171171
"validation-result.json",
172-
resultBlob
172+
resultBlob,
173+
"output",
173174
);
174175
}
175176

viewer/src/store/slices/cloudSlice.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export interface CloudSlice {
4040
cloudLoadProjects: () => Promise<void>;
4141
cloudSelectProject: (project: string | null) => void;
4242
cloudLoadFiles: (project?: string) => Promise<void>;
43-
cloudUpload: (project: string, filename: string, blob: Blob) => Promise<boolean>;
43+
cloudUpload: (project: string, filename: string, blob: Blob, category?: "bim" | "output") => Promise<boolean>;
4444
cloudDownload: (project: string, filename: string) => Promise<Blob | null>;
4545
cloudDelete: (project: string, filename: string) => Promise<boolean>;
4646
cloudReset: () => void;
@@ -100,10 +100,10 @@ export const createCloudSlice: StateCreator<CloudSlice> = (set, get) => ({
100100
}
101101
},
102102

103-
cloudUpload: async (project: string, filename: string, blob: Blob) => {
103+
cloudUpload: async (project: string, filename: string, blob: Blob, category?: "bim" | "output") => {
104104
set({ cloudPhase: "uploading", cloudError: null });
105105
try {
106-
await apiUploadFile(project, filename, blob);
106+
await apiUploadFile(project, filename, blob, category);
107107
// Refresh file list after upload
108108
const files = await apiListFiles(project);
109109
set({ cloudFiles: files, cloudPhase: "idle" });

0 commit comments

Comments
 (0)