diff --git a/README.md b/README.md index d2edf7b..c4a415d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,83 @@ -# ironmount +
+

Ironmount

+

Keep your volumes in check!
One interface to manage all your storage

+ + + +
+
+ Demo +
+

+ Volume details view with usage statistics and health check status +

+
+
+
-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 +## Intro + +Ironmount is an easy to use web interface to manage your remote storage and mount them as local volumes on your server. Docker as a first class citizen, Ironmount allows you to easily mount your remote storage directly into your containers with few lines of code. + +### Features + +https://github.com/nicotsx/ironmount/blob/main/screenshots/volume-creation.png?raw=true + +- ✅  Support for multiple protocols: NFS, SMB, FTP, Directory +- 📡  Mount your remote storage as local folders +- 🐳  Docker integration: mount your remote storage directly into your containers via a docker volume syntax +- 🔍  Keep an eye on your mounts with health checks and automatic remounting on error +- 📊  Monitor your mounts usage with detailed statistics and graphs + +### Coming soon + +- 🔐  User authentication and role management +- 💾  Automated backups and snapshots with encryption, strategies and retention policies +- 🔄  Re-exporting your mounts to other protocols (e.g. mount an FTP server as an SMB share with fine-grained permissions) +- ☁️  Integration with cloud storage providers (e.g. AWS S3, Google Drive, Dropbox) +- 🔀  Storage sharding and replication for high availability and performance + +## Installation + +In order to run Ironmount, you need to have Docker and Docker Compose installed on your server. Then, you can use the provided `docker-compose.yml` file to start the application. + +```yaml +services: + ironmount: + image: nicotsx/ironmount:v0.0.1 + container_name: ironmount + restart: unless-stopped + cap_add: + - SYS_ADMIN + ports: + - "4096:4096" + devices: + - /dev/fuse:/dev/fuse + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /run/docker/plugins:/run/docker/plugins + - /var/lib/docker/volumes/:/var/lib/docker/volumes:rshared + - ironmount_data:/data + +volumes: + ironmount_data: + driver: local +``` + +Then, run the following command to start Ironmount: + +```bash +docker-compose up -d +``` + +Once the container is running, you can access the web interface at `http://:4096`. + +## Docker volume usage + +![Preview](https://github.com/nicotsx/ironmount/blob/main/screenshots/docker-instructions.png?raw=true) + +## Volume creation + +![Preview](https://github.com/nicotsx/ironmount/blob/main/screenshots/volume-creation.png?raw=true) diff --git a/apps/client/app/components/create-volume-form.tsx b/apps/client/app/components/create-volume-form.tsx index 700edc2..aaa9d04 100644 --- a/apps/client/app/components/create-volume-form.tsx +++ b/apps/client/app/components/create-volume-form.tsx @@ -52,25 +52,20 @@ export const CreateVolumeForm = ({ onSubmit, mode = "create", initialValues, for form.reset({ name: watchedName, ...defaultValuesForType[watchedBackend as keyof typeof defaultValuesForType] }); }, [watchedBackend, watchedName, form.reset]); - const [testStatus, setTestStatus] = useState<"idle" | "loading" | "success" | "error">("idle"); const [testMessage, setTestMessage] = useState(""); const testBackendConnection = useMutation({ ...testConnectionMutation(), onMutate: () => { setTestMessage(""); - setTestStatus("loading"); }, onError: () => { - setTestStatus("error"); setTestMessage("Failed to test connection. Please try again."); }, onSuccess: (data) => { if (data?.success) { - setTestStatus("success"); setTestMessage(data.message); } else { - setTestStatus("error"); setTestMessage(data?.message || "Connection test failed"); } }, @@ -435,30 +430,24 @@ export const CreateVolumeForm = ({ onSubmit, mode = "create", initialValues, for type="button" variant="outline" onClick={handleTestConnection} - disabled={ - testStatus === "loading" || - !form.watch("server") || - !form.watch("share") || - !form.watch("username") || - !form.watch("password") - } + disabled={testBackendConnection.isPending} className="flex-1" > - {testStatus === "loading" && } - {testStatus === "success" && } - {testStatus === "error" && } - {testStatus === "idle" && "Test Connection"} - {testStatus === "loading" && "Testing..."} - {testStatus === "success" && "Connection Successful"} - {testStatus === "error" && "Test Failed"} + {testBackendConnection.isPending && } + {testBackendConnection.isSuccess && } + {testBackendConnection.isError && } + {testBackendConnection.isIdle && "Test Connection"} + {testBackendConnection.isPending && "Testing..."} + {testBackendConnection.isSuccess && "Connection Successful"} + {testBackendConnection.isError && "Test Failed"} {testMessage && (
- {testStatus === "loading" && } - {testStatus === "success" && } - {testStatus === "error" && } - {testStatus === "idle" && "Test Connection"} - {testStatus === "loading" && "Testing..."} - {testStatus === "success" && "Connection Successful"} - {testStatus === "error" && "Test Failed"} + {testBackendConnection.isPending && } + {testBackendConnection.isSuccess && } + {testBackendConnection.isError && } + {testBackendConnection.isIdle && "Test Connection"} + {testBackendConnection.isPending && "Testing..."} + {testBackendConnection.isSuccess && "Connection Successful"} + {testBackendConnection.isError && "Test Failed"}
{testMessage && (
- {testStatus === "loading" && } - {testStatus === "success" && } - {testStatus === "error" && } - {testStatus === "idle" && "Test Connection"} - {testStatus === "loading" && "Testing..."} - {testStatus === "success" && "Connection Successful"} - {testStatus === "error" && "Test Failed"} + {testBackendConnection.isPending && } + {testBackendConnection.isSuccess && } + {testBackendConnection.isError && } + {testBackendConnection.isIdle && "Test Connection"} + {testBackendConnection.isPending && "Testing..."} + {testBackendConnection.isSuccess && "Connection Successful"} + {testBackendConnection.isError && "Test Failed"}
{testMessage && (
- ); diff --git a/notes.md b/notes.md new file mode 100644 index 0000000..a67dab3 --- /dev/null +++ b/notes.md @@ -0,0 +1,4 @@ +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/screenshots/docker-instructions.png b/screenshots/docker-instructions.png new file mode 100644 index 0000000..59f41ae Binary files /dev/null and b/screenshots/docker-instructions.png differ diff --git a/screenshots/volume-creation.png b/screenshots/volume-creation.png new file mode 100644 index 0000000..99554dd Binary files /dev/null and b/screenshots/volume-creation.png differ diff --git a/screenshots/volume-details.png b/screenshots/volume-details.png new file mode 100644 index 0000000..166142f Binary files /dev/null and b/screenshots/volume-details.png differ