lfss 0.9.2__py3-none-any.whl → 0.11.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.
lfss/svc/common_impl.py CHANGED
@@ -7,7 +7,7 @@ from ..eng.datatype import UserRecord, FileRecord, PathContents, AccessLevel, Fi
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
9
  from ..eng.utils import format_last_modified, ensure_uri_compnents
10
- from ..eng.config import CHUNK_SIZE
10
+ from ..eng.config import CHUNK_SIZE, DEBUG_MODE
11
11
 
12
12
  from .app_base import skip_request_log, db, logger
13
13
 
@@ -60,10 +60,15 @@ async def emit_file(
60
60
  else:
61
61
  arng_e = range_end
62
62
 
63
- if arng_s >= file_record.file_size or arng_e >= file_record.file_size:
64
- raise HTTPException(status_code=416, detail="Range not satisfiable")
65
- if arng_s > arng_e:
66
- raise HTTPException(status_code=416, detail="Invalid range")
63
+ if file_record.file_size > 0:
64
+ if arng_s >= file_record.file_size or arng_e >= file_record.file_size:
65
+ if DEBUG_MODE: print(f"[Invalid range] Actual range: {arng_s}-{arng_e} (size: {file_record.file_size})")
66
+ raise HTTPException(status_code=416, detail="Range not satisfiable")
67
+ if arng_s > arng_e:
68
+ raise HTTPException(status_code=416, detail="Invalid range")
69
+ else:
70
+ if not (arng_s == 0 and arng_e == -1):
71
+ raise HTTPException(status_code=416, detail="Invalid range (file size is 0)")
67
72
 
68
73
  headers = {
69
74
  "Content-Disposition": f"{disposition}; filename={fname}",
@@ -87,7 +92,7 @@ async def emit_file(
87
92
  status_code=206 if range_start != -1 or range_end != -1 else 200
88
93
  )
89
94
 
90
- async def get_file_impl(
95
+ async def get_impl(
91
96
  request: Request,
92
97
  user: UserRecord,
93
98
  path: str,
@@ -96,40 +101,19 @@ async def get_file_impl(
96
101
  is_head = False,
97
102
  ):
98
103
  path = ensure_uri_compnents(path)
104
+ if path.startswith("/"): path = path[1:]
99
105
 
100
106
  # handle directory query
101
107
  if path == "": path = "/"
102
108
  if path.endswith("/"):
103
- # return file under the path as json
104
- async with unique_cursor() as cur:
105
- fconn = FileConn(cur)
106
- if user.id == 0:
107
- raise HTTPException(status_code=401, detail="Permission denied, credential required")
108
- if thumb:
109
- return await emit_thumbnail(path, download, create_time=None)
110
-
111
- if path == "/":
112
- peer_users = await UserConn(cur).list_peer_users(user.id, AccessLevel.READ)
113
- return PathContents(
114
- dirs = await fconn.list_root_dirs(user.username, *[x.username for x in peer_users], skim=True) \
115
- if not user.is_admin else await fconn.list_root_dirs(skim=True),
116
- files = []
117
- )
118
-
119
- if not await check_path_permission(path, user, cursor=cur) >= AccessLevel.READ:
120
- raise HTTPException(status_code=403, detail="Permission denied")
121
-
122
- return await fconn.list_path(path)
109
+ return await _get_dir_impl(user=user, path=path, download=download, thumb=thumb, is_head=is_head)
123
110
 
124
111
  # handle file query
125
112
  async with unique_cursor() as cur:
126
113
  fconn = FileConn(cur)
127
114
  file_record = await fconn.get_file_record(path, throw=True)
128
- uconn = UserConn(cur)
129
- owner = await uconn.get_user_by_id(file_record.owner_id, throw=True)
130
-
131
115
  if not await check_path_permission(path, user, cursor=cur) >= AccessLevel.READ:
132
- allow_access, reason = check_file_read_permission(user, owner, file_record)
116
+ allow_access, reason = await check_file_read_permission(user, file_record, cursor=cur)
133
117
  if not allow_access:
134
118
  raise HTTPException(status_code=403 if user.id != 0 else 401, detail=reason)
135
119
 
@@ -147,6 +131,9 @@ async def get_file_impl(
147
131
  else:
148
132
  range_start, range_end = -1, -1
149
133
 
134
+ if DEBUG_MODE:
135
+ print(f"Get range: {range_start}-{range_end}")
136
+
150
137
  if thumb:
151
138
  if (range_start != -1 or range_end != -1): logger.warning("Range request for thumbnail")
152
139
  return await emit_thumbnail(path, download, create_time=file_record.create_time, is_head=is_head)
@@ -156,11 +143,55 @@ async def get_file_impl(
156
143
  else:
157
144
  return await emit_file(file_record, None, "inline", is_head = is_head, range_start=range_start, range_end=range_end)
158
145
 
146
+ async def _get_dir_impl(
147
+ user: UserRecord,
148
+ path: str,
149
+ download: bool = False,
150
+ thumb: bool = False,
151
+ is_head = False,
152
+ ):
153
+ """ handle directory query, return file under the path as json """
154
+ assert path.endswith("/")
155
+ async with unique_cursor() as cur:
156
+ fconn = FileConn(cur)
157
+ if user.id == 0:
158
+ raise HTTPException(status_code=401, detail="Permission denied, credential required")
159
+ if thumb:
160
+ return await emit_thumbnail(path, download, create_time=None)
161
+
162
+ if path == "/":
163
+ if is_head: return Response(status_code=200)
164
+ peer_users = await UserConn(cur).list_peer_users(user.id, AccessLevel.READ)
165
+ return PathContents(
166
+ dirs = await fconn.list_root_dirs(user.username, *[x.username for x in peer_users], skim=True) \
167
+ if not user.is_admin else await fconn.list_root_dirs(skim=True),
168
+ files = []
169
+ )
170
+
171
+ if not await check_path_permission(path, user, cursor=cur) >= AccessLevel.READ:
172
+ raise HTTPException(status_code=403, detail="Permission denied")
173
+
174
+ path_sp = path.split("/")
175
+ if is_head:
176
+ if len(path_sp) == 2:
177
+ assert path_sp[1] == ""
178
+ if await UserConn(cur).get_user(path_sp[0]):
179
+ return Response(status_code=200)
180
+ else:
181
+ raise HTTPException(status_code=404, detail="User not found")
182
+ else:
183
+ if await FileConn(cur).count_dir_files(path, flat=True) > 0:
184
+ return Response(status_code=200)
185
+ else:
186
+ raise HTTPException(status_code=404, detail="Path not found")
187
+
188
+ return await fconn.list_path(path)
189
+
159
190
  async def put_file_impl(
160
191
  request: Request,
161
192
  user: UserRecord,
162
193
  path: str,
163
- conflict: Literal["overwrite", "skip", "abort"] = "abort",
194
+ conflict: Literal["overwrite", "skip", "abort"] = "overwrite",
164
195
  permission: int = 0,
165
196
  ):
166
197
  path = ensure_uri_compnents(path)
@@ -187,7 +218,9 @@ async def put_file_impl(
187
218
  exists_flag = True
188
219
  if await check_path_permission(path, user) < AccessLevel.WRITE:
189
220
  raise HTTPException(status_code=403, detail="Permission denied, cannot overwrite other's file")
190
- await db.delete_file(path)
221
+ old_record = await db.delete_file(path)
222
+ if old_record and permission == FileReadPermission.UNSET.value:
223
+ permission = old_record.permission.value # inherit permission
191
224
 
192
225
  # check content-type
193
226
  content_type = request.headers.get("Content-Type", "application/octet-stream")
@@ -213,7 +246,7 @@ async def post_file_impl(
213
246
  path: str,
214
247
  user: UserRecord,
215
248
  file: UploadFile,
216
- conflict: Literal["overwrite", "skip", "abort"] = "abort",
249
+ conflict: Literal["overwrite", "skip", "abort"] = "overwrite",
217
250
  permission: int = 0,
218
251
  ):
219
252
  path = ensure_uri_compnents(path)
@@ -240,7 +273,9 @@ async def post_file_impl(
240
273
  exists_flag = True
241
274
  if await check_path_permission(path, user) < AccessLevel.WRITE:
242
275
  raise HTTPException(status_code=403, detail="Permission denied, cannot overwrite other's file")
243
- await db.delete_file(path)
276
+ old_record = await db.delete_file(path)
277
+ if old_record and permission == FileReadPermission.UNSET.value:
278
+ permission = old_record.permission.value # inherit permission
244
279
 
245
280
  async def blob_reader():
246
281
  nonlocal file
@@ -260,7 +295,7 @@ async def delete_impl(path: str, user: UserRecord):
260
295
  logger.info(f"DELETE {path}, user: {user.username}")
261
296
 
262
297
  if path.endswith("/"):
263
- res = await db.delete_path(path, user)
298
+ res = await db.delete_dir(path, user)
264
299
  else:
265
300
  res = await db.delete_file(path, user)
266
301
 
@@ -292,8 +327,8 @@ async def copy_impl(
292
327
  else:
293
328
  async with unique_cursor() as cur:
294
329
  fconn = FileConn(cur)
295
- dst_fcount = await fconn.count_path_files(dst_path, flat=True)
330
+ dst_fcount = await fconn.count_dir_files(dst_path, flat=True)
296
331
  if dst_fcount > 0:
297
332
  raise HTTPException(status_code=409, detail="Destination exists")
298
- await db.copy_path(src_path, dst_path, op_user)
333
+ await db.copy_dir(src_path, dst_path, op_user)
299
334
  return Response(status_code=201, content="OK")
@@ -1,28 +1,30 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lfss
3
- Version: 0.9.2
3
+ Version: 0.11.4
4
4
  Summary: Lightweight file storage service
5
5
  Home-page: https://github.com/MenxLi/lfss
6
- Author: li_mengxun
7
- Author-email: limengxun45@outlookc.com
6
+ Author: Li, Mengxun
7
+ Author-email: mengxunli@whu.edu.cn
8
8
  Requires-Python: >=3.10
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: Programming Language :: Python :: 3.10
11
11
  Classifier: Programming Language :: Python :: 3.11
12
12
  Classifier: Programming Language :: Python :: 3.12
13
- Requires-Dist: aiofiles (==23.*)
13
+ Requires-Dist: aiofiles (==24.*)
14
14
  Requires-Dist: aiosqlite (==0.*)
15
15
  Requires-Dist: fastapi (==0.*)
16
16
  Requires-Dist: mimesniff (==1.*)
17
17
  Requires-Dist: pillow
18
18
  Requires-Dist: python-multipart
19
19
  Requires-Dist: requests (==2.*)
20
+ Requires-Dist: rich
21
+ Requires-Dist: stream-zip (==0.*)
20
22
  Requires-Dist: uvicorn (==0.*)
21
23
  Project-URL: Repository, https://github.com/MenxLi/lfss
22
24
  Description-Content-Type: text/markdown
23
25
 
24
- # Lightweight File Storage Service (LFSS)
25
- [![PyPI](https://img.shields.io/pypi/v/lfss)](https://pypi.org/project/lfss/)
26
+ # Lite File Storage Service (LFSS)
27
+ [![PyPI](https://img.shields.io/pypi/v/lfss)](https://pypi.org/project/lfss/) [![PyPI - Downloads](https://img.shields.io/pypi/dm/lfss)](https://pypi.org/project/lfss/)
26
28
 
27
29
  My experiment on a lightweight and high-performance file/object storage service...
28
30
 
@@ -55,8 +57,8 @@ Or, you can start a web server at `/frontend` and open `index.html` in your brow
55
57
 
56
58
  The API usage is simple, just `GET`, `PUT`, `DELETE` to the `/<username>/file/url` path.
57
59
  The authentication can be acheived through one of the following methods:
58
- 1. `Authorization` header with the value `Bearer sha256(<username><password>)`.
59
- 2. `token` query parameter with the value `sha256(<username><password>)`.
60
+ 1. `Authorization` header with the value `Bearer sha256(<username>:<password>)`.
61
+ 2. `token` query parameter with the value `sha256(<username>:<password>)`.
60
62
  3. HTTP Basic Authentication with the username and password (If WebDAV is enabled).
61
63
 
62
64
  You can refer to `frontend` as an application example, `lfss/api/connector.py` for more APIs.
@@ -0,0 +1,52 @@
1
+ Readme.md,sha256=B-foESzFWoSI5MEd89AWUzKcVRrTwipM28TK8GN0o8c,1920
2
+ docs/Enviroment_variables.md,sha256=CZ5DrrXSLU5RLBEVQ-gLMaOIuFthd7dEiTzO7ODrPRQ,788
3
+ docs/Known_issues.md,sha256=ZqETcWP8lzTOel9b2mxEgCnADFF8IxOrEtiVO1NoMAk,251
4
+ docs/Permission.md,sha256=thUJx7YRoU63Pb-eqo5l5450DrZN3QYZ36GCn8r66no,3152
5
+ docs/Webdav.md,sha256=-Ja-BTWSY1BEMAyZycvEMNnkNTPZ49gSPzmf3Lbib70,1547
6
+ docs/changelog.md,sha256=fE0rE2IcovbxMhdTeqhnCnknT1vtVr7A860zIh7AEnE,1581
7
+ frontend/api.js,sha256=F35jQjWF2LITkuO-wZJuEKyafLWFx_M4C2tEYJV8zak,22631
8
+ frontend/index.html,sha256=-k0bJ5FRqdl_H-O441D_H9E-iejgRCaL_z5UeYaS2qc,3384
9
+ frontend/info.css,sha256=Ny0N3GywQ3a9q1_Qph_QFEKB4fEnTe_2DJ1Y5OsLLmQ,595
10
+ frontend/info.js,sha256=xGUJPCSrtDhuSu0ELLQZ77PmVWldg-prU1mwQGbdEoA,5797
11
+ frontend/login.css,sha256=VMM0QfbDFYerxKWKSGhMI1yg5IRBXg0TTdLJEEhQZNk,355
12
+ frontend/login.js,sha256=xJkulk8dlvV4BhevADLeUrnZwShiFTWv3Wg2iJFUZlY,2423
13
+ frontend/popup.css,sha256=TJZYFW1ZcdD1IVTlNPYNtMWKPbN6XDbQ4hKBOFK8uLg,1284
14
+ frontend/popup.js,sha256=cyUjtO0wbtqbEodHfwyUsak9iWbcDXeWMGDhpCPbcoE,5453
15
+ frontend/scripts.js,sha256=T3kMjTxrjOkp93OV4ZMGgCLRRaQgRmNzzxriOMGVeZM,24412
16
+ frontend/state.js,sha256=vbNL5DProRKmSEY7xu9mZH6IY0PBenF8WGxPtGgDnLI,1680
17
+ frontend/styles.css,sha256=xcNLqI3KBsY5TLnku8UIP0Jfr7QLajr1_KNlZj9eheM,4935
18
+ frontend/thumb.css,sha256=rNsx766amYS2DajSQNabhpQ92gdTpNoQKmV69OKvtpI,295
19
+ frontend/thumb.js,sha256=46ViD2TlTTWy0fx6wjoAs_5CQ4ajYB90vVzM7UO2IHw,6182
20
+ 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
23
+ lfss/cli/__init__.py,sha256=lPwPmqpa7EXQ4zlU7E7LOe6X2kw_xATGdwoHphUEirA,827
24
+ lfss/cli/balance.py,sha256=fUbKKAUyaDn74f7mmxMfBL4Q4voyBLHu6Lg_g8GfMOQ,4121
25
+ lfss/cli/cli.py,sha256=QLItJBjCJv6mWMUp5T6M0tUBuBzWr8yxoqn6V55Mb7I,8193
26
+ lfss/cli/log.py,sha256=TBlt8mhHMouv8ZBUMHYfGZiV6-0yPdajJQ5mkGHEojI,3016
27
+ lfss/cli/panel.py,sha256=Xq3I_n-ctveym-Gh9LaUpzHiLlvt3a_nuDiwUS-MGrg,1597
28
+ lfss/cli/serve.py,sha256=vTo6_BiD7Dn3VLvHsC5RKRBC3lMu45JVr_0SqpgHdj0,1086
29
+ lfss/cli/user.py,sha256=1mTroQbaKxHjFCPHT67xwd08v-zxH0RZ_OnVc-4MzL0,5364
30
+ lfss/cli/vacuum.py,sha256=arEY89kYJKEpzuzjKtf21V7s0QzM1t3QWa1hNghhT0Q,6611
31
+ lfss/eng/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
+ lfss/eng/bounded_pool.py,sha256=BI1dU-MBf82TMwJBYbjhEty7w1jIUKc5Bn9SnZ_-hoY,1288
33
+ lfss/eng/config.py,sha256=0dncHYn3tYw4pKBwXuP_huz0u7ud23fJ6SUmUPfLmeM,967
34
+ lfss/eng/connection_pool.py,sha256=1aq7nSgd7hB9YNV4PjD1RDRyl_moDw3ubBtSLyfgGBs,6320
35
+ lfss/eng/database.py,sha256=fd0ICcOYO5yQbXfYO9OaKk280-jSbJO2c008JUvHVcs,57238
36
+ lfss/eng/datatype.py,sha256=27UB7-l9SICy5lAvKjdzpTL_GohZjzstQcr9PtAq7nM,2709
37
+ lfss/eng/error.py,sha256=JGf5NV-f4rL6tNIDSAx5-l9MG8dEj7F2w_MuOjj1d1o,732
38
+ lfss/eng/log.py,sha256=yciFQ7Utz1AItNekS4YtdP6bM7i1krA6qSAU2wVQv24,7698
39
+ lfss/eng/thumb.py,sha256=AFyWEkkpuCKGWOB9bLlaDwPKzQ9JtCSSmHMhX2Gu3CI,3096
40
+ lfss/eng/utils.py,sha256=jQUJWWmzOPmXdTCId2Y307m1cZfB4hpzHcTjO0mkOrU,6683
41
+ lfss/sql/init.sql,sha256=FBmVzkNjYUnWjEELRFzf7xb50GngmzmeDVffT1Uk8u8,1625
42
+ lfss/sql/pragma.sql,sha256=uENx7xXjARmro-A3XAK8OM8v5AxDMdCCRj47f86UuXg,206
43
+ lfss/svc/app.py,sha256=r1KUO3sPaaJWbkJF0bcVTD7arPKLs2jFlq52Ixicomo,220
44
+ 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
48
+ 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,,
@@ -1,6 +1,7 @@
1
1
  [console_scripts]
2
2
  lfss-balance=lfss.cli.balance:main
3
3
  lfss-cli=lfss.cli.cli:main
4
+ lfss-log=lfss.cli.log:main
4
5
  lfss-panel=lfss.cli.panel:main
5
6
  lfss-serve=lfss.cli.serve:main
6
7
  lfss-user=lfss.cli.user:main
@@ -1,50 +0,0 @@
1
- Readme.md,sha256=JVe9T6N1Rz4hTiiCVoDYe2VB0dAi60VcBgb2twQdfZc,1834
2
- docs/Enviroment_variables.md,sha256=LUZF1o70emp-5UPsvXPjcxapP940OqEZzSyyUUT9bEQ,569
3
- docs/Known_issues.md,sha256=ZqETcWP8lzTOel9b2mxEgCnADFF8IxOrEtiVO1NoMAk,251
4
- docs/Permission.md,sha256=mvK8gVBBgoIFJqikcaReU_bUo-mTq_ECqJaDDJoQF7Q,3126
5
- docs/Webdav.md,sha256=9Q41ROEJodVVAnlo1Tf0jqsyrbuHhv_ElSsXbIPXYIg,1547
6
- frontend/api.js,sha256=GlQsNoZFEcy7QUUsLbXv7aP-KxRnIxM37FQHTaakGiQ,19387
7
- frontend/index.html,sha256=-k0bJ5FRqdl_H-O441D_H9E-iejgRCaL_z5UeYaS2qc,3384
8
- frontend/info.css,sha256=Ny0N3GywQ3a9q1_Qph_QFEKB4fEnTe_2DJ1Y5OsLLmQ,595
9
- frontend/info.js,sha256=xGUJPCSrtDhuSu0ELLQZ77PmVWldg-prU1mwQGbdEoA,5797
10
- frontend/login.css,sha256=VMM0QfbDFYerxKWKSGhMI1yg5IRBXg0TTdLJEEhQZNk,355
11
- frontend/login.js,sha256=QoO8yKmBHDVP-ZomCMOaV7xVUVIhpl7esJrb6T5aHQE,2466
12
- frontend/popup.css,sha256=TJZYFW1ZcdD1IVTlNPYNtMWKPbN6XDbQ4hKBOFK8uLg,1284
13
- frontend/popup.js,sha256=3PgaGZmxSdV1E-D_MWgcR7aHWkcsHA1BNKSOkmP66tA,5191
14
- frontend/scripts.js,sha256=2-Omsb1-s4Wc859_SYw8JGyeUSiADaH9va4w87Mozns,24134
15
- frontend/state.js,sha256=vbNL5DProRKmSEY7xu9mZH6IY0PBenF8WGxPtGgDnLI,1680
16
- frontend/styles.css,sha256=xcNLqI3KBsY5TLnku8UIP0Jfr7QLajr1_KNlZj9eheM,4935
17
- frontend/thumb.css,sha256=rNsx766amYS2DajSQNabhpQ92gdTpNoQKmV69OKvtpI,295
18
- frontend/thumb.js,sha256=46ViD2TlTTWy0fx6wjoAs_5CQ4ajYB90vVzM7UO2IHw,6182
19
- frontend/utils.js,sha256=IYUZl77ugiXKcLxSNOWC4NSS0CdD5yRgUsDb665j0xM,2556
20
- lfss/api/__init__.py,sha256=8IJqrpWK1doIyVVbntvVic82A57ncwl5b0BRHX4Ri6A,6660
21
- lfss/api/connector.py,sha256=hHSEEWecKQGZH6oxAmYoG3q7lFfacCbOKVZiUIXT2y8,11819
22
- lfss/cli/__init__.py,sha256=lPwPmqpa7EXQ4zlU7E7LOe6X2kw_xATGdwoHphUEirA,827
23
- lfss/cli/balance.py,sha256=fUbKKAUyaDn74f7mmxMfBL4Q4voyBLHu6Lg_g8GfMOQ,4121
24
- lfss/cli/cli.py,sha256=aYjB8d4k6JUd9efxZK-XOj-mlG4JeOr_0lnj2qqCiK0,8066
25
- lfss/cli/panel.py,sha256=Xq3I_n-ctveym-Gh9LaUpzHiLlvt3a_nuDiwUS-MGrg,1597
26
- lfss/cli/serve.py,sha256=vTo6_BiD7Dn3VLvHsC5RKRBC3lMu45JVr_0SqpgHdj0,1086
27
- lfss/cli/user.py,sha256=1mTroQbaKxHjFCPHT67xwd08v-zxH0RZ_OnVc-4MzL0,5364
28
- lfss/cli/vacuum.py,sha256=GOG72d3NYe9bYCNc3y8JecEmM-DrKlGq3JQcisv_xBg,3702
29
- lfss/eng/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
- lfss/eng/bounded_pool.py,sha256=BI1dU-MBf82TMwJBYbjhEty7w1jIUKc5Bn9SnZ_-hoY,1288
31
- lfss/eng/config.py,sha256=DmnUYMeLOL-45OstysyMpSBPmLofgzvcSrsWjHvssYs,915
32
- lfss/eng/connection_pool.py,sha256=-tePasJxiZZ73ymgWf_kFnaKouc4Rrr4K6EXwjb7Mm4,6141
33
- lfss/eng/database.py,sha256=cfMq7Hgj8cHFtynDzpRiqb0XYNb6OKWMYc8PcWl8eVw,47285
34
- lfss/eng/datatype.py,sha256=27UB7-l9SICy5lAvKjdzpTL_GohZjzstQcr9PtAq7nM,2709
35
- lfss/eng/error.py,sha256=sDbXo2R3APJAV0KtoYGCHx2qVZso7svtDzq-WjnzhAw,595
36
- lfss/eng/log.py,sha256=u6WRZZsE7iOx6_CV2NHh1ugea26p408FI4WstZh896A,5139
37
- lfss/eng/thumb.py,sha256=YO1yTI8WzW7pBpQN9x5PtPayxhftb32IJl1zPSS9mks,3243
38
- lfss/eng/utils.py,sha256=zZ7r9BsNV8XJJVNOxfIqRCO1bxNzh7bc9vEJiCkgbKI,6208
39
- lfss/sql/init.sql,sha256=8LjHx0TBCkBD62xFfssSeHDqKYVQQJkZAg4rSm046f4,1496
40
- lfss/sql/pragma.sql,sha256=uENx7xXjARmro-A3XAK8OM8v5AxDMdCCRj47f86UuXg,206
41
- lfss/svc/app.py,sha256=ftWCpepBx-gTSG7i-TB-IdinPPstAYYQjCgnTfeMZeI,219
42
- lfss/svc/app_base.py,sha256=nc02DP4iMKP41fRl8M-iAhbHwyb4QJJTKKSJwtdCox4,6617
43
- lfss/svc/app_dav.py,sha256=nPMdPsYNcgxqHOt5bDaaA0Wy8AdRDJajEda_-KxOoHA,17466
44
- lfss/svc/app_native.py,sha256=xwMCOWp4ne3rmtiiYhfxETi__V-zPEfHw-c4iWNtXWc,9471
45
- lfss/svc/common_impl.py,sha256=_biK0F_AAw4PnMNWR0WuHJSRyIp1iTSOOIPBauZCJ9M,12143
46
- lfss/svc/request_log.py,sha256=v8yXEIzPjaksu76Oh5vgdbUEUrw8Kt4etLAXBWSGie8,3207
47
- lfss-0.9.2.dist-info/METADATA,sha256=0Q5klZ2iwBF1ZUQ5iximW02mMmoAM5ib08s0IsdyuLE,2594
48
- lfss-0.9.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
49
- lfss-0.9.2.dist-info/entry_points.txt,sha256=VJ8svMz7RLtMCgNk99CElx7zo7M-N-z7BWDVw2HA92E,205
50
- lfss-0.9.2.dist-info/RECORD,,
File without changes