6 Commits

Author SHA1 Message Date
Leo Vasanko
dd37238510 Update modified immediately when entering a folder 2023-11-13 02:19:13 -08:00
Leo Vasanko
c8d5f335b1 Fix upload of zero-sized files. 2023-11-13 02:13:11 -08:00
Leo Vasanko
bb80b3ee54 Clear file upload input to allow re-uploading the same item. 2023-11-13 01:38:22 -08:00
Leo Vasanko
06d860c601 Only update time-ago modified field on current folder (optimization, full update was slow for large storages). 2023-11-13 00:52:03 -08:00
Leo Vasanko
c321de13fd Don't reload backend on wwwroot changes. 2023-11-13 00:48:45 -08:00
Leo Vasanko
278e8303c4 Upload manager UI fix/tuning. 2023-11-13 00:37:56 -08:00
7 changed files with 30 additions and 22 deletions

View File

@@ -37,16 +37,23 @@ async def upload(req, ws):
)
req = msgspec.json.decode(text, type=FileRange)
pos = req.start
data = None
while pos < req.end and (data := await ws.recv()) and isinstance(data, bytes):
while True:
data = await ws.recv()
if not isinstance(data, bytes):
break
if len(data) > req.end - pos:
raise ValueError(
f"Expected up to {req.end - pos} bytes, got {len(data)} bytes"
)
sentsize = await alink(("upload", req.name, pos, data, req.size))
pos += typing.cast(int, sentsize)
if pos >= req.end:
break
if pos != req.end:
d = f"{len(data)} bytes" if isinstance(data, bytes) else data
raise ValueError(f"Expected {req.end - pos} more bytes, got {d}")
# Report success
res = StatusMsg(status="ack", req=req)
print("ack", res)
await asend(ws, res)

View File

@@ -34,9 +34,11 @@ class File:
self.open_rw()
assert self.fd is not None
if file_size is not None:
assert pos + len(buffer) <= file_size
os.ftruncate(self.fd, file_size)
os.lseek(self.fd, pos, os.SEEK_SET)
os.write(self.fd, buffer)
if buffer:
os.lseek(self.fd, pos, os.SEEK_SET)
os.write(self.fd, buffer)
def __getitem__(self, slice):
if self.fd is None:

View File

@@ -1,6 +1,6 @@
import os
import re
from pathlib import Path, PurePath
from pathlib import Path
from sanic import Sanic
@@ -15,7 +15,6 @@ def run(*, dev=False):
# Silence Sanic's warning about running in production rather than debug
os.environ["SANIC_IGNORE_PRODUCTION_WARNING"] = "1"
confdir = config.conffile.parent
wwwroot = PurePath(__file__).parent / "wwwroot"
if opts.get("ssl"):
# Run plain HTTP redirect/acme server on port 80
server80.app.prepare(port=80, motd=False)
@@ -27,7 +26,7 @@ def run(*, dev=False):
motd=False,
dev=dev,
auto_reload=dev,
reload_dir={confdir, wwwroot},
reload_dir={confdir},
access_log=True,
) # type: ignore
if dev:

View File

@@ -44,8 +44,6 @@ watchEffect(() => {
onMounted(loadSession)
onMounted(watchConnect)
onUnmounted(watchDisconnect)
// Update human-readable x seconds ago messages from mtimes
setInterval(documentStore.updateModified, 1000)
const headerMain = ref<typeof HeaderMain | null>(null)
let vert = 0
let timer: any = null

View File

@@ -79,7 +79,7 @@
</template>
<script setup lang="ts">
import { ref, computed, watchEffect } from 'vue'
import { ref, computed, watchEffect, onMounted, onUnmounted } from 'vue'
import { useDocumentStore } from '@/stores/documents'
import type { Document } from '@/repositories/Document'
import FileRenameInput from './FileRenameInput.vue'
@@ -229,6 +229,13 @@ watchEffect(() => {
focusBreadcrumb()
}
})
// Update human-readable x seconds ago messages from mtimes
let modifiedTimer: any = null
const updateModified = () => {
for (const doc of props.documents) doc.modified = formatUnixDate(doc.mtime)
}
onMounted(() => { updateModified(); modifiedTimer = setInterval(updateModified, 1000) })
onUnmounted(() => { clearInterval(modifiedTimer) })
const mkdir = (doc: Document, name: string) => {
const control = connect(controlUrl, {
open() {

View File

@@ -19,9 +19,10 @@ type CloudFile = {
function uploadHandler(event: Event) {
event.preventDefault()
event.stopPropagation()
// @ts-ignore
const infiles = Array.from(event.dataTransfer?.files || event.target.files) as File[]
const input = event.target as HTMLInputElement | null
const infiles = Array.from((input ?? (event as DragEvent).dataTransfer)?.files ?? []) as File[]
if (input) input.value = ''
if (!infiles.length) return
const loc = props.path!.join('/')
let files = []
@@ -76,7 +77,7 @@ const speed = computed(() => {
if (tsince > 1 / s) return 1 / tsince // Next block is late or not coming, decay
return s // "Current speed"
})
const speeddisp = computed(() => speed.value ? speed.value.toFixed(speed.value < 100 ? 1 : 0) + '\u202FMB/s': 'stalled')
const speeddisp = computed(() => speed.value ? speed.value.toFixed(speed.value < 10 ? 1 : 0) + '\u202FMB/s': 'stalled')
setInterval(() => {
if (Date.now() - uprogress.tlast > 3000) {
// Reset
@@ -165,10 +166,6 @@ const worker = async () => {
const ws = await WSCreate()
while (upqueue.length) {
const f = upqueue[0]
if (f.cloudPos === f.file.size) {
upqueue.shift()
continue
}
const start = f.cloudPos
const end = Math.min(f.file.size, start + (1<<20))
const control = { name: f.cloudName, size: f.file.size, start, end }
@@ -179,6 +176,7 @@ const worker = async () => {
ws.sendMsg(control)
// @ts-ignore
await ws.sendData(data)
if (f.cloudPos === f.file.size) upqueue.shift()
}
if (upqueue.length) startWorker()
uprogress.status = "idle"
@@ -219,7 +217,7 @@ onUnmounted(() => {
{{ (uprogress.filepos / uprogress.filesize * 100).toFixed(0) + '\u202F%' }}
</span>
</span>
<span class="position" v-if="uprogress.filesize > 1e7">
<span class="position" v-if="uprogress.total > 1e7">
{{ (uprogress.uploaded / 1e6).toFixed(0) + '\u202F/\u202F' + (uprogress.total / 1e6).toFixed(0) + '\u202FMB' }}
</span>
<span class="speed">{{ speeddisp }}</span>

View File

@@ -53,9 +53,6 @@ export const useDocumentStore = defineStore({
}
this.document = docs as Document[]
},
updateModified() {
for (const doc of this.document) doc.modified = formatUnixDate(doc.mtime)
},
login(username: string, privileged: boolean) {
this.user.username = username
this.user.privileged = privileged