Compare commits
4 Commits
e0aef07783
...
61f9026e23
Author | SHA1 | Date | |
---|---|---|---|
61f9026e23 | |||
3e50149d4d | |||
7077b21159 | |||
938c5ca657 |
|
@ -1,6 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import secrets
|
import secrets
|
||||||
|
import sys
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
from pathlib import Path, PurePath
|
from pathlib import Path, PurePath
|
||||||
|
@ -90,6 +91,8 @@ def config_update(modify):
|
||||||
return "read"
|
return "read"
|
||||||
f.write(new)
|
f.write(new)
|
||||||
f.close()
|
f.close()
|
||||||
|
if sys.platform == "win32":
|
||||||
|
conffile.unlink() # Windows doesn't support atomic replace
|
||||||
tmpname.rename(conffile) # Atomic replace
|
tmpname.rename(conffile) # Atomic replace
|
||||||
except:
|
except:
|
||||||
f.close()
|
f.close()
|
||||||
|
|
|
@ -149,15 +149,3 @@ class Space(msgspec.Struct):
|
||||||
free: int
|
free: int
|
||||||
usage: int
|
usage: int
|
||||||
storage: int
|
storage: int
|
||||||
|
|
||||||
|
|
||||||
def make_dir_data(root):
|
|
||||||
if len(root) == 3:
|
|
||||||
return FileEntry(*root)
|
|
||||||
id_, size, mtime, listing = root
|
|
||||||
converted = {}
|
|
||||||
for name, data in listing.items():
|
|
||||||
converted[name] = make_dir_data(data)
|
|
||||||
sz = sum(x.size for x in converted.values())
|
|
||||||
mt = max(x.mtime for x in converted.values())
|
|
||||||
return DirEntry(id_, sz, max(mt, mtime), converted)
|
|
||||||
|
|
|
@ -110,26 +110,6 @@ class State:
|
||||||
with self.lock:
|
with self.lock:
|
||||||
del self._listing[self._slice(relpath)]
|
del self._listing[self._slice(relpath)]
|
||||||
|
|
||||||
def _index(self, rel: PurePosixPath):
|
|
||||||
idx = 0
|
|
||||||
ret = []
|
|
||||||
|
|
||||||
def _dir(self, idx: int):
|
|
||||||
level = self._listing[idx].level + 1
|
|
||||||
end = len(self._listing)
|
|
||||||
idx += 1
|
|
||||||
ret = []
|
|
||||||
while idx < end and (r := self._listing[idx]).level >= level:
|
|
||||||
if r.level == level:
|
|
||||||
ret.append(idx)
|
|
||||||
return ret, idx
|
|
||||||
|
|
||||||
def update(self, rel: PurePosixPath, value: FileEntry):
|
|
||||||
begin = 0
|
|
||||||
parents = []
|
|
||||||
while self._listing[begin].level < len(rel.parts):
|
|
||||||
parents.append(begin)
|
|
||||||
|
|
||||||
|
|
||||||
state = State()
|
state = State()
|
||||||
rootpath: Path = None # type: ignore
|
rootpath: Path = None # type: ignore
|
||||||
|
@ -149,7 +129,7 @@ def watcher_thread(loop):
|
||||||
global rootpath
|
global rootpath
|
||||||
import inotify.adapters
|
import inotify.adapters
|
||||||
|
|
||||||
while True:
|
while not quit:
|
||||||
rootpath = config.config.path
|
rootpath = config.config.path
|
||||||
i = inotify.adapters.InotifyTree(rootpath.as_posix())
|
i = inotify.adapters.InotifyTree(rootpath.as_posix())
|
||||||
# Initialize the tree from filesystem
|
# Initialize the tree from filesystem
|
||||||
|
@ -160,8 +140,8 @@ def watcher_thread(loop):
|
||||||
state.root = new
|
state.root = new
|
||||||
broadcast(format_update(old, new), loop)
|
broadcast(format_update(old, new), loop)
|
||||||
|
|
||||||
# The watching is not entirely reliable, so do a full refresh every minute
|
# The watching is not entirely reliable, so do a full refresh every 30 seconds
|
||||||
refreshdl = time.monotonic() + 60.0
|
refreshdl = time.monotonic() + 30.0
|
||||||
|
|
||||||
for event in i.event_gen():
|
for event in i.event_gen():
|
||||||
if quit:
|
if quit:
|
||||||
|
@ -238,11 +218,15 @@ def _walk(rel: PurePosixPath, isfile: int, st: stat_result) -> list[FileEntry]:
|
||||||
try:
|
try:
|
||||||
li = []
|
li = []
|
||||||
for f in path.iterdir():
|
for f in path.iterdir():
|
||||||
|
if quit:
|
||||||
|
raise SystemExit("quit")
|
||||||
if f.name.startswith("."):
|
if f.name.startswith("."):
|
||||||
continue # No dotfiles
|
continue # No dotfiles
|
||||||
s = f.stat()
|
s = f.stat()
|
||||||
li.append((int(not stat.S_ISDIR(s.st_mode)), f.name, s))
|
li.append((int(not stat.S_ISDIR(s.st_mode)), f.name, s))
|
||||||
for [isfile, name, s] in humansorted(li):
|
for [isfile, name, s] in humansorted(li):
|
||||||
|
if quit:
|
||||||
|
raise SystemExit("quit")
|
||||||
subtree = _walk(rel / name, isfile, s)
|
subtree = _walk(rel / name, isfile, s)
|
||||||
child = subtree[0]
|
child = subtree[0]
|
||||||
entry.mtime = max(entry.mtime, child.mtime)
|
entry.mtime = max(entry.mtime, child.mtime)
|
||||||
|
@ -337,7 +321,7 @@ async def abroadcast(msg):
|
||||||
|
|
||||||
async def start(app, loop):
|
async def start(app, loop):
|
||||||
config.load_config()
|
config.load_config()
|
||||||
use_inotify = False and sys.platform == "linux"
|
use_inotify = sys.platform == "linux"
|
||||||
app.ctx.watcher = threading.Thread(
|
app.ctx.watcher = threading.Thread(
|
||||||
target=watcher_thread if use_inotify else watcher_thread_poll,
|
target=watcher_thread if use_inotify else watcher_thread_poll,
|
||||||
args=[loop],
|
args=[loop],
|
||||||
|
|
|
@ -29,7 +29,7 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
Homepage = ""
|
Homepage = "https://git.zi.fi/Vasanko/cista-storage"
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
cista = "cista.__main__:main"
|
cista = "cista.__main__:main"
|
||||||
|
@ -40,20 +40,18 @@ dev = [
|
||||||
"ruff",
|
"ruff",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.hatchling]
|
|
||||||
# Build frontend
|
|
||||||
pre_build = "npm run build --prefix cista-front"
|
|
||||||
|
|
||||||
[tool.hatch.version]
|
[tool.hatch.version]
|
||||||
source = "vcs"
|
source = "vcs"
|
||||||
|
|
||||||
[tool.hatch.build]
|
[tool.hatch.build]
|
||||||
|
artifacts = ["cista/wwwroot"]
|
||||||
|
hooks.custom.path = "scripts/build-frontend.py"
|
||||||
hooks.vcs.version-file = "cista/_version.py"
|
hooks.vcs.version-file = "cista/_version.py"
|
||||||
hooks.vcs.template = """
|
hooks.vcs.template = """
|
||||||
# This file is automatically generated by hatch build.
|
# This file is automatically generated by hatch build.
|
||||||
__version__ = {version!r}
|
__version__ = {version!r}
|
||||||
"""
|
"""
|
||||||
|
only-packages = true
|
||||||
targets.sdist.include = [
|
targets.sdist.include = [
|
||||||
"/cista",
|
"/cista",
|
||||||
]
|
]
|
||||||
|
|
12
scripts/build-frontend.py
Normal file
12
scripts/build-frontend.py
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# noqa: INP001
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
||||||
|
|
||||||
|
|
||||||
|
class CustomBuildHook(BuildHookInterface):
|
||||||
|
def initialize(self, version, build_data):
|
||||||
|
super().initialize(version, build_data)
|
||||||
|
print("Building Cista frontend...")
|
||||||
|
subprocess.run("npm install --prefix frontend".split(" "), check=True) # noqa: S603
|
||||||
|
subprocess.run("npm run build --prefix frontend".split(" "), check=True) # noqa: S603
|
Loading…
Reference in New Issue
Block a user