You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

344 lines
11 KiB

3 years ago
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>SeaweedFS Filer</title>
  5. <meta name="viewport" content="width=device-width, initial-scale=1">
  6. <link rel="stylesheet" href="/seaweedfsstatic/bootstrap/3.3.1/css/bootstrap.min.css">
  7. <style>
  8. body {
  9. padding-bottom: 128px;
  10. }
  11. #drop-area {
  12. border: 1px transparent;
  13. margin-top: 5px;
  14. }
  15. #drop-area.highlight {
  16. border-color: purple;
  17. border: 2px dashed #ccc;
  18. }
  19. .button {
  20. display: inline-block;
  21. padding: 2px;
  22. background: #ccc;
  23. cursor: pointer;
  24. border-radius: 2px;
  25. border: 1px solid #ccc;
  26. float: right;
  27. margin-left: 2px;
  28. margin-bottom: 0;
  29. }
  30. label {
  31. font-weight: normal;
  32. }
  33. .button:hover {
  34. background: #ddd;
  35. }
  36. #fileElem {
  37. display: none;
  38. }
  39. td, th {
  40. vertical-align: bottom;
  41. }
  42. .table-hover > tbody > tr:hover > * > div.operations {
  43. display: block;
  44. }
  45. .table > tbody > tr {
  46. height: 40px;
  47. }
  48. div.operations {
  49. display: none;
  50. }
  51. .footer {
  52. position: absolute;
  53. bottom: 0px;
  54. right: 5%;
  55. min-width: 25%;
  56. border-left: 1px solid #ccc;
  57. border-right: 1px solid #ccc;
  58. }
  59. .add-files {
  60. font-size: 46px;
  61. text-align: center;
  62. border: 1px dashed #999;
  63. padding-bottom: 9px;
  64. margin: 0 2px;
  65. }
  66. </style>
  67. </head>
  68. <body>
  69. <div class="container">
  70. <div class="page-header">
  71. <h1>
  72. <a href="https://github.com/chrislusf/seaweedfs"><img src="/seaweedfsstatic/seaweed50x50.png"></img></a>
  73. SeaweedFS Filer
  74. </h1>
  75. </div>
  76. <div class="row">
  77. <div>
  78. <div class="btn-group btn-group-sm pull-right" role="group" style="margin-top:3px;">
  79. <label class="btn btn-default" onclick="handleCreateDir()">New Folder</label>
  80. <label class="btn btn-default" for="fileElem">Upload</label>
  81. </div>
  82. <ol class="breadcrumb">
  83. {{ range $entry := .Breadcrumbs }}
  84. <li><a href="{{ printpath $entry.Link }}">
  85. {{ $entry.Name }}
  86. </li></a>
  87. {{ end }}
  88. </ol>
  89. </div>
  90. </div>
  91. <div class="row" id="drop-area">
  92. <form class="upload-form">
  93. <input type="file" id="fileElem" multiple onchange="handleFiles(this.files)">
  94. {{if .EmptyFolder}}
  95. <div class="row add-files">
  96. +
  97. </div>
  98. {{else}}
  99. <table width="100%" class="table table-hover">
  100. {{$path := .Path }}
  101. {{ range $entry_index, $entry := .Entries }}
  102. <tr>
  103. <td>
  104. {{if $entry.IsDirectory}}
  105. <img src="/seaweedfsstatic/images/folder.gif" width="20" height="16">
  106. <a href="{{ printpath $path "/" $entry.Name "/"}}" >
  107. {{ $entry.Name }}
  108. </a>
  109. {{else}}
  110. <a href="{{ printpath $path "/" $entry.Name }}" >
  111. {{ $entry.Name }}
  112. </a>
  113. {{end}}
  114. </td>
  115. <td align="right" nowrap>
  116. {{if $entry.IsDirectory}}
  117. {{else}}
  118. {{ $entry.Mime }}&nbsp;
  119. {{end}}
  120. </td>
  121. <td align="right" nowrap>
  122. {{if $entry.IsDirectory}}
  123. {{else}}
  124. {{ $entry.Size | humanizeBytes }}&nbsp;
  125. {{end}}
  126. </td>
  127. <td align="right" nowrap>
  128. {{ $entry.Timestamp.Format "2006-01-02 15:04" }}
  129. </td>
  130. <td style="width:75px">
  131. <div class="btn-group btn-group-xs pull-right operations" role="group">
  132. <label class="btn" onclick="handleRename('{{ $entry.Name }}', '{{ printpath $path "/" }}')"><img src="/seaweedfsstatic/images/rename-16.png" width="14" height="14"></label>
  133. {{if $entry.IsDirectory}}
  134. <label class="btn" onclick="handleDelete('{{ printpath $path "/" $entry.Name "/" }}')"><img src="/seaweedfsstatic/images/delete-16.png" width="14" height="14"></label>
  135. {{else}}
  136. <label class="btn" onclick="handleDelete('{{ printpath $path "/" $entry.Name }}')"><img src="/seaweedfsstatic/images/delete-16.png" width="14" height="14"></label>
  137. {{end}}
  138. </div>
  139. </td>
  140. </tr>
  141. {{ end }}
  142. </table>
  143. {{end}}
  144. </form>
  145. </div>
  146. {{if .ShouldDisplayLoadMore}}
  147. <div class="row">
  148. <a href={{ print .Path "?limit=" .Limit "&lastFileName=" .LastFileName}} >
  149. Load more
  150. </a>
  151. </div>
  152. {{end}}
  153. <br/>
  154. <br/>
  155. <div id="progress-area" class="footer" style="display: none;">
  156. </div>
  157. </div>
  158. </body>
  159. <script type="text/javascript">
  160. // ************************ Drag and drop ***************** //
  161. let dropArea = document.getElementById("drop-area");
  162. let progressArea = document.getElementById("progress-area");
  163. // Prevent default drag behaviors
  164. ;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
  165. dropArea.addEventListener(eventName, preventDefaults, false);
  166. document.body.addEventListener(eventName, preventDefaults, false);
  167. });
  168. // Highlight drop area when item is dragged over it
  169. ;['dragenter', 'dragover'].forEach(eventName => {
  170. dropArea.addEventListener(eventName, highlight, false);
  171. });
  172. ;['dragleave', 'drop'].forEach(eventName => {
  173. dropArea.addEventListener(eventName, unhighlight, false);
  174. });
  175. // Handle dropped files
  176. dropArea.addEventListener('drop', handleDrop, false);
  177. function preventDefaults(e) {
  178. e.preventDefault();
  179. e.stopPropagation();
  180. }
  181. function highlight(e) {
  182. dropArea.classList.add('highlight');
  183. }
  184. function unhighlight(e) {
  185. dropArea.classList.remove('highlight');
  186. }
  187. function handleDrop(e) {
  188. var dt = e.dataTransfer;
  189. var files = dt.files;
  190. handleFiles(files);
  191. }
  192. var uploadList = {};
  193. function handleFiles(files) {
  194. files = [...files];
  195. files.forEach(startUpload);
  196. renderProgress();
  197. files.forEach(uploadFile);
  198. }
  199. function startUpload(file, i) {
  200. uploadList[file.name] = {'name': file.name, 'percent': 0, 'finish': false};
  201. }
  202. function renderProgress() {
  203. var values = Object.values(uploadList);
  204. var html = '<table class="table">\n<tr><th>Uploading</th><\/tr>\n';
  205. for (let i of values) {
  206. var progressBarClass = 'progress-bar-striped active';
  207. if (i.percent >= 100) {
  208. progressBarClass = 'progress-bar-success';
  209. }
  210. html += '<tr>\n<td>\n';
  211. html += '<div class="progress" style="margin-bottom: 2px;">\n';
  212. html += '<div class="progress-bar ' + progressBarClass + '" role="progressbar" aria-valuenow="' + '100" aria-valuemin="0" aria-valuemax="100" style="width:' + i.percent + '%;">';
  213. html += '<span style="margin-right: 10px;">' + i.name + '</span>' + i.percent + '%<\/div>';
  214. html += '<\/div>\n<\/td>\n<\/tr>\n';
  215. }
  216. html += '<\/table>\n';
  217. progressArea.innerHTML = html;
  218. if (values.length > 0) {
  219. progressArea.attributes.style.value = '';
  220. }
  221. }
  222. function reportProgress(file, percent) {
  223. var item = uploadList[file]
  224. item.percent = percent;
  225. renderProgress();
  226. }
  227. function finishUpload(file) {
  228. uploadList[file]['finish'] = true;
  229. renderProgress();
  230. var allFinish = true;
  231. for (let i of Object.values(uploadList)) {
  232. if (!i.finish) {
  233. allFinish = false;
  234. break;
  235. }
  236. }
  237. if (allFinish) {
  238. console.log('All Finish');
  239. window.location.reload();
  240. }
  241. }
  242. function uploadFile(file, i) {
  243. var url = window.location.href;
  244. var xhr = new XMLHttpRequest();
  245. var fileName = file.name;
  246. xhr.upload.addEventListener('progress', function(e) {
  247. if (e.lengthComputable) {
  248. var percent = Math.ceil((e.loaded / e.total) * 100);
  249. reportProgress(fileName, percent)
  250. }
  251. });
  252. xhr.upload.addEventListener('loadend', function(e) {
  253. finishUpload(fileName);
  254. });
  255. var formData = new FormData();
  256. xhr.open('POST', url, true);
  257. formData.append('file', file);
  258. xhr.send(formData);
  259. }
  260. function handleCreateDir() {
  261. var dirName = prompt('Folder Name:', '');
  262. dirName = dirName.trim();
  263. if (dirName == null || dirName == '') {
  264. return;
  265. }
  266. var baseUrl = window.location.href;
  267. if (!baseUrl.endsWith('/')) {
  268. baseUrl += '/';
  269. }
  270. var url = baseUrl + dirName;
  271. if (!url.endsWith('/')) {
  272. url += '/';
  273. }
  274. var xhr = new XMLHttpRequest();
  275. xhr.open('POST', url, false);
  276. xhr.setRequestHeader('Content-Type', '');
  277. xhr.send();
  278. window.location.reload();
  279. }
  280. function handleRename(originName, basePath) {
  281. var newName = prompt('New Name:', originName);
  282. if (newName == null || newName == '') {
  283. return;
  284. }
  285. var url = basePath + newName;
  286. var originPath = basePath + originName;
  287. url += '?mv.from=' + originPath;
  288. var xhr = new XMLHttpRequest();
  289. xhr.open('POST', url, false);
  290. xhr.setRequestHeader('Content-Type', '');
  291. xhr.send();
  292. window.location.reload();
  293. }
  294. function handleDelete(path) {
  295. if (!confirm('Are you sure to delete ' + path + '?')) {
  296. return;
  297. }
  298. var url = path;
  299. if (url.endsWith('/')) {
  300. url += '?recursive=true';
  301. }
  302. var xhr = new XMLHttpRequest();
  303. xhr.open('DELETE', url, false);
  304. xhr.send();
  305. window.location.reload();
  306. }
  307. </script>
  308. </html>