diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a7f45702 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ + +forkapp/forkapp +tools/forkapp +forkapp/forkapp.exe +tools/forkapp.exe diff --git a/README.md b/README.md index 2734ba65..5d53626a 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,9 @@ 3. 等待编译完成,点击任务进入详情页 4. 在详情页下载插件压缩包![image](https://user-images.githubusercontent.com/1214708/153843272-81843b45-6dc8-4945-871f-a9a467f63c33.png) + +## ForkApp + +1. ./forkapp forkapp -from ../applications/luci-app-plex -to ../applications/luci-app-demo +2. ./forkapp upload -ip 192.168.100.1 -pwd "password" -from ../applications/luci-app-demo -to /root/ +3. ./forkapp upload -ip 192.168.100.1 -pwd "password" -from ../applications/luci-app-demo -to /root/ -script ../tools/simple-install.sh -install diff --git a/applications/luci-app-feishuvpn/Makefile b/applications/luci-app-feishuvpn/Makefile new file mode 100644 index 00000000..73d39652 --- /dev/null +++ b/applications/luci-app-feishuvpn/Makefile @@ -0,0 +1,18 @@ + + +include $(TOPDIR)/rules.mk + +PKG_VERSION:=1.0.2-20231208 +PKG_RELEASE:= + +LUCI_TITLE:=LuCI support for FeiShuVpn +LUCI_PKGARCH:=all +LUCI_DEPENDS:=+lsblk +docker +luci-lib-taskd +luci-lib-docker + +define Package/luci-app-feishuvpn/conffiles +/etc/config/feishuvpn +endef + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/applications/luci-app-feishuvpn/luasrc/controller/feishuvpn.lua b/applications/luci-app-feishuvpn/luasrc/controller/feishuvpn.lua new file mode 100755 index 00000000..fcea53a5 --- /dev/null +++ b/applications/luci-app-feishuvpn/luasrc/controller/feishuvpn.lua @@ -0,0 +1,12 @@ +-- 定义luci.controller.feishuvpn模块 +module("luci.controller.feishuvpn", package.seeall) + +-- index函数:配置FeiShuVpn的管理界面入口 +function index() + -- 创建admin/services/feishuvpn目录下的入口,重定向到config页面 + -- dependent = true表示该入口依赖于其他服务 + entry({"admin", "services", "feishuvpn"}, alias("admin", "services", "feishuvpn", "config"), _("FeiShuVpn"), 30).dependent = true + + -- 创建admin/services/feishuvpn/config页面,用于配置FeiShuVpn + entry({"admin", "services", "feishuvpn", "config"}, cbi("feishuvpn")) +end \ No newline at end of file diff --git a/applications/luci-app-feishuvpn/luasrc/model/cbi/feishuvpn.lua b/applications/luci-app-feishuvpn/luasrc/model/cbi/feishuvpn.lua new file mode 100644 index 00000000..ce3c2732 --- /dev/null +++ b/applications/luci-app-feishuvpn/luasrc/model/cbi/feishuvpn.lua @@ -0,0 +1,57 @@ +--[[ +LuCI - Lua Configuration Interface +]]-- + +local taskd = require "luci.model.tasks" +local docker = require "luci.docker" +local feishuvpn_model = require "luci.model.feishuvpn" +local m, s, o + +m = taskd.docker_map("feishuvpn", "feishuvpn", "/usr/libexec/istorec/feishuvpn.sh", + translate("FeiShuVpn"), + translate("FeiShuVpn is p2p vpn client.") + .. translate("Official website:") .. ' https://wiki.feishuwg.com/') + +local dk = docker.new({socket_path="/var/run/docker.sock"}) +local dockerd_running = dk:_ping().code == 200 +local docker_info = dockerd_running and dk:info().body or {} +local docker_aspace = 0 +if docker_info.DockerRootDir then + local statvfs = nixio.fs.statvfs(docker_info.DockerRootDir) + docker_aspace = statvfs and (statvfs.bavail * statvfs.bsize) or 0 +end + +s = m:section(SimpleSection, translate("Service Status"), translate("FeiShuVpn status:")) +s:append(Template("feishuvpn/status")) + +s = m:section(TypedSection, "main", translate("Setup"), + (docker_aspace < 2147483648 and + (translate("The free space of Docker is less than 2GB, which may cause the installation to fail.") + .. "
") or "") .. translate("The following parameters will only take effect during installation or upgrade:")) +s.addremove=false +s.anonymous=true + +o = s:option(Value, "port", translate("Port").."*") +o.default = "9091" +o.datatype = "port" + +o = s:option(Value, "image_name", translate("Image").."*") +o.rmempty = false +o.datatype = "string" +o.default = "registry.cn-qingdao.aliyuncs.com/feishuwg/p2p:v2.2" +o:value("registry.cn-qingdao.aliyuncs.com/feishuwg/p2p:v2.2", "registry.cn-qingdao.aliyuncs.com/feishuwg/p2p:v2.2") + +local blocks = feishuvpn_model.blocks() +local home = feishuvpn_model.home() + +o = s:option(Value, "config_path", translate("Config path").."*") +o.rmempty = false +o.datatype = "string" + +local paths, default_path = feishuvpn_model.find_paths(blocks, home, "Configs") +for _, val in pairs(paths) do + o:value(val, val) +end +o.default = default_path + +return m diff --git a/applications/luci-app-feishuvpn/luasrc/model/feishuvpn.lua b/applications/luci-app-feishuvpn/luasrc/model/feishuvpn.lua new file mode 100644 index 00000000..73233bc1 --- /dev/null +++ b/applications/luci-app-feishuvpn/luasrc/model/feishuvpn.lua @@ -0,0 +1,55 @@ +local util = require "luci.util" +local jsonc = require "luci.jsonc" + +local feishuvpn = {} + +feishuvpn.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 + +feishuvpn.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 + +feishuvpn.find_paths = function(blocks, home_dirs, path_name) + local default_path = '' + local configs = {} + + default_path = home_dirs[path_name] .. "/FeiShuVpn" + if #blocks == 0 then + table.insert(configs, default_path) + else + for _, val in pairs(blocks) do + table.insert(configs, val .. "/" .. path_name .. "/FeiShuVpn") + end + local without_conf_dir = "/root/" .. path_name .. "/FeiShuVpn" + if default_path == without_conf_dir then + default_path = configs[1] + end + end + + return configs, default_path +end + +return feishuvpn diff --git a/applications/luci-app-feishuvpn/luasrc/view/feishuvpn/status.htm b/applications/luci-app-feishuvpn/luasrc/view/feishuvpn/status.htm new file mode 100644 index 00000000..adaeec78 --- /dev/null +++ b/applications/luci-app-feishuvpn/luasrc/view/feishuvpn/status.htm @@ -0,0 +1,31 @@ +<% +local util = require "luci.util" +local container_status = util.trim(util.exec("/usr/libexec/istorec/feishuvpn.sh status")) +local container_install = (string.len(container_status) > 0) +local container_running = container_status == "running" +-%> +
+ +
+ <% if container_running then %> + + <% else %> + + <% end %> +
+
+<% +if container_running then + local port=util.trim(util.exec("/usr/libexec/istorec/feishuvpn.sh port")) + if port == "" then + port="9091" + end +-%> +
+ +
+ + +
+
+<% end %> diff --git a/applications/luci-app-feishuvpn/po/zh-cn/feishuvpn.po b/applications/luci-app-feishuvpn/po/zh-cn/feishuvpn.po new file mode 100644 index 00000000..14ba3bb9 --- /dev/null +++ b/applications/luci-app-feishuvpn/po/zh-cn/feishuvpn.po @@ -0,0 +1,50 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +msgid "FeiShuVpn" +msgstr "飞鼠组网" + +msgid "Official website:" +msgstr "官方网站:" + +msgid "FeiShuVpn is p2p vpn client." +msgstr "飞鼠组网是一个点对点的组网工具。" + +msgid "Config path" +msgstr "配置文件路径" + +msgid "Port" +msgstr "端口" + +msgid "Service Status" +msgstr "服务状态" + +msgid "FeiShuVpn status:" +msgstr "飞鼠组网的状态信息如下:" + +msgid "Setup" +msgstr "安装配置" + +msgid "The following parameters will only take effect during installation or upgrade:" +msgstr "以下参数只在安装或者升级时才会生效:" + +msgid "Status" +msgstr "状态" + +msgid "FeiShuVpn is running" +msgstr "飞鼠组网运行中" + +msgid "FeiShuVpn is not running" +msgstr "飞鼠组网未运行" + +msgid "Open FeiShuVpn" +msgstr "打开飞鼠组网" + +msgid "Not required, all disk will be mounted under %s" +msgstr "可不填,所有硬盘都会挂载到 %s 下" + +msgid "The free space of Docker is less than 2GB, which may cause the installation to fail." +msgstr "Docker 可用空间已不足2GB,可能导致安装失败。" + +msgid "Please make sure there has enough space" +msgstr "请确保有足够空间" diff --git a/applications/luci-app-feishuvpn/root/etc/config/feishuvpn b/applications/luci-app-feishuvpn/root/etc/config/feishuvpn new file mode 100644 index 00000000..ce673778 --- /dev/null +++ b/applications/luci-app-feishuvpn/root/etc/config/feishuvpn @@ -0,0 +1,3 @@ +config main + option 'config_path' '' + diff --git a/applications/luci-app-feishuvpn/root/usr/libexec/istorec/feishuvpn.sh b/applications/luci-app-feishuvpn/root/usr/libexec/istorec/feishuvpn.sh new file mode 100755 index 00000000..0ba2e807 --- /dev/null +++ b/applications/luci-app-feishuvpn/root/usr/libexec/istorec/feishuvpn.sh @@ -0,0 +1,74 @@ +#!/bin/sh +# Author Xiaobao(xiaobao@linkease.com) + +ACTION=${1} +shift 1 + +do_install() { + local image_name=`uci get feishuvpn.@main[0].image_name 2>/dev/null` + local config=`uci get feishuvpn.@main[0].config_path 2>/dev/null` + + if [ -z "$config" ]; then + echo "config path is empty!" + exit 1 + fi + + [ -z "$image_name" ] && image_name="registry.cn-qingdao.aliyuncs.com/feishuwg/p2p:v2.2" + echo "docker pull ${image_name}" + docker pull ${image_name} + docker rm -f feishuvpn + + local cmd="docker run --restart=unless-stopped -d -h FeiShuVpnServer -v \"$config:/data/conf\" " + + cmd="$cmd\ + --cap-add=ALL \ + --privileged=true \ + --device=/dev/net/tun \ + --dns=127.0.0.1 \ + --network=host " + + # local tz="`uci get system.@system[0].zonename | sed 's/ /_/g'`" + # [ -z "$tz" ] || cmd="$cmd -e TZ=$tz" + + cmd="$cmd -v /mnt:/mnt" + mountpoint -q /mnt && cmd="$cmd:rslave" + cmd="$cmd --name feishuvpn \"$image_name\"" + + echo "$cmd" + eval "$cmd" +} + +usage() { + echo "usage: $0 sub-command" + echo "where sub-command is one of:" + echo " install Install the feishuvpn" + echo " upgrade Upgrade the feishuvpn" + echo " rm/start/stop/restart Remove/Start/Stop/Restart the feishuvpn" + echo " status FeiShuVpn status" + echo " port FeiShuVpn port" +} + +case ${ACTION} in + "install") + do_install + ;; + "upgrade") + do_install + ;; + "rm") + docker rm -f feishuvpn + ;; + "start" | "stop" | "restart") + docker ${ACTION} feishuvpn + ;; + "status") + docker ps --all -f 'name=feishuvpn' --format '{{.State}}' + ;; + "port") + echo 9091 + ;; + *) + usage + exit 1 + ;; +esac diff --git a/applications/luci-app-htreader/Makefile b/applications/luci-app-htreader/Makefile new file mode 100644 index 00000000..6884f6f6 --- /dev/null +++ b/applications/luci-app-htreader/Makefile @@ -0,0 +1,18 @@ + + +include $(TOPDIR)/rules.mk + +PKG_VERSION:=1.0.2-20231208 +PKG_RELEASE:= + +LUCI_TITLE:=LuCI support for HTReader +LUCI_PKGARCH:=all +LUCI_DEPENDS:=+lsblk +docker +luci-lib-taskd +luci-lib-docker + +define Package/luci-app-htreader/conffiles +/etc/config/htreader +endef + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/applications/luci-app-htreader/luasrc/controller/htreader.lua b/applications/luci-app-htreader/luasrc/controller/htreader.lua new file mode 100755 index 00000000..92233c17 --- /dev/null +++ b/applications/luci-app-htreader/luasrc/controller/htreader.lua @@ -0,0 +1,7 @@ + +module("luci.controller.htreader", package.seeall) + +function index() + entry({"admin", "services", "htreader"}, alias("admin", "services", "htreader", "config"), _("HTReader"), 30).dependent = true + entry({"admin", "services", "htreader", "config"}, cbi("htreader")) +end diff --git a/applications/luci-app-htreader/luasrc/model/cbi/htreader.lua b/applications/luci-app-htreader/luasrc/model/cbi/htreader.lua new file mode 100644 index 00000000..e797b469 --- /dev/null +++ b/applications/luci-app-htreader/luasrc/model/cbi/htreader.lua @@ -0,0 +1,69 @@ +--[[ +LuCI - Lua Configuration Interface +]]-- + +local taskd = require "luci.model.tasks" +local docker = require "luci.docker" +local htreader_model = require "luci.model.htreader" +local m, s, o + +m = taskd.docker_map("htreader", "htreader", "/usr/libexec/istorec/htreader.sh", + translate("HTReader"), + translate("HTReader is book reader in web.") + .. translate("Official website:") .. ' https://github.com/XIU2/Yuedu') + +local dk = docker.new({socket_path="/var/run/docker.sock"}) +local dockerd_running = dk:_ping().code == 200 +local docker_info = dockerd_running and dk:info().body or {} +local docker_aspace = 0 +if docker_info.DockerRootDir then + local statvfs = nixio.fs.statvfs(docker_info.DockerRootDir) + docker_aspace = statvfs and (statvfs.bavail * statvfs.bsize) or 0 +end + +s = m:section(SimpleSection, translate("Service Status"), translate("HTReader status:")) +s:append(Template("htreader/status")) + +s = m:section(TypedSection, "main", translate("Setup"), + (docker_aspace < 2147483648 and + (translate("The free space of Docker is less than 2GB, which may cause the installation to fail.") + .. "
") or "") .. translate("The following parameters will only take effect during installation or upgrade:")) +s.addremove=false +s.anonymous=true + +o = s:option(Value, "port", translate("Port").."*") +o.default = "9060" +o.datatype = "port" + +o = s:option(Flag, "multiuser", translate("Multiple user version")) +o.default = 0 +o.rmempty = false + +o = s:option(Value, "password", translate("password")) +o.datatype = "string" +o:depends("multiuser", 1) + +o = s:option(Value, "active_code", translate("Active code")) +o.datatype = "string" +o:depends("multiuser", 1) + +o = s:option(Value, "image_name", translate("Image").."*") +o.rmempty = false +o.datatype = "string" +o.default = "hectorqin/reader" +o:value("hectorqin/reader", "hectorqin/reader") + +local blocks = htreader_model.blocks() +local home = htreader_model.home() + +o = s:option(Value, "config_path", translate("Config path").."*") +o.rmempty = false +o.datatype = "string" + +local paths, default_path = htreader_model.find_paths(blocks, home, "Configs") +for _, val in pairs(paths) do + o:value(val, val) +end +o.default = default_path + +return m diff --git a/applications/luci-app-htreader/luasrc/model/htreader.lua b/applications/luci-app-htreader/luasrc/model/htreader.lua new file mode 100644 index 00000000..2e62528d --- /dev/null +++ b/applications/luci-app-htreader/luasrc/model/htreader.lua @@ -0,0 +1,55 @@ +local util = require "luci.util" +local jsonc = require "luci.jsonc" + +local htreader = {} + +htreader.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 + +htreader.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 + +htreader.find_paths = function(blocks, home_dirs, path_name) + local default_path = '' + local configs = {} + + default_path = home_dirs[path_name] .. "/HTReader" + if #blocks == 0 then + table.insert(configs, default_path) + else + for _, val in pairs(blocks) do + table.insert(configs, val .. "/" .. path_name .. "/HTReader") + end + local without_conf_dir = "/root/" .. path_name .. "/HTReader" + if default_path == without_conf_dir then + default_path = configs[1] + end + end + + return configs, default_path +end + +return htreader diff --git a/applications/luci-app-htreader/luasrc/view/htreader/status.htm b/applications/luci-app-htreader/luasrc/view/htreader/status.htm new file mode 100644 index 00000000..b4a239f3 --- /dev/null +++ b/applications/luci-app-htreader/luasrc/view/htreader/status.htm @@ -0,0 +1,31 @@ +<% +local util = require "luci.util" +local container_status = util.trim(util.exec("/usr/libexec/istorec/htreader.sh status")) +local container_install = (string.len(container_status) > 0) +local container_running = container_status == "running" +-%> +
+ +
+ <% if container_running then %> + + <% else %> + + <% end %> +
+
+<% +if container_running then + local port=util.trim(util.exec("/usr/libexec/istorec/htreader.sh port")) + if port == "" then + port="9060" + end +-%> +
+ +
+ + +
+
+<% end %> diff --git a/applications/luci-app-htreader/po/zh-cn/htreader.po b/applications/luci-app-htreader/po/zh-cn/htreader.po new file mode 100644 index 00000000..0a37d418 --- /dev/null +++ b/applications/luci-app-htreader/po/zh-cn/htreader.po @@ -0,0 +1,44 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +msgid "Official website:" +msgstr "官方网站:" + +msgid "HTReader is book reader in web." +msgstr "HTReader 是一个网页版本在线读书。" + +msgid "Config path" +msgstr "配置文件路径" + +msgid "Port" +msgstr "端口" + +msgid "Service Status" +msgstr "服务状态" + +msgid "HTReader status:" +msgstr "HTReader 的状态信息如下:" + +msgid "Setup" +msgstr "安装配置" + +msgid "The following parameters will only take effect during installation or upgrade:" +msgstr "以下参数只在安装或者升级时才会生效:" + +msgid "Status" +msgstr "状态" + +msgid "HTReader is running" +msgstr "HTReader 运行中" + +msgid "HTReader is not running" +msgstr "HTReader 未运行" + +msgid "Open HTReader" +msgstr "打开 HTReader" + +msgid "The free space of Docker is less than 2GB, which may cause the installation to fail." +msgstr "Docker 可用空间已不足2GB,可能导致安装失败。" + +msgid "Please make sure there has enough space" +msgstr "请确保有足够空间" diff --git a/applications/luci-app-htreader/root/etc/config/htreader b/applications/luci-app-htreader/root/etc/config/htreader new file mode 100644 index 00000000..a4bb805c --- /dev/null +++ b/applications/luci-app-htreader/root/etc/config/htreader @@ -0,0 +1,4 @@ +config main + option 'port' '9060' + option 'multiuser' '0' + diff --git a/applications/luci-app-htreader/root/usr/libexec/istorec/htreader.sh b/applications/luci-app-htreader/root/usr/libexec/istorec/htreader.sh new file mode 100755 index 00000000..48e2a656 --- /dev/null +++ b/applications/luci-app-htreader/root/usr/libexec/istorec/htreader.sh @@ -0,0 +1,86 @@ +#!/bin/sh +# Author Xiaobao(xiaobao@linkease.com) + +ACTION=${1} +shift 1 + +do_install() { + local port=`uci get htreader.@main[0].port 2>/dev/null` + local multiuser=`uci get htreader.@main[0].multiuser 2>/dev/null` + local active_code=`uci get htreader.@main[0].active_code 2>/dev/null` + local password=`uci get htreader.@main[0].password 2>/dev/null` + local image_name=`uci get htreader.@main[0].image_name 2>/dev/null` + local config=`uci get htreader.@main[0].config_path 2>/dev/null` + + if [ -z "$config" ]; then + echo "config path is empty!" + exit 1 + fi + + [ -z "$image_name" ] && image_name="hectorqin/reader" + echo "docker pull ${image_name}" + docker pull ${image_name} + docker rm -f htreader + + [ -z "$port" ] && port=9060 + + mkdir -p $config/storage + mkdir -p $config/logs + local cmd="docker run --restart=unless-stopped -d -h HTReaderServer \ + -e \"SPRING_PROFILES_ACTIVE=prod\" \ + -v \"$config/logs:/logs\" \ + -v \"$config/storage:/storage\" " + + if [ "$multiuser" = "1" ]; then + cmd="$cmd -e \"READER_APP_SECUREKEY=$password\" -e \"READER_APP_INVITECODE=$active_code\" " + fi + + cmd="$cmd\ + --dns=172.17.0.1 \ + -p $port:8080 " + + local tz="`uci get system.@system[0].zonename | sed 's/ /_/g'`" + [ -z "$tz" ] || cmd="$cmd -e TZ=$tz" + + cmd="$cmd -v /mnt:/mnt" + mountpoint -q /mnt && cmd="$cmd:rslave" + cmd="$cmd --name htreader \"$image_name\"" + + echo "$cmd" + eval "$cmd" +} + +usage() { + echo "usage: $0 sub-command" + echo "where sub-command is one of:" + echo " install Install the htreader" + echo " upgrade Upgrade the htreader" + echo " rm/start/stop/restart Remove/Start/Stop/Restart the htreader" + echo " status HTReader status" + echo " port HTReader port" +} + +case ${ACTION} in + "install") + do_install + ;; + "upgrade") + do_install + ;; + "rm") + docker rm -f htreader + ;; + "start" | "stop" | "restart") + docker ${ACTION} htreader + ;; + "status") + docker ps --all -f 'name=htreader' --format '{{.State}}' + ;; + "port") + docker ps --all -f 'name=htreader' --format '{{.Ports}}' | grep -om1 '0.0.0.0:[0-9]*->9060/tcp' | sed 's/0.0.0.0:\([0-9]*\)->.*/\1/' + ;; + *) + usage + exit 1 + ;; +esac diff --git a/applications/luci-app-ittools/Makefile b/applications/luci-app-ittools/Makefile new file mode 100644 index 00000000..407d8990 --- /dev/null +++ b/applications/luci-app-ittools/Makefile @@ -0,0 +1,18 @@ + + +include $(TOPDIR)/rules.mk + +PKG_VERSION:=1.0.2-20231208 +PKG_RELEASE:= + +LUCI_TITLE:=LuCI support for ITTools +LUCI_PKGARCH:=all +LUCI_DEPENDS:=+lsblk +docker +luci-lib-taskd +luci-lib-docker + +define Package/luci-app-ittools/conffiles +/etc/config/ittools +endef + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/applications/luci-app-ittools/luasrc/controller/ittools.lua b/applications/luci-app-ittools/luasrc/controller/ittools.lua new file mode 100755 index 00000000..5e31f0b7 --- /dev/null +++ b/applications/luci-app-ittools/luasrc/controller/ittools.lua @@ -0,0 +1,7 @@ + +module("luci.controller.ittools", package.seeall) + +function index() + entry({"admin", "services", "ittools"}, alias("admin", "services", "ittools", "config"), _("ITTools"), 30).dependent = true + entry({"admin", "services", "ittools", "config"}, cbi("ittools")) +end diff --git a/applications/luci-app-ittools/luasrc/model/cbi/ittools.lua b/applications/luci-app-ittools/luasrc/model/cbi/ittools.lua new file mode 100644 index 00000000..79eaef81 --- /dev/null +++ b/applications/luci-app-ittools/luasrc/model/cbi/ittools.lua @@ -0,0 +1,45 @@ +--[[ +LuCI - Lua Configuration Interface +]]-- + +local taskd = require "luci.model.tasks" +local docker = require "luci.docker" +local m, s, o + +m = taskd.docker_map("ittools", "ittools", "/usr/libexec/istorec/ittools.sh", + translate("ITTools"), + translate("ITTools is useful tools for developer and people working in IT.") + .. translate("Official website:") .. ' https://it-tools.tech/') + +local dk = docker.new({socket_path="/var/run/docker.sock"}) +local dockerd_running = dk:_ping().code == 200 +local docker_info = dockerd_running and dk:info().body or {} +local docker_aspace = 0 +if docker_info.DockerRootDir then + local statvfs = nixio.fs.statvfs(docker_info.DockerRootDir) + docker_aspace = statvfs and (statvfs.bavail * statvfs.bsize) or 0 +end + +s = m:section(SimpleSection, translate("Service Status"), translate("ITTools status:")) +s:append(Template("ittools/status")) + +s = m:section(TypedSection, "main", translate("Setup"), + (docker_aspace < 2147483648 and + (translate("The free space of Docker is less than 2GB, which may cause the installation to fail.") + .. "
") or "") .. translate("The following parameters will only take effect during installation or upgrade:")) +s.addremove=false +s.anonymous=true + +o = s:option(Value, "port", translate("Port").."*") +o.default = "9070" +o.datatype = "port" +o:depends("hostnet", 0) + +o = s:option(Value, "image_name", translate("Image").."*") +o.rmempty = false +o.datatype = "string" +o.default = "corentinth/it-tools:latest" +o:value("corentinth/it-tools:latest", "corentinth/it-tools:latest") +o:value("ghcr.io/corentinth/it-tools:latest", "ghcr.io/corentinth/it-tools:latest") + +return m diff --git a/applications/luci-app-ittools/luasrc/model/ittools.lua b/applications/luci-app-ittools/luasrc/model/ittools.lua new file mode 100644 index 00000000..f1f6ef5e --- /dev/null +++ b/applications/luci-app-ittools/luasrc/model/ittools.lua @@ -0,0 +1,55 @@ +local util = require "luci.util" +local jsonc = require "luci.jsonc" + +local ittools = {} + +ittools.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 + +ittools.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 + +ittools.find_paths = function(blocks, home_dirs, path_name) + local default_path = '' + local configs = {} + + default_path = home_dirs[path_name] .. "/ITTools" + if #blocks == 0 then + table.insert(configs, default_path) + else + for _, val in pairs(blocks) do + table.insert(configs, val .. "/" .. path_name .. "/ITTools") + end + local without_conf_dir = "/root/" .. path_name .. "/ITTools" + if default_path == without_conf_dir then + default_path = configs[1] + end + end + + return configs, default_path +end + +return ittools diff --git a/applications/luci-app-ittools/luasrc/view/ittools/status.htm b/applications/luci-app-ittools/luasrc/view/ittools/status.htm new file mode 100644 index 00000000..79c0bee4 --- /dev/null +++ b/applications/luci-app-ittools/luasrc/view/ittools/status.htm @@ -0,0 +1,31 @@ +<% +local util = require "luci.util" +local container_status = util.trim(util.exec("/usr/libexec/istorec/ittools.sh status")) +local container_install = (string.len(container_status) > 0) +local container_running = container_status == "running" +-%> +
+ +
+ <% if container_running then %> + + <% else %> + + <% end %> +
+
+<% +if container_running then + local port=util.trim(util.exec("/usr/libexec/istorec/ittools.sh port")) + if port == "" then + port="9070" + end +-%> +
+ +
+ + +
+
+<% end %> diff --git a/applications/luci-app-ittools/po/zh-cn/ittools.po b/applications/luci-app-ittools/po/zh-cn/ittools.po new file mode 100644 index 00000000..d1d9c5cc --- /dev/null +++ b/applications/luci-app-ittools/po/zh-cn/ittools.po @@ -0,0 +1,44 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +msgid "ITTools" +msgid "开发工具集" + +msgid "Official website:" +msgstr "官方网站:" + +msgid "ITTools is useful tools for developer and people working in IT." +msgstr "开发工具集是集成了很多有用的网页工具。" + +msgid "Port" +msgstr "端口" + +msgid "Service Status" +msgstr "服务状态" + +msgid "ITTools status:" +msgstr "ITTools 的状态信息如下:" + +msgid "Setup" +msgstr "安装配置" + +msgid "The following parameters will only take effect during installation or upgrade:" +msgstr "以下参数只在安装或者升级时才会生效:" + +msgid "Status" +msgstr "状态" + +msgid "ITTools is running" +msgstr "ITTools 运行中" + +msgid "ITTools is not running" +msgstr "ITTools 未运行" + +msgid "Open ITTools" +msgstr "打开 ITTools" + +msgid "The free space of Docker is less than 2GB, which may cause the installation to fail." +msgstr "Docker 可用空间已不足2GB,可能导致安装失败。" + +msgid "Please make sure there has enough space" +msgstr "请确保有足够空间" diff --git a/applications/luci-app-ittools/root/etc/config/ittools b/applications/luci-app-ittools/root/etc/config/ittools new file mode 100644 index 00000000..26043d63 --- /dev/null +++ b/applications/luci-app-ittools/root/etc/config/ittools @@ -0,0 +1,3 @@ +config main + option 'port' '9070' + diff --git a/applications/luci-app-ittools/root/usr/libexec/istorec/ittools.sh b/applications/luci-app-ittools/root/usr/libexec/istorec/ittools.sh new file mode 100755 index 00000000..353f1f0f --- /dev/null +++ b/applications/luci-app-ittools/root/usr/libexec/istorec/ittools.sh @@ -0,0 +1,68 @@ +#!/bin/sh +# Author Xiaobao(xiaobao@linkease.com) + +ACTION=${1} +shift 1 + +do_install() { + local port=`uci get ittools.@main[0].port 2>/dev/null` + local image_name=`uci get ittools.@main[0].image_name 2>/dev/null` + + [ -z "$image_name" ] && image_name="linuxserver/ittools:latest" + echo "docker pull ${image_name}" + docker pull ${image_name} + docker rm -f ittools + + [ -z "$port" ] && port=9070 + + local cmd="docker run --restart=unless-stopped -d -h ITToolsServer " + + cmd="$cmd\ + --dns=172.17.0.1 \ + -p $port:80 " + + local tz="`uci get system.@system[0].zonename | sed 's/ /_/g'`" + [ -z "$tz" ] || cmd="$cmd -e TZ=$tz" + + cmd="$cmd -v /mnt:/mnt" + mountpoint -q /mnt && cmd="$cmd:rslave" + cmd="$cmd --name ittools \"$image_name\"" + + echo "$cmd" + eval "$cmd" +} + +usage() { + echo "usage: $0 sub-command" + echo "where sub-command is one of:" + echo " install Install the ittools" + echo " upgrade Upgrade the ittools" + echo " rm/start/stop/restart Remove/Start/Stop/Restart the ittools" + echo " status ITTools status" + echo " port ITTools port" +} + +case ${ACTION} in + "install") + do_install + ;; + "upgrade") + do_install + ;; + "rm") + docker rm -f ittools + ;; + "start" | "stop" | "restart") + docker ${ACTION} ittools + ;; + "status") + docker ps --all -f 'name=ittools' --format '{{.State}}' + ;; + "port") + docker ps --all -f 'name=ittools' --format '{{.Ports}}' | grep -om1 '0.0.0.0:[0-9]*->9070/tcp' | sed 's/0.0.0.0:\([0-9]*\)->.*/\1/' + ;; + *) + usage + exit 1 + ;; +esac diff --git a/applications/luci-app-mtphotos/Makefile b/applications/luci-app-mtphotos/Makefile new file mode 100644 index 00000000..490c09dc --- /dev/null +++ b/applications/luci-app-mtphotos/Makefile @@ -0,0 +1,18 @@ + + +include $(TOPDIR)/rules.mk + +PKG_VERSION:=1.0.2-20231208 +PKG_RELEASE:= + +LUCI_TITLE:=LuCI support for MTPhotos +LUCI_PKGARCH:=all +LUCI_DEPENDS:=+lsblk +docker +luci-lib-taskd +luci-lib-docker + +define Package/luci-app-mtphotos/conffiles +/etc/config/mtphotos +endef + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/applications/luci-app-mtphotos/luasrc/controller/mtphotos.lua b/applications/luci-app-mtphotos/luasrc/controller/mtphotos.lua new file mode 100755 index 00000000..53b15c19 --- /dev/null +++ b/applications/luci-app-mtphotos/luasrc/controller/mtphotos.lua @@ -0,0 +1,7 @@ + +module("luci.controller.mtphotos", package.seeall) + +function index() + entry({"admin", "services", "mtphotos"}, alias("admin", "services", "mtphotos", "config"), _("MTPhotos"), 30).dependent = true + entry({"admin", "services", "mtphotos", "config"}, cbi("mtphotos")) +end diff --git a/applications/luci-app-mtphotos/luasrc/model/cbi/mtphotos.lua b/applications/luci-app-mtphotos/luasrc/model/cbi/mtphotos.lua new file mode 100644 index 00000000..60150181 --- /dev/null +++ b/applications/luci-app-mtphotos/luasrc/model/cbi/mtphotos.lua @@ -0,0 +1,73 @@ +--[[ +LuCI - Lua Configuration Interface +]]-- + +local taskd = require "luci.model.tasks" +local docker = require "luci.docker" +local mtphotos_model = require "luci.model.mtphotos" +local m, s, o + +m = taskd.docker_map("mtphotos", "mtphotos", "/usr/libexec/istorec/mtphotos.sh", + translate("MTPhotos"), + translate("MTPhotos is a photo manager, made by MTPhotos, Inc.") + .. translate("Official website:") .. ' https://mtmt.tech/') + +local dk = docker.new({socket_path="/var/run/docker.sock"}) +local dockerd_running = dk:_ping().code == 200 +local docker_info = dockerd_running and dk:info().body or {} +local docker_aspace = 0 +if docker_info.DockerRootDir then + local statvfs = nixio.fs.statvfs(docker_info.DockerRootDir) + docker_aspace = statvfs and (statvfs.bavail * statvfs.bsize) or 0 +end + +s = m:section(SimpleSection, translate("Service Status"), translate("MTPhotos status:")) +s:append(Template("mtphotos/status")) + +s = m:section(TypedSection, "main", translate("Setup"), + (docker_aspace < 2147483648 and + (translate("The free space of Docker is less than 2GB, which may cause the installation to fail.") + .. "
") or "") .. translate("The following parameters will only take effect during installation or upgrade:")) +s.addremove=false +s.anonymous=true + +o = s:option(Value, "port", translate("Port").."*") +o.default = "8063" +o.datatype = "port" +o:depends("hostnet", 0) + +o = s:option(Value, "image_name", translate("Image").."*") +o.rmempty = false +o.datatype = "string" +o:value("mtphotos/mt-photos:nodb-latest", "mtphotos/mt-photos:nodb-latest") +o:value("mtphotos/mt-photos:latest", "mtphotos/mt-photos:latest") +if "x86_64" == docker_info.Architecture then + o.default = "mtphotos/mt-photos:latest" +else + o:value("mtphotos/mt-photos:arm-latest", "mtphotos/mt-photos:arm-latest") + o.default = "mtphotos/mt-photos:arm-latest" +end + +local blocks = mtphotos_model.blocks() +local home = mtphotos_model.home() + +o = s:option(Value, "config_path", translate("Config path").."*") +o.rmempty = false +o.datatype = "string" + +local paths, default_path = mtphotos_model.find_paths(blocks, home, "Configs") +for _, val in pairs(paths) do + o:value(val.."/Config", val.."/Config") +end +o.default = default_path.."/Config" + +o = s:option(Value, "upload_path", translate("Upload path").."*") +o.rmempty = false +o.datatype = "string" + +for _, val in pairs(paths) do + o:value(val.."/Upload", val.."/Upload") +end +o.default = default_path.."/Upload" + +return m diff --git a/applications/luci-app-mtphotos/luasrc/model/mtphotos.lua b/applications/luci-app-mtphotos/luasrc/model/mtphotos.lua new file mode 100644 index 00000000..3aad421b --- /dev/null +++ b/applications/luci-app-mtphotos/luasrc/model/mtphotos.lua @@ -0,0 +1,55 @@ +local util = require "luci.util" +local jsonc = require "luci.jsonc" + +local mtphotos = {} + +mtphotos.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 + +mtphotos.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 + +mtphotos.find_paths = function(blocks, home_dirs, path_name) + local default_path = '' + local configs = {} + + default_path = home_dirs[path_name] .. "/MTPhotos" + if #blocks == 0 then + table.insert(configs, default_path) + else + for _, val in pairs(blocks) do + table.insert(configs, val .. "/" .. path_name .. "/MTPhotos") + end + local without_conf_dir = "/root/" .. path_name .. "/MTPhotos" + if default_path == without_conf_dir then + default_path = configs[1] + end + end + + return configs, default_path +end + +return mtphotos diff --git a/applications/luci-app-mtphotos/luasrc/view/mtphotos/status.htm b/applications/luci-app-mtphotos/luasrc/view/mtphotos/status.htm new file mode 100644 index 00000000..df8fc665 --- /dev/null +++ b/applications/luci-app-mtphotos/luasrc/view/mtphotos/status.htm @@ -0,0 +1,31 @@ +<% +local util = require "luci.util" +local container_status = util.trim(util.exec("/usr/libexec/istorec/mtphotos.sh status")) +local container_install = (string.len(container_status) > 0) +local container_running = container_status == "running" +-%> +
+ +
+ <% if container_running then %> + + <% else %> + + <% end %> +
+
+<% +if container_running then + local port=util.trim(util.exec("/usr/libexec/istorec/mtphotos.sh port")) + if port == "" then + port="8063" + end +-%> +
+ +
+ + +
+
+<% end %> diff --git a/applications/luci-app-mtphotos/po/zh-cn/mtphotos.po b/applications/luci-app-mtphotos/po/zh-cn/mtphotos.po new file mode 100644 index 00000000..21e90deb --- /dev/null +++ b/applications/luci-app-mtphotos/po/zh-cn/mtphotos.po @@ -0,0 +1,47 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +msgid "Official website:" +msgstr "官方网站:" + +msgid "MTPhotos is a photo manager, made by MTPhotos, Inc." +msgstr "MTPhotos 是一个相册管理软件。" + +msgid "Config path" +msgstr "配置文件路径" + +msgid "Upload path" +msgstr "上传文件路径" + +msgid "Port" +msgstr "端口" + +msgid "Service Status" +msgstr "服务状态" + +msgid "MTPhotos status:" +msgstr "MTPhotos 的状态信息如下:" + +msgid "Setup" +msgstr "安装配置" + +msgid "The following parameters will only take effect during installation or upgrade:" +msgstr "以下参数只在安装或者升级时才会生效:" + +msgid "Status" +msgstr "状态" + +msgid "MTPhotos is running" +msgstr "MTPhotos 运行中" + +msgid "MTPhotos is not running" +msgstr "MTPhotos 未运行" + +msgid "Open MTPhotos" +msgstr "打开 MTPhotos" + +msgid "The free space of Docker is less than 2GB, which may cause the installation to fail." +msgstr "Docker 可用空间已不足2GB,可能导致安装失败。" + +msgid "Please make sure there has enough space" +msgstr "请确保有足够空间" diff --git a/applications/luci-app-mtphotos/root/etc/config/mtphotos b/applications/luci-app-mtphotos/root/etc/config/mtphotos new file mode 100644 index 00000000..69abbb98 --- /dev/null +++ b/applications/luci-app-mtphotos/root/etc/config/mtphotos @@ -0,0 +1,5 @@ +config main + option 'port' '8063' +# option 'config_path' '' +# option 'upload_path' '' + diff --git a/applications/luci-app-mtphotos/root/usr/libexec/istorec/mtphotos.sh b/applications/luci-app-mtphotos/root/usr/libexec/istorec/mtphotos.sh new file mode 100755 index 00000000..ce6a99a8 --- /dev/null +++ b/applications/luci-app-mtphotos/root/usr/libexec/istorec/mtphotos.sh @@ -0,0 +1,77 @@ +#!/bin/sh +# Author Xiaobao(xiaobao@linkease.com) + +ACTION=${1} +shift 1 + +do_install() { + local port=`uci get mtphotos.@main[0].port 2>/dev/null` + local image_name=`uci get mtphotos.@main[0].image_name 2>/dev/null` + local config=`uci get mtphotos.@main[0].config_path 2>/dev/null` + local upload=`uci get mtphotos.@main[0].upload_path 2>/dev/null` + + if [ -z "$config" ]; then + echo "config path is empty!" + exit 1 + fi + + [ -z "$image_name" ] && image_name="mtphotos/mt-photos:latest" + echo "docker pull ${image_name}" + docker pull ${image_name} + docker rm -f mtphotos + + [ -z "$port" ] && port=8063 + + local cmd="docker run --restart=unless-stopped -d -h MTPhotosServer \ + -v \"$upload:/upload\" \ + -v \"$config:/config\" " + + cmd="$cmd\ + --dns=172.17.0.1 \ + -p $port:8063 " + + local tz="`uci get system.@system[0].zonename | sed 's/ /_/g'`" + [ -z "$tz" ] || cmd="$cmd -e TZ=$tz" + + cmd="$cmd -v /mnt:/mnt" + mountpoint -q /mnt && cmd="$cmd:rslave" + cmd="$cmd --name mtphotos \"$image_name\"" + + echo "$cmd" + eval "$cmd" +} + +usage() { + echo "usage: $0 sub-command" + echo "where sub-command is one of:" + echo " install Install the mtphotos" + echo " upgrade Upgrade the mtphotos" + echo " rm/start/stop/restart Remove/Start/Stop/Restart the mtphotos" + echo " status MTPhotos status" + echo " port MTPhotos port" +} + +case ${ACTION} in + "install") + do_install + ;; + "upgrade") + do_install + ;; + "rm") + docker rm -f mtphotos + ;; + "start" | "stop" | "restart") + docker ${ACTION} mtphotos + ;; + "status") + docker ps --all -f 'name=mtphotos' --format '{{.State}}' + ;; + "port") + docker ps --all -f 'name=mtphotos' --format '{{.Ports}}' | grep -om1 '0.0.0.0:[0-9]*->8063/tcp' | sed 's/0.0.0.0:\([0-9]*\)->.*/\1/' + ;; + *) + usage + exit 1 + ;; +esac diff --git a/applications/luci-app-plex/simple-install.sh b/applications/luci-app-plex/simple-install.sh deleted file mode 100755 index b0eb2245..00000000 --- a/applications/luci-app-plex/simple-install.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -# run in router -APPNAME=$1 - -if [ -z "${APPNAME}" ]; then - APPNAME=plex -fi - -mkdir -p /usr/lib/lua/luci/view/${APPNAME} -cp ./luasrc/controller/${APPNAME}.lua /usr/lib/lua/luci/controller/ -cp ./luasrc/view/${APPNAME}/* /usr/lib/lua/luci/view/${APPNAME}/ -cp -rf ./luasrc/model/* /usr/lib/lua/luci/model/ -cp -rf ./root/* / -rm -rf /tmp/luci-* - diff --git a/applications/luci-app-rtbwmon/Makefile b/applications/luci-app-rtbwmon/Makefile index 8db787f7..6c4ee92c 100644 --- a/applications/luci-app-rtbwmon/Makefile +++ b/applications/luci-app-rtbwmon/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk -PKG_VERSION:=1.0.3-1 +PKG_VERSION:=1.0.4-1 PKG_RELEASE:= PKG_MAINTAINER:=jjm2473 diff --git a/applications/luci-app-rtbwmon/htdocs/luci-static/rtbwmon/rtbwmon.js b/applications/luci-app-rtbwmon/htdocs/luci-static/rtbwmon/rtbwmon.js index 0ce637e4..d8a64158 100644 --- a/applications/luci-app-rtbwmon/htdocs/luci-static/rtbwmon/rtbwmon.js +++ b/applications/luci-app-rtbwmon/htdocs/luci-static/rtbwmon/rtbwmon.js @@ -511,6 +511,17 @@ let iface_select = document.getElementById('iface_select'); let selected = iface_select.value; let ifaces = responseText.trimEnd().split('\n').filter(line=>line).map(iface=>{ + let priority = 0; + switch (iface) { + case "br-lan": + priority = -2; + break; + case "docker0": + priority = -1; + break; + } + return {iface:iface, priority:priority}; + }).sort((a,b)=>a.priority-b.priority).map(o=>o.iface).map(iface=>{ let option = document.createElement('option'); option.value = iface; option.innerHTML = iface; diff --git a/applications/luci-app-rtbwmon/root/usr/libexec/rtbwmon.sh b/applications/luci-app-rtbwmon/root/usr/libexec/rtbwmon.sh index 05859eb7..4785895e 100755 --- a/applications/luci-app-rtbwmon/root/usr/libexec/rtbwmon.sh +++ b/applications/luci-app-rtbwmon/root/usr/libexec/rtbwmon.sh @@ -29,7 +29,7 @@ lookup() { } get_wan_iface() { - tail -n +2 /proc/net/route | sed -n -e 's/^\([^\t]\+\)\t00000000\t[^\t]\+\t[^\t]\+\t[^\t]\+\t[^\t]\+\t[^\t]\+\t00000000\t.*$/\1/p' + tail -n +2 /proc/net/route | sed -n -e 's/^\([^\t]\+\)\t00000000\t[^\t]\+\t[^\t]\+\t[^\t]\+\t[^\t]\+\t[^\t]\+\t00000000\t.*$/\1/p' | head -1 } get_arp_excluded() { @@ -38,7 +38,7 @@ get_arp_excluded() { enforce_wan_iface() { local INTERFACE="$1" - [[ "$INTERFACE" = "br-lan" ]] && INTERFACE=`uci show network.wan | grep -E 'network\.wan\.(device|ifname)' | sed -n -e "1s/network\\.wan\\.[^=]\\+='\\([^']\\+\\)'\$/\\1/p"` + [[ "$INTERFACE" = "br-lan" ]] && INTERFACE=`uci show network.wan | grep -E 'network\.wan\.(device|ifname)=' | sed -n -e "1s/network\\.wan\\.[^=]\\+='\\([^']\\+\\)'\$/\\1/p"` [ -z "$INTERFACE" ] && INTERFACE="/" echo "$INTERFACE" } @@ -175,7 +175,7 @@ show_ifaces() { local WAN_INTERFACE=`get_wan_iface` [ -z "$WAN_INTERFACE" ] && return 1 WAN_INTERFACE="$(enforce_wan_iface "$WAN_INTERFACE")" - ip addr show scope global up | grep '^ \+inet ' | sed -n -e 's/^.* \([^ ]\+\)$/\1/p' | grep -Fv "$WAN_INTERFACE" | sort -u + ip addr show scope global up | grep '^ \+inet ' | sed -n -e 's/^.* \([^ ]\+\)$/\1/p' | grep -Fxv "$WAN_INTERFACE" | sort -u } prerm() { diff --git a/applications/luci-app-runmynas/Makefile b/applications/luci-app-runmynas/Makefile new file mode 100644 index 00000000..c1ac84d9 --- /dev/null +++ b/applications/luci-app-runmynas/Makefile @@ -0,0 +1,20 @@ + + +include $(TOPDIR)/rules.mk + +PKG_VERSION:=1.1.1-20231208 +PKG_RELEASE:= + +LUCI_TITLE:=LuCI support for runmynas +LUCI_PKGARCH:=all +LUCI_DEPENDS:=+docker +luci-lib-iform +luci-lib-taskd +LUCI_EXTRA_DEPENDS:=luci-lib-iform (>=1.1) + +define Package/luci-app-runmynas/conffiles +/etc/config/runmynas +endef + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature + diff --git a/applications/luci-app-runmynas/luasrc/controller/runmynas.lua b/applications/luci-app-runmynas/luasrc/controller/runmynas.lua new file mode 100755 index 00000000..487e461b --- /dev/null +++ b/applications/luci-app-runmynas/luasrc/controller/runmynas.lua @@ -0,0 +1,174 @@ +local util = require "luci.util" +local http = require "luci.http" +local docker = require "luci.model.docker" +local iform = require "luci.iform" +local runmynas_model = require "luci.model.runmynas_disk" + +module("luci.controller.runmynas", package.seeall) + +function index() + + entry({"admin", "services", "runmynas"}, call("redirect_index"), _("RunMyNAS"), 30).dependent = true + entry({"admin", "services", "runmynas", "pages"}, call("runmynas_index")).leaf = true + entry({"admin", "services", "runmynas", "form"}, call("runmynas_form")) + entry({"admin", "services", "runmynas", "submit"}, call("runmynas_submit")) + +end + +local appname = "runmynas" +local page_index = {"admin", "services", "runmynas", "pages"} + +function redirect_index() + http.redirect(luci.dispatcher.build_url(unpack(page_index))) +end + +function runmynas_index() + luci.template.render("runmynas/main", {prefix=luci.dispatcher.build_url(unpack(page_index))}) +end + +function runmynas_form() + local error = "" + local scope = "" + local success = 0 + + local blocks = runmynas_model.blocks() + local home = runmynas_model.home() + local paths, default_path = runmynas_model.find_paths(blocks, home, "Configs") + local data = get_data(default_path) + local result = { + data = data, + schema = get_schema(data, paths) + } + local response = { + error = error, + scope = scope, + success = success, + result = result, + } + http.prepare_content("application/json") + http.write_json(response) +end + +function get_schema(data, paths) + local actions + actions = { + { + name = "build", + text = "运行", + type = "apply", + }, + } + local schema = { + actions = actions, + containers = get_containers(data, paths), + description = "自定义你的 iStoreNAS,本插件只能运行在 X86 平台,可以去定制其他平台的固件。源码地址:https://github.com/linkease/iStoreNAS", + title = "RunMyNAS" + } + return schema +end + +function get_containers(data, paths) + local containers = { + main_container(data, paths) + } + return containers +end + +function main_container(data, paths) + local names = {} + for k, v in pairs(paths) do + names[k] = v + end + local main_c2 = { + properties = { + { + name = "download", + required = true, + title = "源码下载", + type = "string", + enum = {"github", "koolcenter"}, + enumNames = {"Github", "Koolcenter"} + }, + { + name = "target", + required = true, + title = "平台", + type = "string", + enum = {"x86_64", "rk35xx", "rk33xx"}, + enumNames = {"x86_64", "rk35xx", "rk33xx"} + }, + { + name = "path", + required = true, + title = "运行路径", + type = "string", + enum = paths, + enumNames = names + }, + }, + description = "请选择合适的平台运行:", + title = "运行操作" + } + return main_c2 +end + +function get_data(default_path) + local uci = require "luci.model.uci".cursor() + local target = uci:get_first(appname, appname, "target", "x86_64") + local download = uci:get_first(appname, appname, "download", "github") + local path = uci:get_first(appname, appname, "path", default_path) + local data = { + target = target, + download = download, + path = path, + } + return data +end + +function runmynas_submit() + local error = "" + local scope = "" + local success = 0 + local result + + local jsonc = require "luci.jsonc" + local json_parse = jsonc.parse + local content = http.content() + local req = json_parse(content) + result = runmynas(req) + http.prepare_content("application/json") + local resp = { + error = error, + scope = scope, + success = success, + result = result, + } + http.write_json(resp) +end + +function runmynas(req) + local download = req["download"] + local target = req["target"] + local path = req["path"] + + -- save config + local uci = require "luci.model.uci".cursor() + uci:tset(appname, "@"..appname.."[0]", { + target = target or "x86_64", + download = download or "github", + path = path, + }) + uci:save(appname) + uci:commit(appname) + + local exec_cmd = string.format("/usr/libexec/istorec/runmynas.sh %s", req["$apply"]) + exec_cmd = "/etc/init.d/tasks task_add runmynas " .. luci.util.shellquote(exec_cmd) + os.execute(exec_cmd .. " >/dev/null 2>&1") + + local result = { + async = true, + async_state = appname + } + return result +end + diff --git a/applications/luci-app-runmynas/luasrc/model/runmynas_disk.lua b/applications/luci-app-runmynas/luasrc/model/runmynas_disk.lua new file mode 100644 index 00000000..896722f6 --- /dev/null +++ b/applications/luci-app-runmynas/luasrc/model/runmynas_disk.lua @@ -0,0 +1,55 @@ +local util = require "luci.util" +local jsonc = require "luci.jsonc" + +local runmynas_disk = {} + +runmynas_disk.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 + +runmynas_disk.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 + +runmynas_disk.find_paths = function(blocks, home_dirs, path_name) + local default_path = '' + local configs = {} + + default_path = home_dirs[path_name] .. "/RunMyNAS" + if #blocks == 0 then + table.insert(configs, default_path) + else + for _, val in pairs(blocks) do + table.insert(configs, val .. "/" .. path_name .. "/RunMyNAS") + end + local without_conf_dir = "/root/" .. path_name .. "/RunMyNAS" + if default_path == without_conf_dir then + default_path = configs[1] + end + end + + return configs, default_path +end + +return runmynas_disk diff --git a/applications/luci-app-runmynas/luasrc/view/runmynas/main.htm b/applications/luci-app-runmynas/luasrc/view/runmynas/main.htm new file mode 100644 index 00000000..109546fc --- /dev/null +++ b/applications/luci-app-runmynas/luasrc/view/runmynas/main.htm @@ -0,0 +1,28 @@ +<%+header%> + +<%+tasks/embed%> + + +
+
+ + + + + +<%+footer%> diff --git a/applications/luci-app-runmynas/po/zh-cn/runmynas.po b/applications/luci-app-runmynas/po/zh-cn/runmynas.po new file mode 100644 index 00000000..1be7e761 --- /dev/null +++ b/applications/luci-app-runmynas/po/zh-cn/runmynas.po @@ -0,0 +1,62 @@ +msgid "runmynas" +msgstr "runmynas" + +msgid "The runmynas service is running." +msgstr "runmynas已启动" + +msgid "The runmynas service is not running." +msgstr "runmynas服务未启动" + +msgid "The runmynas service is not installed." +msgstr "runmynas服务未安装" + +msgid "open runmynas" +msgstr "打开runmynas" + +msgid "stop runmynas" +msgstr "停止runmynas" + +msgid "run runmynas" +msgstr "启动runmynas" + +msgid "uninstall runmynas" +msgstr "删除runmynas" + +msgid "install runmynas" +msgstr "安装runmynas" + +msgid "Collecting data..." +msgstr "收集数据..." + +msgid "storage path" +msgstr "存储路径(建议插入U盘或硬盘,然后输入路径。例如:/mnt/sda1/runmynas)" + +msgid "Storage path could not be empty!" +msgstr "存储路径不能为空!" + +msgid "Version" +msgstr "系统版本" + +msgid "Port (optional)" +msgstr "端口" + +msgid "Password (optional)" +msgstr "密码" + +msgid "standard version" +msgstr "标准版本" + +msgid "full version" +msgstr "全量版本" + +msgid "UserName" +msgstr "用户名" + +msgid "Password" +msgstr "密码" + +msgid "LanAddress" +msgstr "内网地址" + +msgid "WanAddress" +msgstr "外网地址" \ No newline at end of file diff --git a/applications/luci-app-runmynas/root/etc/config/runmynas b/applications/luci-app-runmynas/root/etc/config/runmynas new file mode 100644 index 00000000..f22e9655 --- /dev/null +++ b/applications/luci-app-runmynas/root/etc/config/runmynas @@ -0,0 +1,2 @@ +config runmynas + option 'target' '' diff --git a/applications/luci-app-runmynas/root/usr/libexec/istorec/runmynas.sh b/applications/luci-app-runmynas/root/usr/libexec/istorec/runmynas.sh new file mode 100755 index 00000000..ab0e8eea --- /dev/null +++ b/applications/luci-app-runmynas/root/usr/libexec/istorec/runmynas.sh @@ -0,0 +1,84 @@ +#!/bin/sh +# Author Xiaobao(xiaobao@linkease.com) + +ACTION=${1} +shift 1 + +command_exists() { + command -v "$@" >/dev/null 2>&1 +} + +Download_Files(){ + local URL=$1 + local FileName=$2 + if command_exists curl; then + curl -sSLk ${URL} -o ${FileName} + elif command_exists wget; then + wget -c --no-check-certificate ${URL} -O ${FileName} + fi + if [ $? -eq 0 ]; then + echo "Download OK" + else + echo "Download failed" + exit 1 + fi +} + +do_build() { + local download=`uci get runmynas.@runmynas[0].download 2>/dev/null` + local target=`uci get runmynas.@runmynas[0].target 2>/dev/null` + local path=`uci get runmynas.@runmynas[0].path 2>/dev/null` + [ ! -z $download ] || download="github" + [ ! -z $target ] || target="x86_64" + + if echo `uname -m` | grep -Eqi 'x86_64'; then + echo "Support x86_64" + else + echo "Not x86_64, only support x86_64, exit" + exit 1 + fi + + if [ -z "$path" ]; then + echo "path is empty" + exit 3 + fi + + mkdir -p $path + if [ ! -f "${path}/runmynas.sh" ]; then + if [ "$download" = "github" ]; then + DLURL=`curl -s https://api.github.com/repos/linkease/iStoreNAS/releases/latest | grep tarball_url | cut -d '"' -f 4` + else + DLURL="https://fw0.koolcenter.com/iStoreNAS/runmynas/runmynas.tar.gz" + fi + echo "download $DLURL" + rm -f /tmp/rumynas-source.tar.gz + Download_Files ${DLURL} /tmp/rumynas-source.tar.gz + tar -C ${path} --strip-components=1 -zxf /tmp/rumynas-source.tar.gz + if [ ! -f "${path}/runmynas.sh" ]; then + echo "runmynas.sh not found, failed!" + exit 2 + fi + rm -f /tmp/rumynas-source.tar.gz + fi + + cd ${path} + echo "./runmynas.sh $target" + ./runmynas.sh $target +} + +usage() { + echo "usage: $0 sub-command" + echo "where sub-command is one of:" + echo " build Build your NAS" +} + +case ${ACTION} in + "build") + do_build + ;; + *) + usage + exit 1 + ;; +esac + diff --git a/applications/luci-app-typecho/Makefile b/applications/luci-app-typecho/Makefile new file mode 100644 index 00000000..f44c3807 --- /dev/null +++ b/applications/luci-app-typecho/Makefile @@ -0,0 +1,18 @@ + + +include $(TOPDIR)/rules.mk + +PKG_VERSION:=1.0.2-20240313 +PKG_RELEASE:= + +LUCI_TITLE:=LuCI support for TypeCho +LUCI_PKGARCH:=all +LUCI_DEPENDS:=+lsblk +docker +luci-lib-taskd +luci-lib-docker + +define Package/luci-app-typecho/conffiles +/etc/config/typecho +endef + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/applications/luci-app-typecho/luasrc/controller/typecho.lua b/applications/luci-app-typecho/luasrc/controller/typecho.lua new file mode 100755 index 00000000..4bfbce9b --- /dev/null +++ b/applications/luci-app-typecho/luasrc/controller/typecho.lua @@ -0,0 +1,7 @@ + +module("luci.controller.typecho", package.seeall) + +function index() + entry({"admin", "services", "typecho"}, alias("admin", "services", "typecho", "config"), _("TypeCho"), 30).dependent = true + entry({"admin", "services", "typecho", "config"}, cbi("typecho")) +end diff --git a/applications/luci-app-typecho/luasrc/model/cbi/typecho.lua b/applications/luci-app-typecho/luasrc/model/cbi/typecho.lua new file mode 100644 index 00000000..ca3ae6a2 --- /dev/null +++ b/applications/luci-app-typecho/luasrc/model/cbi/typecho.lua @@ -0,0 +1,59 @@ +--[[ +LuCI - Lua Configuration Interface +]]-- + +local taskd = require "luci.model.tasks" +local docker = require "luci.docker" +local typecho_model = require "luci.model.typecho" +local m, s, o + +m = taskd.docker_map("typecho", "typecho", "/usr/libexec/istorec/typecho.sh", + translate("TypeCho"), + translate("TypeCho is a lightweight blog.") + .. translate("Official website:") .. ' https://typecho.org/') + +local dk = docker.new({socket_path="/var/run/docker.sock"}) +local dockerd_running = dk:_ping().code == 200 +local docker_info = dockerd_running and dk:info().body or {} +local docker_aspace = 0 +if docker_info.DockerRootDir then + local statvfs = nixio.fs.statvfs(docker_info.DockerRootDir) + docker_aspace = statvfs and (statvfs.bavail * statvfs.bsize) or 0 +end + +s = m:section(SimpleSection, translate("Service Status"), translate("TypeCho status:")) +s:append(Template("typecho/status")) + +s = m:section(TypedSection, "main", translate("Setup"), + (docker_aspace < 2147483648 and + (translate("The free space of Docker is less than 2GB, which may cause the installation to fail.") + .. "
") or "") .. translate("The following parameters will only take effect during installation or upgrade:")) +s.addremove=false +s.anonymous=true + +o = s:option(Value, "port", translate("Port").."*") +o.default = "9080" +o.datatype = "port" + +o = s:option(Value, "image_name", translate("Image").."*") +o.rmempty = false +o.datatype = "string" +o.default = "joyqi/typecho:nightly-php7.4" +--if "x86_64" == docker_info.Architecture then +--end +o:value("joyqi/typecho:nightly-php7.4", "joyqi/typecho:nightly-php7.4") + +local blocks = typecho_model.blocks() +local home = typecho_model.home() + +o = s:option(Value, "config_path", translate("Config path").."*") +o.rmempty = false +o.datatype = "string" + +local paths, default_path = typecho_model.find_paths(blocks, home, "Configs") +for _, val in pairs(paths) do + o:value(val, val) +end +o.default = default_path + +return m diff --git a/applications/luci-app-typecho/luasrc/model/typecho.lua b/applications/luci-app-typecho/luasrc/model/typecho.lua new file mode 100644 index 00000000..420c8643 --- /dev/null +++ b/applications/luci-app-typecho/luasrc/model/typecho.lua @@ -0,0 +1,55 @@ +local util = require "luci.util" +local jsonc = require "luci.jsonc" + +local typecho = {} + +typecho.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 + +typecho.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 + +typecho.find_paths = function(blocks, home_dirs, path_name) + local default_path = '' + local configs = {} + + default_path = home_dirs[path_name] .. "/TypeCho" + if #blocks == 0 then + table.insert(configs, default_path) + else + for _, val in pairs(blocks) do + table.insert(configs, val .. "/" .. path_name .. "/TypeCho") + end + local without_conf_dir = "/root/" .. path_name .. "/TypeCho" + if default_path == without_conf_dir then + default_path = configs[1] + end + end + + return configs, default_path +end + +return typecho diff --git a/applications/luci-app-typecho/luasrc/view/typecho/status.htm b/applications/luci-app-typecho/luasrc/view/typecho/status.htm new file mode 100644 index 00000000..7dc1d4d6 --- /dev/null +++ b/applications/luci-app-typecho/luasrc/view/typecho/status.htm @@ -0,0 +1,31 @@ +<% +local util = require "luci.util" +local container_status = util.trim(util.exec("/usr/libexec/istorec/typecho.sh status")) +local container_install = (string.len(container_status) > 0) +local container_running = container_status == "running" +-%> +
+ +
+ <% if container_running then %> + + <% else %> + + <% end %> +
+
+<% +if container_running then + local port=util.trim(util.exec("/usr/libexec/istorec/typecho.sh port")) + if port == "" then + port="9080" + end +-%> +
+ +
+ + +
+
+<% end %> diff --git a/applications/luci-app-typecho/po/zh-cn/typecho.po b/applications/luci-app-typecho/po/zh-cn/typecho.po new file mode 100644 index 00000000..57e92d77 --- /dev/null +++ b/applications/luci-app-typecho/po/zh-cn/typecho.po @@ -0,0 +1,44 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +msgid "Official website:" +msgstr "官方网站:" + +msgid "TypeCho is a lightweight blog." +msgstr "TypeCho 是一个轻量级博客。" + +msgid "Config path" +msgstr "配置文件路径" + +msgid "Port" +msgstr "端口" + +msgid "Service Status" +msgstr "服务状态" + +msgid "TypeCho status:" +msgstr "TypeCho 的状态信息如下:" + +msgid "Setup" +msgstr "安装配置" + +msgid "The following parameters will only take effect during installation or upgrade:" +msgstr "以下参数只在安装或者升级时才会生效:" + +msgid "Status" +msgstr "状态" + +msgid "TypeCho is running" +msgstr "TypeCho 运行中" + +msgid "TypeCho is not running" +msgstr "TypeCho 未运行" + +msgid "Open TypeCho" +msgstr "打开 TypeCho" + +msgid "The free space of Docker is less than 2GB, which may cause the installation to fail." +msgstr "Docker 可用空间已不足2GB,可能导致安装失败。" + +msgid "Please make sure there has enough space" +msgstr "请确保有足够空间" diff --git a/applications/luci-app-typecho/root/etc/config/typecho b/applications/luci-app-typecho/root/etc/config/typecho new file mode 100644 index 00000000..8feb9f5f --- /dev/null +++ b/applications/luci-app-typecho/root/etc/config/typecho @@ -0,0 +1,4 @@ +config main + option 'port' '9080' + option 'config_path' '' + diff --git a/applications/luci-app-typecho/root/usr/libexec/istorec/typecho.sh b/applications/luci-app-typecho/root/usr/libexec/istorec/typecho.sh new file mode 100755 index 00000000..ea539188 --- /dev/null +++ b/applications/luci-app-typecho/root/usr/libexec/istorec/typecho.sh @@ -0,0 +1,75 @@ +#!/bin/sh +# Author Xiaobao(xiaobao@linkease.com) + +ACTION=${1} +shift 1 + +do_install() { + local port=`uci get typecho.@main[0].port 2>/dev/null` + local image_name=`uci get typecho.@main[0].image_name 2>/dev/null` + local config=`uci get typecho.@main[0].config_path 2>/dev/null` + + if [ -z "$config" ]; then + echo "config path is empty!" + exit 1 + fi + [ -z "$image_name" ] && image_name="joyqi/typecho:nightly-php7.4" + echo "docker pull ${image_name}" + docker pull ${image_name} + docker rm -f typecho + + [ -z "$port" ] && port=9080 + + mkdir -p $config + chmod 777 $config + local cmd="docker run --restart=unless-stopped -d -h TypeChoServer -v \"$config:/app/usr\" " + + cmd="$cmd\ + --dns=172.17.0.1 \ + -p $port:80 " + + local tz="`uci get system.@system[0].zonename | sed 's/ /_/g'`" + [ -z "$tz" ] || cmd="$cmd -e TZ=$tz" + + cmd="$cmd -v /mnt:/mnt" + mountpoint -q /mnt && cmd="$cmd:rslave" + cmd="$cmd --name typecho \"$image_name\"" + + echo "$cmd" + eval "$cmd" +} + +usage() { + echo "usage: $0 sub-command" + echo "where sub-command is one of:" + echo " install Install the typecho" + echo " upgrade Upgrade the typecho" + echo " rm/start/stop/restart Remove/Start/Stop/Restart the typecho" + echo " status TypeCho status" + echo " port TypeCho port" +} + +case ${ACTION} in + "install") + do_install + ;; + "upgrade") + do_install + ;; + "rm") + docker rm -f typecho + ;; + "start" | "stop" | "restart") + docker ${ACTION} typecho + ;; + "status") + docker ps --all -f 'name=typecho' --format '{{.State}}' + ;; + "port") + docker ps --all -f 'name=typecho' --format '{{.Ports}}' | grep -om1 '0.0.0.0:[0-9]*->9080/tcp' | sed 's/0.0.0.0:\([0-9]*\)->.*/\1/' + ;; + *) + usage + exit 1 + ;; +esac diff --git a/applications/luci-app-ubuntu/simple-install.sh b/applications/luci-app-ubuntu/simple-install.sh deleted file mode 100755 index f8281c0f..00000000 --- a/applications/luci-app-ubuntu/simple-install.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh - -# run in router -APPNAME=$1 - -if [ -z "${APPNAME}" ]; then - APPNAME=ubuntu -fi - -mkdir -p /usr/lib/lua/luci/view/${APPNAME} -cp ./luasrc/controller/${APPNAME}.lua /usr/lib/lua/luci/controller/ -cp ./luasrc/view/${APPNAME}/* /usr/lib/lua/luci/view/${APPNAME}/ -cp -rf ./root/* / -rm -rf /tmp/luci-* - diff --git a/applications/luci-app-webvirtcloud/Makefile b/applications/luci-app-webvirtcloud/Makefile index 6137ed76..b33855e2 100644 --- a/applications/luci-app-webvirtcloud/Makefile +++ b/applications/luci-app-webvirtcloud/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk -PKG_VERSION:=0.3.8-20231208-1 +PKG_VERSION:=0.3.8-20230323-1 PKG_RELEASE:= LUCI_TITLE:=LuCI support for webvirtcloud diff --git a/applications/luci-app-webvirtcloud/root/usr/libexec/istorec/webvirtcloud.sh b/applications/luci-app-webvirtcloud/root/usr/libexec/istorec/webvirtcloud.sh index 6bc3408c..98b6e6fb 100755 --- a/applications/luci-app-webvirtcloud/root/usr/libexec/istorec/webvirtcloud.sh +++ b/applications/luci-app-webvirtcloud/root/usr/libexec/istorec/webvirtcloud.sh @@ -22,6 +22,9 @@ do_install() { echo "docker pull ${IMAGE_NAME}" docker pull ${IMAGE_NAME} docker rm -f webvirtcloud + mkdir -p "$config" + rm -rf "$config/vmwebvirt" + cp /usr/sbin/vmeasedaemon "$config/vmwebvirt" local cmd="docker run --restart=unless-stopped -d \ --cgroupns=host \ @@ -31,7 +34,7 @@ do_install() { -v \"$config/dbconfig:/srv/webvirtcloud/dbconfig\" \ -v \"$config/libvirt:/etc/libvirt\" \ -v \"$config/images:/var/lib/libvirt/images\" \ - -v /usr/sbin/vmeasedaemon:/usr/sbin/vmwebvirt \ + -v \"$config/vmwebvirt:/usr/sbin/vmwebvirt\" \ -v /var/run/vmease:/srv/vmease \ -p $port:80 \ --privileged \ @@ -87,7 +90,6 @@ case ${ACTION} in docker ${ACTION} webvirtcloud ;; "stop") - /etc/init.d/vmease stop docker ${ACTION} webvirtcloud ;; "restart") diff --git a/applications/luci-app-xunlei/Makefile b/applications/luci-app-xunlei/Makefile index fe3e2bc5..d65d2043 100644 --- a/applications/luci-app-xunlei/Makefile +++ b/applications/luci-app-xunlei/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk -PKG_VERSION:=1.0.2-20231208 +PKG_VERSION:=1.0.3-20240523 PKG_RELEASE:= LUCI_TITLE:=LuCI support for Xunlei diff --git a/applications/luci-app-xunlei/luasrc/view/xunlei/status.htm b/applications/luci-app-xunlei/luasrc/view/xunlei/status.htm index 68ebf304..d785a935 100644 --- a/applications/luci-app-xunlei/luasrc/view/xunlei/status.htm +++ b/applications/luci-app-xunlei/luasrc/view/xunlei/status.htm @@ -25,7 +25,7 @@
- +
<% end %> diff --git a/applications/vmease/Makefile b/applications/vmease/Makefile index fbc70872..c91bb478 100644 --- a/applications/vmease/Makefile +++ b/applications/vmease/Makefile @@ -10,11 +10,11 @@ include $(TOPDIR)/rules.mk PKG_ARCH_VMEASE:=$(ARCH) PKG_NAME:=vmease -PKG_VERSION:=0.3.9 +PKG_VERSION:=0.5.1 PKG_RELEASE:=$(PKG_ARCH_VMEASE)-2 PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=http://fw0.koolcenter.com/binary/vmease/ -PKG_HASH:=862eed6f8744c2524bc23e4478975412060fdb53a4b99e9c0d6b9e8f1cf86494 +PKG_HASH:=2ca2b8b276702a239f7f937363fcec925e93f687831e0494d231e642a0cd1d4b PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-binary-$(PKG_VERSION) diff --git a/applications/xunyou/Makefile b/applications/xunyou/Makefile index c773b3a3..fdc93304 100644 --- a/applications/xunyou/Makefile +++ b/applications/xunyou/Makefile @@ -9,12 +9,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=xunyou -PKG_VERSION:=2.0.3.6 +PKG_VERSION:=2.0.4.5 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)_v$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://partnerdownload.xunyou.com/routerplugin/koolshare/ -PKG_HASH:=9ee6fd35ad9a8dc491457f42f08dae1d489d4247254421388bde8ea12c4eb63d +PKG_HASH:=00d8dfd34dc4033c53f7e751edc2f7e266f951c4dca0f22a6e77cbe6d813ddab PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)_v$(PKG_VERSION) diff --git a/forkapp/build.sh b/forkapp/build.sh new file mode 100755 index 00000000..582553d2 --- /dev/null +++ b/forkapp/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -a -ldflags '-s -w -extldflags "-static"' -o forkapp +CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath -a -ldflags '-s -w -extldflags "-static"' -o forkapp.exe + diff --git a/forkapp/copydir.go b/forkapp/copydir.go new file mode 100644 index 00000000..e5baf550 --- /dev/null +++ b/forkapp/copydir.go @@ -0,0 +1,119 @@ +package main + +import ( + "fmt" + "io" + "os" + "path/filepath" +) + +type ChangePathFunc func(srcFile, dstFile string) string +type CopyFunc func(srcFile, dstFile string, overwrite bool) error + +func CopyDirectory(scrDir, dest string, overwrite bool, changePathFn ChangePathFunc, cpFn CopyFunc) error { + entries, err := os.ReadDir(scrDir) + if err != nil { + return err + } + for _, entry := range entries { + sourcePath := filepath.Join(scrDir, entry.Name()) + fileInfo, err := os.Stat(sourcePath) + if err != nil { + return err + } + + ostat := fileInfo.Sys() + destPath := filepath.Join(dest, entry.Name()) + destPath = changePathFn(sourcePath, destPath) + + switch fileInfo.Mode() & os.ModeType { + case os.ModeDir: + if err = CreateIfNotExists(destPath, 0755); err != nil { + return err + } + if err = CopyDirectory(sourcePath, destPath, overwrite, changePathFn, cpFn); err != nil { + return err + } + case os.ModeSymlink: + if err = CopySymLink(sourcePath, destPath); err != nil { + return err + } + default: + if err = cpFn(sourcePath, destPath, overwrite); err != nil { + return err + } + } + + err = chown(destPath, ostat) + if err != nil { + return err + } + + fInfo, err := entry.Info() + if err != nil { + return err + } + + isSymlink := fInfo.Mode()&os.ModeSymlink != 0 + if !isSymlink { + if err := os.Chmod(destPath, fInfo.Mode()); err != nil { + return err + } + } + } + return nil +} + +func Copy(srcFile, dstFile string, overwrite bool) error { + if !overwrite && Exists(dstFile) { + return nil + } + out, err := os.Create(dstFile) + if err != nil { + return err + } + + defer out.Close() + + in, err := os.Open(srcFile) + if err != nil { + return err + } + + defer in.Close() + + _, err = io.Copy(out, in) + if err != nil { + return err + } + + return nil +} + +func Exists(filePath string) bool { + if _, err := os.Stat(filePath); os.IsNotExist(err) { + return false + } + + return true +} + +func CreateIfNotExists(dir string, perm os.FileMode) error { + if Exists(dir) { + return nil + } + + if err := os.MkdirAll(dir, perm); err != nil { + return fmt.Errorf("failed to create directory: '%s', error: '%s'", dir, err.Error()) + } + + return nil +} + +func CopySymLink(source, dest string) error { + link, err := os.Readlink(source) + if err != nil { + return err + } + return os.Symlink(link, dest) +} diff --git a/forkapp/copydir_linux.go b/forkapp/copydir_linux.go new file mode 100644 index 00000000..fdfe717a --- /dev/null +++ b/forkapp/copydir_linux.go @@ -0,0 +1,19 @@ +//go:build linux +// +build linux + +package main + +import ( + "os" + "syscall" +) + +func chown(destPath string, ostat interface{}) error { + stat, ok := ostat.(*syscall.Stat_t) + if ok { + if err := os.Lchown(destPath, int(stat.Uid), int(stat.Gid)); err != nil { + return err + } + } + return nil +} diff --git a/forkapp/copydir_other.go b/forkapp/copydir_other.go new file mode 100644 index 00000000..bd877fcc --- /dev/null +++ b/forkapp/copydir_other.go @@ -0,0 +1,8 @@ +//go:build !linux +// +build !linux + +package main + +func chown(destPath string, ostat interface{}) error { + return nil +} diff --git a/forkapp/forkapp.go b/forkapp/forkapp.go new file mode 100644 index 00000000..279fca19 --- /dev/null +++ b/forkapp/forkapp.go @@ -0,0 +1,215 @@ +package main + +import ( + "errors" + "fmt" + "io/ioutil" + "log" + "os" + "path" + "path/filepath" + "strings" + + "github.com/urfave/cli" +) + +func doForkApp(fromPath, toPath, fromApp, toApp string, replaces []string, force bool) error { + p := len("luci-app-") + fromApp = fromApp[p:] + toApp = toApp[p:] + if !Exists(toPath) { + os.Mkdir(toPath, 0755) + } + toLen := len(toPath) + changePathFn := func(src, dst string) string { + return dst[:toLen] + strings.ReplaceAll(dst[toLen:], fromApp, toApp) + } + cpFn := func(srcFile, dstFile string, overwrite bool) error { + if !overwrite && Exists(dstFile) { + return nil + } + out, err := os.Create(dstFile) + if err != nil { + return err + } + defer out.Close() + + b, err := ioutil.ReadFile(srcFile) + if err != nil { + return err + } + rlt := strings.ReplaceAll(string(b), fromApp, toApp) + for _, repl := range replaces { + ss := strings.Split(repl, "/") + if len(ss) == 2 { + rlt = strings.ReplaceAll(rlt, ss[0], ss[1]) + } + } + _, err = out.WriteString(rlt) + if err != nil { + fmt.Println(dstFile, "failed") + } else { + fmt.Println(dstFile, "ok") + } + return err + } + return CopyDirectory(fromPath, toPath, force, changePathFn, cpFn) +} + +func uploadOrInstall(c *cli.Context) error { + fromPath := c.String("from") + toPath := c.String("to") + if fromPath == "" { + return errors.New("invalid fromPath") + } + if toPath == "" { + return errors.New("invalid toPath") + } + if c.String("ip") == "" { + return errors.New("invalid ip") + } + pwd := c.String("pwd") + if pwd == "" { + pwd = "password" + } + port := c.String("port") + if port == "" { + port = "22" + } + if !Exists(fromPath) { + return errors.New("fromPath not found") + } + install := c.Bool("install") + sshConn, err := NewSSHClient(c.String("ip"), port, "root", pwd) + if err != nil { + return err + } + if b, err := SshDirExists(sshConn, toPath); err != nil { + return err + } else if !b { + return errors.New(fmt.Sprintf("to: %s not found", toPath)) + } + defer sshConn.Close() + scriptPath := c.String("script") + err = SshCopyDirectory(sshConn, fromPath, toPath, scriptPath) + if err != nil { + return err + } + if install { + targetScript := path.Join(toPath, filepath.Base(fromPath), filepath.Base(scriptPath)) + if b, err := SshFileExists(sshConn, targetScript); err != nil || !b { + fmt.Println("Path:", targetScript, "not found") + return nil + } + cmd := fmt.Sprintf("cd \"%s\" && ./%s", path.Join(toPath, filepath.Base(fromPath)), filepath.Base(scriptPath)) + var rlt []byte + rlt, err = SshRunCmd(sshConn, cmd) + if err != nil || !strings.Contains(string(rlt), "Ok") { + fmt.Println("Run", cmd, "failed") + } + } + return err +} + +func main() { + var cliApp *cli.App + cliApp = &cli.App{ + Name: "forkApp", + Usage: "fork a luci app", + Action: func(c *cli.Context) error { + cli.ShowAppHelp(c) + return nil + }, + Commands: []cli.Command{ + { + Name: "fork", + Aliases: []string{"forkApp", "forkapp"}, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "from", + Usage: "-from ../luci-app-plex", + }, + cli.StringFlag{ + Name: "to", + Usage: "-to luci-app-ittools", + }, + cli.BoolFlag{ + Name: "force", + Usage: "-force true", + }, + cli.StringSliceFlag{ + Name: "replace", + Usage: "-replace Plex/ITTools", + }, + }, + Action: func(c *cli.Context) error { + fromPath := c.String("from") + toPath := c.String("to") + if fromPath == "" { + return errors.New("invalid fromPath") + } + if toPath == "" { + return errors.New("invalid toPath") + } + if !Exists(fromPath) { + return errors.New("fromPath not found") + } + fromApp := filepath.Base(fromPath) + if !strings.HasPrefix(fromApp, "luci-app-") { + return errors.New("dir name should be luci-app-xxx") + } + toApp := filepath.Base(toPath) + if !strings.HasPrefix(toApp, "luci-app-") { + return errors.New("dir name should be luci-app-xxx") + } + if !Exists(filepath.Dir(toPath)) { + return errors.New(fmt.Sprintf("toPath: %s not found", filepath.Dir(toPath))) + } + return doForkApp(fromPath, toPath, fromApp, toApp, c.StringSlice("replace"), c.Bool("force")) + }, + }, + { + Name: "upload", + Aliases: []string{"u"}, + Flags: []cli.Flag{ + cli.StringSliceFlag{ + Name: "from", + Usage: "../luci-app-plex", + }, + cli.StringSliceFlag{ + Name: "to", + Usage: "/root", + }, + cli.StringFlag{ + Name: "ip", + Usage: "-ip 192.168.100.1", + }, + cli.StringFlag{ + Name: "port", + Usage: "-port 22", + }, + cli.StringFlag{ + Name: "pwd", + Usage: "-pwd password", + }, + cli.BoolFlag{ + Name: "install", + Usage: "-install true", + }, + cli.StringFlag{ + Name: "script", + Usage: "-script ../tools/simple-instal.sh", + }, + }, + Action: func(c *cli.Context) error { + return uploadOrInstall(c) + }, + }, + }, + } + + err := cliApp.Run(os.Args) + if err != nil { + log.Fatal(err) + } +} diff --git a/forkapp/go.mod b/forkapp/go.mod new file mode 100644 index 00000000..681d8da6 --- /dev/null +++ b/forkapp/go.mod @@ -0,0 +1,33 @@ +module github.com/linkease/forkapp + +go 1.21.6 + +require ( + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 + github.com/mholt/archiver/v4 v4.0.0-alpha.8 + github.com/urfave/cli v1.22.14 + golang.org/x/crypto v0.21.0 +) + +require ( + github.com/andybalholm/brotli v1.0.4 // indirect + github.com/bodgit/plumbing v1.2.0 // indirect + github.com/bodgit/sevenzip v1.3.0 // indirect + github.com/bodgit/windows v1.0.0 // indirect + github.com/connesc/cipherio v0.2.1 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/dsnet/compress v0.0.1 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/klauspost/compress v1.15.9 // indirect + github.com/klauspost/pgzip v1.2.5 // indirect + github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect + github.com/pierrec/lz4/v4 v4.1.15 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/therootcompany/xz v1.0.1 // indirect + github.com/ulikunitz/xz v0.5.10 // indirect + go4.org v0.0.0-20200411211856-f5505b9728dd // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect +) diff --git a/forkapp/go.sum b/forkapp/go.sum new file mode 100644 index 00000000..5d399eaa --- /dev/null +++ b/forkapp/go.sum @@ -0,0 +1,288 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/bodgit/plumbing v1.2.0 h1:gg4haxoKphLjml+tgnecR4yLBV5zo4HAZGCtAh3xCzM= +github.com/bodgit/plumbing v1.2.0/go.mod h1:b9TeRi7Hvc6Y05rjm8VML3+47n4XTZPtQ/5ghqic2n8= +github.com/bodgit/sevenzip v1.3.0 h1:1ljgELgtHqvgIp8W8kgeEGHIWP4ch3xGI8uOBZgLVKY= +github.com/bodgit/sevenzip v1.3.0/go.mod h1:omwNcgZTEooWM8gA/IJ2Nk/+ZQ94+GsytRzOJJ8FBlM= +github.com/bodgit/windows v1.0.0 h1:rLQ/XjsleZvx4fR1tB/UxQrK+SJ2OFHzfPjLWWOhDIA= +github.com/bodgit/windows v1.0.0/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/connesc/cipherio v0.2.1 h1:FGtpTPMbKNNWByNrr9aEBtaJtXjqOzkIXNYJp6OEycw= +github.com/connesc/cipherio v0.2.1/go.mod h1:ukY0MWJDFnJEbXMQtOcn2VmTpRfzcTz4OoVrWGGJZcA= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= +github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= +github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= +github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM= +github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60VhnHxb2X3T1teMyv5A= +github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk= +github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY= +github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= +github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= +github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= +github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= +github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go4.org v0.0.0-20200411211856-f5505b9728dd h1:BNJlw5kRTzdmyfh5U8F93HA2OwkP7ZGwA51eJ/0wKOU= +go4.org v0.0.0-20200411211856-f5505b9728dd/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/forkapp/sshremote.go b/forkapp/sshremote.go new file mode 100644 index 00000000..a12b2707 --- /dev/null +++ b/forkapp/sshremote.go @@ -0,0 +1,236 @@ +package main + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "net" + "os" + "path" + "path/filepath" + "strings" + "time" + + "github.com/kballard/go-shellquote" + "github.com/mholt/archiver/v4" + "golang.org/x/crypto/ssh" +) + +func NewSSHClient(remote, sshPort, username, password string) (*ssh.Client, error) { + c, err := net.DialTimeout("tcp", remote+":"+sshPort, time.Second*5) + if err != nil { + return nil, err + } + sconn, chans, reqs, err := ssh.NewClientConn(c, remote, &ssh.ClientConfig{ + User: username, + Auth: []ssh.AuthMethod{ + ssh.Password(password), + }, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + Timeout: time.Second * 5, + }) + sshConn := ssh.NewClient(sconn, chans, reqs) + return sshConn, err +} + +func SshDirExists(sshConn *ssh.Client, remoteDir string) (bool, error) { + session, err := sshConn.NewSession() + if err != nil { + return false, err + } + defer session.Close() + var errMsg bytes.Buffer + var output bytes.Buffer + session.Stderr = &errMsg + session.Stdout = &output + err = session.Run(fmt.Sprintf("[ -d \"%s\" ] && echo Ok", remoteDir)) + if err != nil { + return false, err + } + if strings.Contains(output.String(), "Ok") { + return true, nil + } + return false, nil +} + +func SshFileExists(sshConn *ssh.Client, remoteDir string) (bool, error) { + session, err := sshConn.NewSession() + if err != nil { + return false, err + } + defer session.Close() + var errMsg bytes.Buffer + var output bytes.Buffer + session.Stderr = &errMsg + session.Stdout = &output + err = session.Run(fmt.Sprintf("[ -f \"%s\" ] && echo Ok", remoteDir)) + if err != nil { + return false, err + } + if strings.Contains(output.String(), "Ok") { + return true, nil + } + return false, nil +} + +func SshCreateDir(sshConn *ssh.Client, remotePath string) error { + session, err := sshConn.NewSession() + if err != nil { + return err + } + defer session.Close() + + return session.Run("mkdir -p " + remotePath) +} + +func SshFetchFile(sshConn *ssh.Client, remotePath string) ([]byte, error) { + session, err := sshConn.NewSession() + if err != nil { + return nil, err + } + defer session.Close() + var errMsg bytes.Buffer + var output bytes.Buffer + session.Stderr = &errMsg + session.Stdout = &output + err = session.Run(fmt.Sprintf("cat \"%s\"", remotePath)) + if err != nil { + oute := errMsg.Bytes() + if len(oute) == 0 { + return nil, err + } + return nil, errors.New(string(oute)) + } + outb := output.Bytes() + return outb, nil +} + +func SshCopy(session *ssh.Session, size int64, mode os.FileMode, fileName string, contents io.Reader, destinationPath string) error { + return sshCopy(session, size, mode, fileName, contents, destinationPath) +} + +func SshCopyPath(session *ssh.Session, filePath, destinationPath string) error { + f, err := os.Open(filePath) + if err != nil { + return err + } + defer f.Close() + s, err := f.Stat() + if err != nil { + return err + } + return sshCopy(session, s.Size(), s.Mode().Perm(), path.Base(filePath), f, destinationPath) +} + +func sshCopy(session *ssh.Session, size int64, mode os.FileMode, fileName string, contents io.Reader, destination string) error { + defer session.Close() + w, err := session.StdinPipe() + + if err != nil { + return err + } + + cmd := shellquote.Join("scp", "-t", destination) + if err := session.Start(cmd); err != nil { + w.Close() + return err + } + + errors := make(chan error) + + go func() { + errors <- session.Wait() + }() + + fmt.Fprintf(w, "C%#o %d %s\n", mode, size, fileName) + io.Copy(w, contents) + fmt.Fprint(w, "\x00") + w.Close() + + return <-errors +} + +func SshCopyDirectory(sshConn *ssh.Client, srcDir, dest, installScript string) error { + baseName := filepath.Base(srcDir) + var files []archiver.File + var err error + if installScript != "" && Exists(installScript) { + files, err = archiver.FilesFromDisk(nil, map[string]string{ + srcDir: baseName, + installScript: path.Join(baseName, filepath.Base(installScript)), + }) + } else { + files, err = archiver.FilesFromDisk(nil, map[string]string{ + srcDir: baseName, + }) + } + if err != nil { + return err + } + + // we can use the CompressedArchive type to gzip a tarball + // (compression is not required; you could use Tar directly) + format := archiver.CompressedArchive{ + Compression: nil, + Archival: archiver.Tar{}, + } + + session, err := sshConn.NewSession() + if err != nil { + return err + } + defer session.Close() + w, err := session.StdinPipe() + + if err != nil { + return err + } + + cmd := shellquote.Join("tar", "xC", dest, "-f", "-") + if err := session.Start(cmd); err != nil { + w.Close() + return err + } + + errors := make(chan error) + + go func() { + errors <- session.Wait() + }() + + // create the archive + go func() { + err = format.Archive(context.Background(), w, files) + w.Close() + }() + + err1 := <-errors + if err1 != nil { + return err1 + } + return err +} + +func SshRunCmd(sshConn *ssh.Client, cmd string) ([]byte, error) { + session, err := sshConn.NewSession() + if err != nil { + return nil, err + } + defer session.Close() + var errMsg bytes.Buffer + var output bytes.Buffer + session.Stderr = &errMsg + session.Stdout = &output + err = session.Run(cmd) + if err != nil { + oute := errMsg.Bytes() + if len(oute) == 0 { + return nil, err + } + return nil, errors.New(string(oute)) + } + outb := output.Bytes() + return outb, nil +} diff --git a/tools/simple-install.sh b/tools/simple-install.sh new file mode 100755 index 00000000..bb7d2b57 --- /dev/null +++ b/tools/simple-install.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +# run in router +APPNAME=$1 + +CURR=`pwd` +if [ -z "${APPNAME}" ]; then + APPNAME=`basename "$CURR"|cut -d '-' -f 3` +fi + +if [ -z "${APPNAME}" ]; then + echo "please run in luci-app-xxx paths" + exit 1 +fi + +if [ ! -d luasrc ]; then + echo "luasrc not found, please run in luci-app-xxx paths" + exit 1 +fi + +mkdir -p /usr/lib/lua/luci/view/${APPNAME} +if [ -f ./luasrc/controller/${APPNAME}.lua ]; then + cp ./luasrc/controller/${APPNAME}.lua /usr/lib/lua/luci/controller/ +fi +if [ -d ./luasrc/view/${APPNAME} ]; then + cp ./luasrc/view/${APPNAME}/* /usr/lib/lua/luci/view/${APPNAME}/ +fi +if [ -d ./luasrc/model ]; then + cp -rf ./luasrc/model/* /usr/lib/lua/luci/model/ +fi +cp -rf ./root/* / +rm -rf /tmp/luci-* +echo "Ok" +