lfss 0.2.3__py3-none-any.whl → 0.2.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
frontend/index.html CHANGED
@@ -13,7 +13,7 @@
13
13
  <label for="endpoint">Endpoint</label>
14
14
  <input type="text" id="endpoint" placeholder="http://localhost:8000" autocomplete="off">
15
15
  </div>
16
- <div class="input-group" style="min-width: 800px;">
16
+ <div class="input-group" style="min-width: 300px;">
17
17
  <label for="token">Token</label>
18
18
  <input type="text" id="token" placeholder="" autocomplete="off">
19
19
  </div>
frontend/popup.css CHANGED
@@ -27,4 +27,27 @@ div.floating-window.window{
27
27
  flex-direction: column;
28
28
  justify-content: center;
29
29
  align-items: center;
30
+ }
31
+
32
+ div.popup-window{
33
+ position: fixed;
34
+ top: 0.5rem;
35
+ right: 1rem;
36
+ border-radius: 0.5rem;
37
+ box-shadow: 0 5px 10px rgba(0,0,0,0.2);
38
+ color: white;
39
+ display: block;
40
+ text-align: left;
41
+ animation: popup-appear 0.5s ease;
42
+ }
43
+
44
+ @keyframes popup-appear{
45
+ from{
46
+ opacity: 0;
47
+ transform: translateX(1rem);
48
+ }
49
+ to{
50
+ opacity: 1;
51
+ transform: translateX(0);
52
+ }
30
53
  }
frontend/popup.js CHANGED
@@ -86,4 +86,45 @@ export function showFloatingWindowLineInput(onSubmit = (v) => {}, {
86
86
  }
87
87
 
88
88
  return [floatingWindow, closeWindow];
89
+ }
90
+
91
+ const shownPopups = [];
92
+ export function showPopup(content = '', {
93
+ level = "info",
94
+ width = "auto",
95
+ timeout = 3000,
96
+ showTime = true
97
+ } = {}){
98
+ const popup = document.createElement("div");
99
+ popup.classList.add("popup-window");
100
+ popup.innerHTML = showTime? `<span>[${new Date().toLocaleTimeString()}]</span> ${content}` : content;
101
+ popup.style.width = width;
102
+ const popupHeight = '1rem';
103
+ popup.style.height = popupHeight;
104
+ popup.style.maxHeight = popupHeight;
105
+ popup.style.minHeight = popupHeight;
106
+ const paddingHeight = '1rem';
107
+ popup.style.padding = paddingHeight;
108
+
109
+ // traverse shownPopups and update the top position of each popup
110
+ if (shownPopups.length > 0) {
111
+ for (let i = 0; i < shownPopups.length; i++) {
112
+ shownPopups[i].style.top = `${i * (parseInt(popupHeight) + 2*parseInt(paddingHeight))*1.2 + 0.5}rem`;
113
+ }
114
+ }
115
+ popup.style.top = `${shownPopups.length * (parseInt(popupHeight) + 2*parseInt(paddingHeight))*1.2 + 0.5}rem`;
116
+
117
+ if (level === "error") popup.style.backgroundColor = "darkred";
118
+ if (level === "warning") popup.style.backgroundColor = "darkorange";
119
+ if (level === "info") popup.style.backgroundColor = "darkblue";
120
+ if (level === "success") popup.style.backgroundColor = "darkgreen";
121
+ document.body.appendChild(popup);
122
+ shownPopups.push(popup);
123
+ window.setTimeout(() => {
124
+ if (popup.parentNode) document.body.removeChild(popup);
125
+ shownPopups.splice(shownPopups.indexOf(popup), 1);
126
+ for (let i = 0; i < shownPopups.length; i++) {
127
+ shownPopups[i].style.top = `${i * (parseInt(popupHeight) + 2*parseInt(paddingHeight))*1.2 + 0.5}rem`;
128
+ }
129
+ }, timeout);
89
130
  }
frontend/scripts.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import Connector from './api.js';
2
2
  import { permMap } from './api.js';
