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.

354 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: 39px;
  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()">
  80. <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> New Folder
  81. </label>
  82. <label class="btn btn-default" for="fileElem">
  83. <span class="glyphicon glyphicon-cloud-upload" aria-hidden="true"></span> Upload
  84. </label>
  85. </div>
  86. <ol class="breadcrumb">
  87. {{ range $entry := .Breadcrumbs }}
  88. <li><a href="{{ printpath $entry.Link }}">
  89. {{ $entry.Name }}
  90. </li></a>
  91. {{ end }}
  92. </ol>
  93. </div>
  94. </div>
  95. <div class="row" id="drop-area">
  96. <form class="upload-form">
  97. <input type="file" id="fileElem" multiple onchange="handleFiles(this.files)">
  98. {{if .EmptyFolder}}
  99. <div class="row add-files">
  100. +
  101. </div>
  102. {{else}}
  103. <table width="100%" class="table table-hover">
  104. {{$path := .Path }}
  105. {{ range $entry_index, $entry := .Entries }}
  106. <tr>
  107. <td>
  108. {{if $entry.IsDirectory}}
  109. <span class="glyphicon glyphicon-folder-open" aria-hidden="true"></span>&nbsp;
  110. <a href="{{ printpath $path "/" $entry.Name "/"}}" >
  111. {{ $entry.Name }}
  112. </a>
  113. {{else}}
  114. <a href="{{ printpath $path "/" $entry.Name }}" >
  115. {{ $entry.Name }}
  116. </a>
  117. {{end}}
  118. </td>
  119. <td align="right" nowrap>
  120. {{if $entry.IsDirectory}}
  121. {{else}}
  122. {{ $entry.Mime }}&nbsp;
  123. {{end}}
  124. </td>
  125. <td align="right" nowrap>
  126. {{if $entry.IsDirectory}}
  127. {{else}}
  128. {{ $entry.Size | humanizeBytes }}&nbsp;
  129. {{end}}
  130. </td>
  131. <td align="right" nowrap>
  132. {{ $entry.Timestamp.Format "2006-01-02 15:04" }}
  133. </td>
  134. <td style="width:75px">
  135. <div class="btn-group btn-group-xs pull-right operations" role="group">
  136. <label class="btn" onclick="handleRename('{{ $entry.Name }}', '{{ printpath $path "/" }}')">
  137. <span class="glyphicon glyphicon-edit" aria-hidden="true"></span>
  138. </label>
  139. {{if $entry.IsDirectory}}
  140. <label class="btn" onclick="handleDelete('{{ printpath $path "/" $entry.Name "/" }}')">
  141. <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
  142. </label>
  143. {{else}}
  144. <label class="btn" onclick="handleDelete('{{ printpath $path "/" $entry.Name }}')">
  145. <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
  146. </label>
  147. {{end}}
  148. </div>
  149. </td>
  150. </tr>
  151. {{ end }}
  152. </table>
  153. {{end}}
  154. </form>
  155. </div>
  156. {{if .ShouldDisplayLoadMore}}
  157. <div class="row">
  158. <a href={{ print .Path "?limit=" .Limit "&lastFileName=" .LastFileName}} >
  159. Load more
  160. </a>
  161. </div>
  162. {{end}}
  163. <br/>
  164. <br/>
  165. <div id="progress-area" class="footer" style="display: none;">
  166. </div>
  167. </div>
  168. </body>
  169. <script type="text/javascript">
  170. // ************************ Drag and drop ***************** //
  171. let dropArea = document.getElementById("drop-area");
  172. let progressArea = document.getElementById("progress-area");
  173. // Prevent default drag behaviors
  174. ;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
  175. dropArea.addEventListener(eventName, preventDefaults, false);
  176. document.body.addEventListener(eventName, preventDefaults, false);
  177. });
  178. // Highlight drop area when item is dragged over it
  179. ;['dragenter', 'dragover'].forEach(eventName => {
  180. dropArea.addEventListener(eventName, highlight, false);
  181. });
  182. ;['dragleave', 'drop'].forEach(eventName => {
  183. dropArea.addEventListener(eventName, unhighlight, false);
  184. });
  185. // Handle dropped files
  186. dropArea.addEventListener('drop', handleDrop, false);
  187. function preventDefaults(e) {
  188. e.preventDefault();
  189. e.stopPropagation();
  190. }
  191. function highlight(e) {
  192. dropArea.classList.add('highlight');
  193. }
  194. function unhighlight(e) {
  195. dropArea.classList.remove('highlight');
  196. }
  197. function handleDrop(e) {
  198. var dt = e.dataTransfer;
  199. var files = dt.files;
  200. handleFiles(files);
  201. }
  202. var uploadList = {};
  203. function handleFiles(files) {
  204. files = [...files];
  205. files.forEach(startUpload);
  206. renderProgress();
  207. files.forEach(uploadFile);
  208. }
  209. function startUpload(file, i) {
  210. uploadList[file.name] = {'name': file.name, 'percent': 0, 'finish': false};
  211. }
  212. function renderProgress() {
  213. var values = Object.values(uploadList);
  214. var html = '<table class="table">\n<tr><th>Uploading</th><\/tr>\n';
  215. for (let i of values) {
  216. var progressBarClass = 'progress-bar-striped active';
  217. if (i.percent >= 100) {
  218. progressBarClass = 'progress-bar-success';
  219. }
  220. html += '<tr>\n<td>\n';
  221. html += '<div class="progress" style="margin-bottom: 2px;">\n';
  222. html += '<div class="progress-bar ' + progressBarClass + '" role="progressbar" aria-valuenow="' + '100" aria-valuemin="0" aria-valuemax="100" style="width:' + i.percent + '%;">';
  223. html += '<span style="margin-right: 10px;">' + i.name + '</span>' + i.percent + '%<\/div>';
  224. html += '<\/div>\n<\/td>\n<\/tr>\n';
  225. }
  226. html += '<\/table>\n';
  227. progressArea.innerHTML = html;
  228. if (values.length > 0) {
  229. progressArea.attributes.style.value = '';
  230. }
  231. }
  232. function reportProgress(file, percent) {
  233. var item = uploadList[file]
  234. item.percent = percent;
  235. renderProgress();
  236. }
  237. function finishUpload(file) {
  238. uploadList[file]['finish'] = true;
  239. renderProgress();
  240. var allFinish = true;
  241. for (let i of Object.values(uploadList)) {
  242. if (!i.finish) {
  243. allFinish = false;
  244. break;
  245. }
  246. }
  247. if (allFinish) {
  248. console.log('All Finish');
  249. window.location.reload();
  250. }
  251. }
  252. function uploadFile(file, i) {
  253. var url = window.location.href;
  254. var xhr = new XMLHttpRequest();
  255. var fileName = file.name;
  256. xhr.upload.addEventListener('progress', function(e) {
  257. if (e.lengthComputable) {
  258. var percent = Math.ceil((e.loaded / e.total) * 100);
  259. reportProgress(fileName, percent)
  260. }
  261. });
  262. xhr.upload.addEventListener('loadend', function(e) {
  263. finishUpload(fileName);
  264. });
  265. var formData = new FormData();
  266. xhr.open('POST', url, true);
  267. formData.append('file', file);
  268. xhr.send(formData);
  269. }
  270. function handleCreateDir() {
  271. var dirName = prompt('Folder Name:', '');
  272. dirName = dirName.trim();
  273. if (dirName == null || dirName == '') {
  274. return;
  275. }
  276. var baseUrl = window.location.href;
  277. if (!baseUrl.endsWith('/')) {
  278. baseUrl += '/';
  279. }
  280. var url = baseUrl + dirName;
  281. if (!url.endsWith('/')) {
  282. url += '/';
  283. }
  284. var xhr = new XMLHttpRequest();
  285. xhr.open('POST', url, false);
  286. xhr.setRequestHeader('Content-Type', '');
  287. xhr.send();
  288. window.location.reload();
  289. }
  290. function handleRename(originName, basePath) {
  291. var newName = prompt('New Name:', originName);
  292. if (newName == null || newName == '') {
  293. return;
  294. }
  295. var url = basePath + newName;
  296. var originPath = basePath + originName;
  297. url += '?mv.from=' + originPath;
  298. var xhr = new XMLHttpRequest();
  299. xhr.open('POST', url, false);
  300. xhr.setRequestHeader('Content-Type', '');
  301. xhr.send();
  302. window.location.reload();
  303. }
  304. function handleDelete(path) {
  305. if (!confirm('Are you sure to delete ' + path + '?')) {
  306. return;
  307. }
  308. var url = path;
  309. if (url.endsWith('/')) {
  310. url += '?recursive=true';
  311. }
  312. var xhr = new XMLHttpRequest();
  313. xhr.open('DELETE', url, false);
  314. xhr.send();
  315. window.location.reload();
  316. }
  317. </script>
  318. </html>