diff --git a/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.html b/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.html
index 685d9f3cf..2f2155412 100644
--- a/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.html
+++ b/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.html
@@ -51,14 +51,39 @@
+ @if (bundle.status === statuses.Failed && bundle.last_error) {
+
+
+ @if (bundle.last_error.timestamp) {
+
+ {{ bundle.last_error.timestamp | date: 'short' }}
+
+ }
+ {{ bundle.last_error.exception_type || ($localize`Unknown error`) }}
+ @if (bundle.last_error.message) {
+ {{ bundle.last_error.message }}
+ }
+
+ }
@if (bundle.status === statuses.Processing || bundle.status === statuses.Pending) {
}
- {{ statusLabel(bundle.status) }}
+ @if (bundle.status !== statuses.Failed) {
+ {{ statusLabel(bundle.status) }}
+ }
- @if (bundle.last_error && bundle.status === statuses.Failed) {
- {{ bundle.last_error }}
- }
|
@if (bundle.size_bytes !== undefined && bundle.size_bytes !== null) {
diff --git a/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.scss b/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.scss
new file mode 100644
index 000000000..c8ffc4d5d
--- /dev/null
+++ b/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.scss
@@ -0,0 +1,4 @@
+:host ::ng-deep .popover {
+ min-width: 300px;
+ max-width: 400px;
+ }
diff --git a/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.spec.ts b/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.spec.ts
index 1784f6ff6..113cd65a3 100644
--- a/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.spec.ts
+++ b/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.spec.ts
@@ -81,6 +81,7 @@ describe('ShareLinkBundleManageDialogComponent', () => {
documents: [1],
status: ShareLinkBundleStatus.Pending,
file_version: FileVersion.Archive,
+ last_error: undefined,
...overrides,
}) as ShareLinkBundleSummary
diff --git a/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.ts b/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.ts
index fdc9f0126..6eef144f9 100644
--- a/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.ts
+++ b/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.ts
@@ -1,7 +1,7 @@
import { Clipboard } from '@angular/cdk/clipboard'
import { CommonModule } from '@angular/common'
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
-import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
+import { NgbActiveModal, NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { Subject, catchError, of, switchMap, takeUntil, timer } from 'rxjs'
import { FileVersion } from 'src/app/data/share-link'
@@ -21,9 +21,11 @@ import { ConfirmButtonComponent } from '../confirm-button/confirm-button.compone
@Component({
selector: 'pngx-share-link-bundle-manage-dialog',
templateUrl: './share-link-bundle-manage-dialog.component.html',
+ styleUrls: ['./share-link-bundle-manage-dialog.component.scss'],
imports: [
ConfirmButtonComponent,
CommonModule,
+ NgbPopoverModule,
NgxBootstrapIconsModule,
FileSizePipe,
],
diff --git a/src-ui/src/app/data/share-link-bundle.ts b/src-ui/src/app/data/share-link-bundle.ts
index cdabd1d15..fe6134997 100644
--- a/src-ui/src/app/data/share-link-bundle.ts
+++ b/src-ui/src/app/data/share-link-bundle.ts
@@ -7,6 +7,13 @@ export enum ShareLinkBundleStatus {
Failed = 'failed',
}
+export type ShareLinkBundleError = {
+ bundle_id: number
+ message?: string
+ exception_type?: string
+ timestamp?: string
+}
+
export interface ShareLinkBundleSummary {
id: number
slug: string
@@ -18,7 +25,7 @@ export interface ShareLinkBundleSummary {
status: ShareLinkBundleStatus
built_at?: string
size_bytes?: number
- last_error?: string
+ last_error?: ShareLinkBundleError
}
export interface ShareLinkBundleCreatePayload {
diff --git a/src/documents/migrations/0007_sharelinkbundle.py b/src/documents/migrations/0007_sharelinkbundle.py
index 7216a09dd..7efc197a4 100644
--- a/src/documents/migrations/0007_sharelinkbundle.py
+++ b/src/documents/migrations/0007_sharelinkbundle.py
@@ -120,8 +120,10 @@ class Migration(migrations.Migration):
),
(
"last_error",
- models.TextField(
+ models.JSONField(
blank=True,
+ null=True,
+ default=None,
verbose_name="last error",
),
),
diff --git a/src/documents/models.py b/src/documents/models.py
index bb4af0b32..a9f4f7a72 100644
--- a/src/documents/models.py
+++ b/src/documents/models.py
@@ -817,9 +817,11 @@ class ShareLinkBundle(SoftDeleteModel):
null=True,
)
- last_error = models.TextField(
+ last_error = models.JSONField(
_("last error"),
blank=True,
+ null=True,
+ default=None,
)
file_path = models.CharField(
diff --git a/src/documents/tasks.py b/src/documents/tasks.py
index e2ddf4c35..c5c8146de 100644
--- a/src/documents/tasks.py
+++ b/src/documents/tasks.py
@@ -647,7 +647,7 @@ def build_share_link_bundle(bundle_id: int):
bundle.remove_file()
bundle.status = ShareLinkBundle.Status.PROCESSING
- bundle.last_error = ""
+ bundle.last_error = None
bundle.size_bytes = None
bundle.built_at = None
bundle.file_path = ""
@@ -695,7 +695,7 @@ def build_share_link_bundle(bundle_id: int):
bundle.size_bytes = final_path.stat().st_size
bundle.status = ShareLinkBundle.Status.READY
bundle.built_at = timezone.now()
- bundle.last_error = ""
+ bundle.last_error = None
bundle.save(
update_fields=[
"file_path",
@@ -713,7 +713,12 @@ def build_share_link_bundle(bundle_id: int):
exc,
)
bundle.status = ShareLinkBundle.Status.FAILED
- bundle.last_error = str(exc)
+ bundle.last_error = {
+ "bundle_id": bundle_id,
+ "exception_type": exc.__class__.__name__,
+ "message": str(exc),
+ "timestamp": timezone.now().isoformat(),
+ }
bundle.save(update_fields=["status", "last_error"])
try:
temp_zip_path.unlink()
diff --git a/src/documents/tests/test_share_link_bundles.py b/src/documents/tests/test_share_link_bundles.py
index 507097276..62b815ec5 100644
--- a/src/documents/tests/test_share_link_bundles.py
+++ b/src/documents/tests/test_share_link_bundles.py
@@ -81,7 +81,7 @@ class ShareLinkBundleAPITests(DirectoriesMixin, APITestCase):
status=ShareLinkBundle.Status.FAILED,
)
bundle.documents.set([self.document])
- bundle.last_error = "Something went wrong"
+ bundle.last_error = {"message": "Something went wrong"}
bundle.size_bytes = 100
bundle.file_path = "path/to/file.zip"
bundle.save()
@@ -91,7 +91,7 @@ class ShareLinkBundleAPITests(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
bundle.refresh_from_db()
self.assertEqual(bundle.status, ShareLinkBundle.Status.PENDING)
- self.assertEqual(bundle.last_error, "")
+ self.assertIsNone(bundle.last_error)
self.assertIsNone(bundle.size_bytes)
self.assertEqual(bundle.file_path, "")
delay_mock.assert_called_once_with(bundle.pk)
@@ -172,7 +172,7 @@ class ShareLinkBundleAPITests(DirectoriesMixin, APITestCase):
file_version=ShareLink.FileVersion.ARCHIVE,
status=ShareLinkBundle.Status.FAILED,
file_path=str(bundle_path.relative_to(settings.MEDIA_ROOT)),
- last_error="Boom",
+ last_error={"message": "Boom"},
size_bytes=10,
)
bundle.documents.set([self.document])
@@ -183,7 +183,7 @@ class ShareLinkBundleAPITests(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, status.HTTP_503_SERVICE_UNAVAILABLE)
bundle.refresh_from_db()
self.assertEqual(bundle.status, ShareLinkBundle.Status.PENDING)
- self.assertEqual(bundle.last_error, "")
+ self.assertIsNone(bundle.last_error)
self.assertIsNone(bundle.size_bytes)
self.assertEqual(bundle.file_path, "")
delay_mock.assert_called_once_with(bundle.pk)
@@ -335,7 +335,7 @@ class ShareLinkBundleBuildTaskTests(DirectoriesMixin, APITestCase):
bundle.refresh_from_db()
self.assertEqual(bundle.status, ShareLinkBundle.Status.READY)
- self.assertEqual(bundle.last_error, "")
+ self.assertIsNone(bundle.last_error)
self.assertIsNotNone(bundle.built_at)
self.assertGreater(bundle.size_bytes or 0, 0)
final_path = bundle.absolute_file_path
@@ -404,7 +404,9 @@ class ShareLinkBundleBuildTaskTests(DirectoriesMixin, APITestCase):
bundle.refresh_from_db()
self.assertEqual(bundle.status, ShareLinkBundle.Status.FAILED)
- self.assertEqual(bundle.last_error, "zip failure")
+ self.assertIsInstance(bundle.last_error, dict)
+ self.assertEqual(bundle.last_error.get("message"), "zip failure")
+ self.assertEqual(bundle.last_error.get("exception_type"), "RuntimeError")
scratch_zips = list(Path(settings.SCRATCH_DIR).glob("*.zip"))
self.assertTrue(scratch_zips)
for path in scratch_zips:
diff --git a/src/documents/views.py b/src/documents/views.py
index 735028077..b5ba2bf31 100644
--- a/src/documents/views.py
+++ b/src/documents/views.py
@@ -2865,7 +2865,7 @@ class ShareLinkBundleViewSet(ModelViewSet, PassUserMixin):
)
bundle.remove_file()
bundle.status = ShareLinkBundle.Status.PENDING
- bundle.last_error = ""
+ bundle.last_error = None
bundle.size_bytes = None
bundle.built_at = None
bundle.file_path = ""
@@ -2898,7 +2898,7 @@ class ShareLinkBundleViewSet(ModelViewSet, PassUserMixin):
)
bundle.remove_file()
bundle.status = ShareLinkBundle.Status.PENDING
- bundle.last_error = ""
+ bundle.last_error = None
bundle.size_bytes = None
bundle.built_at = None
bundle.file_path = ""
@@ -2958,7 +2958,7 @@ class SharedLinkView(View):
if bundle.status == ShareLinkBundle.Status.FAILED:
bundle.remove_file()
bundle.status = ShareLinkBundle.Status.PENDING
- bundle.last_error = ""
+ bundle.last_error = None
bundle.size_bytes = None
bundle.built_at = None
bundle.file_path = ""
@@ -2982,7 +2982,7 @@ class SharedLinkView(View):
file_path = bundle.absolute_file_path
if file_path is None or not file_path.exists():
bundle.status = ShareLinkBundle.Status.PENDING
- bundle.last_error = ""
+ bundle.last_error = None
bundle.size_bytes = None
bundle.built_at = None
bundle.file_path = ""
|