|
|
@@ -105,6 +105,36 @@ function getArchiveFileType(filename: string | null | undefined): string | undef
|
|
|
|
|
|
// formatDate imported from '../utils/date' - handles UTC conversion
|
|
|
|
|
|
+/**
|
|
|
+ * Open an archive file in the slicer.
|
|
|
+ * Fetches a short-lived download token, then builds a token-authenticated URL
|
|
|
+ * that bypasses auth middleware (slicer protocol handlers can't send auth headers).
|
|
|
+ */
|
|
|
+async function openInSlicerWithToken(
|
|
|
+ archiveId: number,
|
|
|
+ filename: string,
|
|
|
+ resourceType: 'file' | 'source',
|
|
|
+ slicer: SlicerType,
|
|
|
+): Promise<void> {
|
|
|
+ try {
|
|
|
+ if (resourceType === 'source') {
|
|
|
+ const { token } = await api.createSourceSlicerToken(archiveId);
|
|
|
+ const path = api.getSourceSlicerDownloadUrl(archiveId, token, filename);
|
|
|
+ openInSlicer(`${window.location.origin}${path}`, slicer);
|
|
|
+ } else {
|
|
|
+ const { token } = await api.createArchiveSlicerToken(archiveId);
|
|
|
+ const path = api.getArchiveSlicerDownloadUrl(archiveId, token, filename);
|
|
|
+ openInSlicer(`${window.location.origin}${path}`, slicer);
|
|
|
+ }
|
|
|
+ } catch {
|
|
|
+ // Fallback to direct URL (works when auth is disabled)
|
|
|
+ const path = resourceType === 'source'
|
|
|
+ ? api.getSource3mfForSlicer(archiveId, filename)
|
|
|
+ : api.getArchiveForSlicer(archiveId, filename);
|
|
|
+ openInSlicer(`${window.location.origin}${path}`, slicer);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
function ArchiveCard({
|
|
|
archive,
|
|
|
printerName,
|
|
|
@@ -337,8 +367,7 @@ function ArchiveCard({
|
|
|
icon: <ExternalLink className="w-4 h-4" />,
|
|
|
onClick: () => {
|
|
|
const filename = archive.print_name || archive.filename || 'model';
|
|
|
- const downloadUrl = `${window.location.origin}${api.getArchiveForSlicer(archive.id, filename)}`;
|
|
|
- openInSlicer(downloadUrl, preferredSlicer);
|
|
|
+ openInSlicerWithToken(archive.id, filename, 'file', preferredSlicer);
|
|
|
},
|
|
|
disabled: !archive.file_path,
|
|
|
title: !archive.file_path ? t('archives.card.noFileForReprint') : undefined,
|
|
|
@@ -349,8 +378,7 @@ function ArchiveCard({
|
|
|
icon: <ExternalLink className="w-4 h-4" />,
|
|
|
onClick: () => {
|
|
|
const filename = archive.print_name || archive.filename || 'model';
|
|
|
- const downloadUrl = `${window.location.origin}${api.getArchiveForSlicer(archive.id, filename)}`;
|
|
|
- openInSlicer(downloadUrl, preferredSlicer);
|
|
|
+ openInSlicerWithToken(archive.id, filename, 'file', preferredSlicer);
|
|
|
},
|
|
|
},
|
|
|
]),
|
|
|
@@ -738,8 +766,7 @@ function ArchiveCard({
|
|
|
e.stopPropagation();
|
|
|
// Open source 3MF in Bambu Studio - use filename in URL for slicer compatibility
|
|
|
const sourceName = (archive.print_name || archive.filename || 'source').replace(/\.gcode\.3mf$/i, '') + '_source';
|
|
|
- const downloadUrl = `${window.location.origin}${api.getSource3mfForSlicer(archive.id, sourceName)}`;
|
|
|
- openInSlicer(downloadUrl, preferredSlicer);
|
|
|
+ openInSlicerWithToken(archive.id, sourceName, 'source', preferredSlicer);
|
|
|
}}
|
|
|
title={t('archives.card.openSource3mf')}
|
|
|
>
|
|
|
@@ -1008,8 +1035,7 @@ function ArchiveCard({
|
|
|
className="min-w-0 p-1 sm:p-1.5"
|
|
|
onClick={() => {
|
|
|
const filename = archive.print_name || archive.filename || 'model';
|
|
|
- const downloadUrl = `${window.location.origin}${api.getArchiveForSlicer(archive.id, filename)}`;
|
|
|
- openInSlicer(downloadUrl, preferredSlicer);
|
|
|
+ openInSlicerWithToken(archive.id, filename, 'file', preferredSlicer);
|
|
|
}}
|
|
|
title={t('archives.card.openInBambuStudio')}
|
|
|
>
|
|
|
@@ -1024,8 +1050,7 @@ function ArchiveCard({
|
|
|
className="flex-1 min-w-0"
|
|
|
onClick={() => {
|
|
|
const filename = archive.print_name || archive.filename || 'model';
|
|
|
- const downloadUrl = `${window.location.origin}${api.getArchiveForSlicer(archive.id, filename)}`;
|
|
|
- openInSlicer(downloadUrl, preferredSlicer);
|
|
|
+ openInSlicerWithToken(archive.id, filename, 'file', preferredSlicer);
|
|
|
}}
|
|
|
title={t('archives.card.openInBambuStudioToSlice')}
|
|
|
>
|
|
|
@@ -1546,8 +1571,7 @@ function ArchiveListRow({
|
|
|
icon: <ExternalLink className="w-4 h-4" />,
|
|
|
onClick: () => {
|
|
|
const filename = archive.print_name || archive.filename || 'model';
|
|
|
- const downloadUrl = `${window.location.origin}${api.getArchiveForSlicer(archive.id, filename)}`;
|
|
|
- openInSlicer(downloadUrl, preferredSlicer);
|
|
|
+ openInSlicerWithToken(archive.id, filename, 'file', preferredSlicer);
|
|
|
},
|
|
|
disabled: !archive.file_path,
|
|
|
title: !archive.file_path ? t('archives.card.noFileForReprint') : undefined,
|
|
|
@@ -1558,8 +1582,7 @@ function ArchiveListRow({
|
|
|
icon: <ExternalLink className="w-4 h-4" />,
|
|
|
onClick: () => {
|
|
|
const filename = archive.print_name || archive.filename || 'model';
|
|
|
- const downloadUrl = `${window.location.origin}${api.getArchiveForSlicer(archive.id, filename)}`;
|
|
|
- openInSlicer(downloadUrl, preferredSlicer);
|
|
|
+ openInSlicerWithToken(archive.id, filename, 'file', preferredSlicer);
|
|
|
},
|
|
|
},
|
|
|
]),
|
|
|
@@ -1900,8 +1923,7 @@ function ArchiveListRow({
|
|
|
size="sm"
|
|
|
onClick={() => {
|
|
|
const filename = archive.print_name || archive.filename || 'model';
|
|
|
- const downloadUrl = `${window.location.origin}${api.getArchiveForSlicer(archive.id, filename)}`;
|
|
|
- openInSlicer(downloadUrl, preferredSlicer);
|
|
|
+ openInSlicerWithToken(archive.id, filename, 'file', preferredSlicer);
|
|
|
}}
|
|
|
title={t('archives.card.openInBambuStudio')}
|
|
|
>
|