3
- import { showFloatingWindowLineInput } from './popup.js';
3
+ import { showFloatingWindowLineInput, showPopup } from './popup.js';
4
4
  import { formatSize, decodePathURI, ensurePathURI, copyToClipboard, getRandomString, cvtGMT2Local, debounce, encodePathURI } from './utils.js';
5
5
 
6
6
  const conn = new Connector();
@@ -168,7 +168,12 @@ Are you sure you want to proceed?
168
168
  let counter = 0;
169
169
  async function uploadFile(...args){
170
170
  const [file, path] = args;
171
- await conn.put(path, file);
171
+ try{
172
+ await conn.put(path, file);
173
+ }
174
+ catch (err){
175
+ showPopup('Failed to upload file [' + file.name + ']: ' + err, {level: 'error', timeout: 5000});
176
+ }
172
177
  counter += 1;
173
178
  console.log("Uploading file: ", counter, "/", files.length);
174
179
  }
@@ -261,6 +266,7 @@ function refreshFileList(){
261
266
 
262
267
  const deleteButton = document.createElement('a');
263
268
  deleteButton.textContent = 'Delete';
269
+ deleteButton.classList.add('delete-btn');
264
270
  deleteButton.href = '#';
265
271
  deleteButton.addEventListener('click', () => {
266
272
  const dirurl = dir.url + (dir.url.endsWith('/') ? '' : '/');
@@ -270,6 +276,8 @@ function refreshFileList(){
270
276
  conn.delete(dirurl)
271
277
  .then(() => {
272
278
  refreshFileList();
279
+ }, (err)=>{
280
+ showPopup('Failed to delete path: ' + err, {level: 'error', timeout: 5000});
273
281
  });
274
282
  });
275
283
  actContainer.appendChild(deleteButton);
@@ -332,7 +340,12 @@ function refreshFileList(){
332
340
  console.warn("Permission string mismatch", permStr, permStrFromMap);
333
341
  }
334
342
  }
335
- conn.setFilePermission(file.url, perm)
343
+ conn.setFilePermission(file.url, perm).then(
344
+ () => {},
345
+ (err) => {
346
+ showPopup('Failed to set permission: ' + err, {level: 'error', timeout: 5000});
347
+ }
348
+ );
336
349
  });
337
350
 
338
351
  accessTd.appendChild(select);
@@ -345,23 +358,24 @@ function refreshFileList(){
345
358
  const actContainer = document.createElement('div');
346
359
  actContainer.classList.add('action-container');
347
360
 
348
- const viewButton = document.createElement('a');
349
- viewButton.textContent = 'View';
350
- viewButton.href = conn.config.endpoint + '/' + file.url + '?token=' + conn.config.token;
351
- viewButton.target = '_blank';
352
- actContainer.appendChild(viewButton);
353
-
354
361
  const copyButton = document.createElement('a');
362
+ copyButton.style.cursor = 'pointer';
355
363
  copyButton.textContent = 'Share';
356
- copyButton.href = '#';
357
364
  copyButton.addEventListener('click', () => {
358
365
  copyToClipboard(conn.config.endpoint + '/' + file.url);
366
+ showPopup('Link copied to clipboard', {level: "success"});
359
367
  });
360
368
  actContainer.appendChild(copyButton);
361
369
 
370
+ const viewButton = document.createElement('a');
371
+ viewButton.textContent = 'View';
372
+ viewButton.href = conn.config.endpoint + '/' + file.url + '?token=' + conn.config.token;
373
+ viewButton.target = '_blank';
374
+ actContainer.appendChild(viewButton);
375
+
362
376
  const moveButton = document.createElement('a');
363
377
  moveButton.textContent = 'Move';
364
- moveButton.href = '#';
378
+ moveButton.style.cursor = 'pointer';
365
379
  moveButton.addEventListener('click', () => {
366
380
  showFloatingWindowLineInput((dstPath) => {
367
381
  dstPath = encodePathURI(dstPath);
@@ -371,7 +385,11 @@ function refreshFileList(){
371
385
  conn.moveFile(file.url, dstPath)
372
386
  .then(() => {
373
387
  refreshFileList();
374
- });
388
+ },
389
+ (err) => {
390
+ showPopup('Failed to move file: ' + err, {level: 'error'});
391
+ }
392
+ );
375
393
  }, {
376
394
  text: 'Enter the destination path: ',
377
395
  placeholder: 'Destination path',
@@ -388,6 +406,7 @@ function refreshFileList(){
388
406
 
389
407
  const deleteButton = document.createElement('a');
390
408
  deleteButton.textContent = 'Delete';
409
+ deleteButton.classList.add('delete-btn');
391
410
  deleteButton.href = '#';
392
411
  deleteButton.addEventListener('click', () => {
393
412
  if (!confirm('Are you sure you want to delete ' + file.url + '?')){
@@ -396,6 +415,8 @@ function refreshFileList(){
396
415
  conn.delete(file.url)
397
416
  .then(() => {
398
417
  refreshFileList();
418
+ }, (err) => {
419
+ showPopup('Failed to delete file: ' + err, {level: 'error', timeout: 5000});
399
420
  });
400
421
  });
401
422
  actContainer.appendChild(deleteButton);
frontend/styles.css CHANGED
@@ -14,7 +14,7 @@ input[type=button], button{
14
14
  padding: 0.8rem;
15
15
  margin: 0;
16
16
  border: none;
17
- border-radius: 0.2rem;
17
+ border-radius: 0.25rem;
18
18
  cursor: pointer;
19
19
  }
20
20
 
@@ -23,7 +23,7 @@ input[type=text], input[type=password]
23
23
  width: 100%;
24
24
  padding: 0.75rem;
25
25
  border: 1px solid #ccc;
26
- border-radius: 0.2rem;
26
+ border-radius: 0.25rem;
27
27
  height: 1rem;
28
28
  }
29
29
 
@@ -209,4 +209,12 @@ a{
209
209
  background-color: #195f8b;
210
210
  transform: scale(1.1);
211
211
  color: white;
212
+ }
213
+
214
+ .delete-btn{
215
+ color: darkred !important;
216
+ }
217
+ .delete-btn:hover{
218
+ color: white !important;
219
+ background-color: #990511c7 !important;
212
220
  }
lfss/src/server.py CHANGED
@@ -206,6 +206,8 @@ router_api = APIRouter(prefix="/_api")
206
206
  @router_api.get("/bundle")
207
207
  async def bundle_files(path: str, user: DBUserRecord = Depends(get_current_user)):
208
208
  logger.info(f"GET bundle({path}), user: {user.username}")
209
+ if user.id == 0:
210
+ raise HTTPException(status_code=401, detail="Permission denied")
209
211
  path = ensure_uri_compnents(path)
210
212
  assert path.endswith("/") or path == ""
211
213
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lfss
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: Lightweight file storage service
5
5
  Home-page: https://github.com/MenxLi/lfss
6
6
  Author: li, mengxun
@@ -2,11 +2,11 @@ Readme.md,sha256=HJTfAkTka7i9n8JaA_Sftn1RFOplCviJ16Rq4WsDOFc,1056
2
2
  docs/Known_issues.md,sha256=rfdG3j1OJF-59S9E06VPyn0nZKbW-ybPxkoZ7MEZWp8,81
3
3
  docs/Permission.md,sha256=EY1Y4tT5PDdHDb2pXsVgMAJXxkUihTZfPT0_z7Q53FA,1485
4
4
  frontend/api.js,sha256=D_MaQlmBGzzSR30a23Kh3DxPeF8MpKYe5uXSb9srRTU,7281
5
- frontend/index.html,sha256=2IOrJge3HmQLldKeqAI5Eqz2kkVr8NYlS434_9PwZ0I,2019
6
- frontend/popup.css,sha256=nbCHFZwt8XrCOODkxuYmLd14nKHAZsOZ5JLqUI6iTDQ,592
7
- frontend/popup.js,sha256=y4P1gaF05HwmhQZN-NRFYNbQ3zKBdoTXtTJpT3FTDQQ,2918
8
- frontend/scripts.js,sha256=L9lWhhprdAJOWSOCX9BywZzljm0yJ5PET5AaheCAGqg,16824
9
- frontend/styles.css,sha256=1klxag-IvfHm316Tj8CFHqJmhSCA3E6Pcdtj51mJMiY,3922
5
+ frontend/index.html,sha256=JP6Sd-1JdlEfWQ4fjmSs-CrNw-2iq1RlS55SuXJq5lg,2019
6
+ frontend/popup.css,sha256=VzkjG1ZTLxhHMtTyobnlvqYmVsTmdbJJed2Pu1cc06c,1007
7
+ frontend/popup.js,sha256=dH5n7C2Vo9gCsMfQ4ajL4D1ETl0Wk9rIldxUb7x0f_c,4634
8
+ frontend/scripts.js,sha256=TJCTPag_7Dyn6HDB6Px0WO5sroFEZ9IjYDKmFlb5e2c,18053
9
+ frontend/styles.css,sha256=Ql_-W5dbh6LlJL2Kez8qsqPSF2fckFgmOYP4SMfKJVs,4065
10
10
  frontend/utils.js,sha256=biE2te5ezswZyuwlDTYbHEt7VWKfCcUrusNt1lHjkLw,2263
11
11
  lfss/cli/panel.py,sha256=iGdVmdWYjA_7a78ZzWEB_3ggIOBeUKTzg6F5zLaB25c,1401
12
12
  lfss/cli/serve.py,sha256=bO3GT0kuylMGN-7bZWP4e71MlugGZ_lEMkYaYld_Ntg,985
@@ -16,9 +16,9 @@ lfss/src/config.py,sha256=rE2ZCpozSDqdFrVvpItfGb_O3wPh_ErotdLTZ1DA23s,280
16
16
  lfss/src/database.py,sha256=2ZgQGd6tDR6daXl3jOOBO1-mHEwNd809m16PopQ8DZg,27535
17
17
  lfss/src/error.py,sha256=S5ui3tJ0uKX4EZnt2Db-KbDmJXlECI_OY95cNMkuegc,218
18
18
  lfss/src/log.py,sha256=7mRHFwhx7GKtm_cRryoEIlRQhHTLQC3Qd-N81YsoKao,5174
19
- lfss/src/server.py,sha256=7fh2FLyyFeTgjgeM4GRtddVKUTb1qgzU0So0SOLcGB0,11488
19
+ lfss/src/server.py,sha256=j_dHb-EuPYhfkJ8pbGgJZLrOxWt1SqZmU2sP7FN3TBw,11582
20
20
  lfss/src/utils.py,sha256=MrjKc8W2Y7AbgVGadSNAA50tRMbGYWRrA4KUhOCwuUU,694
21
- lfss-0.2.3.dist-info/METADATA,sha256=YUcuXDq-7lTNFcMwMsxbp3uP3TcrjtDLBRUtDiackQQ,1715
22
- lfss-0.2.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
23
- lfss-0.2.3.dist-info/entry_points.txt,sha256=nUIJhenyZbcymvPVhrzV7SAtRhd7O52DflbRrpQUC04,110
24
- lfss-0.2.3.dist-info/RECORD,,
21
+ lfss-0.2.4.dist-info/METADATA,sha256=k-moYayIKGuaxwrLFVht5MccgOv35lafbX4ODCBaaUQ,1715
22
+ lfss-0.2.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
23
+ lfss-0.2.4.dist-info/entry_points.txt,sha256=nUIJhenyZbcymvPVhrzV7SAtRhd7O52DflbRrpQUC04,110
24
+ lfss-0.2.4.dist-info/RECORD,,
File without changes