now files do presigned url setup
This commit is contained in:
@@ -51,6 +51,10 @@ const deleteFileInputSchema = v.object({
|
||||
fileId: v.string(),
|
||||
});
|
||||
|
||||
const getFileAccessUrlInputSchema = v.object({
|
||||
fileId: v.string(),
|
||||
});
|
||||
|
||||
export const deleteFileSC = command(deleteFileInputSchema, async (input) => {
|
||||
const event = getRequestEvent();
|
||||
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||
@@ -64,6 +68,22 @@ export const deleteFileSC = command(deleteFileInputSchema, async (input) => {
|
||||
: { data: null, error: res.error };
|
||||
});
|
||||
|
||||
export const getFileAccessUrlSQ = query(
|
||||
getFileAccessUrlInputSchema,
|
||||
async (input) => {
|
||||
const event = getRequestEvent();
|
||||
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||
if (!fctx.userId) {
|
||||
return unauthorized(fctx);
|
||||
}
|
||||
|
||||
const res = await fc.getFileAccessUrl(fctx, input.fileId, fctx.userId, 3600);
|
||||
return res.isOk()
|
||||
? { data: res.value, error: null }
|
||||
: { data: null, error: res.error };
|
||||
},
|
||||
);
|
||||
|
||||
export const cleanupDanglingFilesSC = command(v.object({}), async () => {
|
||||
const event = getRequestEvent();
|
||||
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import type { File } from "@pkg/logic/domains/files/data";
|
||||
import { cleanupDanglingFilesSC, deleteFileSC, getFilesSQ } from "./files.remote";
|
||||
import {
|
||||
cleanupDanglingFilesSC,
|
||||
deleteFileSC,
|
||||
getFileAccessUrlSQ,
|
||||
getFilesSQ,
|
||||
} from "./files.remote";
|
||||
import { toast } from "svelte-sonner";
|
||||
|
||||
class FilesViewModel {
|
||||
@@ -104,6 +109,25 @@ class FilesViewModel {
|
||||
this.cleanupLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async openFile(fileId: string) {
|
||||
try {
|
||||
const result = await getFileAccessUrlSQ({ fileId });
|
||||
if (result?.error || !result?.data?.url) {
|
||||
toast.error(result?.error?.message || "Failed to open file", {
|
||||
description: result?.error?.description || "Please try again later",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
window.open(result.data.url, "_blank", "noopener,noreferrer");
|
||||
} catch (error) {
|
||||
toast.error("Failed to open file", {
|
||||
description:
|
||||
error instanceof Error ? error.message : "Please try again later",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const filesVM = new FilesViewModel();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getMobileController } from "@pkg/logic/domains/mobile/controller";
|
||||
import { getFileController } from "@pkg/logic/domains/files/controller";
|
||||
import {
|
||||
mobilePaginationSchema,
|
||||
listMobileDeviceMediaFiltersSchema,
|
||||
@@ -12,6 +13,7 @@ import { command, getRequestEvent, query } from "$app/server";
|
||||
import * as v from "valibot";
|
||||
|
||||
const mc = getMobileController();
|
||||
const fc = getFileController();
|
||||
|
||||
const getDevicesInputSchema = v.object({
|
||||
search: v.optional(v.string()),
|
||||
@@ -104,9 +106,34 @@ export const getDeviceMediaSQ = query(
|
||||
});
|
||||
|
||||
const res = await mc.listDeviceMedia(fctx, filters, input.pagination);
|
||||
return res.isOk()
|
||||
? { data: res.value, error: null }
|
||||
: { data: null, error: res.error };
|
||||
if (res.isErr()) {
|
||||
return { data: null, error: res.error };
|
||||
}
|
||||
|
||||
const rows = res.value.data;
|
||||
const withAccessUrls = await Promise.all(
|
||||
rows.map(async (row) => {
|
||||
const access = await fc.getFileAccessUrl(
|
||||
fctx,
|
||||
row.fileId,
|
||||
fctx.userId!,
|
||||
3600,
|
||||
);
|
||||
|
||||
return {
|
||||
...row,
|
||||
r2Url: access.isOk() ? access.value.url : null,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
data: {
|
||||
...res.value,
|
||||
data: withAccessUrls,
|
||||
},
|
||||
error: null,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -98,14 +98,13 @@
|
||||
{new Date(item.uploadedAt).toLocaleString()}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<a
|
||||
href={item.r2Url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => void filesVM.openFile(item.id)}
|
||||
class="text-xs text-primary underline"
|
||||
>
|
||||
Open
|
||||
</a>
|
||||
</button>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user