diff --git a/applications/luci-app-clouddrive2/Makefile b/applications/luci-app-clouddrive2/Makefile
new file mode 100644
index 00000000..670dc842
--- /dev/null
+++ b/applications/luci-app-clouddrive2/Makefile
@@ -0,0 +1,19 @@
+
+
+include $(TOPDIR)/rules.mk
+
+PKG_VERSION:=1.0.0-1
+PKG_RELEASE:=
+
+LUCI_TITLE:=LuCI support for CloudDrive2
+LUCI_PKGARCH:=all
+LUCI_DEPENDS:=+mount-utils +lsblk +docker +luci-lib-taskd
+
+define Package/luci-app-clouddrive2/conffiles
+/etc/config/clouddrive2
+endef
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
diff --git a/applications/luci-app-clouddrive2/luasrc/controller/clouddrive2.lua b/applications/luci-app-clouddrive2/luasrc/controller/clouddrive2.lua
new file mode 100755
index 00000000..6d6be66b
--- /dev/null
+++ b/applications/luci-app-clouddrive2/luasrc/controller/clouddrive2.lua
@@ -0,0 +1,7 @@
+
+module("luci.controller.clouddrive2", package.seeall)
+
+function index()
+ entry({"admin", "services", "clouddrive2"}, alias("admin", "services", "clouddrive2", "config"), _("CloudDrive2"), 30).dependent = true
+ entry({"admin", "services", "clouddrive2", "config"}, cbi("clouddrive2"))
+end
diff --git a/applications/luci-app-clouddrive2/luasrc/model/cbi/clouddrive2.lua b/applications/luci-app-clouddrive2/luasrc/model/cbi/clouddrive2.lua
new file mode 100644
index 00000000..97a2e347
--- /dev/null
+++ b/applications/luci-app-clouddrive2/luasrc/model/cbi/clouddrive2.lua
@@ -0,0 +1,56 @@
+--[[
+LuCI - Lua Configuration Interface
+]]--
+
+local taskd = require "luci.model.tasks"
+local clouddrive2_model = require "luci.model.clouddrive2"
+local m, s, o
+
+m = taskd.docker_map("clouddrive2", "clouddrive2", "/usr/libexec/istorec/clouddrive2.sh",
+ translate("CloudDrive2"),
+ translate("CloudDrive is a powerful multi-cloud drive management tool with local mounting of cloud drives.")
+ .. translate("Official website:") .. ' https://www.clouddrive2.com/' .. '
'
+ .. translate("Since mounting within the container requires the use of a special shared mount point, to avoid compatibility issues, only mounting to /mnt/CloudNAS is supported (mounting to other paths cannot be seen by the host). The shared mount point is automatically created by this plug-in, uninstalling the plug-in may cause the deployed CloudDrive container to fail to start or mount (iStoreOS is an exception, because /mnt is the shared mount point by default).") .. '
'
+ .. translate("Disclaimer: This LuCI plug-in is developed by individuals. It only facilitates users to deploy CloudDrive containers (https://hub.docker.com/u/cloudnas) and has nothing to do with CloudDrive. Since CloudDrive is not open source software, although this plug-in has restricted its permissions to the greatest extent, it does not make any guarantees about the software content and services provided by CloudDrive. Use at your own risk!"))
+
+s = m:section(SimpleSection, translate("Service Status"), translate("CloudDrive2 status:"))
+s:append(Template("clouddrive2/status"))
+
+s = m:section(TypedSection, "clouddrive2", translate("Setup"),
+ translate("The following parameters will only take effect during installation or upgrade:"))
+s.addremove=false
+s.anonymous=true
+
+o = s:option(Value, "image", translate("Docker image"))
+o.rmempty = false
+o.datatype = "string"
+o:value("default", translate("Default (cloudnas/clouddrive2)"))
+o:value("cloudnas/clouddrive2-unstable", "cloudnas/clouddrive2-unstable")
+o.default = "default"
+
+o = s:option(Value, "port", translate("Port"))
+o.default = "19798"
+o.datatype = "port"
+
+local blocks = clouddrive2_model.blocks()
+local home = clouddrive2_model.home()
+
+o = s:option(Value, "config_path", translate("Config path").."*")
+o.rmempty = false
+o.datatype = "string"
+
+local paths, default_path = clouddrive2_model.find_paths(blocks, home, "Configs")
+for _, val in pairs(paths) do
+ o:value(val, val)
+end
+o.default = default_path
+
+o = s:option(Value, "cache_path", translate("Temporary file path"), translate("Default use 'temp' in 'config path' if not set, please make sure there has enough space"))
+o.datatype = "string"
+local paths, default_path = clouddrive2_model.find_paths(blocks, home, "Caches")
+for _, val in pairs(paths) do
+ o:value(val, val)
+end
+o.default = default_path
+
+return m
diff --git a/applications/luci-app-clouddrive2/luasrc/model/clouddrive2.lua b/applications/luci-app-clouddrive2/luasrc/model/clouddrive2.lua
new file mode 100644
index 00000000..a16e6adb
--- /dev/null
+++ b/applications/luci-app-clouddrive2/luasrc/model/clouddrive2.lua
@@ -0,0 +1,55 @@
+local util = require "luci.util"
+local jsonc = require "luci.jsonc"
+
+local clouddrive2 = {}
+
+clouddrive2.blocks = function()
+ local f = io.popen("lsblk -s -f -b -o NAME,FSSIZE,MOUNTPOINT --json", "r")
+ local vals = {}
+ if f then
+ local ret = f:read("*all")
+ f:close()
+ local obj = jsonc.parse(ret)
+ for _, val in pairs(obj["blockdevices"]) do
+ local fsize = val["fssize"]
+ if fsize ~= nil and string.len(fsize) > 10 and val["mountpoint"] then
+ -- fsize > 1G
+ vals[#vals+1] = val["mountpoint"]
+ end
+ end
+ end
+ return vals
+end
+
+clouddrive2.home = function()
+ local uci = require "luci.model.uci".cursor()
+ local home_dirs = {}
+ home_dirs["main_dir"] = uci:get_first("quickstart", "main", "main_dir", "/root")
+ home_dirs["Configs"] = uci:get_first("quickstart", "main", "conf_dir", home_dirs["main_dir"].."/Configs")
+ home_dirs["Public"] = uci:get_first("quickstart", "main", "pub_dir", home_dirs["main_dir"].."/Public")
+ home_dirs["Downloads"] = uci:get_first("quickstart", "main", "dl_dir", home_dirs["Public"].."/Downloads")
+ home_dirs["Caches"] = uci:get_first("quickstart", "main", "tmp_dir", home_dirs["main_dir"].."/Caches")
+ return home_dirs
+end
+
+clouddrive2.find_paths = function(blocks, home_dirs, path_name)
+ local default_path = ''
+ local configs = {}
+
+ default_path = home_dirs[path_name] .. "/CloudDrive2"
+ if #blocks == 0 then
+ table.insert(configs, default_path)
+ else
+ for _, val in pairs(blocks) do
+ table.insert(configs, val .. "/" .. path_name .. "/CloudDrive2")
+ end
+ local without_conf_dir = "/root/" .. path_name .. "/CloudDrive2"
+ if default_path == without_conf_dir then
+ default_path = configs[1]
+ end
+ end
+
+ return configs, default_path
+end
+
+return clouddrive2
diff --git a/applications/luci-app-clouddrive2/luasrc/view/clouddrive2/status.htm b/applications/luci-app-clouddrive2/luasrc/view/clouddrive2/status.htm
new file mode 100644
index 00000000..8a25c4f4
--- /dev/null
+++ b/applications/luci-app-clouddrive2/luasrc/view/clouddrive2/status.htm
@@ -0,0 +1,31 @@
+<%
+local util = require "luci.util"
+local container_status = util.trim(util.exec("/usr/libexec/istorec/clouddrive2.sh status"))
+local container_install = (string.len(container_status) > 0)
+local container_running = container_status == "running"
+-%>
+