diff --git a/client/src/app/instance/search/components/result/download.component.html b/client/src/app/instance/search/components/result/download.component.html index aa6200cf7adbff512727c2846192822eb5f950e0..fc7758eae9a93233b2f07b64d89e196c5bd0f002 100644 --- a/client/src/app/instance/search/components/result/download.component.html +++ b/client/src/app/instance/search/components/result/download.component.html @@ -44,14 +44,11 @@ <a *ngIf="!archiveInProgress" [href]="createFilesArchiveUrl()" (click)="createFilesArchive($event, createFilesArchiveUrl())" class="btn btn-outline-primary" title="Download an archive with all files"> <span class="fas fa-archive"></span> Files archive </a> - <div *ngIf="archiveInProgress && !archiveIsAvailable"> + <div *ngIf="archiveInProgress"> <span class="fas fa-circle-notch fa-spin fa-3x"></span> <span class="sr-only">Loading...</span> Please wait archive is under construction... </div> - <a *ngIf="archiveIsAvailable" [href]="getArchiveUrl()" (click)="click($event, getArchiveUrl(), 'zip')" class="btn btn-outline-primary" title="Download an archive with all files"> - <span class="fas fa-archive"></span> {{ archiveName }} - </a> </div> </div> </div> diff --git a/client/src/app/instance/search/components/result/download.component.ts b/client/src/app/instance/search/components/result/download.component.ts index 4b3c199ac2a5fc7ed474960ee75d51986118759a..9a8f21ad7ffe148d176fc41a08cdc4efca1b1a10 100644 --- a/client/src/app/instance/search/components/result/download.component.ts +++ b/client/src/app/instance/search/components/result/download.component.ts @@ -8,7 +8,7 @@ */ import { Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core'; -import { HttpClient, HttpRequest, HttpEventType } from '@angular/common/http'; +import { HttpClient } from '@angular/common/http'; import { interval, Subscription} from 'rxjs'; import { Criterion, ConeSearch, criterionToString } from '../../../store/models'; @@ -36,8 +36,8 @@ export class DownloadComponent implements OnDestroy { @Output() startsDownloadingFile: EventEmitter<{url: string, filename: string}> = new EventEmitter(); archiveName = ''; + archiveId =''; archiveInProgress = false; - archiveIsAvailable = false; archiveIsAvailableSubscription: Subscription constructor(private appConfig: AppConfigService, private http: HttpClient) { } @@ -96,7 +96,7 @@ export class DownloadComponent implements OnDestroy { /** * Allows to download file. */ - click(event, href, extension): void { + click(event, href, extension): void { event.preventDefault(); const url = href; @@ -110,7 +110,7 @@ export class DownloadComponent implements OnDestroy { * * @fires EventEmitter<string> */ - broadcastVotable(): void { + broadcastVotable(): void { this.broadcast.emit(this.getUrl('votable')); } @@ -131,15 +131,16 @@ export class DownloadComponent implements OnDestroy { } getArchiveUrl() { - return `${getHost(this.appConfig.apiUrl)}/download-archive?archive_name=${this.archiveName}`; + return `${getHost(this.appConfig.apiUrl)}/download-archive/${this.datasetSelected}?archive_id=${this.archiveId}`; } testArchiveIsAvailable() { - const url = `${getHost(this.appConfig.apiUrl)}/archive-is-available?archive_name=${this.archiveName}`; + const url = `${getHost(this.appConfig.apiUrl)}/archive-is-available?archive_id=${this.archiveId}`; this.http.get<{"archive_is_available": boolean}>(url).subscribe(data => { if (data.archive_is_available) { - this.archiveIsAvailable = true; + this.archiveInProgress = false; this.archiveIsAvailableSubscription.unsubscribe(); + this.startsDownloadingFile.emit({ url: this.getArchiveUrl(), filename: this.archiveName }) } }); } @@ -150,9 +151,10 @@ export class DownloadComponent implements OnDestroy { createFilesArchive(event, href): void { event.preventDefault(); - this.http.get<{"archive": string}>(href).subscribe(data => { + this.http.get<{"archive_name": string, "archive_id": string}>(href).subscribe(data => { this.archiveInProgress = true; - this.archiveName = data.archive; + this.archiveName = data.archive_name; + this.archiveId = data.archive_id; this.archiveIsAvailableSubscription = interval(1000).subscribe(() => this.testArchiveIsAvailable()); }); } diff --git a/client/src/app/instance/store/actions/download-file.actions.ts b/client/src/app/instance/store/actions/download-file.actions.ts index f5ff418da3290c6dd1108f964d8b6d28bea38b02..a6d140f26fb2e558b51a5e4877881857f1637d63 100644 --- a/client/src/app/instance/store/actions/download-file.actions.ts +++ b/client/src/app/instance/store/actions/download-file.actions.ts @@ -11,4 +11,4 @@ import { createAction, props } from '@ngrx/store'; export const startsDownloadingFile = createAction('[File] Starts Downloading File', props<{ url: string, filename: string }>()); export const updateDownloadProgress = createAction('[File] Update Download Progress', props<{ progress: number, filename: string }>()); -export const startsArchiveFilesCreation = createAction('[File] Starts Archive Files Creation'); +export const fileDownloaded = createAction('[File] File Downloaded', props<{ filename: string }>()); diff --git a/client/src/app/instance/store/effects/download-file.effects.ts b/client/src/app/instance/store/effects/download-file.effects.ts index 21091c776b82fc7c4b0af03fa8559ffb4970ae7c..2a4754ba2138dfd034049955e7e76f971e3ce820 100644 --- a/client/src/app/instance/store/effects/download-file.effects.ts +++ b/client/src/app/instance/store/effects/download-file.effects.ts @@ -10,11 +10,9 @@ import { Injectable } from '@angular/core'; import { HttpEventType } from '@angular/common/http'; -import { Actions, createEffect, ofType, concatLatestFrom } from '@ngrx/effects'; +import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; -import { of } from 'rxjs'; -import { map, tap, mergeMap, catchError } from 'rxjs/operators'; -import { ToastrService } from 'ngx-toastr'; +import { map, mergeMap } from 'rxjs/operators'; import { DownloadFileService } from '../services/download-file.service'; import * as downloadFileActions from '../actions/download-file.actions'; @@ -42,6 +40,7 @@ export class DownloadFileEffects { } if (event.type === HttpEventType.Response) { + this.store.dispatch(downloadFileActions.fileDownloaded({ filename: action.filename })); this.downloadFileService.saveDownloadedFile(event.body as Blob, action.filename); } }) @@ -53,7 +52,6 @@ export class DownloadFileEffects { constructor( private actions$: Actions, private downloadFileService: DownloadFileService, - private store: Store<{ }>, - private toastr: ToastrService + private store: Store<{ }> ) {} } diff --git a/client/src/app/instance/store/reducers/download-file.reducer.ts b/client/src/app/instance/store/reducers/download-file.reducer.ts index c0021b4683c468798cd401a0b49314989448e1fc..d30e4bf195d5615dc7c25e4960fafecd23fddc29 100644 --- a/client/src/app/instance/store/reducers/download-file.reducer.ts +++ b/client/src/app/instance/store/reducers/download-file.reducer.ts @@ -45,6 +45,17 @@ export const fileReducer = createReducer( progress: progress } ] + })), + on(downloadFileActions.fileDownloaded, (state, { filename }) => ({ + ...state, + downloadedFiles: [ + ...state.downloadedFiles.filter(f => f.name !== filename), + { + name: filename, + state: 'DONE', + progress: 100 + } + ] })) ); diff --git a/server/app/routes.php b/server/app/routes.php index d44c212b774490e7975b3157fc5a66c380c0d922..634a35997b6a3b2f32f1650b343747e7414c8f62 100644 --- a/server/app/routes.php +++ b/server/app/routes.php @@ -70,7 +70,7 @@ $app->group('', function (RouteCollectorProxy $group) { $app->get('/search/{dname}', App\Action\SearchAction::class); $app->get('/archive/{dname}', App\Action\ArchiveAction::class); $app->get('/archive-is-available', App\Action\ArchiveIsAvailableAction::class); -$app->get('/download-archive', App\Action\DownloadArchiveAction::class); +$app->get('/download-archive/{dname}', App\Action\DownloadArchiveAction::class); $app->get('/dataset-file-explorer/{dname}[{fpath:.*}]', App\Action\DatasetFileExplorerAction::class); $app->get('/download-instance-file/{iname}/[{fpath:.*}]', App\Action\DownloadInstanceFileAction::class); $app->get('/download-file/{dname}/[{fpath:.*}]', App\Action\DownloadFileAction::class); diff --git a/server/src/Action/ArchiveAction.php b/server/src/Action/ArchiveAction.php index 6a743ab65a339870ee0aebc20cd340733aa176b7..a6009f5ae15e072696726935b245731e54f883dc 100644 --- a/server/src/Action/ArchiveAction.php +++ b/server/src/Action/ArchiveAction.php @@ -112,12 +112,13 @@ final class ArchiveAction extends AbstractAction // Create the name of the future archive $archiveName = 'archive_' . $dataset->getName() . '_' . (new \DateTime())->format('Y-m-d\TH:i:s') . '.zip'; + $archiveId = uniqid(); // Publish message in the archive queue $channel = $this->rmq->channel(); $channel->queue_declare('archive', false, false, false, false); $msg = new AMQPMessage(json_encode(array( - 'archive_name' => $archiveName, + 'archive_id' => $archiveId, 'dataset_name' => $datasetName, 'query' => $request->getUri()->getQuery(), 'param_a' => $queryParams['a'] @@ -125,7 +126,7 @@ final class ArchiveAction extends AbstractAction $channel->basic_publish($msg, '', 'archive'); // Just returns the future archive name - $payload = json_encode(array('archive' => $archiveName)); + $payload = json_encode(array('archive_name' => $archiveName, 'archive_id' => $archiveId)); $response->getBody()->write($payload); return $response; } diff --git a/server/src/Action/ArchiveIsAvailableAction.php b/server/src/Action/ArchiveIsAvailableAction.php index 39f3a4ec85ed88a021b218b33f72d03ded379c33..81219d4b3852af6089eba0559b6d829d4984aa98 100644 --- a/server/src/Action/ArchiveIsAvailableAction.php +++ b/server/src/Action/ArchiveIsAvailableAction.php @@ -68,10 +68,10 @@ final class ArchiveIsAvailableAction extends AbstractAction } $queryParams = $request->getQueryParams(); - $archiveName = $queryParams['archive_name']; + $archiveId = $queryParams['archive_id'] . '.zip'; // Search the file - $filePath = $this->dataPath . '/ARCHIVE/' . $archiveName; + $filePath = $this->dataPath . '/ARCHIVE/' . $archiveId; $isAvailable = false; if (file_exists($filePath)) { diff --git a/server/src/Action/DownloadArchiveAction.php b/server/src/Action/DownloadArchiveAction.php index d3795acc97cc61d45f420f6b1ed029327ab02ac1..13ce380bcdf245777d6eecbe540a59565c21ee8d 100644 --- a/server/src/Action/DownloadArchiveAction.php +++ b/server/src/Action/DownloadArchiveAction.php @@ -66,18 +66,38 @@ final class DownloadArchiveAction extends AbstractAction if ($request->getMethod() === OPTIONS) { return $response->withHeader('Access-Control-Allow-Methods', 'GET, OPTIONS'); } + + // Search the correct dataset with primary key + $dataset = $this->em->find('App\Entity\Dataset', $args['dname']); + + // If dataset is not found 404 + if (is_null($dataset)) { + throw new HttpNotFoundException( + $request, + 'Dataset with name ' . $args['dname'] . ' is not found' + ); + } + + // If dataset is private and authorization enabled + if (!$dataset->getPublic() && boolval($this->settings['enabled'])) { + $this->verifyDatasetAuthorization( + $request, + $dataset->getName(), + explode(',', $this->settings['admin_roles']) + ); + } $queryParams = $request->getQueryParams(); - $archiveName = $queryParams['archive_name']; + $archiveId = $queryParams['archive_id']; // Search the file - $filePath = $this->dataPath . '/ARCHIVE/' . $archiveName; + $filePath = $this->dataPath . '/ARCHIVE/' . $archiveId . '.zip'; // If the file not found 404 if (!file_exists($filePath)) { throw new HttpNotFoundException( $request, - 'Archive file with name ' . $archiveName . ' is not found' + 'Archive file with name ' . $archiveId . '.zip is not found' ); } diff --git a/tasks/src/anis_tasks/archive.py b/tasks/src/anis_tasks/archive.py index 8cca0299695ca68f0a3364719c5fc02622abc370..1f799b7768a235e825ae54837092a019b0b8140a 100644 --- a/tasks/src/anis_tasks/archive.py +++ b/tasks/src/anis_tasks/archive.py @@ -21,7 +21,7 @@ def archive_handler(ch, method, properties, body): # create a ZipFile object data_path = utils.get_data_path() - zip_path = data_path + "/ARCHIVE/" + message["archive_name"] + zip_path = data_path + "/ARCHIVE/" + message["archive_id"] + ".zip" zip = ZipFile(zip_path + ".tmp", 'w') # Search files