Skip to content
Snippets Groups Projects
Commit 005f1fa2 authored by Antoine Lambert's avatar Antoine Lambert
Browse files

browse/vault: Handle dead link for cooked archive

A download link for an archive cooked through the vault (directory or
revision_gitfast) does not remain alive ad vitam aeternam.

As the history of previously cooked objects remains stored in the browser
local storage, we need to handle the case where one wants to download a
previously cooked archive from a dead link.

In that case, check the link availability first before proceeding to the
real download. If it is dead, display a modal asking the user if he wants
to recook the archive.

Closes T1082
parent 5b1db1fa
No related branches found
No related tags found
No related merge requests found
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* See top-level LICENSE file for more information * See top-level LICENSE file for more information
*/ */
import {handleFetchErrors} from 'utils/functions'; import {handleFetchError, handleFetchErrors} from 'utils/functions';
let progress = `<div class="progress"> let progress = `<div class="progress">
<div class="progress-bar progress-bar-success progress-bar-striped" <div class="progress-bar progress-bar-success progress-bar-striped"
...@@ -36,6 +36,77 @@ function updateProgressBar(progressBar, cookingTask) { ...@@ -36,6 +36,77 @@ function updateProgressBar(progressBar, cookingTask) {
} }
} }
let recookTask;
// called when the user wants to download a cooked archive
export function fetchCookedObject(fetchUrl) {
recookTask = null;
// first, check if the link is still available from the vault
fetch(fetchUrl, {credentials: 'same-origin'})
.then(response => {
// link is still alive, proceed to download
if (response.ok) {
$('#vault-fetch-iframe').attr('src', fetchUrl);
// link is dead
} else {
// get the associated cooking task
let vaultCookingTasks = JSON.parse(localStorage.getItem('swh-vault-cooking-tasks'));
for (let i = 0; i < vaultCookingTasks.length; ++i) {
if (vaultCookingTasks[i].fetch_url === fetchUrl) {
recookTask = vaultCookingTasks[i];
break;
}
}
// display a modal asking the user if he wants to recook the archive
$('#vault-recook-object-modal').modal('show');
}
});
}
// called when the user wants to recook an archive
// for which the download link is not available anymore
export function recookObject() {
if (recookTask) {
// stop cookink tasks status polling
clearTimeout(checkVaultId);
// build cook request url
let cookingUrl;
if (recookTask.object_type === 'directory') {
cookingUrl = Urls.vault_cook_directory(recookTask.object_id);
} else {
cookingUrl = Urls.vault_cook_revision_gitfast(recookTask.object_id);
}
if (recookTask.email) {
cookingUrl += '?email=' + recookTask.email;
}
// request archive cooking
fetch(cookingUrl, {credentials: 'same-origin', method: 'POST'})
.then(handleFetchError)
.then(() => {
// update task status
recookTask.status = 'new';
let vaultCookingTasks = JSON.parse(localStorage.getItem('swh-vault-cooking-tasks'));
for (let i = 0; i < vaultCookingTasks.length; ++i) {
if (vaultCookingTasks[i].object_id === recookTask.object_id) {
vaultCookingTasks[i] = recookTask;
break;
}
}
// save updated tasks to local storage
localStorage.setItem('swh-vault-cooking-tasks', JSON.stringify(vaultCookingTasks));
// restart cooking tasks status polling
checkVaultCookingTasks();
// hide recook archive modal
$('#vault-recook-object-modal').modal('hide');
})
// something went wrong
.catch(() => {
checkVaultCookingTasks();
$('#vault-recook-object-modal').modal('hide');
});
}
}
function checkVaultCookingTasks() { function checkVaultCookingTasks() {
let vaultCookingTasks = JSON.parse(localStorage.getItem('swh-vault-cooking-tasks')); let vaultCookingTasks = JSON.parse(localStorage.getItem('swh-vault-cooking-tasks'));
if (!vaultCookingTasks || vaultCookingTasks.length === 0) { if (!vaultCookingTasks || vaultCookingTasks.length === 0) {
...@@ -83,6 +154,7 @@ function checkVaultCookingTasks() { ...@@ -83,6 +154,7 @@ function checkVaultCookingTasks() {
let rowTask = $('#vault-task-' + cookingTask.object_id); let rowTask = $('#vault-task-' + cookingTask.object_id);
let downloadLinkWait = 'Waiting for download link to be available';
if (!rowTask.length) { if (!rowTask.length) {
let browseUrl; let browseUrl;
...@@ -111,10 +183,10 @@ function checkVaultCookingTasks() { ...@@ -111,10 +183,10 @@ function checkVaultCookingTasks() {
} }
tableRow += `<td class="vault-object-id" data-object-id="${cookingTask.object_id}"><a href="${browseUrl}">${cookingTask.object_id}</a></td>`; tableRow += `<td class="vault-object-id" data-object-id="${cookingTask.object_id}"><a href="${browseUrl}">${cookingTask.object_id}</a></td>`;
tableRow += `<td style="width: 350px">${progressBar.outerHTML}</td>`; tableRow += `<td style="width: 350px">${progressBar.outerHTML}</td>`;
let downloadLink = 'Waiting for download link to be available'; let downloadLink = downloadLinkWait;
if (cookingTask.status === 'done') { if (cookingTask.status === 'done') {
downloadLink = `<a class="btn btn-default btn-sm" href="${cookingTask.fetch_url}` + downloadLink = `<button class="btn btn-default btn-sm" onclick="swh.vault.fetchCookedObject('${cookingTask.fetch_url}')` +
'"><i class="fa fa-download fa-fw" aria-hidden="true"></i>Download</a>'; '"><i class="fa fa-download fa-fw" aria-hidden="true"></i>Download</button>';
} else if (cookingTask.status === 'failed') { } else if (cookingTask.status === 'failed') {
downloadLink = ''; downloadLink = '';
} }
...@@ -126,10 +198,12 @@ function checkVaultCookingTasks() { ...@@ -126,10 +198,12 @@ function checkVaultCookingTasks() {
updateProgressBar(progressBar, cookingTask); updateProgressBar(progressBar, cookingTask);
let downloadLink = rowTask.find('.vault-dl-link'); let downloadLink = rowTask.find('.vault-dl-link');
if (cookingTask.status === 'done') { if (cookingTask.status === 'done') {
downloadLink[0].innerHTML = `<a class="btn btn-default btn-sm" href="${cookingTask.fetch_url}` + downloadLink[0].innerHTML = `<button class="btn btn-default btn-sm" onclick="swh.vault.fetchCookedObject('${cookingTask.fetch_url}')` +
'"><i class="fa fa-download fa-fw" aria-hidden="true"></i>Download</a>'; '"><i class="fa fa-download fa-fw" aria-hidden="true"></i>Download</button>';
} else if (cookingTask.status === 'failed') { } else if (cookingTask.status === 'failed') {
downloadLink[0].innerHTML = ''; downloadLink[0].innerHTML = '';
} else if (cookingTask.status === 'new') {
downloadLink[0].innerHTML = downloadLinkWait;
} }
} }
} }
......
...@@ -37,7 +37,31 @@ See top-level LICENSE file for more information ...@@ -37,7 +37,31 @@ See top-level LICENSE file for more information
<tbody></tbody> <tbody></tbody>
</table> </table>
</div> </div>
<iframe id="vault-fetch-iframe" style="display:none;"></iframe>
<div class="modal fade" id="vault-recook-object-modal" tabindex="-1" role="dialog" aria-labelledby="vault-recook-object-modal-label" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h6 class="modal-title" id="vault-recook-object-modal-label">Download link no more available</h6>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>
The requested archive is no more available to download from the Sofware Heritage Vault.
</p>
<p>
Do you want to cook it again ?
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-default btn-sm" onclick="swh.vault.recookObject()">Ok</button>
</div>
</div>
</div>
</div>
<script> <script>
swh.webapp.initPage('vault'); swh.webapp.initPage('vault');
swh.vault.initUi(); swh.vault.initUi();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment