lfss 0.8.3__py3-none-any.whl → 0.8.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.
Readme.md CHANGED
@@ -1,9 +1,17 @@
1
1
  # Lightweight File Storage Service (LFSS)
2
2
  [![PyPI](https://img.shields.io/pypi/v/lfss)](https://pypi.org/project/lfss/)
3
3
 
4
- My experiment on a lightweight and high-performance file/object storage service.
4
+ My experiment on a lightweight and high-performance file/object storage service...
5
+
6
+ **Highlights:**
7
+
8
+ - User storage limit and access control.
9
+ - Pagination and sorted file listing for vast number of files.
10
+ - High performance: high concurrency, near-native speed on stress tests.
11
+ - Support range requests, so you can stream large files / resume download.
12
+
5
13
  It stores small files and metadata in sqlite, large files in the filesystem.
6
- Tested on 2 million files, and it works fine...
14
+ Tested on 2 million files, and it is still fast.
7
15
 
8
16
  Usage:
9
17
  ```sh
@@ -22,8 +30,8 @@ lfss-panel --open
22
30
  Or, you can start a web server at `/frontend` and open `index.html` in your browser.
23
31
 
24
32
  The API usage is simple, just `GET`, `PUT`, `DELETE` to the `/<username>/file/url` path.
25
- Authentication is done via `Authorization` header with the value `Bearer <token>`, or through the `token` query parameter.
26
- You can refer to `frontend` as an application example, and `frontend/api.js` or `lfss/api/connector.py` for the API usage.
33
+ Authentication via `Authorization` header with the value `Bearer <token>`, or through the `token` query parameter.
34
+ You can refer to `frontend` as an application example, `lfss/api/connector.py` for more APIs.
27
35
 
28
36
  By default, the service exposes all files to the public for `GET` requests,
29
37
  but file-listing is restricted to the user's own files.
frontend/thumb.js CHANGED
@@ -39,10 +39,14 @@ function getIconSVGFromMimeType(mimeType){
39
39
  return ICON_ZIP;
40
40
  }
41
41
  if ([
42
- "text/html", "application/xhtml+xml", "application/xml", "text/css", "application/javascript", "text/javascript", "application/json", "text/x-python", "text/x-java-source",
43
- "application/x-httpd-php", "text/x-ruby", "text/x-perl", "application/x-sh", "application/sql", "text/x-c", "text/x-c++", "text/x-csharp", "text/x-go", "text/x-haskell",
44
- "text/x-lua", "text/x-markdown", "application/wasm", "application/x-tcl", "text/x-yaml", "application/x-latex", "application/x-tex", "text/x-scss", "application/x-lisp",
45
- "application/x-rust", "application/x-ruby", "text/x-asm"
42
+ "text/html", "application/xhtml+xml", "application/xml", "text/css", "text/x-scss", "application/javascript", "text/javascript",
43
+ "application/json", "text/x-yaml", "text/x-markdown", "application/wasm",
44
+ "text/x-ruby", "application/x-ruby", "text/x-perl", "application/x-lisp",
45
+ "text/x-haskell", "text/x-lua", "application/x-tcl",
46
+ "text/x-python", "text/x-java-source", "text/x-go", "application/x-rust", "text/x-asm",
47
+ "application/sql", "text/x-c", "text/x-c++", "text/x-csharp",
48
+ "application/x-httpd-php", "application/x-sh", "application/x-shellscript",
49
+ "application/x-latex", "application/x-tex",
46
50
  ].includes(mimeType)){
47
51
  return ICON_CODE;
48
52
  }
lfss/api/connector.py CHANGED
@@ -114,7 +114,6 @@ class Connector:
114
114
 
115
115
  if isinstance(file, str):
116
116
  assert os.path.exists(file), "File does not exist on disk"
117
- fsize = os.path.getsize(file)
118
117
 
119
118
  with open(file, 'rb') if isinstance(file, str) else SpooledTemporaryFile(max_size=1024*1024*32) as fp:
120
119
 
@@ -124,7 +123,6 @@ class Connector:
124
123
  fp.seek(0)
125
124
 
126
125
  # https://stackoverflow.com/questions/12385179/
127
- print(f"Uploading {fsize} bytes")
128
126
  response = self._fetch_factory('POST', path, search_params={
129
127
  'permission': int(permission),
130
128
  'conflict': conflict
lfss/cli/cli.py CHANGED
@@ -1,6 +1,6 @@
1
- from lfss.api import Connector, upload_directory, upload_file, download_file, download_directory
2
1
  from pathlib import Path
3
2
  import argparse, typing
3
+ from lfss.api import Connector, upload_directory, upload_file, download_file, download_directory
4
4
  from lfss.src.datatype import FileReadPermission, FileSortKey, DirSortKey
5
5
  from lfss.src.utils import decode_uri_compnents
6
6
  from . import catch_request_error, line_sep
@@ -18,7 +18,6 @@ def parse_permission(s: str) -> FileReadPermission:
18
18
 
19
19
  def parse_arguments():
20
20
  parser = argparse.ArgumentParser(description="Command line interface, please set LFSS_ENDPOINT and LFSS_TOKEN environment variables.")
21
- parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
22
21
 
23
22
  sp = parser.add_subparsers(dest="command", required=True)
24
23
 
@@ -26,6 +25,7 @@ def parse_arguments():
26
25
  sp_upload = sp.add_parser("upload", help="Upload files")
27
26
  sp_upload.add_argument("src", help="Source file or directory", type=str)
28
27
  sp_upload.add_argument("dst", help="Destination url path", type=str)
28
+ sp_upload.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
29
29
  sp_upload.add_argument("-j", "--jobs", type=int, default=1, help="Number of concurrent uploads")
30
30
  sp_upload.add_argument("--interval", type=float, default=0, help="Interval between files, only works with directory upload")
31
31
  sp_upload.add_argument("--conflict", choices=["overwrite", "abort", "skip", "skip-ahead"], default="abort", help="Conflict resolution")
@@ -36,6 +36,7 @@ def parse_arguments():
36
36
  sp_download = sp.add_parser("download", help="Download files")
37
37
  sp_download.add_argument("src", help="Source url path", type=str)
38
38
  sp_download.add_argument("dst", help="Destination file or directory", type=str)
39
+ sp_download.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
39
40
  sp_download.add_argument("-j", "--jobs", type=int, default=1, help="Number of concurrent downloads")
40
41
  sp_download.add_argument("--interval", type=float, default=0, help="Interval between files, only works with directory download")
41
42
  sp_download.add_argument("--overwrite", action="store_true", help="Overwrite existing files")
lfss/src/datatype.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from enum import IntEnum
2
2
  import dataclasses, typing
3
+ from .utils import fmt_storage_size
3
4
 
4
5
  class FileReadPermission(IntEnum):
5
6
  UNSET = 0 # not set
@@ -18,8 +19,12 @@ class UserRecord:
18
19
  max_storage: int
19
20
  permission: 'FileReadPermission'
20
21
 
22
+ def __post_init__(self):
23
+ self.permission = FileReadPermission(self.permission)
24
+
21
25
  def __str__(self):
22
- return f"User {self.username} (id={self.id}, admin={self.is_admin}, created at {self.create_time}, last active at {self.last_active}, storage={self.max_storage}, permission={self.permission})"
26
+ return f"User {self.username} (id={self.id}, admin={self.is_admin}, created at {self.create_time}, last active at {self.last_active}, " + \
27
+ f"storage={fmt_storage_size(self.max_storage)}, permission={self.permission.name})"
23
28
 
24
29
  @dataclasses.dataclass
25
30
  class FileRecord:
@@ -33,9 +38,12 @@ class FileRecord:
33
38
  external: bool
34
39
  mime_type: str
35
40
 
41
+ def __post_init__(self):
42
+ self.permission = FileReadPermission(self.permission)
43
+
36
44
  def __str__(self):
37
45
  return f"File {self.url} [{self.mime_type}] (owner={self.owner_id}, created at {self.create_time}, accessed at {self.access_time}, " + \
38
- f"file_id={self.file_id}, permission={self.permission}, size={self.file_size}, external={self.external})"
46
+ f"file_id={self.file_id}, permission={self.permission.name}, size={fmt_storage_size(self.file_size)}, external={self.external})"
39
47
 
40
48
  @dataclasses.dataclass
41
49
  class DirectoryRecord:
@@ -47,7 +55,7 @@ class DirectoryRecord:
47
55
  n_files: int = -1
48
56
 
49
57
  def __str__(self):
50
- return f"Directory {self.url} (size={self.size}, created at {self.create_time}, updated at {self.update_time}, accessed at {self.access_time}, n_files={self.n_files})"
58
+ return f"Directory {self.url} (size={fmt_storage_size(self.size)}, created at {self.create_time}, updated at {self.update_time}, accessed at {self.access_time}, n_files={self.n_files})"
51
59
 
52
60
  @dataclasses.dataclass
53
61
  class PathContents:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lfss
3
- Version: 0.8.3
3
+ Version: 0.8.4
4
4
  Summary: Lightweight file storage service
5
5
  Home-page: https://github.com/MenxLi/lfss
6
6
  Author: li, mengxun
@@ -24,9 +24,17 @@ Description-Content-Type: text/markdown
24
24
  # Lightweight File Storage Service (LFSS)
25
25
  [![PyPI](https://img.shields.io/pypi/v/lfss)](https://pypi.org/project/lfss/)
26
26
 
27
- My experiment on a lightweight and high-performance file/object storage service.
27
+ My experiment on a lightweight and high-performance file/object storage service...
28
+
29
+ **Highlights:**
30
+
31
+ - User storage limit and access control.
32
+ - Pagination and sorted file listing for vast number of files.
33
+ - High performance: high concurrency, near-native speed on stress tests.
34
+ - Support range requests, so you can stream large files / resume download.
35
+
28
36
  It stores small files and metadata in sqlite, large files in the filesystem.
29
- Tested on 2 million files, and it works fine...
37
+ Tested on 2 million files, and it is still fast.
30
38
 
31
39
  Usage:
32
40
  ```sh
@@ -45,8 +53,8 @@ lfss-panel --open
45
53
  Or, you can start a web server at `/frontend` and open `index.html` in your browser.
46
54
 
47
55
  The API usage is simple, just `GET`, `PUT`, `DELETE` to the `/<username>/file/url` path.
48
- Authentication is done via `Authorization` header with the value `Bearer <token>`, or through the `token` query parameter.
49
- You can refer to `frontend` as an application example, and `frontend/api.js` or `lfss/api/connector.py` for the API usage.
56
+ Authentication via `Authorization` header with the value `Bearer <token>`, or through the `token` query parameter.
57
+ You can refer to `frontend` as an application example, `lfss/api/connector.py` for more APIs.
50
58
 
51
59
  By default, the service exposes all files to the public for `GET` requests,
52
60
  but file-listing is restricted to the user's own files.
@@ -1,4 +1,4 @@
1
- Readme.md,sha256=LpbTvUWjCOv4keMNDrZvEnNAmCQnvaxvlq2srWixXn0,1299
1
+ Readme.md,sha256=J1tGk7B9EyIXT-RN7VGz_229UeKvZHVLpn1FvzNDxL4,1538
2
2
  docs/Known_issues.md,sha256=rfdG3j1OJF-59S9E06VPyn0nZKbW-ybPxkoZ7MEZWp8,81
3
3
  docs/Permission.md,sha256=9r9nEmhqfz18RTS8FI0fZ9F0a31r86OoAyx3EQxxpk0,2317
4
4
  frontend/api.js,sha256=wUJNAkL8QigAiwR_jaMPUhCQEsL-lp0wZ6XeueYgunE,18049
@@ -13,13 +13,13 @@ frontend/scripts.js,sha256=nWH6NgavZTVmjK44i2DeRi6mJzGSe4qeQPUbDaEVt58,21735
13
13
  frontend/state.js,sha256=vbNL5DProRKmSEY7xu9mZH6IY0PBenF8WGxPtGgDnLI,1680
14
14
  frontend/styles.css,sha256=xcNLqI3KBsY5TLnku8UIP0Jfr7QLajr1_KNlZj9eheM,4935
15
15
  frontend/thumb.css,sha256=rNsx766amYS2DajSQNabhpQ92gdTpNoQKmV69OKvtpI,295
16
- frontend/thumb.js,sha256=tX9LtxT6O2O6IUlpdvID6S973SUpWxgPVVqI9pwlVw8,6113
16
+ frontend/thumb.js,sha256=46ViD2TlTTWy0fx6wjoAs_5CQ4ajYB90vVzM7UO2IHw,6182
17
17
  frontend/utils.js,sha256=IYUZl77ugiXKcLxSNOWC4NSS0CdD5yRgUsDb665j0xM,2556
18
18
  lfss/api/__init__.py,sha256=c_-GokKJ_3REgve16AwgXRfFyl9mwy7__FxCIIVlk1Q,6660
19
- lfss/api/connector.py,sha256=tCUwTlbzTwHvPnFb8nlnc6LEnrXwdCnCCThyBISt2Tg,11319
19
+ lfss/api/connector.py,sha256=e2nhqrRGWixSJXRVDBxadq9oeiL0sGWLaL7FFzvLFJ8,11231
20
20
  lfss/cli/__init__.py,sha256=lPwPmqpa7EXQ4zlU7E7LOe6X2kw_xATGdwoHphUEirA,827
21
21
  lfss/cli/balance.py,sha256=R2rbO2tg9TVnnQIVeU0GJVeMS-5LDhEdk4mbOE9qGq0,4121
22
- lfss/cli/cli.py,sha256=LxUrviHtsqi-vs_GWZw2qRs9dBNvx9PSQHLW6SwUmhA,8167
22
+ lfss/cli/cli.py,sha256=WVxDtIYCgFkEp9HoVLGi7AAhZJi5BCML7uT5D4yVcuE,8262
23
23
  lfss/cli/panel.py,sha256=iGdVmdWYjA_7a78ZzWEB_3ggIOBeUKTzg6F5zLaB25c,1401
24
24
  lfss/cli/serve.py,sha256=T-jz_PJcaY9FJRRfyWV9MbjDI73YdOOCn4Nr2PO-s0c,993
25
25
  lfss/cli/user.py,sha256=uqHQ7onddTjJAYg3B1DIc8hDl0aCkIMZolLKhQrBd0k,4046
@@ -31,14 +31,14 @@ lfss/src/bounded_pool.py,sha256=BI1dU-MBf82TMwJBYbjhEty7w1jIUKc5Bn9SnZ_-hoY,1288
31
31
  lfss/src/config.py,sha256=7k_6L_aG7vD-lVcmgpwMIg-ggpMMDNU8IbaB4y1effE,861
32
32
  lfss/src/connection_pool.py,sha256=4YULPcmndy33FyBSo9w1fZjAlBX2q-xPP27xJOTA06I,5228
33
33
  lfss/src/database.py,sha256=zoiBm7CVHHV4TqwmK6lPnZvK9mzDNtrNvAJCRaIYMU8,36302
34
- lfss/src/datatype.py,sha256=yyOcxhGwz-EJi003f8hGl82EJuY4F92y6fSX6cK60Bc,2126
34
+ lfss/src/datatype.py,sha256=1xdxSKhpJXoBKumUokL3zQ2VyZ0Wwp8q6PaJf1idVw0,2435
35
35
  lfss/src/error.py,sha256=Bh_GUtuNsMSMIKbFreU4CfZzL96TZdNAIcgmDxvGbQ0,333
36
36
  lfss/src/log.py,sha256=u6WRZZsE7iOx6_CV2NHh1ugea26p408FI4WstZh896A,5139
37
37
  lfss/src/server.py,sha256=YLsp6bab7q0I2hI4uUYIiWc2S0k6d6bbMaweg6VbVV4,23743
38
38
  lfss/src/stat.py,sha256=WlRiiAl0rHX9oPi3thi6K4GKn70WHpClSDCt7iZUow4,3197
39
39
  lfss/src/thumb.py,sha256=qjCNMpnCozMuzkhm-2uAYy1eAuYTeWG6xqs-13HX-7k,3266
40
40
  lfss/src/utils.py,sha256=DxjHabdiISMkrm1WQlpsZFKL3by6YrzBNQaDt_uZlRk,5744
41
- lfss-0.8.3.dist-info/METADATA,sha256=2Q3LdTB3vX_i8kpXIJ9SmMv-GLC1kslzN0RUBW9ksSA,2059
42
- lfss-0.8.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
43
- lfss-0.8.3.dist-info/entry_points.txt,sha256=VJ8svMz7RLtMCgNk99CElx7zo7M-N-z7BWDVw2HA92E,205
44
- lfss-0.8.3.dist-info/RECORD,,
41
+ lfss-0.8.4.dist-info/METADATA,sha256=OUvtod8R5Z7DnNLczVG6FpsHEQ2J1ct8QlbdZhU2fIk,2298
42
+ lfss-0.8.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
43
+ lfss-0.8.4.dist-info/entry_points.txt,sha256=VJ8svMz7RLtMCgNk99CElx7zo7M-N-z7BWDVw2HA92E,205
44
+ lfss-0.8.4.dist-info/RECORD,,
File without changes