fix share password issues

This commit is contained in:
2025-09-06 19:19:54 +02:00
parent 5c09120c23
commit a0c90f57d5
11 changed files with 256 additions and 54 deletions

View File

@@ -120,19 +120,24 @@ export async function listShareFiles(
| { success: true; files: DirectoryEntry[]; parent: DirectoryEntry | null }
| { success: false }
> {
const { data } = await useFetch<
const { data, error } = await useFetch<
ApiResponse<{ files: DirectoryEntry[]; parent: DirectoryEntry | null }>
>(getApiUrl('warrens/files/ls_share'), {
method: 'POST',
headers: getApiHeaders(),
// This is only required for development
headers:
password != null
? { ...getApiHeaders(), 'X-Share-Password': password }
: getApiHeaders(),
body: JSON.stringify({
shareId: shareId,
path: path,
password: password,
}),
});
if (data.value == null) {
const errorMessage = await error.value?.data;
console.log(errorMessage);
return {
success: false,
};
@@ -174,3 +179,32 @@ export async function fetchShareFile(
data: data.value,
};
}
export async function verifySharePassword(
shareId: string,
password: string
): Promise<{ success: boolean }> {
const { data } = await useFetch<ApiResponse<null>>(
getApiUrl(`warrens/files/verify_share_password`),
{
method: 'POST',
headers: getApiHeaders(),
body: JSON.stringify({
shareId: shareId,
password: password,
}),
responseType: 'json',
cache: 'default',
}
);
if (data.value == null || data.value.status !== 200) {
return {
success: false,
};
}
return {
success: true,
};
}

View File

@@ -1,5 +1,12 @@
<script lang="ts" setup>
import { fetchShareFile, getShare, listShareFiles } from '~/lib/api/shares';
import byteSize from 'byte-size';
import { toast } from 'vue-sonner';
import {
fetchShareFile,
getShare,
listShareFiles,
verifySharePassword,
} from '~/lib/api/shares';
import type { DirectoryEntry } from '~/shared/types';
import type { Share } from '~/shared/types/shares';
import { useImageViewer, useTextEditor } from '~/stores/viewers';
@@ -60,7 +67,39 @@ async function getShareFromQuery(): Promise<{
}
async function submitPassword() {
loadFiles();
if (share == null || loading.value) {
return;
}
if (!passwordValid.value) {
loading.value = true;
const result = await verifySharePassword(share.data.id, password.value);
loading.value = false;
if (result.success) {
passwordValid.value = true;
let cookie = `X-Share-Password=${password.value}; Path=/; SameSite=Lax; Secure;`;
if (share.data.expiresAt != null) {
const dayjs = useDayjs();
const diff = dayjs(share.data.expiresAt).diff(dayjs()) / 1000;
cookie += `Max-Age=${diff};`;
}
document.cookie = cookie;
} else {
toast.error('Share', {
id: 'SHARE_PASSWORD_TOAST',
description: 'Invalid password',
});
return;
}
}
if (share.file.fileType === 'directory') {
loadFiles();
}
}
async function loadFiles() {
@@ -68,10 +107,6 @@ async function loadFiles() {
return;
}
if (share.file.fileType !== 'directory') {
return;
}
loading.value = true;
const result = await listShareFiles(
@@ -81,18 +116,6 @@ async function loadFiles() {
);
if (result.success) {
passwordValid.value = true;
let cookie = `X-Share-Password=${password.value}; Path=/; SameSite=Lax; Secure;`;
if (share.data.expiresAt != null) {
const dayjs = useDayjs();
const diff = dayjs(share.data.expiresAt).diff(dayjs()) / 1000;
cookie += `Max-Age=${diff};`;
}
document.cookie = cookie;
warrenStore.setCurrentWarrenEntries(result.files, result.parent);
}
@@ -102,7 +125,11 @@ async function loadFiles() {
async function onEntryClicked(entry: DirectoryEntry, event: MouseEvent) {
event.stopPropagation();
if (warrenStore.current == null) {
if (
warrenStore.current == null ||
share == null ||
(share.data.password && !passwordValid.value)
) {
return;
}
@@ -110,7 +137,10 @@ async function onEntryClicked(entry: DirectoryEntry, event: MouseEvent) {
return;
}
const entryPath = joinPaths(warrenStore.current.path, entry.name);
const entryPath =
entry !== share.file
? joinPaths(warrenStore.current.path, entry.name)
: warrenStore.current.path;
if (entry.fileType === 'directory') {
warrenStore.setCurrentWarrenPath(entryPath);
@@ -217,17 +247,35 @@ function onEntryDownload(entry: DirectoryEntry) {
>
<div
:class="[
'h-[min(98vh,600px)] w-full max-w-screen-xl rounded-lg border transition-all',
passwordValid ? 'max-w-screen-xl' : 'max-w-lg',
'w-full rounded-lg border transition-all',
passwordValid && share.file.fileType === 'directory'
? 'h-[min(98vh,600px)] max-w-screen-xl'
: 'max-w-2xl',
]"
>
<div
class="flex flex-row items-center justify-between gap-4 px-6 pt-6"
>
<div class="flex w-full flex-row">
<div class="flex grow flex-col gap-1.5">
<h3 class="leading-none font-semibold">Share</h3>
<p class="text-muted-foreground text-sm">
<div class="flex flex-row items-center justify-between gap-4 p-6">
<button
:disabled="share.data.password && !passwordValid"
:class="[
'flex min-w-0 grow flex-row items-center gap-2 text-left',
(!share.data.password || passwordValid) &&
'cursor-pointer',
]"
@click="(e) => onEntryClicked(share!.file, e)"
>
<DirectoryEntryIcon :entry="share.file" />
<div class="flex flex-col overflow-hidden">
<h3
:title="share.file.name"
class="truncate leading-none font-semibold"
>
{{ share.file.name }}
</h3>
<p class="text-muted-foreground text-sm text-nowrap">
{{ byteSize(share.file.size) }}
</p>
<p class="text-muted-foreground text-sm text-nowrap">
Created
{{
$dayjs(share.data.createdAt).format(
@@ -236,19 +284,12 @@ function onEntryDownload(entry: DirectoryEntry) {
}}
</p>
</div>
<div class="flex flex-row items-center justify-end gap-4">
<p>{{ share.file.name }}</p>
<DirectoryEntryIcon
:entry="{ ...share.file, name: '/' }"
/>
</div>
</div>
</button>
<div class="flex flex-row items-end">
<Button
:class="
share.file.fileType !== 'file' &&
entries == null &&
'hidden'
share.data.password && !passwordValid && 'hidden'
"
size="icon"
variant="outline"
@@ -258,7 +299,13 @@ function onEntryDownload(entry: DirectoryEntry) {
</div>
</div>
<div class="flex w-full flex-col p-6">
<div
v-if="
share.file.fileType === 'directory' ||
(share.data.password && !passwordValid)
"
class="flex w-full flex-col px-6 pb-6"
>
<DirectoryList
v-if="entries != null"
:entries