Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76659c6cdb | ||
|
|
a44a50878c | ||
|
|
b8816d482c |
@@ -271,6 +271,18 @@ async def update_user(request, username):
|
||||
return json(response)
|
||||
|
||||
|
||||
@bp.delete("/users/<username>")
|
||||
async def delete_user(request, username):
|
||||
verify(request, privileged=True)
|
||||
if username not in config.config.users:
|
||||
raise BadRequest("User does not exist")
|
||||
try:
|
||||
config.del_user(username)
|
||||
except Exception as e:
|
||||
raise BadRequest(str(e)) from e
|
||||
return json({"message": f"User {username} deleted"})
|
||||
|
||||
|
||||
@bp.put("/config/public")
|
||||
async def update_public(request):
|
||||
verify(request, privileged=True)
|
||||
|
||||
@@ -78,7 +78,7 @@ const filesystemdl = async (sel: SelectedItems, handle: FileSystemDirectoryHandl
|
||||
h = await h.getDirectoryHandle(dir.normalize('NFC'), { create: true })
|
||||
} catch (error) {
|
||||
console.error('Failed to create directory', hdir, error)
|
||||
return
|
||||
throw new Error(`Failed to create directory ${hdir}: ${error}`)
|
||||
}
|
||||
console.log('Created', hdir)
|
||||
}
|
||||
@@ -90,37 +90,42 @@ const filesystemdl = async (sel: SelectedItems, handle: FileSystemDirectoryHandl
|
||||
fileHandle = await h.getFileHandle(name, { create: true })
|
||||
} catch (error) {
|
||||
console.error('Failed to create file', rel, full, hdir + name, error)
|
||||
return
|
||||
throw new Error(`Failed to create file ${hdir + name}: ${error}`)
|
||||
}
|
||||
const writable = await fileHandle.createWritable()
|
||||
const url = `/files/${rel}`
|
||||
console.log('Fetching', url)
|
||||
const res = await fetch(url)
|
||||
if (!res.ok) {
|
||||
store.error = `Failed to download ${url}: ${res.status} ${res.statusText}`
|
||||
throw new Error(`Failed to download ${url}: ${res.status} ${res.statusText}`)
|
||||
}
|
||||
if (res.body) {
|
||||
++store.dprogress.fileidx
|
||||
const reader = res.body.getReader()
|
||||
await writable.truncate(0)
|
||||
store.error = "Direct download."
|
||||
store.dprogress.tlast = Date.now()
|
||||
while (true) {
|
||||
const { value, done } = await reader.read()
|
||||
if (done) break
|
||||
await writable.write(value)
|
||||
const now = Date.now()
|
||||
const size = value.byteLength
|
||||
store.dprogress.xfer += size
|
||||
store.dprogress.filepos += size
|
||||
store.dprogress.statbytes += size
|
||||
store.dprogress.statdur += now - store.dprogress.tlast
|
||||
store.dprogress.tlast = now
|
||||
try {
|
||||
const writable = await fileHandle.createWritable()
|
||||
const url = `/files/${rel}`
|
||||
console.log('Fetching', url)
|
||||
const res = await fetch(url)
|
||||
if (!res.ok) {
|
||||
store.error = `Failed to download ${url}: ${res.status} ${res.statusText}`
|
||||
throw new Error(`Failed to download ${url}: ${res.status} ${res.statusText}`)
|
||||
}
|
||||
if (res.body) {
|
||||
++store.dprogress.fileidx
|
||||
const reader = res.body.getReader()
|
||||
await writable.truncate(0)
|
||||
store.error = "Direct download."
|
||||
store.dprogress.tlast = Date.now()
|
||||
while (true) {
|
||||
const { value, done } = await reader.read()
|
||||
if (done) break
|
||||
await writable.write(value)
|
||||
const now = Date.now()
|
||||
const size = value.byteLength
|
||||
store.dprogress.xfer += size
|
||||
store.dprogress.filepos += size
|
||||
store.dprogress.statbytes += size
|
||||
store.dprogress.statdur += now - store.dprogress.tlast
|
||||
store.dprogress.tlast = now
|
||||
}
|
||||
}
|
||||
await writable.close()
|
||||
console.log('Saved', hdir + name)
|
||||
} catch (error) {
|
||||
console.error('Failed to write file', hdir + name, error)
|
||||
throw new Error(`Failed to write file ${hdir + name}: ${error}`)
|
||||
}
|
||||
await writable.close()
|
||||
console.log('Saved', hdir + name)
|
||||
}
|
||||
statReset()
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
</div>
|
||||
<h3>Users</h3>
|
||||
<button @click="addUser" class="button" title="Add new user">➕ Add User</button>
|
||||
<div v-if="success" class="success-message">
|
||||
<div v-if="success" class="success-message" @click="copySuccess(false)">
|
||||
{{ success }}
|
||||
<button @click="copySuccess" class="button small" title="Copy to clipboard"><EFBFBD></button>
|
||||
<button v-if="success.includes('Password:') || success.includes('New password:')" @click.stop="copySuccess(true)" class="button small" title="Copy to clipboard">{{ copyButtonText }}</button>
|
||||
</div>
|
||||
<table class="user-table">
|
||||
<thead>
|
||||
@@ -70,6 +70,7 @@ const loading = ref(true)
|
||||
const users = ref<User[]>([])
|
||||
const error = ref('')
|
||||
const success = ref('')
|
||||
const copyButtonText = ref('📋')
|
||||
const serverSettings = reactive({
|
||||
public: false
|
||||
})
|
||||
@@ -170,11 +171,25 @@ const deleteUserAction = async (username: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
const copySuccess = async () => {
|
||||
const passwordMatch = success.value.match(/Password: (.+)/)
|
||||
const copySuccess = async (isButtonClick: boolean = false) => {
|
||||
const passwordMatch = success.value.match(/(?:Password|New password): (.+)/)
|
||||
if (passwordMatch) {
|
||||
await navigator.clipboard.writeText(passwordMatch[1])
|
||||
// Maybe flash or something, but for now just copy
|
||||
if (isButtonClick) {
|
||||
// Show "Copied!" indication on button
|
||||
copyButtonText.value = '✅ Copied!'
|
||||
// Hide password and button immediately after copying
|
||||
const baseMessage = success.value.replace(/(?:Password|New password): .+/, 'Password copied to clipboard!')
|
||||
success.value = baseMessage
|
||||
// Hide the entire message after 3 seconds
|
||||
setTimeout(() => {
|
||||
success.value = ''
|
||||
copyButtonText.value = '📋'
|
||||
}, 3000)
|
||||
} else {
|
||||
// Just hide the message when clicking elsewhere
|
||||
success.value = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,8 +73,8 @@ watchEffect(() => {
|
||||
store.query = props.query
|
||||
})
|
||||
|
||||
watch(documents, (docs) => {
|
||||
store.prefs.gallery = docs.some(d => d.previewable)
|
||||
watch([() => props.path.join('/'), () => props.query], () => {
|
||||
store.prefs.gallery = documents.value.some(d => d.previewable)
|
||||
}, { immediate: true })
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user