lfss 0.11.4__py3-none-any.whl → 0.11.5__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.
docs/Client.md ADDED
@@ -0,0 +1,37 @@
1
+
2
+ # Client-side CLI tools
3
+
4
+ To install python CLI tools without dependencies (to avoid conflicts with your existing packages):
5
+ ```sh
6
+ pip install requests
7
+ pip install lfss --no-deps
8
+ ```
9
+
10
+ Then set the `LFSS_ENDPOINT`, `LFSS_TOKEN` environment variables,
11
+ then you can use the following commands:
12
+ ```sh
13
+ # Query a path
14
+ lfss query remote/file[/or_dir/]
15
+
16
+ # List directories of a specified path
17
+ lfss list-dirs remote/dir/
18
+
19
+ # List files of a specified path,
20
+ # with pagination and sorting
21
+ lfss list-files --offset 0 --limit 100 --order access_time remote/dir/
22
+
23
+ # Upload a file
24
+ lfss upload local/file.txt remote/file.txt
25
+
26
+ # Upload a directory, note the ending slashes
27
+ lfss upload local/dir/ remote/dir/
28
+
29
+ # Download a file
30
+ lfss download remote/file.txt local/file.txt
31
+
32
+ # Download a directory, with verbose output and 8 concurrent jobs
33
+ # Overwrite existing files
34
+ lfss download -v -j 8 --conflict overwrite remote/dir/ local/dir/
35
+ ```
36
+
37
+ More commands can be found using `lfss-cli --help`.
docs/changelog.md CHANGED
@@ -1,5 +1,21 @@
1
1
  ## 0.11
2
2
 
3
+ ### 0.11.5
4
+ - Script entry default to client CLI.
5
+ - Fix single file download name deduce with decoding.
6
+ - Fix code misspell (minor).
7
+
8
+ ### 0.11.4
9
+ - Fix SQL query for LIKE clause to escape special characters in path.
10
+
11
+ ### 0.11.3
12
+ - Add method to get multiple files, maybe with content, at once.
13
+ - Allow copy directory files that the user is not the owner of.
14
+ - Environment variables to set origin and disable file logging.
15
+ - Fix error handling for some endpoints.
16
+ - Redirect CLI error output to stderr.
17
+ - Increase thumb image size to 64x64.
18
+
3
19
  ### 0.11.2
4
20
  - Improve frontend directory upload feedback.
5
21
  - Set default large file threashold to 1M.
lfss/api/__init__.py CHANGED
@@ -2,7 +2,7 @@ import os, time, pathlib
2
2
  from threading import Lock
3
3
  from .connector import Connector
4
4
  from ..eng.datatype import FileRecord
5
- from ..eng.utils import decode_uri_compnents
5
+ from ..eng.utils import decode_uri_components
6
6
  from ..eng.bounded_pool import BoundedThreadPoolExecutor
7
7
 
