cista 1.4.2__tar.gz → 1.5.0__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.
- {cista-1.4.2 → cista-1.5.0}/PKG-INFO +3 -3
- {cista-1.4.2 → cista-1.5.0}/cista/__main__.py +7 -7
- {cista-1.4.2 → cista-1.5.0}/cista/_version.py +1 -1
- {cista-1.4.2 → cista-1.5.0}/cista/api.py +6 -0
- {cista-1.4.2 → cista-1.5.0}/cista/app.py +7 -32
- cista-1.4.2/cista/frontend-build/assets/icons-DMD182WZ.js → cista-1.5.0/cista/frontend-build/assets/icons-6j2UZ_HA.js +1 -1
- cista-1.5.0/cista/frontend-build/assets/index-BHQJhxzY.js +32 -0
- cista-1.5.0/cista/frontend-build/assets/index-De5UYAfC.css +1 -0
- cista-1.5.0/cista/frontend-build/assets/searchWorker-DbTcSX-c.js +1 -0
- {cista-1.4.2 → cista-1.5.0}/cista/frontend-build/index.html +3 -3
- {cista-1.4.2 → cista-1.5.0}/cista/preview.py +2 -2
- {cista-1.4.2 → cista-1.5.0}/cista/protocol.py +28 -0
- {cista-1.4.2 → cista-1.5.0}/cista/serve.py +21 -10
- cista-1.5.0/cista/watching.py +728 -0
- {cista-1.4.2 → cista-1.5.0}/pyproject.toml +2 -2
- cista-1.4.2/cista/frontend-build/assets/index-C8Gp9T8Z.js +0 -32
- cista-1.4.2/cista/frontend-build/assets/index-zDODUQOB.css +0 -1
- cista-1.4.2/cista/frontend-build/assets/searchWorker-CxfnO8mP.js +0 -1
- cista-1.4.2/cista/watching.py +0 -459
- {cista-1.4.2 → cista-1.5.0}/.gitignore +0 -0
- {cista-1.4.2 → cista-1.5.0}/README.md +0 -0
- {cista-1.4.2 → cista-1.5.0}/cista/__init__.py +0 -0
- {cista-1.4.2 → cista-1.5.0}/cista/auth.py +0 -0
- {cista-1.4.2 → cista-1.5.0}/cista/config.py +0 -0
- {cista-1.4.2 → cista-1.5.0}/cista/droppy.py +0 -0
- {cista-1.4.2 → cista-1.5.0}/cista/fileio.py +0 -0
- {cista-1.4.2 → cista-1.5.0}/cista/frontend-build/assets/logo-ctv8tVwU.svg +0 -0
- {cista-1.4.2 → cista-1.5.0}/cista/frontend-build/robots.txt +0 -0
- {cista-1.4.2 → cista-1.5.0}/cista/server80.py +0 -0
- {cista-1.4.2 → cista-1.5.0}/cista/session.py +0 -0
- {cista-1.4.2 → cista-1.5.0}/cista/sso.py +0 -0
- {cista-1.4.2 → cista-1.5.0}/cista/util/__init__.py +0 -0
- {cista-1.4.2 → cista-1.5.0}/cista/util/apphelpers.py +0 -0
- {cista-1.4.2 → cista-1.5.0}/cista/util/asynclink.py +0 -0
- {cista-1.4.2 → cista-1.5.0}/cista/util/filename.py +0 -0
- {cista-1.4.2 → cista-1.5.0}/cista/util/lrucache.py +0 -0
- {cista-1.4.2 → cista-1.5.0}/cista/util/pwgen.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cista
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
4
4
|
Summary: Dropbox-like file server with modern web interface
|
|
5
5
|
Project-URL: Homepage, https://git.zi.fi/Vasanko/cista-storage
|
|
6
6
|
Author: Vasanko
|
|
@@ -16,8 +16,8 @@ Requires-Python: >=3.11
|
|
|
16
16
|
Requires-Dist: argon2-cffi>=25.1.0
|
|
17
17
|
Requires-Dist: av>=15.0.0
|
|
18
18
|
Requires-Dist: blake3>=1.0.5
|
|
19
|
-
Requires-Dist: docopt>=0.
|
|
20
|
-
Requires-Dist: fastapi-vue>=0.5.
|
|
19
|
+
Requires-Dist: docopt-ng>=0.9.0
|
|
20
|
+
Requires-Dist: fastapi-vue>=0.5.2
|
|
21
21
|
Requires-Dist: fastapi[standard]>=0.128.0
|
|
22
22
|
Requires-Dist: html5tagger>=1.3.0
|
|
23
23
|
Requires-Dist: httpx>=0.28.0
|
|
@@ -28,7 +28,7 @@ def create_banner():
|
|
|
28
28
|
def create_startup_box(*, folder, url, unix=None, dev=False, paskia_url=None):
|
|
29
29
|
"""Create a framed startup box with server information."""
|
|
30
30
|
title = f"Cista {cista.__version__}"
|
|
31
|
-
listen =
|
|
31
|
+
listen = unix if unix else url
|
|
32
32
|
location = f"{folder} @ {listen}"
|
|
33
33
|
lines = [title, location]
|
|
34
34
|
if paskia_url:
|
|
@@ -57,7 +57,7 @@ Usage:
|
|
|
57
57
|
Options:
|
|
58
58
|
-c CONFDIR Custom config directory
|
|
59
59
|
-l LISTEN-ADDR Listen on
|
|
60
|
-
:
|
|
60
|
+
:8989 (localhost port, plain http)
|
|
61
61
|
<addr>:3000 (bind another address, port)
|
|
62
62
|
/path/to/unix.sock (unix socket)
|
|
63
63
|
example.com (run on 80 and 443 with LetsEncrypt)
|
|
@@ -80,7 +80,7 @@ Environment:
|
|
|
80
80
|
first_time_help = """\
|
|
81
81
|
No config file found! Get started with:
|
|
82
82
|
cista --user yourname --privileged # If you want user accounts
|
|
83
|
-
cista -l :
|
|
83
|
+
cista -l :8989 /path/to/files # Run the server on localhost:8989
|
|
84
84
|
|
|
85
85
|
See cista --help for other options!
|
|
86
86
|
"""
|
|
@@ -140,8 +140,8 @@ def _main():
|
|
|
140
140
|
if listen:
|
|
141
141
|
settings["listen"] = listen
|
|
142
142
|
elif not exists:
|
|
143
|
-
settings["listen"] = ":
|
|
144
|
-
|
|
143
|
+
settings["listen"] = ":8989"
|
|
144
|
+
config.update_config(settings)
|
|
145
145
|
# Prepare to serve
|
|
146
146
|
url, opts = serve.parse_listen(config.config.listen)
|
|
147
147
|
if not config.config.path.is_dir():
|
|
@@ -186,7 +186,7 @@ def _user(args):
|
|
|
186
186
|
# Defaults for new config when user is created
|
|
187
187
|
operation = config.update_config(
|
|
188
188
|
{
|
|
189
|
-
"listen": ":
|
|
189
|
+
"listen": ":8989",
|
|
190
190
|
"path": Path.home() / "Downloads",
|
|
191
191
|
"public": False,
|
|
192
192
|
}
|
|
@@ -215,7 +215,7 @@ def _user(args):
|
|
|
215
215
|
|
|
216
216
|
if operation == "created":
|
|
217
217
|
sys.stderr.write(
|
|
218
|
-
"Now you can run the server:\n cista # defaults set: -l :
|
|
218
|
+
"Now you can run the server:\n cista # defaults set: -l :8989 ~/Downloads\n"
|
|
219
219
|
)
|
|
220
220
|
|
|
221
221
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# This file is automatically generated by hatch build.
|
|
2
|
-
__version__ = '1.
|
|
2
|
+
__version__ = '1.5.0'
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import typing
|
|
3
|
+
from pathlib import PurePosixPath
|
|
3
4
|
from secrets import token_bytes
|
|
4
5
|
|
|
5
6
|
import msgspec
|
|
@@ -53,6 +54,9 @@ async def upload(req, ws):
|
|
|
53
54
|
if pos != req.end:
|
|
54
55
|
d = f"{len(data)} bytes" if isinstance(data, bytes) else data
|
|
55
56
|
raise ValueError(f"Expected {req.end - pos} more bytes, got {d}")
|
|
57
|
+
# Signal the watcher about the uploaded file and its parent directories
|
|
58
|
+
path = PurePosixPath(req.name)
|
|
59
|
+
watching.notify_change(path, *path.parents)
|
|
56
60
|
# Report success
|
|
57
61
|
res = StatusMsg(status="ack", req=req)
|
|
58
62
|
await asend(ws, res)
|
|
@@ -87,6 +91,8 @@ async def control(req, ws):
|
|
|
87
91
|
while True:
|
|
88
92
|
cmd = msgspec.json.decode(await ws.recv(), type=ControlTypes)
|
|
89
93
|
await asyncio.to_thread(cmd)
|
|
94
|
+
# Signal the watcher about affected paths
|
|
95
|
+
watching.notify_change(*cmd.affected_paths())
|
|
90
96
|
await asend(ws, StatusMsg(status="ack", req=cmd))
|
|
91
97
|
|
|
92
98
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import datetime
|
|
3
3
|
import mimetypes
|
|
4
|
-
import threading
|
|
5
4
|
from concurrent.futures import ThreadPoolExecutor
|
|
6
5
|
from multiprocessing import cpu_count
|
|
7
6
|
from pathlib import Path, PurePath, PurePosixPath
|
|
@@ -56,7 +55,6 @@ async def main_start(app):
|
|
|
56
55
|
# Sanic sometimes fails to execute after_server_stop, so we do it before instead (potentially interrupting handlers)
|
|
57
56
|
@app.before_server_stop
|
|
58
57
|
async def main_stop(app):
|
|
59
|
-
quit.set()
|
|
60
58
|
watching.stop(app)
|
|
61
59
|
app.ctx.threadexec.shutdown()
|
|
62
60
|
app.ctx.zipexec.shutdown(cancel_futures=True)
|
|
@@ -174,9 +172,8 @@ def _load_wwwroot(www):
|
|
|
174
172
|
|
|
175
173
|
@app.before_server_start
|
|
176
174
|
async def start(app):
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
app.add_task(refresh_wwwroot(), name="refresh_wwwroot")
|
|
175
|
+
if not app.debug:
|
|
176
|
+
await load_wwwroot(app)
|
|
180
177
|
|
|
181
178
|
|
|
182
179
|
async def load_wwwroot(app):
|
|
@@ -186,36 +183,14 @@ async def load_wwwroot(app):
|
|
|
186
183
|
)
|
|
187
184
|
|
|
188
185
|
|
|
189
|
-
quit = threading.Event()
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
async def refresh_wwwroot():
|
|
193
|
-
try:
|
|
194
|
-
while not quit.is_set():
|
|
195
|
-
try:
|
|
196
|
-
wwwold = www
|
|
197
|
-
await load_wwwroot(app)
|
|
198
|
-
changes = ""
|
|
199
|
-
for name in sorted(www):
|
|
200
|
-
attr = www[name]
|
|
201
|
-
if wwwold.get(name) == attr:
|
|
202
|
-
continue
|
|
203
|
-
headers = attr[2]
|
|
204
|
-
changes += f"{headers['last-modified']} {headers['etag']} /{name}\n"
|
|
205
|
-
for name in sorted(set(wwwold) - set(www)):
|
|
206
|
-
changes += f"Deleted /{name}\n"
|
|
207
|
-
if changes:
|
|
208
|
-
logger.info(f"Updated wwwroot:\n{changes}", end="", flush=True)
|
|
209
|
-
except Exception as e:
|
|
210
|
-
logger.error(f"Error loading wwwroot: {e!r}")
|
|
211
|
-
await asyncio.sleep(0.5)
|
|
212
|
-
except asyncio.CancelledError:
|
|
213
|
-
pass
|
|
214
|
-
|
|
215
|
-
|
|
216
186
|
@app.route("/<path:path>", methods=["GET", "HEAD"])
|
|
217
187
|
async def wwwroot(req, path=""):
|
|
218
188
|
"""Frontend files only"""
|
|
189
|
+
if app.debug:
|
|
190
|
+
raise NotFound(
|
|
191
|
+
"Dev mode: frontend-build is not served on backend (you should connect vite)",
|
|
192
|
+
extra={"name": path},
|
|
193
|
+
)
|
|
219
194
|
name = unquote(path)
|
|
220
195
|
if name not in www:
|
|
221
196
|
raise NotFound(f"File not found: /{path}", extra={"name": name})
|