diff --git a/Dockerfile.dev b/Dockerfile.dev index 4b5d2e4..dbf57eb 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,5 +1,7 @@ FROM oven/bun:1.2.20-alpine AS base +RUN apk add --no-cache davfs2 + WORKDIR /app COPY ./package.json ./bun.lock ./ diff --git a/README.md b/README.md index b19c332..d2edf7b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # ironmount docker run --rm -it -v nicolas:/data alpine sh -lc 'echo hello > /data/hi && cat /data/hi' + +mount -t davfs http://192.168.2.42 /mnt/webdav diff --git a/apps/client/app/api-client/types.gen.ts b/apps/client/app/api-client/types.gen.ts index 473fca2..7bcea27 100644 --- a/apps/client/app/api-client/types.gen.ts +++ b/apps/client/app/api-client/types.gen.ts @@ -27,6 +27,22 @@ export type ListVolumesResponses = { } | { backend: "smb"; + password: string; + server: string; + share: string; + username: string; + vers?: "1.0" | "2.0" | "2.1" | "3.0"; + port?: number | string; + domain?: string; + } + | { + backend: "webdav"; + path: string; + server: string; + port?: number | string; + password?: string; + ssl?: boolean; + username?: string; }; createdAt: number; lastError: string; @@ -34,7 +50,7 @@ export type ListVolumesResponses = { name: string; path: string; status: "error" | "mounted" | "unknown" | "unmounted"; - type: "directory" | "nfs" | "smb"; + type: "directory" | "nfs" | "smb" | "webdav"; updatedAt: number; }>; }; @@ -57,6 +73,22 @@ export type CreateVolumeData = { } | { backend: "smb"; + password: string; + server: string; + share: string; + username: string; + vers?: "1.0" | "2.0" | "2.1" | "3.0"; + port?: number | string; + domain?: string; + } + | { + backend: "webdav"; + path: string; + server: string; + port?: number | string; + password?: string; + ssl?: boolean; + username?: string; }; name: string; }; @@ -95,6 +127,22 @@ export type TestConnectionData = { } | { backend: "smb"; + password: string; + server: string; + share: string; + username: string; + vers?: "1.0" | "2.0" | "2.1" | "3.0"; + port?: number | string; + domain?: string; + } + | { + backend: "webdav"; + path: string; + server: string; + port?: number | string; + password?: string; + ssl?: boolean; + username?: string; }; }; path?: never; @@ -175,6 +223,22 @@ export type GetVolumeResponses = { } | { backend: "smb"; + password: string; + server: string; + share: string; + username: string; + vers?: "1.0" | "2.0" | "2.1" | "3.0"; + port?: number | string; + domain?: string; + } + | { + backend: "webdav"; + path: string; + server: string; + port?: number | string; + password?: string; + ssl?: boolean; + username?: string; }; createdAt: number; lastError: string; @@ -182,7 +246,7 @@ export type GetVolumeResponses = { name: string; path: string; status: "error" | "mounted" | "unknown" | "unmounted"; - type: "directory" | "nfs" | "smb"; + type: "directory" | "nfs" | "smb" | "webdav"; updatedAt: number; }; }; @@ -205,6 +269,22 @@ export type UpdateVolumeData = { } | { backend: "smb"; + password: string; + server: string; + share: string; + username: string; + vers?: "1.0" | "2.0" | "2.1" | "3.0"; + port?: number | string; + domain?: string; + } + | { + backend: "webdav"; + path: string; + server: string; + port?: number | string; + password?: string; + ssl?: boolean; + username?: string; }; }; path: { @@ -241,6 +321,22 @@ export type UpdateVolumeResponses = { } | { backend: "smb"; + password: string; + server: string; + share: string; + username: string; + vers?: "1.0" | "2.0" | "2.1" | "3.0"; + port?: number | string; + domain?: string; + } + | { + backend: "webdav"; + path: string; + server: string; + port?: number | string; + password?: string; + ssl?: boolean; + username?: string; }; createdAt: number; name: string; diff --git a/apps/client/app/components/create-volume-form.tsx b/apps/client/app/components/create-volume-form.tsx index 7ab51a5..9a523ec 100644 --- a/apps/client/app/components/create-volume-form.tsx +++ b/apps/client/app/components/create-volume-form.tsx @@ -62,7 +62,7 @@ export const CreateVolumeForm = ({ onSubmit, mode = "create", initialValues, for const handleTestConnection = async () => { const formValues = getValues(); - if (formValues.backend === "nfs" || formValues.backend === "smb") { + if (formValues.backend === "nfs" || formValues.backend === "smb" || formValues.backend === "webdav") { testBackendConnection.mutate({ body: { config: formValues }, }); @@ -108,6 +108,7 @@ export const CreateVolumeForm = ({ onSubmit, mode = "create", initialValues, for Directory NFS SMB + WebDAV Choose the storage backend for this volume. @@ -187,6 +188,102 @@ export const CreateVolumeForm = ({ onSubmit, mode = "create", initialValues, for )} + {watchedBackend === "webdav" && ( + <> + ( + + Server + + + + WebDAV server hostname or IP address. + + + )} + /> + ( + + Path + + + + Path to the WebDAV directory on the server. + + + )} + /> + ( + + Username (Optional) + + + + Username for WebDAV authentication (optional). + + + )} + /> + ( + + Password (Optional) + + + + Password for WebDAV authentication (optional). + + + )} + /> + ( + + Port + + field.onChange(parseInt(e.target.value, 10) || undefined)} + /> + + WebDAV server port (default: 80 for HTTP, 443 for HTTPS). + + + )} + /> + ( + + Use SSL/HTTPS + +
+ field.onChange(e.target.checked)} + className="rounded border-gray-300" + /> + Enable HTTPS for secure connections +
+
+ Use HTTPS instead of HTTP for secure connections. + +
+ )} + /> + + )} + {watchedBackend === "smb" && ( <> )} + + {watchedBackend === "webdav" && ( +
+
+ +
+ {testMessage && ( +
+ {testMessage} +
+ )} +
+ )} ); diff --git a/apps/client/app/components/volume-icon.tsx b/apps/client/app/components/volume-icon.tsx index 0a5ceab..7f7015f 100644 --- a/apps/client/app/components/volume-icon.tsx +++ b/apps/client/app/components/volume-icon.tsx @@ -1,5 +1,5 @@ import type { BackendType } from "@ironmount/schemas"; -import { Folder, Server, Share2 } from "lucide-react"; +import { Cloud, Folder, Server, Share2 } from "lucide-react"; type VolumeIconProps = { backend: BackendType; @@ -26,6 +26,12 @@ const getIconAndColor = (backend: BackendType) => { color: "text-purple-600 dark:text-purple-400", label: "SMB", }; + case "webdav": + return { + icon: Cloud, + color: "text-green-600 dark:text-green-400", + label: "WebDAV", + }; default: return { icon: Folder, diff --git a/apps/client/app/modules/details/components/storage-chart.tsx b/apps/client/app/modules/details/components/storage-chart.tsx index 684154c..fe5be08 100644 --- a/apps/client/app/modules/details/components/storage-chart.tsx +++ b/apps/client/app/modules/details/components/storage-chart.tsx @@ -18,7 +18,7 @@ export function StorageChart({ statfs }: Props) { { name: "Used", value: statfs.used, - fill: "blue", + fill: "#2B7EFF", }, { name: "Free", @@ -29,19 +29,7 @@ export function StorageChart({ statfs }: Props) { [statfs], ); - const chartConfig = { - value: { - label: "Storage", - }, - used: { - label: "Used", - color: "hsl(var(--destructive))", - }, - free: { - label: "Free", - color: "hsl(var(--primary))", - }, - } satisfies ChartConfig; + const chartConfig = {} satisfies ChartConfig; const usagePercentage = React.useMemo(() => { return Math.round((statfs.used / statfs.total) * 100); @@ -75,37 +63,70 @@ export function StorageChart({ statfs }: Props) { - - - [, name]} - /> - } - /> - -