diff --git a/apps/desktop/src/app/history/history.component.html b/apps/desktop/src/app/history/history.component.html
index 2a0a198f9..bddf4d502 100644
--- a/apps/desktop/src/app/history/history.component.html
+++ b/apps/desktop/src/app/history/history.component.html
@@ -14,15 +14,13 @@
{{ 'history.title' | translate }}
{{ 'history.back' | translate }}
-
+
{{ 'history.date' | translate }}
{{ 'history.source' | translate }}
{{ 'upload.steps.destination' | translate }}
{{ 'history.knowledge_box' | translate }}
-
+
{{ 'history.data' | translate }}
@@ -30,14 +28,19 @@ {{ 'history.title' | translate }}
{{ upload.from }}
{{ upload.to }}
{{ upload.kbSlug }}
-
+
+
{{ upload.total }}
diff --git a/apps/desktop/src/app/history/history.component.ts b/apps/desktop/src/app/history/history.component.ts
index 1968b223c..cd7a9ad10 100644
--- a/apps/desktop/src/app/history/history.component.ts
+++ b/apps/desktop/src/app/history/history.component.ts
@@ -35,6 +35,10 @@ export class HistoryComponent {
progress: (100 * sync.files.filter((f) => f.status === FileStatus.UPLOADED).length) / sync.files.length,
started: sync.started,
completed: sync.completed,
+ errors: sync.files
+ .filter((f) => f.status === FileStatus.ERROR)
+ .map((f) => f.error || 'Unknown error')
+ .join(' – '),
})),
),
);
diff --git a/apps/desktop/src/app/history/history.module.ts b/apps/desktop/src/app/history/history.module.ts
index 503428eb0..4e81e9b18 100644
--- a/apps/desktop/src/app/history/history.module.ts
+++ b/apps/desktop/src/app/history/history.module.ts
@@ -5,7 +5,7 @@ import { ProgressBarModule } from '@flaps/common';
import { TranslateModule } from '@ngx-translate/core';
import { HistoryComponent } from './history.component';
-import { PaButtonModule, PaIconModule, PaTableModule } from '@guillotinaweb/pastanaga-angular';
+import { PaButtonModule, PaIconModule, PaTableModule, PaTooltipModule } from '@guillotinaweb/pastanaga-angular';
import { BackButtonComponent } from '@nuclia/sistema';
@NgModule({
@@ -18,6 +18,7 @@ import { BackButtonComponent } from '@nuclia/sistema';
PaButtonModule,
PaIconModule,
PaTableModule,
+ PaTooltipModule,
],
exports: [],
declarations: [HistoryComponent],
diff --git a/apps/desktop/src/app/sync/destinations/nuclia-cloud.ts b/apps/desktop/src/app/sync/destinations/nuclia-cloud.ts
index c791b8350..311b589d4 100644
--- a/apps/desktop/src/app/sync/destinations/nuclia-cloud.ts
+++ b/apps/desktop/src/app/sync/destinations/nuclia-cloud.ts
@@ -1,5 +1,5 @@
-import { INuclia, Nuclia, NucliaOptions, WritableKnowledgeBox } from '@nuclia/core';
-import { catchError, from, map, Observable, of, switchMap } from 'rxjs';
+import { Account, INuclia, Nuclia, NucliaOptions, WritableKnowledgeBox } from '@nuclia/core';
+import { catchError, from, map, Observable, of, switchMap, tap } from 'rxjs';
import {
ConnectorParameters,
ConnectorSettings,
@@ -25,7 +25,8 @@ export const NucliaCloudKB: DestinationConnectorDefinition = {
class NucliaCloudKBImpl implements IDestinationConnector {
nuclia: INuclia;
- kb?: WritableKnowledgeBox;
+ kbs: { [slug: string]: WritableKnowledgeBox } = {};
+ account?: Account;
constructor(nuclia: INuclia) {
this.nuclia = nuclia;
@@ -56,10 +57,18 @@ class NucliaCloudKBImpl implements IDestinationConnector {
): Observable {
if (params && params['kb'] && data.blob) {
const blob = data.blob;
- const kb$ = this.kb
- ? of(this.kb)
- : this.nuclia.db.getKnowledgeBox(localStorage.getItem(ACCOUNT_KEY) || '', params['kb']);
- return kb$.pipe(
+ const mimetype = lookup(filename) || 'application/octet-stream';
+ return this.getAccount().pipe(
+ tap((account) => {
+ const applicableLimit = this.isMedia(mimetype)
+ ? account.limits.upload.upload_limit_max_media_file_size
+ : account.limits.upload.upload_limit_max_non_media_file_size;
+ if (blob.size > applicableLimit) {
+ console.error(`File too large. Size=${blob.size}, limit=${applicableLimit}`);
+ throw new Error(`File "${filename}" is too large.`);
+ }
+ }),
+ switchMap(() => this.getKb(params['kb'])),
switchMap((kb) =>
from(sha256(originalId)).pipe(
switchMap((slug) =>
@@ -77,7 +86,7 @@ class NucliaCloudKBImpl implements IDestinationConnector {
),
switchMap((resource) => {
return resource.upload('file', new File([blob], filename), false, {
- contentType: lookup(filename) || 'application/octet-stream',
+ contentType: mimetype,
});
}),
),
@@ -95,10 +104,7 @@ class NucliaCloudKBImpl implements IDestinationConnector {
data: { uri: string; extra_headers: { [key: string]: string } },
): Observable {
if (params && params['kb']) {
- const kb$ = this.kb
- ? of(this.kb)
- : this.nuclia.db.getKnowledgeBox(localStorage.getItem(ACCOUNT_KEY) || '', params['kb']);
- return kb$.pipe(
+ return this.getKb(params['kb']).pipe(
switchMap((kb) => kb.createResource({ title: filename, files: { [filename]: { file: data } } })),
map(() => undefined),
);
@@ -128,4 +134,28 @@ class NucliaCloudKBImpl implements IDestinationConnector {
})),
);
}
+
+ private getKb(slug: string): Observable {
+ if (!this.kbs[slug]) {
+ return this.nuclia.db
+ .getKnowledgeBox(localStorage.getItem(ACCOUNT_KEY) || '', slug)
+ .pipe(tap((kb) => (this.kbs[slug] = kb)));
+ } else {
+ return of(this.kbs[slug]);
+ }
+ }
+
+ private getAccount(): Observable {
+ if (!this.account) {
+ return this.nuclia.db
+ .getAccount(localStorage.getItem(ACCOUNT_KEY) || '')
+ .pipe(tap((account) => (this.account = account)));
+ } else {
+ return of(this.account);
+ }
+ }
+
+ private isMedia(mimetype: string): boolean {
+ return mimetype.startsWith('image/') || mimetype.startsWith('video/') || mimetype.startsWith('audio/');
+ }
}
diff --git a/apps/desktop/src/app/sync/models.ts b/apps/desktop/src/app/sync/models.ts
index 90b130e27..a6d95c6c2 100644
--- a/apps/desktop/src/app/sync/models.ts
+++ b/apps/desktop/src/app/sync/models.ts
@@ -35,6 +35,7 @@ export enum FileStatus {
PENDING = 'PENDING',
PROCESSING = 'PROCESSING',
UPLOADED = 'UPLOADED',
+ ERROR = 'ERROR',
}
export interface SyncItem {
@@ -43,6 +44,7 @@ export interface SyncItem {
originalId: string;
metadata: { [key: string]: string };
status: FileStatus;
+ error?: string;
}
export interface SearchResults {
diff --git a/apps/desktop/src/app/sync/sync.service.ts b/apps/desktop/src/app/sync/sync.service.ts
index 814e83c92..6783dacdf 100644
--- a/apps/desktop/src/app/sync/sync.service.ts
+++ b/apps/desktop/src/app/sync/sync.service.ts
@@ -172,7 +172,6 @@ export class SyncService {
destinationInstance.uploadLink!(f.title, sync.destination.params, link).pipe(
tap(() => {
f.status = FileStatus.UPLOADED;
- sync.completed = true;
this.onQueueUpdate();
}),
),
@@ -191,9 +190,14 @@ export class SyncService {
return destinationInstance
.upload(f.originalId, f.title, sync.destination.params, { blob })
.pipe(
+ catchError((error) => {
+ f.status = FileStatus.ERROR;
+ f.error = error.toString();
+ this.onQueueUpdate();
+ return of(undefined);
+ }),
tap(() => {
- f.status = FileStatus.UPLOADED;
- sync.completed = true;
+ f.status = FileStatus.ERROR ? FileStatus.ERROR : FileStatus.UPLOADED;
this.onQueueUpdate();
}),
);
@@ -216,6 +220,7 @@ export class SyncService {
);
}),
tap(() => {
+ sync.completed = true;
this.onQueueUpdate();
}),
);
diff --git a/package.json b/package.json
index 42e75b1b6..0d2c0f059 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "nuclia",
- "version": "1.2.3",
+ "version": "1.2.4",
"license": "MIT",
"author": "Nuclia.cloud",
"description": "Nuclia frontend apps and libs",