342 lines
12 KiB
Cheetah
342 lines
12 KiB
Cheetah
{{ define "content" }}
|
|
<style>
|
|
.breadcrumb:before {
|
|
content: '/';
|
|
}
|
|
#notifications {
|
|
top: 20px;
|
|
right: 30px;
|
|
position: fixed;
|
|
z-index: 2
|
|
}
|
|
</style>
|
|
|
|
<nav class="nav-extended">
|
|
<div class="nav-wrapper container">
|
|
<a href="/buckets/{{$.BucketName}}" class="brand-logo center"><i class="material-icons">folder_open</i>{{ .BucketName }}</a>
|
|
{{ if not .Objects }}
|
|
<ul class="right">
|
|
<li>
|
|
<a class="waves-effect waves-light btn" href="#" onclick="deleteBucket({{ .BucketName }})">
|
|
Delete <i class="material-icons right">delete</i>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
{{ end }}
|
|
</div>
|
|
|
|
<div class="nav-wrapper container">
|
|
<a href="/buckets" class="breadcrumb"><i class="material-icons">arrow_back</i> buckets </a>
|
|
{{ $url := printf "/buckets/%s/" $.BucketName }}
|
|
<a href="{{ $url }}" class="breadcrumb">{{ $.BucketName }}</a>
|
|
|
|
{{ range $index, $path := .Paths }}
|
|
{{ $url = printf "%s%s/" $url $path }}
|
|
<a href="{{ $url }}" class="breadcrumb">{{ $path }}</a>
|
|
{{ end }}
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="section" style="margin: 10px; position: relative;">
|
|
{{ if .Objects }}
|
|
<table class="striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Key</th>
|
|
<th>Size</th>
|
|
<th>Owner</th>
|
|
<th>Last Modified</th>
|
|
<th style="min-width:165px;"></th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tbody>
|
|
{{ range $index, $object := .Objects }}
|
|
<tr>
|
|
<td
|
|
{{ if $object.IsFolder }}
|
|
onclick="location.href='/buckets/{{ $.BucketName }}/{{ $object.Key }}'"}
|
|
style="cursor:pointer;"
|
|
{{ end }}>
|
|
<i class="material-icons">{{ $object.Icon }}</i> {{ $object.DisplayName }}
|
|
</td>
|
|
<td>{{ $object.Size }} bytes</td>
|
|
<td>{{ $object.Owner }}</td>
|
|
<td>{{ $object.LastModified }}</td>
|
|
<td>
|
|
{{ if not $object.IsFolder }}
|
|
<button class="dropdown-trigger waves-effect waves-teal btn" data-target="actions-dropdown-{{ $index }}">
|
|
Actions <i class="material-icons right">arrow_drop_down</i>
|
|
</button>
|
|
<!-- Dropdown Structure -->
|
|
<ul id="actions-dropdown-{{ $index }}" class="dropdown-content">
|
|
<li><a target="_blank" href="/api/buckets/{{ $.BucketName }}/objects/{{ $object.Key }}">Download</a></li>
|
|
<li><a onclick="handleOpenDownloadLinkModal({{ $object.Key }})">Download link</a></li>
|
|
{{- if $.AllowDelete }}
|
|
<li><a href="#" onclick="deleteObject({{ $.BucketName }}, {{ $object.Key }})">Delete</a></li>
|
|
{{- end }}
|
|
</ul>
|
|
{{ end }}
|
|
</td>
|
|
</tr>
|
|
{{ end }}
|
|
</tbody>
|
|
</table>
|
|
{{ end }}
|
|
|
|
{{ if not .Objects }}
|
|
<p style="text-align:center;margin-top:2em;color:gray;">No objects in <strong>{{ .BucketName }}/{{ .CurrentPath }}</strong> yet</p>
|
|
{{ end }}
|
|
|
|
<div id="notifications">
|
|
<div id="notification-template" class="card" style="display: none;">
|
|
<div class="card-content" style="padding: 12px">
|
|
<span class="card-title">Uploading</span>
|
|
<p> I am a very simple card </p>
|
|
<div class="progress">
|
|
<div class="indeterminate"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="fixed-action-btn">
|
|
<button type="button" class="btn-floating btn-large red tooltipped" id="upload-file-btn" data-position="top" data-tooltip="Upload files">
|
|
<i class="large material-icons">add</i>
|
|
</button>
|
|
|
|
<button type="button" class="btn-floating btn-large red tooltipped" id="upload-folder-btn" data-position="top" data-tooltip="Upload folder">
|
|
<i class="large material-icons">create_new_folder</i>
|
|
</button>
|
|
|
|
<button type="button" class="btn-floating btn-large red modal-trigger tooltipped" data-target="modal-change-path" data-position="top" data-tooltip="Change path">
|
|
<i class="large material-icons">create</i>
|
|
</button>
|
|
</div>
|
|
|
|
<input type="file" id="upload-folder-input" webkitdirectory multiple style="display: none;">
|
|
<input type="file" id="upload-file-input" name="file" multiple style="display: none;">
|
|
|
|
<div id="modal-change-path" class="modal">
|
|
<form id="change-path-form" enctype="multipart/form-data">
|
|
<div class="modal-content">
|
|
<h4>Change directory path</h4>
|
|
<br>
|
|
<div class="row">
|
|
<div class="col s6">
|
|
<div class="input-field">
|
|
<input name="new-path" id="new-path" type="text">
|
|
<label for="new-path">Path</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="modal-close waves-effect waves-green btn-flat">Cancel</button>
|
|
<button type="submit" class="modal-close waves-effect waves-green btn">Change path</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div id="modal-create-download-link" class="modal">
|
|
<form id="download-link-form">
|
|
<div class="modal-content">
|
|
<div class="row">
|
|
<h4>Create download link for </h4>
|
|
<input name="objectName" id="objectName" type="text" readonly>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col s4">
|
|
<div class="input-field">
|
|
<input name="day" id="gen-link-day" value="0" type="text" pattern="^[\d]+$" class="validate">
|
|
<label for="gen-link-day">Day</label>
|
|
<span class="helper-text" data-error="Invalid day format"></span>
|
|
</div>
|
|
</div>
|
|
<div class="col s4">
|
|
<div class="input-field">
|
|
<input name="hour" id="gen-link-hour" value="1" type="text" pattern="^([0-9]|1\d|2[0-3])$" class="validate">
|
|
<label for="gen-link-hour">Hour</label>
|
|
<span class="helper-text" data-error="Invalid hour format"></span>
|
|
</div>
|
|
</div>
|
|
<div class="col s4">
|
|
<div class="input-field">
|
|
<input name="minute" id="gen-link-minute" value="0" type="text" pattern="^([0-9]|[1-5]\d)$" class="validate">
|
|
<label for="gen-link-minute">Minute</label>
|
|
<span class="helper-text" data-error="Invalid minute format"></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col s3">
|
|
<button id="create-link-btn" class="waves-effect waves-green btn">Create link</button>
|
|
</div>
|
|
<div class="col s9 red-text text-darken-2" id="gen-url-error"></div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col s11">
|
|
<div class="input-field">
|
|
<i class="material-icons prefix" onclick="handleCopyLink()" style="cursor:pointer;">content_copy</i>
|
|
<input name="generated-link" id="generated-link" type="text" readonly>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<script>
|
|
function deleteObject(bucketName, objectName) {
|
|
$.ajax({
|
|
type: 'DELETE',
|
|
url: '/api/buckets/' + bucketName + '/objects/' + objectName,
|
|
success: function () { location.reload(); }
|
|
})
|
|
}
|
|
|
|
function deleteBucket(bucketName) {
|
|
$.ajax({
|
|
type: 'DELETE',
|
|
url: '/api/buckets/' + bucketName,
|
|
success: function () { window.location.replace('/buckets'); }
|
|
})
|
|
}
|
|
|
|
function handleUploadFiles(event) {
|
|
files = event.target.files
|
|
url = "/api/buckets/{{ .BucketName }}/objects"
|
|
uploadFiles(files, url);
|
|
}
|
|
|
|
function handleChangePath(event) {
|
|
event.preventDefault();
|
|
|
|
const form = event.target;
|
|
const formData = new FormData(form);
|
|
|
|
let appendPath = formData.get("new-path")
|
|
if(!appendPath.endsWith("/")) {
|
|
appendPath = appendPath + "/";
|
|
}
|
|
|
|
let currentPath = window.location.href
|
|
if(!currentPath.endsWith("/")) {
|
|
currentPath = currentPath + "/";
|
|
}
|
|
|
|
form.reset();
|
|
window.location.href = currentPath + appendPath;
|
|
}
|
|
|
|
function uploadFiles(files, url) {
|
|
uploadPromises = [];
|
|
for(file of files) {
|
|
uploadPromises.push(uploadFile(file, url));
|
|
}
|
|
Promise.all(uploadPromises).then(values => {
|
|
window.location.reload();
|
|
});
|
|
}
|
|
|
|
function uploadFile(file, url) {
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
if( !!file.webkitRelativePath ) {
|
|
formData.append('path', "{{ .CurrentPath }}" + file.webkitRelativePath );
|
|
} else {
|
|
formData.append('path', "{{ .CurrentPath }}" + file.name );
|
|
}
|
|
|
|
const notification = createNotification(file.name);
|
|
notifications = document.getElementById('notifications');
|
|
notifications.appendChild(notification);
|
|
|
|
return fetch(url, {
|
|
method: "POST",
|
|
body: formData
|
|
}).then(response => {
|
|
notifications.removeChild(notification);
|
|
})
|
|
}
|
|
|
|
function createNotification(fileName) {
|
|
notificationTemplate = document.getElementById('notification-template');
|
|
notification = notificationTemplate.cloneNode(true);
|
|
notification.getElementsByTagName('p')[0].innerHTML = fileName;
|
|
notification.removeAttribute("id");
|
|
notification.removeAttribute("style");
|
|
return notification;
|
|
}
|
|
|
|
function handleOpenDownloadLinkModal(objectName) {
|
|
const downloadLinkForm = document.forms['download-link-form']
|
|
downloadLinkForm.elements['objectName'].value = objectName;
|
|
|
|
const createLinkModalElement = document.getElementById('modal-create-download-link')
|
|
document.getElementById('generated-link').setAttribute('value', "");
|
|
document.getElementById('gen-url-error').innerHTML = "";
|
|
const modalInstance = M.Modal.init(createLinkModalElement);
|
|
modalInstance.open()
|
|
}
|
|
|
|
function handleGenerateDownloadLink(event) {
|
|
event.preventDefault();
|
|
|
|
const form = event.target;
|
|
const formData = new FormData(form);
|
|
const objectName = formData.get('objectName');
|
|
const genUrlMessage = document.getElementById('gen-url-error');
|
|
|
|
const expiryTime = formData.get('day') * 24 * 60 * 60 + formData.get('hour') * 60 * 60 + formData.get('minute') * 60;
|
|
|
|
if(expiryTime > 7 * 24 * 60 * 60) {
|
|
genUrlMessage.innerHTML = "Expiry time must be less than 7 days";
|
|
return;
|
|
}
|
|
|
|
$.ajax({
|
|
type: 'GET',
|
|
url: '/api/buckets/' + {{ $.BucketName }}+ "/objects/" + objectName + "/url?expiry=" + expiryTime,
|
|
success: function (result) {
|
|
genUrlMessage.innerHTML = "";
|
|
document.getElementById("generated-link").setAttribute('value', JSON.parse(result).url);
|
|
},
|
|
error: function(request, status, error) {
|
|
genUrlMessage.innerHTML = "Error when generating url";
|
|
}
|
|
});
|
|
}
|
|
|
|
function handleCopyLink() {
|
|
const url = document.getElementById("generated-link").value;
|
|
|
|
if(!!url) {
|
|
navigator.clipboard.writeText(url).then(function() {
|
|
M.toast({html: 'Copied to clipboard!'});
|
|
}, function(err) {
|
|
console.error('Could not copy:', err);
|
|
});
|
|
}
|
|
}
|
|
|
|
window.onload = (event) => {
|
|
$('#change-path-form').submit(handleChangePath)
|
|
$('#download-link-form').submit(handleGenerateDownloadLink)
|
|
|
|
uploadFolderInput = $('#upload-folder-input');
|
|
$('#upload-folder-btn').click(event => uploadFolderInput.click());
|
|
uploadFolderInput.change(handleUploadFiles);
|
|
|
|
uploadFileInput = $('#upload-file-input');
|
|
$('#upload-file-btn').click(event => uploadFileInput.click());
|
|
uploadFileInput.change(handleUploadFiles);
|
|
|
|
$(document).ready(function(){
|
|
$('.tooltipped').tooltip();
|
|
});
|
|
};
|
|
</script>
|
|
{{ end }}
|