lfss 0.12.3__py3-none-any.whl → 0.13.0__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/changelog.md +17 -0
- frontend/api.js +90 -9
- frontend/base.css +33 -0
- frontend/edit.css +102 -0
- frontend/edit.html +29 -0
- frontend/edit.js +130 -0
- frontend/login.css +1 -0
- frontend/scripts.js +50 -3
- frontend/styles.css +10 -34
- lfss/api/__init__.py +9 -200
- lfss/api/bundle.py +201 -0
- lfss/api/connector.py +56 -52
- lfss/cli/__init__.py +8 -1
- lfss/cli/cli.py +57 -8
- lfss/cli/cli_lib.py +2 -4
- lfss/eng/database.py +125 -64
- lfss/eng/datatype.py +19 -0
- lfss/eng/error.py +14 -2
- lfss/svc/app.py +2 -0
- lfss/svc/app_base.py +6 -2
- lfss/svc/app_native.py +35 -21
- lfss/svc/app_native_user.py +28 -0
- lfss/svc/common_impl.py +30 -9
- {lfss-0.12.3.dist-info → lfss-0.13.0.dist-info}/METADATA +1 -1
- {lfss-0.12.3.dist-info → lfss-0.13.0.dist-info}/RECORD +27 -21
- {lfss-0.12.3.dist-info → lfss-0.13.0.dist-info}/WHEEL +0 -0
- {lfss-0.12.3.dist-info → lfss-0.13.0.dist-info}/entry_points.txt +0 -0
lfss/svc/app_native.py
CHANGED
@@ -2,7 +2,7 @@ from typing import Optional, Literal, Annotated
|
|
2
2
|
from collections import OrderedDict
|
3
3
|
|
4
4
|
from fastapi import Depends, Request, Response, UploadFile, Query
|
5
|
-
from fastapi.responses import StreamingResponse, JSONResponse
|
5
|
+
from fastapi.responses import StreamingResponse, JSONResponse, RedirectResponse
|
6
6
|
from fastapi.exceptions import HTTPException
|
7
7
|
|
8
8
|
from ..eng.utils import ensure_uri_components
|
@@ -16,7 +16,7 @@ from ..eng.datatype import (
|
|
16
16
|
from ..eng.error import InvalidPathError
|
17
17
|
|
18
18
|
from .app_base import *
|
19
|
-
from .common_impl import get_impl, put_file_impl, post_file_impl, delete_impl, copy_impl
|
19
|
+
from .common_impl import get_impl, put_file_impl, post_file_impl, delete_impl, copy_impl, move_impl
|
20
20
|
|
21
21
|
@router_fs.get("/{path:path}")
|
22
22
|
@handle_exception
|
@@ -139,6 +139,7 @@ async def get_file_meta(path: str, user: UserRecord = Depends(registered_user)):
|
|
139
139
|
record = await fconn.get_dir_record(path)
|
140
140
|
return record
|
141
141
|
|
142
|
+
# TODO: will remove in next major version, use /move and /set-perm instead
|
142
143
|
@router_api.post("/meta")
|
143
144
|
@handle_exception
|
144
145
|
async def update_file_meta(
|
@@ -147,6 +148,7 @@ async def update_file_meta(
|
|
147
148
|
new_path: Optional[str] = None,
|
148
149
|
user: UserRecord = Depends(registered_user)
|
149
150
|
):
|
151
|
+
logger.warning("POST /meta is deprecated and will be removed in next major version")
|
150
152
|
path = ensure_uri_components(path)
|
151
153
|
if path.startswith("/"):
|
152
154
|
path = path[1:]
|
@@ -162,18 +164,13 @@ async def update_file_meta(
|
|
162
164
|
)
|
163
165
|
|
164
166
|
if new_path is not None:
|
165
|
-
|
166
|
-
logger.info(f"Update path of {path} to {new_path}")
|
167
|
-
await db.move_file(path, new_path, user)
|
167
|
+
return await move_impl(user, path, new_path)
|
168
168
|
|
169
169
|
# directory
|
170
170
|
else:
|
171
171
|
assert perm is None, "Permission is not supported for directory"
|
172
172
|
if new_path is not None:
|
173
|
-
|
174
|
-
logger.info(f"Update path of {path} to {new_path}")
|
175
|
-
# will raise duplicate path error if same name path exists in the new path
|
176
|
-
await db.move_dir(path, new_path, user)
|
173
|
+
return await move_impl(user, path, new_path)
|
177
174
|
|
178
175
|
return Response(status_code=200, content="OK")
|
179
176
|
|
@@ -185,15 +182,27 @@ async def copy_file(
|
|
185
182
|
):
|
186
183
|
return await copy_impl(src_path = src, dst_path = dst, op_user = user)
|
187
184
|
|
188
|
-
@router_api.
|
185
|
+
@router_api.post("/move")
|
189
186
|
@handle_exception
|
190
|
-
async def
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
187
|
+
async def move_file(
|
188
|
+
src: str, dst: str,
|
189
|
+
user: UserRecord = Depends(registered_user)
|
190
|
+
):
|
191
|
+
return await move_impl(src_path = src, dst_path = dst, op_user = user)
|
192
|
+
|
193
|
+
@router_api.post("/set-perm")
|
194
|
+
@handle_exception
|
195
|
+
async def set_permission(
|
196
|
+
path: str, perm: int, user: UserRecord = Depends(registered_user)
|
197
|
+
):
|
198
|
+
if path.endswith("/"):
|
199
|
+
raise HTTPException(status_code=400, detail="Path must not end with /")
|
200
|
+
await db.update_file_record(
|
201
|
+
url = ensure_uri_components(path),
|
202
|
+
permission = FileReadPermission(perm),
|
203
|
+
op_user = user,
|
204
|
+
)
|
205
|
+
return Response(status_code=200, content="OK")
|
197
206
|
|
198
207
|
async def validate_path_read_permission(path: str, user: UserRecord):
|
199
208
|
if not path.endswith("/"):
|
@@ -289,12 +298,17 @@ async def get_multiple_files(
|
|
289
298
|
status_code = 206 if partial_content else 200
|
290
299
|
)
|
291
300
|
|
292
|
-
|
301
|
+
|
302
|
+
# --------- Backward compatibility API ---------
|
293
303
|
@router_api.get("/whoami")
|
294
304
|
@handle_exception
|
295
|
-
async def whoami(
|
296
|
-
|
297
|
-
|
305
|
+
async def whoami(r: Request):
|
306
|
+
return RedirectResponse(url=f"/_api/user/whoami?{r.url.query}")
|
307
|
+
|
308
|
+
@router_api.get("/list-peers")
|
309
|
+
@handle_exception
|
310
|
+
async def list_peers(r: Request):
|
311
|
+
return RedirectResponse(url=f"/_api/user/list-peers?{r.url.query}")
|
298
312
|
|
299
313
|
__all__ = [
|
300
314
|
"app", "router_api", "router_fs"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
from fastapi import Depends
|
2
|
+
|
3
|
+
from .app_base import *
|
4
|
+
from ..eng.datatype import UserRecord, AccessLevel
|
5
|
+
from ..eng.database import unique_cursor, UserConn, FileConn
|
6
|
+
|
7
|
+
@router_user.get("/whoami")
|
8
|
+
@handle_exception
|
9
|
+
async def whoami(user: UserRecord = Depends(registered_user)):
|
10
|
+
return user.desensitize()
|
11
|
+
|
12
|
+
@router_user.get("/storage")
|
13
|
+
@handle_exception
|
14
|
+
async def user_storage(user: UserRecord = Depends(registered_user)):
|
15
|
+
async with unique_cursor() as conn:
|
16
|
+
fconn = FileConn(conn)
|
17
|
+
return {
|
18
|
+
"quota": user.max_storage,
|
19
|
+
"used": await fconn.user_size(user.id)
|
20
|
+
}
|
21
|
+
|
22
|
+
@router_user.get("/list-peers")
|
23
|
+
@handle_exception
|
24
|
+
async def list_peers(user: UserRecord = Depends(registered_user), level: AccessLevel = AccessLevel.READ, incoming: bool = False):
|
25
|
+
async with unique_cursor() as conn:
|
26
|
+
uconn = UserConn(conn)
|
27
|
+
peer_users = await uconn.list_peer_users(user.id, level, incoming=incoming)
|
28
|
+
return [u.desensitize() for u in peer_users]
|
lfss/svc/common_impl.py
CHANGED
@@ -150,7 +150,10 @@ async def _get_dir_impl(
|
|
150
150
|
thumb: bool = False,
|
151
151
|
is_head = False,
|
152
152
|
):
|
153
|
-
"""
|
153
|
+
"""
|
154
|
+
handle directory query, return file under the path as json
|
155
|
+
TODO: will change the behavior at next (major) version update
|
156
|
+
"""
|
154
157
|
assert path.endswith("/")
|
155
158
|
async with unique_cursor() as cur:
|
156
159
|
fconn = FileConn(cur)
|
@@ -180,7 +183,7 @@ async def _get_dir_impl(
|
|
180
183
|
else:
|
181
184
|
raise HTTPException(status_code=404, detail="User not found")
|
182
185
|
else:
|
183
|
-
if await
|
186
|
+
if await fconn.is_dir_exist(path):
|
184
187
|
return Response(status_code=200)
|
185
188
|
else:
|
186
189
|
raise HTTPException(status_code=404, detail="Path not found")
|
@@ -320,15 +323,33 @@ async def copy_impl(
|
|
320
323
|
if copy_type == "file":
|
321
324
|
async with unique_cursor() as cur:
|
322
325
|
fconn = FileConn(cur)
|
323
|
-
|
324
|
-
|
325
|
-
raise HTTPException(status_code=409, detail="Destination exists")
|
326
|
+
if await fconn.get_file_record(dst_path, throw=False) is not None:
|
327
|
+
raise HTTPException(status_code=409, detail="Destination exists")
|
326
328
|
await db.copy_file(src_path, dst_path, op_user)
|
327
329
|
else:
|
328
330
|
async with unique_cursor() as cur:
|
329
331
|
fconn = FileConn(cur)
|
330
|
-
|
331
|
-
|
332
|
-
raise HTTPException(status_code=409, detail="Destination exists")
|
332
|
+
if await fconn.is_dir_exist(dst_path):
|
333
|
+
raise HTTPException(status_code=409, detail="Destination exists")
|
333
334
|
await db.copy_dir(src_path, dst_path, op_user)
|
334
|
-
return Response(status_code=201, content="OK")
|
335
|
+
return Response(status_code=201, content="OK")
|
336
|
+
|
337
|
+
async def move_impl(
|
338
|
+
op_user: UserRecord, src_path: str, dst_path: str,
|
339
|
+
):
|
340
|
+
src_path = ensure_uri_components(src_path)
|
341
|
+
dst_path = ensure_uri_components(dst_path)
|
342
|
+
|
343
|
+
is_file = not src_path.endswith("/")
|
344
|
+
if (src_path[-1] == "/") != (dst_path[-1] == "/"):
|
345
|
+
raise HTTPException(status_code=400, detail="Source and destination must be same type")
|
346
|
+
|
347
|
+
if src_path == dst_path:
|
348
|
+
raise HTTPException(status_code=400, detail="Source and destination are the same")
|
349
|
+
|
350
|
+
logger.info(f"Move {src_path} to {dst_path}, user: {op_user.username}")
|
351
|
+
if is_file:
|
352
|
+
await db.move_file(src_path, dst_path, op_user)
|
353
|
+
else:
|
354
|
+
await db.move_dir(src_path, dst_path, op_user)
|
355
|
+
return Response(status_code=200, content="OK")
|
@@ -4,27 +4,32 @@ docs/Enviroment_variables.md,sha256=CZ5DrrXSLU5RLBEVQ-gLMaOIuFthd7dEiTzO7ODrPRQ,
|
|
4
4
|
docs/Known_issues.md,sha256=ZqETcWP8lzTOel9b2mxEgCnADFF8IxOrEtiVO1NoMAk,251
|
5
5
|
docs/Permission.md,sha256=thUJx7YRoU63Pb-eqo5l5450DrZN3QYZ36GCn8r66no,3152
|
6
6
|
docs/Webdav.md,sha256=-Ja-BTWSY1BEMAyZycvEMNnkNTPZ49gSPzmf3Lbib70,1547
|
7
|
-
docs/changelog.md,sha256=
|
8
|
-
frontend/api.js,sha256=
|
7
|
+
docs/changelog.md,sha256=7D1nsEF4taIsXvPJ5i4uHmp2QXB7w3OCKCfcnuzpo4E,3394
|
8
|
+
frontend/api.js,sha256=tcNneh2wswtA7xM4GrrD4dwtU5VKQ-86qwR5dZlHasw,25749
|
9
|
+
frontend/base.css,sha256=iZwhILCy8suGM6Z5e4b4SzBK_WTItclmgwXJxC41TyY,564
|
10
|
+
frontend/edit.css,sha256=QIIEezsLd3gMjA9q25kg2WQFsY51Zkqs72K-8cdPvUc,1932
|
11
|
+
frontend/edit.html,sha256=ISOYQCHLojEQGTpZ2HIf3i6HNUFah59K3m5z9O1wqPs,810
|
12
|
+
frontend/edit.js,sha256=KBS29KjoC0KDwwdkNr7rlfGXjAjkh35xMcweufZemu8,3777
|
9
13
|
frontend/index.html,sha256=-k0bJ5FRqdl_H-O441D_H9E-iejgRCaL_z5UeYaS2qc,3384
|
10
14
|
frontend/info.css,sha256=Ny0N3GywQ3a9q1_Qph_QFEKB4fEnTe_2DJ1Y5OsLLmQ,595
|
11
15
|
frontend/info.js,sha256=xGUJPCSrtDhuSu0ELLQZ77PmVWldg-prU1mwQGbdEoA,5797
|
12
|
-
frontend/login.css,sha256=
|
16
|
+
frontend/login.css,sha256=XWVEH-J1CUDO7zdr9tm_47iNZBis9G9x7cFO92gJ7FE,383
|
13
17
|
frontend/login.js,sha256=xJkulk8dlvV4BhevADLeUrnZwShiFTWv3Wg2iJFUZlY,2423
|
14
18
|
frontend/popup.css,sha256=TJZYFW1ZcdD1IVTlNPYNtMWKPbN6XDbQ4hKBOFK8uLg,1284
|
15
19
|
frontend/popup.js,sha256=cyUjtO0wbtqbEodHfwyUsak9iWbcDXeWMGDhpCPbcoE,5453
|
16
|
-
frontend/scripts.js,sha256=
|
20
|
+
frontend/scripts.js,sha256=tyBagLk3R3GJJSg56wNWAKgYdCh4hVhEbL3lxVSvQ3s,26493
|
17
21
|
frontend/state.js,sha256=vbNL5DProRKmSEY7xu9mZH6IY0PBenF8WGxPtGgDnLI,1680
|
18
|
-
frontend/styles.css,sha256=
|
22
|
+
frontend/styles.css,sha256=vE_iwAzU7tiJHUeMY2pcq3BcdATDncCvF0pgBnKHGrA,4565
|
19
23
|
frontend/thumb.css,sha256=rNsx766amYS2DajSQNabhpQ92gdTpNoQKmV69OKvtpI,295
|
20
24
|
frontend/thumb.js,sha256=46ViD2TlTTWy0fx6wjoAs_5CQ4ajYB90vVzM7UO2IHw,6182
|
21
25
|
frontend/utils.js,sha256=XP5hM_mROYaxK5dqn9qZVwv7GdQuiDzByilFskbrnxA,6068
|
22
|
-
lfss/api/__init__.py,sha256=
|
23
|
-
lfss/api/
|
24
|
-
lfss/
|
26
|
+
lfss/api/__init__.py,sha256=SoNB7vjs2I5SXrYRbfcUZgIoZUhzDsEIDSZhPMf7LiE,232
|
27
|
+
lfss/api/bundle.py,sha256=fN0fPTURO8srRhwrZYhRUlZ9rStrevGitDz-WDbyUCk,6758
|
28
|
+
lfss/api/connector.py,sha256=p6_kmGzGYn0Jpk2K8aHzYTcz7P25AJkuJHkJM0uGAzM,16985
|
29
|
+
lfss/cli/__init__.py,sha256=44qzN7yOoEQO53sPPEHlGZZgT3HXnt1LbTLFS9F0DOg,1332
|
25
30
|
lfss/cli/balance.py,sha256=fUbKKAUyaDn74f7mmxMfBL4Q4voyBLHu6Lg_g8GfMOQ,4121
|
26
|
-
lfss/cli/cli.py,sha256=
|
27
|
-
lfss/cli/cli_lib.py,sha256=
|
31
|
+
lfss/cli/cli.py,sha256=m3h3fPUlJGjDtok8xyHID5CWZ2sYbwGmDpnICyrFSB4,18327
|
32
|
+
lfss/cli/cli_lib.py,sha256=B3NuICVnTxpZ8lHc0x1KEekw9ygGNwd00U3zx3uAbBM,2783
|
28
33
|
lfss/cli/log.py,sha256=TBlt8mhHMouv8ZBUMHYfGZiV6-0yPdajJQ5mkGHEojI,3016
|
29
34
|
lfss/cli/panel.py,sha256=Xq3I_n-ctveym-Gh9LaUpzHiLlvt3a_nuDiwUS-MGrg,1597
|
30
35
|
lfss/cli/serve.py,sha256=vTo6_BiD7Dn3VLvHsC5RKRBC3lMu45JVr_0SqpgHdj0,1086
|
@@ -34,21 +39,22 @@ lfss/eng/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
39
|
lfss/eng/bounded_pool.py,sha256=BI1dU-MBf82TMwJBYbjhEty7w1jIUKc5Bn9SnZ_-hoY,1288
|
35
40
|
lfss/eng/config.py,sha256=0dncHYn3tYw4pKBwXuP_huz0u7ud23fJ6SUmUPfLmeM,967
|
36
41
|
lfss/eng/connection_pool.py,sha256=1aq7nSgd7hB9YNV4PjD1RDRyl_moDw3ubBtSLyfgGBs,6320
|
37
|
-
lfss/eng/database.py,sha256=
|
38
|
-
lfss/eng/datatype.py,sha256=
|
39
|
-
lfss/eng/error.py,sha256=
|
42
|
+
lfss/eng/database.py,sha256=4qbPn-_R_-KqpGF7UrEiXj0nk_PutJu2jwv-8Wvgvs8,61220
|
43
|
+
lfss/eng/datatype.py,sha256=hU4p5lsfhE1mPF0oHtAq1sz0rPoVOYiSt_QAoUYmGpU,3368
|
44
|
+
lfss/eng/error.py,sha256=NpGP_9C7EWATARDb7nCoznOznIHvvRbWbvHdG6ExoMI,1174
|
40
45
|
lfss/eng/log.py,sha256=yciFQ7Utz1AItNekS4YtdP6bM7i1krA6qSAU2wVQv24,7698
|
41
46
|
lfss/eng/thumb.py,sha256=AFyWEkkpuCKGWOB9bLlaDwPKzQ9JtCSSmHMhX2Gu3CI,3096
|
42
47
|
lfss/eng/utils.py,sha256=LB84nX-fEgMlWMPo5ByU48U7RCy8EcxqIHAMWP8d-8w,6773
|
43
48
|
lfss/sql/init.sql,sha256=FBmVzkNjYUnWjEELRFzf7xb50GngmzmeDVffT1Uk8u8,1625
|
44
49
|
lfss/sql/pragma.sql,sha256=uENx7xXjARmro-A3XAK8OM8v5AxDMdCCRj47f86UuXg,206
|
45
|
-
lfss/svc/app.py,sha256=
|
46
|
-
lfss/svc/app_base.py,sha256=
|
50
|
+
lfss/svc/app.py,sha256=GBsqrQOkl37HGb2ZLGE_fTEuyFZwcc95RVr2DP39gsI,283
|
51
|
+
lfss/svc/app_base.py,sha256=DiAJDpDGq2Z7UwjD0irBVZBk6TxZCpWsXBGpbRCZ3gY,7091
|
47
52
|
lfss/svc/app_dav.py,sha256=DRMgByUAQ3gD6wL9xmikV5kvVmATN7QkxGSttFTYxFU,18245
|
48
|
-
lfss/svc/app_native.py,sha256=
|
49
|
-
lfss/svc/
|
53
|
+
lfss/svc/app_native.py,sha256=QSjDv2CBGKL4NneiXkXBGH8B3_MQqkGZEe3g0-ntvAo,11366
|
54
|
+
lfss/svc/app_native_user.py,sha256=iI70MXzh5_DJ_ab64vrJpexHcB0zwuDFOuRa3KozVMo,995
|
55
|
+
lfss/svc/common_impl.py,sha256=h1lmjFf4JUZMRwPhwfLCbsuplvAsZz59sUaYjE_QZBQ,14492
|
50
56
|
lfss/svc/request_log.py,sha256=v8yXEIzPjaksu76Oh5vgdbUEUrw8Kt4etLAXBWSGie8,3207
|
51
|
-
lfss-0.
|
52
|
-
lfss-0.
|
53
|
-
lfss-0.
|
54
|
-
lfss-0.
|
57
|
+
lfss-0.13.0.dist-info/METADATA,sha256=a41tjDaHn9pEcnW-2x_YMrNbP9cLI7jv8MuQtZkJx7U,2818
|
58
|
+
lfss-0.13.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
59
|
+
lfss-0.13.0.dist-info/entry_points.txt,sha256=M4ubn9oLYcTc9wxlLKWwljnluStPWpCDlCGuTVU8twg,255
|
60
|
+
lfss-0.13.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|