Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76659c6cdb | ||
|
|
a44a50878c | ||
|
|
b8816d482c |
@@ -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)
|
||||||
|
|||||||
@@ -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,37 +90,42 @@ 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}`)
|
||||||
}
|
}
|
||||||
const writable = await fileHandle.createWritable()
|
try {
|
||||||
const url = `/files/${rel}`
|
const writable = await fileHandle.createWritable()
|
||||||
console.log('Fetching', url)
|
const url = `/files/${rel}`
|
||||||
const res = await fetch(url)
|
console.log('Fetching', url)
|
||||||
if (!res.ok) {
|
const res = await fetch(url)
|
||||||
store.error = `Failed to download ${url}: ${res.status} ${res.statusText}`
|
if (!res.ok) {
|
||||||
throw new Error(`Failed to download ${url}: ${res.status} ${res.statusText}`)
|
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
|
|
||||||
}
|
}
|
||||||
|
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()
|
statReset()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 = ''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user