diff --git a/apps/main/src/lib/domains/files/files.remote.ts b/apps/main/src/lib/domains/files/files.remote.ts
index 77f4074..d2a5bb0 100644
--- a/apps/main/src/lib/domains/files/files.remote.ts
+++ b/apps/main/src/lib/domains/files/files.remote.ts
@@ -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);
diff --git a/apps/main/src/lib/domains/files/files.vm.svelte.ts b/apps/main/src/lib/domains/files/files.vm.svelte.ts
index 3f880ac..59eade8 100644
--- a/apps/main/src/lib/domains/files/files.vm.svelte.ts
+++ b/apps/main/src/lib/domains/files/files.vm.svelte.ts
@@ -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();
diff --git a/apps/main/src/lib/domains/mobile/mobile.remote.ts b/apps/main/src/lib/domains/mobile/mobile.remote.ts
index 4d27c02..43084d5 100644
--- a/apps/main/src/lib/domains/mobile/mobile.remote.ts
+++ b/apps/main/src/lib/domains/mobile/mobile.remote.ts
@@ -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,
+ };
},
);
diff --git a/apps/main/src/routes/(main)/files/+page.svelte b/apps/main/src/routes/(main)/files/+page.svelte
index e7db948..c8be921 100644
--- a/apps/main/src/routes/(main)/files/+page.svelte
+++ b/apps/main/src/routes/(main)/files/+page.svelte
@@ -98,14 +98,13 @@
{new Date(item.uploadedAt).toLocaleString()}
- void filesVM.openFile(item.id)}
class="text-xs text-primary underline"
>
Open
-
+