Merge pull request #1416 from benhalverson/fix/open-in-slicer

Fix library Open in Slicer URLs for extensionless filenames
This commit is contained in:
Ben Halverson
2026-05-18 11:59:34 +02:00
committed by GitHub
parent 3b552094a7
commit feb44a9dc1
2 changed files with 22 additions and 1 deletions
+16
View File
@@ -198,6 +198,22 @@ describe('API Client Auth Header', () => {
});
});
describe('Slicer download URLs', () => {
it('keeps library slicer URLs ending in .3mf when the display name has no extension', () => {
const path = api.getLibrarySlicerDownloadUrl(12, 'token-abc', 'Mecha Mewtwo No AMS Multi Color Parted Statue');
expect(path).toBe(
'/api/v1/library/files/12/dl/token-abc/Mecha%20Mewtwo%20No%20AMS%20Multi%20Color%20Parted%20Statue.3mf'
);
});
it('sanitizes library slicer URL filenames before encoding them', () => {
const path = api.getLibrarySlicerDownloadUrl(12, 'token-abc', 'folder/model?bad#name.3mf');
expect(path).toBe('/api/v1/library/files/12/dl/token-abc/folder_model_bad_name.3mf');
});
});
describe('FormData requests include auth header', () => {
it('importProjectFile includes Authorization header', async () => {
// Mock fetch directly for FormData requests (MSW can be flaky with multipart in some environments)
+6 -1
View File
@@ -85,6 +85,11 @@ function parseContentDispositionFilename(header: string | null): string | null {
return standardMatch?.[1] || null;
}
function buildSlicerUrlFilename(filename: string): string {
const safe = filename.replace(/[/\\?#]/g, '_');
return safe.toLowerCase().endsWith('.3mf') ? safe : `${safe}.3mf`;
}
async function request<T>(
endpoint: string,
options: RequestInit = {}
@@ -5437,7 +5442,7 @@ export const api = {
createLibrarySlicerToken: (fileId: number) =>
request<{ token: string }>(`/library/files/${fileId}/slicer-token`, { method: 'POST' }),
getLibrarySlicerDownloadUrl: (fileId: number, token: string, filename: string) =>
`${API_BASE}/library/files/${fileId}/dl/${token}/${encodeURIComponent(filename)}`,
`${API_BASE}/library/files/${fileId}/dl/${token}/${encodeURIComponent(buildSlicerUrlFilename(filename))}`,
downloadLibraryFile: async (id: number, filename?: string): Promise<void> => {
const headers: Record<string, string> = {};
if (authToken) {