3 Commits
v1.2.0 ... main

4 changed files with 68 additions and 36 deletions

View File

@@ -271,6 +271,18 @@ async def update_user(request, username):
return json(response) 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") @bp.put("/config/public")
async def update_public(request): async def update_public(request):
verify(request, privileged=True) verify(request, privileged=True)

View File

@@ -78,7 +78,7 @@ const filesystemdl = async (sel: SelectedItems, handle: FileSystemDirectoryHandl
h = await h.getDirectoryHandle(dir.normalize('NFC'), { create: true }) h = await h.getDirectoryHandle(dir.normalize('NFC'), { create: true })
} catch (error) { } catch (error) {
console.error('Failed to create directory', hdir, error) console.error('Failed to create directory', hdir, error)
return throw new Error(`Failed to create directory ${hdir}: ${error}`)
} }
console.log('Created', hdir) console.log('Created', hdir)
} }
@@ -90,8 +90,9 @@ const filesystemdl = async (sel: SelectedItems, handle: FileSystemDirectoryHandl
fileHandle = await h.getFileHandle(name, { create: true }) fileHandle = await h.getFileHandle(name, { create: true })
} catch (error) { } catch (error) {
console.error('Failed to create file', rel, full, hdir + name, error) console.error('Failed to create file', rel, full, hdir + name, error)
return throw new Error(`Failed to create file ${hdir + name}: ${error}`)
} }
try {
const writable = await fileHandle.createWritable() const writable = await fileHandle.createWritable()
const url = `/files/${rel}` const url = `/files/${rel}`
console.log('Fetching', url) console.log('Fetching', url)
@@ -121,6 +122,10 @@ const filesystemdl = async (sel: SelectedItems, handle: FileSystemDirectoryHandl
} }
await writable.close() await writable.close()
console.log('Saved', hdir + name) 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}`)
}
} }
statReset() statReset()
} }

View File

@@ -14,9 +14,9 @@
</div> </div>
<h3>Users</h3> <h3>Users</h3>
<button @click="addUser" class="button" title="Add new user"> Add User</button> <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 }} {{ 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> </div>
<table class="user-table"> <table class="user-table">
<thead> <thead>
@@ -70,6 +70,7 @@ const loading = ref(true)
const users = ref<User[]>([]) const users = ref<User[]>([])
const error = ref('') const error = ref('')
const success = ref('') const success = ref('')
const copyButtonText = ref('📋')
const serverSettings = reactive({ const serverSettings = reactive({
public: false public: false
}) })
@@ -170,11 +171,25 @@ const deleteUserAction = async (username: string) => {
} }
} }
const copySuccess = async () => { const copySuccess = async (isButtonClick: boolean = false) => {
const passwordMatch = success.value.match(/Password: (.+)/) const passwordMatch = success.value.match(/(?:Password|New password): (.+)/)
if (passwordMatch) { if (passwordMatch) {
await navigator.clipboard.writeText(passwordMatch[1]) 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 = ''
}
} }
} }

View File

@@ -73,8 +73,8 @@ watchEffect(() => {
store.query = props.query store.query = props.query
}) })
watch(documents, (docs) => { watch([() => props.path.join('/'), () => props.query], () => {
store.prefs.gallery = docs.some(d => d.previewable) store.prefs.gallery = documents.value.some(d => d.previewable)
}, { immediate: true }) }, { immediate: true })
</script> </script>