8
8
  def upload_file(
@@ -105,7 +105,7 @@ def download_file(
105
105
  assert not src_url.endswith('/'), "Source URL must not end with a slash."
106
106
  while this_try <= n_retries:
107
107
  if os.path.isdir(file_path):
108
- fname = src_url.split('/')[-1]
108
+ fname = decode_uri_components(src_url.split('/')[-1])
109
109
  file_path = os.path.join(file_path, fname)
110
110
 
111
111
  if not overwrite and os.path.exists(file_path):
@@ -176,7 +176,7 @@ def download_directory(
176
176
  with _counter_lock:
177
177
  _counter += 1
178
178
  this_count = _counter
179
- dst_path = f"{directory}{os.path.relpath(decode_uri_compnents(src_url), decode_uri_compnents(src_path))}"
179
+ dst_path = f"{directory}{os.path.relpath(decode_uri_components(src_url), decode_uri_components(src_path))}"
180
180
  if verbose:
181
181
  print(f"[{this_count}/{file_count}] Downloading {src_url} to {dst_path}")
182
182
 
lfss/api/connector.py CHANGED
@@ -11,7 +11,7 @@ from lfss.eng.datatype import (
11
11
  FileReadPermission, FileRecord, DirectoryRecord, UserRecord, PathContents,
12
12
  FileSortKey, DirSortKey
13
13
  )
14
- from lfss.eng.utils import ensure_uri_compnents
14
+ from lfss.eng.utils import ensure_uri_components
15
15
 
16
16
  _default_endpoint = os.environ.get('LFSS_ENDPOINT', 'http://localhost:8000')
17
17
  _default_token = os.environ.get('LFSS_TOKEN', '')
@@ -74,7 +74,7 @@ class Connector:
74
74
  ):
75
75
  if path.startswith('/'):
76
76
  path = path[1:]
77
- path = ensure_uri_compnents(path)
77
+ path = ensure_uri_components(path)
78
78
  def f(**kwargs):
79
79
  search_params_t = [
80
80
  (k, str(v).lower() if isinstance(v, bool) else v)
lfss/cli/cli.py CHANGED
@@ -2,7 +2,7 @@ from pathlib import Path
2
2
  import argparse, typing, sys
3
3
  from lfss.api import Connector, upload_directory, upload_file, download_file, download_directory
4
4
  from lfss.eng.datatype import FileReadPermission, FileSortKey, DirSortKey
5
- from lfss.eng.utils import decode_uri_compnents
5
+ from lfss.eng.utils import decode_uri_components
6
6
  from . import catch_request_error, line_sep
7
7
 
8
8
  def parse_permission(s: str) -> FileReadPermission:
@@ -143,7 +143,7 @@ def main():
143
143
  order_desc=args.reverse,
144
144
  )
145
145
  for i, f in enumerate(line_sep(res)):
146
- f.url = decode_uri_compnents(f.url)
146
+ f.url = decode_uri_components(f.url)
147
147
  print(f"[{i+1}] {f if args.long else f.url}")
148
148
 
149
149
  if len(res) == args.limit:
@@ -160,7 +160,7 @@ def main():
160
160
  order_desc=args.reverse,
161
161
  )
162
162
  for i, d in enumerate(line_sep(res)):
163
- d.url = decode_uri_compnents(d.url)
163
+ d.url = decode_uri_components(d.url)
164
164
  print(f"[{i+1}] {d if args.long else d.url}")
165
165
 
166
166
  if len(res) == args.limit:
lfss/eng/database.py CHANGED
@@ -21,7 +21,7 @@ from .datatype import (
21
21
  )
22
22
  from .config import LARGE_BLOB_DIR, CHUNK_SIZE, LARGE_FILE_BYTES, MAX_MEM_FILE_BYTES
23
23
  from .log import get_logger
24
- from .utils import decode_uri_compnents, hash_credential, concurrent_wrap, debounce_async, static_vars
24
+ from .utils import decode_uri_components, hash_credential, concurrent_wrap, debounce_async, static_vars
25
25
  from .error import *
26
26
 
27
27
  class DBObjectBase(ABC):
@@ -1108,7 +1108,7 @@ class Database:
1108
1108
  async def data_iter():
1109
1109
  async for (r, blob) in self.iter_dir(top_url, None):
1110
1110
  rel_path = r.url[len(top_url):]
1111
- rel_path = decode_uri_compnents(rel_path)
1111
+ rel_path = decode_uri_components(rel_path)
1112
1112
  b_iter: AsyncIterable[bytes]
1113
1113
  if isinstance(blob, bytes):
1114
1114
  async def blob_iter(): yield blob
@@ -1138,7 +1138,7 @@ class Database:
1138
1138
  with zipfile.ZipFile(buffer, 'w') as zf:
1139
1139
  async for (r, blob) in self.iter_dir(top_url, None):
1140
1140
  rel_path = r.url[len(top_url):]
1141
- rel_path = decode_uri_compnents(rel_path)
1141
+ rel_path = decode_uri_components(rel_path)
1142
1142
  if r.external:
1143
1143
  assert isinstance(blob, AsyncIterable)
1144
1144
  zf.writestr(rel_path, b''.join([chunk async for chunk in blob]))
lfss/eng/utils.py CHANGED
@@ -21,19 +21,19 @@ async def copy_file(source: str|pathlib.Path, destination: str|pathlib.Path):
21
21
  def hash_credential(username: str, password: str):
22
22
  return hashlib.sha256(f"{username}:{password}".encode()).hexdigest()
23
23
 
24
- def encode_uri_compnents(path: str):
24
+ def encode_uri_components(path: str):
25
25
  path_sp = path.split("/")
26
26
  mapped = map(lambda x: urllib.parse.quote(x), path_sp)
27
27
  return "/".join(mapped)
28
28
 
29
- def decode_uri_compnents(path: str):
29
+ def decode_uri_components(path: str):
30
30
  path_sp = path.split("/")
31
31
  mapped = map(lambda x: urllib.parse.unquote(x), path_sp)
32
32
  return "/".join(mapped)
33
33
 
34
- def ensure_uri_compnents(path: str):
34
+ def ensure_uri_components(path: str):
35
35
  """ Ensure the path components are safe to use """
36
- return encode_uri_compnents(decode_uri_compnents(path))
36
+ return encode_uri_components(decode_uri_components(path))
37
37
 
38
38
  class TaskManager:
39
39
  def __init__(self):
lfss/svc/app_dav.py CHANGED
@@ -11,7 +11,7 @@ from ..eng.error import *
11
11
  from ..eng.config import DATA_HOME, DEBUG_MODE
12
12
  from ..eng.datatype import UserRecord, FileRecord, DirectoryRecord, AccessLevel
13
13
  from ..eng.database import FileConn, UserConn, check_path_permission
14
- from ..eng.utils import ensure_uri_compnents, decode_uri_compnents, format_last_modified, static_vars
14
+ from ..eng.utils import ensure_uri_components, decode_uri_components, format_last_modified, static_vars
15
15
  from .app_base import *
16
16
  from .common_impl import copy_impl
17
17
 
@@ -36,7 +36,7 @@ async def eval_path(path: str) -> tuple[ptype, str, Optional[FileRecord | Direct
36
36
  and should end with / if it is a directory, otherwise it is a file
37
37
  record is the FileRecord or DirectoryRecord object, it is None if the path does not exist
38
38
  """
39
- path = decode_uri_compnents(path)
39
+ path = decode_uri_components(path)
40
40
  if "://" in path:
41
41
  if not path.startswith("http://") and not path.startswith("https://"):
42
42
  raise HTTPException(status_code=400, detail="Bad Request, unsupported protocol")
@@ -47,7 +47,7 @@ async def eval_path(path: str) -> tuple[ptype, str, Optional[FileRecord | Direct
47
47
  assert path.startswith(route_prefix), "Path should start with the route prefix, got: " + path
48
48
  path = path[len(route_prefix):]
49
49
 
50
- path = ensure_uri_compnents(path)
50
+ path = ensure_uri_components(path)
51
51
  if path.startswith("/"): path = path[1:]
52
52
 
53
53
  # path now is url-safe and without leading slash
@@ -160,7 +160,7 @@ async def create_file_xml_element(frecord: FileRecord) -> ET.Element:
160
160
  href.text = f"/{frecord.url}"
161
161
  propstat = ET.SubElement(file_el, f"{{{DAV_NS}}}propstat")
162
162
  prop = ET.SubElement(propstat, f"{{{DAV_NS}}}prop")
163
- ET.SubElement(prop, f"{{{DAV_NS}}}displayname").text = decode_uri_compnents(frecord.url.split("/")[-1])
163
+ ET.SubElement(prop, f"{{{DAV_NS}}}displayname").text = decode_uri_components(frecord.url.split("/")[-1])
164
164
  ET.SubElement(prop, f"{{{DAV_NS}}}resourcetype")
165
165
  ET.SubElement(prop, f"{{{DAV_NS}}}getcontentlength").text = str(frecord.file_size)
166
166
  ET.SubElement(prop, f"{{{DAV_NS}}}getlastmodified").text = format_last_modified(frecord.create_time)
@@ -178,7 +178,7 @@ async def create_dir_xml_element(drecord: DirectoryRecord) -> ET.Element:
178
178
  href.text = f"/{drecord.url}"
179
179
  propstat = ET.SubElement(dir_el, f"{{{DAV_NS}}}propstat")
180
180
  prop = ET.SubElement(propstat, f"{{{DAV_NS}}}prop")
181
- ET.SubElement(prop, f"{{{DAV_NS}}}displayname").text = decode_uri_compnents(drecord.url.split("/")[-2])
181
+ ET.SubElement(prop, f"{{{DAV_NS}}}displayname").text = decode_uri_components(drecord.url.split("/")[-2])
182
182
  ET.SubElement(prop, f"{{{DAV_NS}}}resourcetype").append(ET.Element(f"{{{DAV_NS}}}collection"))
183
183
  if drecord.size >= 0:
184
184
  ET.SubElement(prop, f"{{{DAV_NS}}}getlastmodified").text = format_last_modified(drecord.create_time)
@@ -211,7 +211,7 @@ async def dav_options(request: Request, path: str):
211
211
  @handle_exception
212
212
  async def dav_propfind(request: Request, path: str, user: UserRecord = Depends(registered_user), body: Optional[ET.Element] = Depends(xml_request_body)):
213
213
  if path.startswith("/"): path = path[1:]
214
- path = ensure_uri_compnents(path)
214
+ path = ensure_uri_components(path)
215
215
 
216
216
  if body and DEBUG_MODE:
217
217
  print("Propfind-body:", ET.tostring(body, encoding="utf-8", method="xml"))
lfss/svc/app_native.py CHANGED
@@ -5,7 +5,7 @@ from fastapi import Depends, Request, Response, UploadFile, Query
5
5
  from fastapi.responses import StreamingResponse, JSONResponse
6
6
  from fastapi.exceptions import HTTPException
7
7
 
8
- from ..eng.utils import ensure_uri_compnents
8
+ from ..eng.utils import ensure_uri_components
9
9
  from ..eng.config import MAX_MEM_FILE_BYTES
10
10
  from ..eng.connection_pool import unique_cursor
11
11
  from ..eng.database import check_file_read_permission, check_path_permission, FileConn, delayed_log_access
@@ -83,7 +83,7 @@ async def delete_file(path: str, user: UserRecord = Depends(registered_user)):
83
83
  @handle_exception
84
84
  async def bundle_files(path: str, user: UserRecord = Depends(registered_user)):
85
85
  logger.info(f"GET bundle({path}), user: {user.username}")
86
- path = ensure_uri_compnents(path)
86
+ path = ensure_uri_components(path)
87
87
  if not path.endswith("/"):
88
88
  raise HTTPException(status_code=400, detail="Path must end with /")
89
89
  if path[0] == "/": # adapt to both /path and path
@@ -123,7 +123,7 @@ async def bundle_files(path: str, user: UserRecord = Depends(registered_user)):
123
123
  @handle_exception
124
124
  async def get_file_meta(path: str, user: UserRecord = Depends(registered_user)):
125
125
  logger.info(f"GET meta({path}), user: {user.username}")
126
- path = ensure_uri_compnents(path)
126
+ path = ensure_uri_components(path)
127
127
  is_file = not path.endswith("/")
128
128
  async with unique_cursor() as cur:
129
129
  fconn = FileConn(cur)
@@ -147,7 +147,7 @@ async def update_file_meta(
147
147
  new_path: Optional[str] = None,
148
148
  user: UserRecord = Depends(registered_user)
149
149
  ):
150
- path = ensure_uri_compnents(path)
150
+ path = ensure_uri_components(path)
151
151
  if path.startswith("/"):
152
152
  path = path[1:]
153
153
 
@@ -162,7 +162,7 @@ async def update_file_meta(
162
162
  )
163
163
 
164
164
  if new_path is not None:
165
- new_path = ensure_uri_compnents(new_path)
165
+ new_path = ensure_uri_components(new_path)
166
166
  logger.info(f"Update path of {path} to {new_path}")
167
167
  await db.move_file(path, new_path, user)
168
168
 
@@ -170,7 +170,7 @@ async def update_file_meta(
170
170
  else:
171
171
  assert perm is None, "Permission is not supported for directory"
172
172
  if new_path is not None:
173
- new_path = ensure_uri_compnents(new_path)
173
+ new_path = ensure_uri_components(new_path)
174
174
  logger.info(f"Update path of {path} to {new_path}")
175
175
  # will raise duplicate path error if same name path exists in the new path
176
176
  await db.move_dir(path, new_path, user)
@@ -194,7 +194,7 @@ async def validate_path_read_permission(path: str, user: UserRecord):
194
194
  @handle_exception
195
195
  async def count_files(path: str, flat: bool = False, user: UserRecord = Depends(registered_user)):
196
196
  await validate_path_read_permission(path, user)
197
- path = ensure_uri_compnents(path)
197
+ path = ensure_uri_components(path)
198
198
  async with unique_cursor() as conn:
199
199
  fconn = FileConn(conn)
200
200
  return { "count": await fconn.count_dir_files(url = path, flat = flat) }
@@ -206,7 +206,7 @@ async def list_files(
206
206
  flat: bool = False, user: UserRecord = Depends(registered_user)
207
207
  ):
208
208
  await validate_path_read_permission(path, user)
209
- path = ensure_uri_compnents(path)
209
+ path = ensure_uri_components(path)
210
210
  async with unique_cursor() as conn:
211
211
  fconn = FileConn(conn)
212
212
  return await fconn.list_dir_files(
@@ -219,7 +219,7 @@ async def list_files(
219
219
  @handle_exception
220
220
  async def count_dirs(path: str, user: UserRecord = Depends(registered_user)):
221
221
  await validate_path_read_permission(path, user)
222
- path = ensure_uri_compnents(path)
222
+ path = ensure_uri_components(path)
223
223
  async with unique_cursor() as conn:
224
224
  fconn = FileConn(conn)
225
225
  return { "count": await fconn.count_path_dirs(url = path) }
@@ -231,7 +231,7 @@ async def list_dirs(
231
231
  skim: bool = True, user: UserRecord = Depends(registered_user)
232
232
  ):
233
233
  await validate_path_read_permission(path, user)
234
- path = ensure_uri_compnents(path)
234
+ path = ensure_uri_components(path)
235
235
  async with unique_cursor() as conn:
236
236
  fconn = FileConn(conn)
237
237
  return await fconn.list_path_dirs(
@@ -263,7 +263,7 @@ async def get_multiple_files(
263
263
  upath2path = OrderedDict[str, str]()
264
264
  for p in path:
265
265
  p_ = p if not p.startswith("/") else p[1:]
266
- upath2path[ensure_uri_compnents(p_)] = p
266
+ upath2path[ensure_uri_components(p_)] = p
267
267
  upaths = list(upath2path.keys())
268
268
 
269
269
  # get files
lfss/svc/common_impl.py CHANGED
@@ -6,7 +6,7 @@ from ..eng.connection_pool import unique_cursor
6
6
  from ..eng.datatype import UserRecord, FileRecord, PathContents, AccessLevel, FileReadPermission
7
7
  from ..eng.database import FileConn, UserConn, delayed_log_access, check_file_read_permission, check_path_permission
8
8
  from ..eng.thumb import get_thumb
9
- from ..eng.utils import format_last_modified, ensure_uri_compnents
9
+ from ..eng.utils import format_last_modified, ensure_uri_components
10
10
  from ..eng.config import CHUNK_SIZE, DEBUG_MODE
11
11
 
12
12
  from .app_base import skip_request_log, db, logger
@@ -100,7 +100,7 @@ async def get_impl(
100
100
  thumb: bool = False,
101
101
  is_head = False,
102
102
  ):
103
- path = ensure_uri_compnents(path)
103
+ path = ensure_uri_components(path)
104
104
  if path.startswith("/"): path = path[1:]
105
105
 
106
106
  # handle directory query
@@ -194,7 +194,7 @@ async def put_file_impl(
194
194
  conflict: Literal["overwrite", "skip", "abort"] = "overwrite",
195
195
  permission: int = 0,
196
196
  ):
197
- path = ensure_uri_compnents(path)
197
+ path = ensure_uri_components(path)
198
198
  assert not path.endswith("/"), "Path must not end with /"
199
199
 
200
200
  access_level = await check_path_permission(path, user)
@@ -249,7 +249,7 @@ async def post_file_impl(
249
249
  conflict: Literal["overwrite", "skip", "abort"] = "overwrite",
250
250
  permission: int = 0,
251
251
  ):
252
- path = ensure_uri_compnents(path)
252
+ path = ensure_uri_components(path)
253
253
  assert not path.endswith("/"), "Path must not end with /"
254
254
 
255
255
  access_level = await check_path_permission(path, user)
@@ -288,7 +288,7 @@ async def post_file_impl(
288
288
  }, content=json.dumps({"url": path}))
289
289
 
290
290
  async def delete_impl(path: str, user: UserRecord):
291
- path = ensure_uri_compnents(path)
291
+ path = ensure_uri_components(path)
292
292
  if await check_path_permission(path, user) < AccessLevel.WRITE:
293
293
  raise HTTPException(status_code=403, detail="Permission denied")
294
294
 
@@ -307,8 +307,8 @@ async def delete_impl(path: str, user: UserRecord):
307
307
  async def copy_impl(
308
308
  op_user: UserRecord, src_path: str, dst_path: str,
309
309
  ):
310
- src_path = ensure_uri_compnents(src_path)
311
- dst_path = ensure_uri_compnents(dst_path)
310
+ src_path = ensure_uri_components(src_path)
311
+ dst_path = ensure_uri_components(dst_path)
312
312
  copy_type = "file" if not src_path[-1] == "/" else "directory"
313
313
  if (src_path[-1] == "/") != (dst_path[-1] == "/"):
314
314
  raise HTTPException(status_code=400, detail="Source and destination must be same type")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lfss
3
- Version: 0.11.4
3
+ Version: 0.11.5
4
4
  Summary: Lightweight file storage service
5
5
  Home-page: https://github.com/MenxLi/lfss
6
6
  Author: Li, Mengxun
@@ -1,9 +1,10 @@
1
1
  Readme.md,sha256=B-foESzFWoSI5MEd89AWUzKcVRrTwipM28TK8GN0o8c,1920
2
+ docs/Client.md,sha256=2GSKrcKkjYHxxA6Afa2vhHj7UFEQ-_EdmNRkZ06jPHU,975
2
3
  docs/Enviroment_variables.md,sha256=CZ5DrrXSLU5RLBEVQ-gLMaOIuFthd7dEiTzO7ODrPRQ,788
3
4
  docs/Known_issues.md,sha256=ZqETcWP8lzTOel9b2mxEgCnADFF8IxOrEtiVO1NoMAk,251
4
5
  docs/Permission.md,sha256=thUJx7YRoU63Pb-eqo5l5450DrZN3QYZ36GCn8r66no,3152
5
6
  docs/Webdav.md,sha256=-Ja-BTWSY1BEMAyZycvEMNnkNTPZ49gSPzmf3Lbib70,1547
6
- docs/changelog.md,sha256=fE0rE2IcovbxMhdTeqhnCnknT1vtVr7A860zIh7AEnE,1581
7
+ docs/changelog.md,sha256=7Pa-yRqF2E0uU69e32bquAGmumgV3I-6irBiW-Jt5Nw,2120
7
8
  frontend/api.js,sha256=F35jQjWF2LITkuO-wZJuEKyafLWFx_M4C2tEYJV8zak,22631
8
9
  frontend/index.html,sha256=-k0bJ5FRqdl_H-O441D_H9E-iejgRCaL_z5UeYaS2qc,3384
9
10
  frontend/info.css,sha256=Ny0N3GywQ3a9q1_Qph_QFEKB4fEnTe_2DJ1Y5OsLLmQ,595
@@ -18,11 +19,11 @@ frontend/styles.css,sha256=xcNLqI3KBsY5TLnku8UIP0Jfr7QLajr1_KNlZj9eheM,4935
18
19
  frontend/thumb.css,sha256=rNsx766amYS2DajSQNabhpQ92gdTpNoQKmV69OKvtpI,295
19
20
  frontend/thumb.js,sha256=46ViD2TlTTWy0fx6wjoAs_5CQ4ajYB90vVzM7UO2IHw,6182
20
21
  frontend/utils.js,sha256=XP5hM_mROYaxK5dqn9qZVwv7GdQuiDzByilFskbrnxA,6068
21
- lfss/api/__init__.py,sha256=zT1JCiUM76wX-GtRrmKhTUzSYYfcmoyI1vYwN0fCcLw,6818
22
- lfss/api/connector.py,sha256=o0_Ws1cmDJdM5YFKy5EhwStU9nW9a05CrjVYDex0Hmo,13931
22
+ lfss/api/__init__.py,sha256=MdRsQSldbV4tZpAdzgr0sws8ru9GPvqUWVcsT6iVRFY,6844
23
+ lfss/api/connector.py,sha256=sz2mvGsHOREhev0FCd9p9RdyrJxRwtt-X63Kt4_E_dQ,13933
23
24
  lfss/cli/__init__.py,sha256=lPwPmqpa7EXQ4zlU7E7LOe6X2kw_xATGdwoHphUEirA,827
24
25
  lfss/cli/balance.py,sha256=fUbKKAUyaDn74f7mmxMfBL4Q4voyBLHu6Lg_g8GfMOQ,4121
25
- lfss/cli/cli.py,sha256=QLItJBjCJv6mWMUp5T6M0tUBuBzWr8yxoqn6V55Mb7I,8193
26
+ lfss/cli/cli.py,sha256=l4_hU7DLJ_QqTG69oKZSVXSA46PjfpxiUc_8ruTY6n0,8196
26
27
  lfss/cli/log.py,sha256=TBlt8mhHMouv8ZBUMHYfGZiV6-0yPdajJQ5mkGHEojI,3016
27
28
  lfss/cli/panel.py,sha256=Xq3I_n-ctveym-Gh9LaUpzHiLlvt3a_nuDiwUS-MGrg,1597
28
29
  lfss/cli/serve.py,sha256=vTo6_BiD7Dn3VLvHsC5RKRBC3lMu45JVr_0SqpgHdj0,1086
@@ -32,21 +33,21 @@ lfss/eng/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
33
  lfss/eng/bounded_pool.py,sha256=BI1dU-MBf82TMwJBYbjhEty7w1jIUKc5Bn9SnZ_-hoY,1288
33
34
  lfss/eng/config.py,sha256=0dncHYn3tYw4pKBwXuP_huz0u7ud23fJ6SUmUPfLmeM,967
34
35
  lfss/eng/connection_pool.py,sha256=1aq7nSgd7hB9YNV4PjD1RDRyl_moDw3ubBtSLyfgGBs,6320
35
- lfss/eng/database.py,sha256=fd0ICcOYO5yQbXfYO9OaKk280-jSbJO2c008JUvHVcs,57238
36
+ lfss/eng/database.py,sha256=obJU3rD_AvwVzIUiSB1gqsC2k7_E6p7dw8PW7eYvi84,57241
36
37
  lfss/eng/datatype.py,sha256=27UB7-l9SICy5lAvKjdzpTL_GohZjzstQcr9PtAq7nM,2709
37
38
  lfss/eng/error.py,sha256=JGf5NV-f4rL6tNIDSAx5-l9MG8dEj7F2w_MuOjj1d1o,732
38
39
  lfss/eng/log.py,sha256=yciFQ7Utz1AItNekS4YtdP6bM7i1krA6qSAU2wVQv24,7698
39
40
  lfss/eng/thumb.py,sha256=AFyWEkkpuCKGWOB9bLlaDwPKzQ9JtCSSmHMhX2Gu3CI,3096
40
- lfss/eng/utils.py,sha256=jQUJWWmzOPmXdTCId2Y307m1cZfB4hpzHcTjO0mkOrU,6683
41
+ lfss/eng/utils.py,sha256=SlgiC5S0V42n9JczFF04jl5tKC7R6gN-FkuqqNoF7co,6688
41
42
  lfss/sql/init.sql,sha256=FBmVzkNjYUnWjEELRFzf7xb50GngmzmeDVffT1Uk8u8,1625
42
43
  lfss/sql/pragma.sql,sha256=uENx7xXjARmro-A3XAK8OM8v5AxDMdCCRj47f86UuXg,206
43
44
  lfss/svc/app.py,sha256=r1KUO3sPaaJWbkJF0bcVTD7arPKLs2jFlq52Ixicomo,220
44
45
  lfss/svc/app_base.py,sha256=s5ieQVI4BT0CBYavRx0dyBqwts7PYnjyCovHNYPHul8,7014
45
- lfss/svc/app_dav.py,sha256=H3aL3MEdYaPK1w3FQvTzrGYGaaow4m8LZ7R35MN351A,18238
46
- lfss/svc/app_native.py,sha256=zQM9o6HKtMVpq2WDZ77oc8tRnBdUdTkYHeV92NonGoo,10601
47
- lfss/svc/common_impl.py,sha256=7QflWnxRqghLOSMpDz2UCRqEn49X1GLS3agCb5msia8,13729
46
+ lfss/svc/app_dav.py,sha256=DRMgByUAQ3gD6wL9xmikV5kvVmATN7QkxGSttFTYxFU,18245
47
+ lfss/svc/app_native.py,sha256=K2vlNs6b4DE2bPgRuHxic9oDp8V3D5M5wNjIibGjQyg,10612
48
+ lfss/svc/common_impl.py,sha256=wlTQm8zEGAfyw9FJvK9zqgLQw47MzNq6IT3OgwdUaCw,13736
48
49
  lfss/svc/request_log.py,sha256=v8yXEIzPjaksu76Oh5vgdbUEUrw8Kt4etLAXBWSGie8,3207
49
- lfss-0.11.4.dist-info/METADATA,sha256=qEO9CaHZXLivlr5de-3nYtpyFsGfIv-L1AMA_Ae4mWI,2732
50
- lfss-0.11.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
51
- lfss-0.11.4.dist-info/entry_points.txt,sha256=R4uOP1y6eD0Qp3j1ySA8kRPVMdt6_W_9o-Zj9Ra4D0A,232
52
- lfss-0.11.4.dist-info/RECORD,,
50
+ lfss-0.11.5.dist-info/METADATA,sha256=h1KBhb0MjCtgrlaUqfDSqLH_6Jxy1q4KaETLEMauIyY,2732
51
+ lfss-0.11.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
52
+ lfss-0.11.5.dist-info/entry_points.txt,sha256=M4ubn9oLYcTc9wxlLKWwljnluStPWpCDlCGuTVU8twg,255
53
+ lfss-0.11.5.dist-info/RECORD,,
@@ -1,4 +1,5 @@
1
1
  [console_scripts]
2
+ lfss=lfss.cli.cli:main
2
3
  lfss-balance=lfss.cli.balance:main
3
4
  lfss-cli=lfss.cli.cli:main
4
5
  lfss-log=lfss.cli.log:main
File without changes