lfss 0.8.3__tar.gz → 0.8.4__tar.gz
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.
- {lfss-0.8.3 → lfss-0.8.4}/PKG-INFO +13 -5
- {lfss-0.8.3 → lfss-0.8.4}/Readme.md +12 -4
- {lfss-0.8.3 → lfss-0.8.4}/frontend/thumb.js +8 -4
- {lfss-0.8.3 → lfss-0.8.4}/lfss/api/connector.py +0 -2
- {lfss-0.8.3 → lfss-0.8.4}/lfss/cli/cli.py +3 -2
- {lfss-0.8.3 → lfss-0.8.4}/lfss/src/datatype.py +11 -3
- {lfss-0.8.3 → lfss-0.8.4}/pyproject.toml +1 -1
- {lfss-0.8.3 → lfss-0.8.4}/docs/Known_issues.md +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/docs/Permission.md +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/frontend/api.js +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/frontend/index.html +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/frontend/info.css +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/frontend/info.js +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/frontend/login.css +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/frontend/login.js +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/frontend/popup.css +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/frontend/popup.js +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/frontend/scripts.js +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/frontend/state.js +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/frontend/styles.css +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/frontend/thumb.css +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/frontend/utils.js +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/api/__init__.py +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/cli/__init__.py +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/cli/balance.py +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/cli/panel.py +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/cli/serve.py +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/cli/user.py +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/cli/vacuum.py +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/sql/init.sql +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/sql/pragma.sql +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/src/__init__.py +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/src/bounded_pool.py +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/src/config.py +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/src/connection_pool.py +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/src/database.py +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/src/error.py +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/src/log.py +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/src/server.py +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/src/stat.py +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/src/thumb.py +0 -0
- {lfss-0.8.3 → lfss-0.8.4}/lfss/src/utils.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: lfss
|
3
|
-
Version: 0.8.
|
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
|
[](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
|
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
|
49
|
-
You can refer to `frontend` as an application example,
|
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,9 +1,17 @@
|
|
1
1
|
# Lightweight File Storage Service (LFSS)
|
2
2
|
[](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
|
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
|
26
|
-
You can refer to `frontend` as an application example,
|
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.
|
@@ -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", "
|
43
|
-
"application/
|
44
|
-
"text/x-
|
45
|
-
"
|
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
|
}
|
@@ -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
|
@@ -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")
|
@@ -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
|
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:
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|