first commit

main
Ben 1 year ago
commit e2351e51f0

@ -0,0 +1,54 @@
#
# Copyright (C) 2006-2014 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=tgappstore
PKG_VERSION=5.0.0
PKG_RELEASE:=6
include $(INCLUDE_DIR)/package.mk
define Package/tgappstore
SECTION:=luci
CATEGORY:=LuCI
SUBMENU:=3. Applications
TITLE:=LuCI appstore module by PrivateRouter
PKGARCH:=all
DEPENDS:=+luci-compat
endef
define Package/tgappstore/description
This package contains LuCI configuration for the appstore, provided by PrivateRouter.
endef
define Build/Prepare
endef
define Build/Configure
endef
define Build/Compile
endef
define Package/tgappstore/install
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_DIR) $(1)/etc/uci-defaults
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/cbi/tgappstore
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/controller/admin
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/view/tgappstore
$(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d
$(INSTALL_CONF) ./files/etc/config/tgappstore $(1)/etc/config/tgappstore
$(INSTALL_DATA) ./files/etc/uci-defaults/40-tgappstore $(1)/etc/uci-defaults/40-tgappstore
$(INSTALL_DATA) ./files/usr/lib/lua/luci/model/cbi/tgappstore/tgappstore.lua $(1)/usr/lib/lua/luci/model/cbi/tgappstore/tgappstore.lua
$(INSTALL_DATA) ./files/usr/lib/lua/luci/controller/admin/tgappstore.lua $(1)/usr/lib/lua/luci/controller/admin/tgappstore.lua
$(INSTALL_DATA) ./files/usr/lib/lua/luci/view/tgappstore/tgappstore.htm $(1)/usr/lib/lua/luci/view/tgappstore/tgappstore.htm
$(INSTALL_DATA) ./files/usr/share/rpcd/acl.d/tgappstore.json $(1)/usr/share/rpcd/acl.d/tgappstore.json
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

@ -0,0 +1,304 @@
config command
option name 'Home Assistant'
option command 'opkg install luci-app-homeassistant'
option info 'Open source home automation that puts local control and privacy first.'
config command
option name 'Jellyfin'
option command 'opkg install luci-app-jellyfin'
option info 'Jellyfin is a Free Software Media System.'
config command
option name 'Nextcloud'
option command 'opkg install luci-app-nextcloud'
option info 'Nextcloud is a suite of self hosted file storage services like dropbox'
config command
option name 'Emby'
option command 'opkg install luci-app-emby'
option info 'Emby is a media server designed to organize, play, and stream audio and video.'
config command
option name 'MegaMedia'
option command 'opkg install luci-app-megamedia'
option info 'All in one installer for qbittorrent, jellyfin, jackett, raadarr, sonarr, lidarr, and prowlarr.'
config command
option name 'Joplin'
option command 'opkg install luci-app-joplin'
option info 'Joplin is a notes and tasks app that will allow you to sync notes from the desktop application and mobile phones'
config command
option name 'Whoogle'
option command 'opkg install luci-app-whoogle'
option info 'Get Google search results, but without any ads, javascript, AMP links, cookies, or IP address tracking.'
config command
option name 'Motioneye'
option command 'opkg install luci-app-motioneye'
option info 'An open source IPCAM surveillance solution that is easy and ready to use.'
config command
option name 'Plugsy'
option command 'opkg install luci-app-plugsy'
option info 'A simple dashboard used to show the status of various connected docker apps.'
config command
option name 'Ghostblog'
option command 'opkg install luci-app-plugsy'
option info 'Ghost is a free and open source blogging platform that simplifies online publishing.'
config command
option name 'SyncThing'
option command 'opkg install luci-app-syncthing'
option info 'Syncthing replaces proprietary sync and cloud services with something open, trustworthy and decentralized.'
config command
option name 'qBittorrent x86'
option command 'opkg install luci-app-qbittorrent'
option info 'qBittorrent is a cross-platform free and open-source BitTorrent client compiled for OpenWRT.'
config command
option name 'Telegram Bot'
option command 'opkg install telegrambot'
option info 'Setup your own Telegram bot for the router that allows remote reboot, client connection reports, and memory status.'
config command
option name 'rTorrent'
option command 'opkg install luci-app-rtorrent'
option info 'rTorrent is a quick and efficient BitTorrent client that uses the libTorrent library.'
config command
option name 'File Assistant'
option command 'opkg install luci-app-fileassistant'
option info 'A simple and easy to use file assistant tool under the NAS menu that helps navigate, edit, upload and delete files.'
config command
option name 'Diskman'
option command 'opkg install luci-app-diskman'
option info 'A Simple Disk Manager for OpenWRT, supports disk partition and format, supports raid, btrfs-raid, and btrfs snapshot'
config command
option name 'Router Bridge'
option command 'opkg install luci-app-bridge'
option info 'Set router as transparent bridge device that communicates with the superior routing without perception and has the function of firewall.'
config command
option name 'NFS File Support'
option command 'opkg install luci-app-nfs'
option info 'Easy router tools to help setup local file sharing on other devices with NFS.'
config command
option name 'File Transfer'
option command 'opkg install luci-app-filetransfer'
option info 'Simple built in router tool to help with uploading or downloading files from your router.'
config command
option name 'mini DLNA'
option command 'opkg install luci-app-minidlna'
option info 'DLNA support for streaming media from a local folder or mounted USB drive.'
config command
option name 'Rclone'
option command 'opkg install luci-app-rclone'
option info 'Rclone is an open source, multi threaded, web UI and program to manage or migrate content on cloud storage.'
config command
option name 'TorGuard WireGuard'
option command 'opkg install tgwireguard'
option info 'Easy client side WireGuard support for TorGuard users. Simply copy and paste your WireGuard config.'
config command
option name 'Samba File Sharing'
option command 'opkg install luci-app-samba4'
option info 'Setup a local folder or mounted USB drive as a shared SMB folder on your network.'
config command
option name 'Adblock'
option command 'opkg install luci-app-adblock'
option info 'Adblocking for your Wifi router that pulls from the largest ad lists automatically.'
config command
option name 'AdGuard Home'
option command 'opkg install adguardhome'
option info 'AdGuard Home is a free and open source network wide advertising and tracker blocker.'
config command
option name 'Transmission'
option command 'opkg install transmission-web-control transmission-daemon transmission-remote luci-app-transmission'
option info 'Transmission is a free and open source bit torrent client that runs directly in your browser.'
config command
option name 'Domoticz'
option command 'opkg install domoticz'
option info 'Domoticz is a Home Automation System that lets you monitor devices.'
config command
option name 'Terminal'
option command 'opkg install luci-app-ttyd'
option info 'This is a useful Command line window tool for accessing your router within the Router Services Menu.'
config command
option name 'Heimdall'
option command 'opkg install luci-app-heimdall'
option info 'Heimdall is a way to organise all those links to your most used web sites.'
config command
option name 'Code Server'
option command 'opkg install luci-app-codeserver'
option info 'Code Server is a self hosted dev env you can run in the browser.'
config command
option name 'Jackett'
option command 'opkg install luci-app-jackett'
option info 'Jackett works as a proxy server.'
config command
option name 'LXC'
option command 'opkg install luci-app-lxc'
option info 'Deploy virtual containers with pre set templates.'
config command
option name 'Plex'
option command 'opkg install luci-app-plex'
option info 'Plex is a media playback system that makes it simple to enjoy your media.'
config command
option name 'WebTop'
option command 'opkg install luci-app-webtop'
option info 'Webtop is a self hosted desktop service technology that deploys a virtual desktop in the browser.'
config command
option name 'Seafile'
option command 'opkg install luci-app-seafile'
option info 'Seafile is an open source, cross platform file hosting software system that can be synchronized.'
config command
option name 'Bookstack'
option command 'opkg install luci-app-bookstack'
option info 'BookStack is a simple, open-source, self hosted platform for organising and storing information.'
config command
option name 'Filebrowser'
option command 'opkg install luci-app-filebrowser'
option info 'Filebrowser is a simple web based file browser.'
config command
option name 'MQTT broker'
option command 'opkg install luci-app-mosquitto'
option info 'Provides a webadmin for most basic mosquitto MQTT parameters for IoT and homeassistant.'
config command
option name 'PrivateRouter Reverse Proxy'
option command 'opkg install luci-app-privaterouterproxy'
option info 'Easy router UI for setting up PrivateRouter reverse proxy to open up services or ports to the outside.'
config command
option name 'Prowlarr'
option command 'opkg install luci-app-prowlarr'
option info 'Prowlarr is an indexer manager and proxy that works with other apps.'
config command
option name 'SearXng'
option command 'opkg install luci-app-searxng'
option info 'SearXNG is a private internet metasearch engine which aggregates results from search services.'
config command
option name 'Nodered'
option command 'opkg install /etc/apps/luci-app-nodered_git-23.076.66121-e78b3f9_all.ipk'
option info 'Node-RED is a flow-based development tool for visual programming.'
config command
option name 'Alltube'
option command 'opkg install luci-app-alltube'
option info 'HTML GUI for youtube-dl'
config command
option name 'Aria2'
option command 'opkg install aria2 luci-app-aria2 webui-aria2 ariang'
option info 'aria2 is a lightweight multi-protocol download utility with added UI support.'
config command
option name 'Account Manager'
option command 'opkg install luci-app-acl'
option info 'Helps manage user accounts logins and passwords to various services on the router.'
config command
option name 'Photoprism'
option command 'opkg install /etc/apps/luci-app-photoprism_230320.42282_all.ipk'
option info 'PhotoPrism is an AI Powered Photos App for the Decentralized Web.'
config command
option name 'SimpleX'
option command 'opkg install /etc/apps/luci-app-simplex_git-23.076.66121-e78b3f9_all.ipk'
option info 'SimpleX Chat is a private and encrypted messenger without any user IDs.'
config command
option name 'Gitea'
option command 'opkg install /etc/apps/luci-app-gitea_git-23.076.66121-e78b3f9_all.ipk'
option info 'A painless self-hosted Git service.'
config command
option name 'Libreddit'
option command 'opkg install /etc/apps/luci-app-libreddit_git-23.076.66121-e78b3f9_all.ipk'
option info 'Libreddit is an alternative private front end to Reddit.'
config command
option name 'Librespeed'
option command 'opkg install /etc/apps/luci-app-librespeed_git-23.076.66121-e78b3f9_all.ipk'
option info 'Free and Open Source Speedtest.'
config command
option name 'Gotify'
option command 'opkg install /etc/apps/luci-app-gotify_git-23.076.66121-e78b3f9_all.ipk'
option info 'A simple server for sending and receiving messages in real time.'
config command
option name 'Mastodon'
option command 'opkg install /etc/apps/luci-app-mastodon_git-23.076.66121-e78b3f9_all.ipk'
option info 'Mastodon is free platform for running a self hosted social network.'
config command
option name 'Navidrome'
option command 'opkg install /etc/apps/luci-app-navidrome_230320.41548_all.ipk'
option info 'Navidrome is an open source music collection server and streamer.'
config command
option name 'Neko'
option command 'opkg install /etc/apps/luci-app-neko_git-23.076.66121-e78b3f9_all.ipk'
option info 'Neko is a self hosted virtual browser and group chat.'
config command
option name 'Owncast'
option command 'opkg install /etc/apps/luci-app-owncast_git-23.076.66121-e78b3f9_all.ipk'
option info 'Owncast is a open source live video and web chat server.'
config command
option name 'Owntone'
option command 'opkg install /etc/apps/luci-app-owntone_230323.01598_all.ipk'
option info 'OwnTone is an open source audio server for itunes and Roku.'
config command
option name 'Wordpress'
option command 'opkg install /etc/apps/luci-app-wordpress_git-23.076.66121-e78b3f9_all.ipk'
option info 'Wordpress is a popular website builder.'
config command
option name 'Qbittorrent Docker'
option command 'opkg install luci-app-qbittorrentdocker'
option info 'Qbittorrent client ruinning inside docker.'
config command
option name 'Coturn WebRTC'
option command 'opkg install coturn luci-app-coturn'
option info 'Coturn is a free open source TURN and STUN Server.'
config command
option name 'Discourse'
option command 'opkg install /etc/apps/luci-app-discourse_git-23.076.66121-e78b3f9_all.ipk'
option info 'Discourse is a free and open source Internet forum.'
config command
option name 'Update App List'
option command 'opkg install tgappstore'
option info 'Update your app list with the latest apps.'

@ -0,0 +1,11 @@
touch /etc/config/tgappstore
uci -q batch <<-EOF >/dev/null
delete ucitrack.@tgappstore[-1]
add ucitrack tgappstore
set ucitrack.@tgappstore[-1].init=tgappstore
commit ucitrack
EOF
rm -f /tmp/luci-indexcache
exit 0

@ -0,0 +1,268 @@
-- Copyright 2012 Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.controller.admin.tgappstore", package.seeall)
function index()
entry({"admin", "apps", "tgappstore"}, firstchild(), _("Install Apps"), 80).acl_depends = { "tgappstore" }
entry({"admin", "apps", "tgappstore", "dashboard"}, template("tgappstore/tgappstore"), _("Select App"), 1)
entry({"admin", "apps", "tgappstore", "config"}, cbi("tgappstore/tgappstore"), _("Configure"), 2)
entry({"admin", "apps", "tgappstore", "run"}, call("action_run"), nil, 3).leaf = true
entry({"admin", "apps", "tgappstore", "download"}, call("action_download"), nil, 3).leaf = true
entry({"command"}, call("action_public"), nil, 1).leaf = true
end
--- Decode a given string into arguments following shell quoting rules
--- [[abc \def "foo\"bar" abc'def']] -> [[abc def]] [[foo"bar]] [[abcdef]]
local function parse_args(str)
local args = { }
local function isspace(c)
if c == 9 or c == 10 or c == 11 or c == 12 or c == 13 or c == 32 then
return c
end
end
local function isquote(c)
if c == 34 or c == 39 or c == 96 then
return c
end
end
local function isescape(c)
if c == 92 then
return c
end
end
local function ismeta(c)
if c == 36 or c == 92 or c == 96 then
return c
end
end
--- Convert given table of byte values into a Lua string and append it to
--- the "args" table. Segment byte value sequence into chunks of 256 values
--- to not trip over the parameter limit for string.char()
local function putstr(bytes)
local chunks = { }
local csz = 256
local upk = unpack
local chr = string.char
local min = math.min
local len = #bytes
local off
for off = 1, len, csz do
chunks[#chunks+1] = chr(upk(bytes, off, min(off + csz - 1, len)))
end
args[#args+1] = table.concat(chunks)
end
--- Scan substring defined by the indexes [s, e] of the string "str",
--- perform unquoting and de-escaping on the fly and store the result in
--- a table of byte values which is passed to putstr()
local function unquote(s, e)
local off, esc, quote
local res = { }
for off = s, e do
local byte = str:byte(off)
local q = isquote(byte)
local e = isescape(byte)
local m = ismeta(byte)
if e then
esc = true
elseif esc then
if m then res[#res+1] = 92 end
res[#res+1] = byte
esc = false
elseif q and quote and q == quote then
quote = nil
elseif q and not quote then
quote = q
else
if m then res[#res+1] = 92 end
res[#res+1] = byte
end
end
putstr(res)
end
--- Find substring boundaries in "str". Ignore escaped or quoted
--- whitespace, pass found start- and end-index for each substring
--- to unquote()
local off, esc, start, quote
for off = 1, #str + 1 do
local byte = str:byte(off)
local q = isquote(byte)
local s = isspace(byte) or (off > #str)
local e = isescape(byte)
if esc then
esc = false
elseif e then
esc = true
elseif q and quote and q == quote then
quote = nil
elseif q and not quote then
start = start or off
quote = q
elseif s and not quote then
if start then
unquote(start, off - 1)
start = nil
end
else
start = start or off
end
end
--- If the "quote" is still set we encountered an unfinished string
if quote then
unquote(start, #str)
end
return args
end
local function parse_cmdline(cmdid, args)
local uci = require "luci.model.uci".cursor()
if uci:get("tgappstore", cmdid) == "command" then
local cmd = uci:get_all("tgappstore", cmdid)
local argv = parse_args(cmd.command)
local i, v
if cmd.param == "1" and args then
for i, v in ipairs(parse_args(luci.http.urldecode(args))) do
argv[#argv+1] = v
end
end
for i, v in ipairs(argv) do
if v:match("[^%w%.%-i/|]") then
argv[i] = '"%s"' % v:gsub('"', '\\"')
end
end
return argv
end
end
function execute_command(callback, ...)
local fs = require "nixio.fs"
local argv = parse_cmdline(...)
if argv then
local outfile = os.tmpname()
local errfile = os.tmpname()
local rv = os.execute(table.concat(argv, " ") .. " >%s 2>%s" %{ outfile, errfile })
local stdout = fs.readfile(outfile, 1024 * 512) or ""
local stderr = fs.readfile(errfile, 1024 * 512) or ""
fs.unlink(outfile)
fs.unlink(errfile)
local binary = not not (stdout:match("[%z\1-\8\14-\31]"))
callback({
ok = true,
command = table.concat(argv, " "),
stdout = not binary and stdout,
stderr = stderr,
exitcode = rv,
binary = binary
})
else
callback({
ok = false,
code = 404,
reason = "No such command"
})
end
end
function return_json(result)
if result.ok then
luci.http.prepare_content("application/json")
luci.http.write_json(result)
else
luci.http.status(result.code, result.reason)
end
end
function action_run(...)
execute_command(return_json, ...)
end
function return_html(result)
if result.ok then
require("luci.template")
luci.template.render("commands_public", {
exitcode = result.exitcode,
stdout = result.stdout,
stderr = result.stderr
})
else
luci.http.status(result.code, result.reason)
end
end
function action_download(...)
local fs = require "nixio.fs"
local argv = parse_cmdline(...)
if argv then
local fd = io.popen(table.concat(argv, " ") .. " 2>/dev/null")
if fd then
local chunk = fd:read(4096) or ""
local name
if chunk:match("[%z\1-\8\14-\31]") then
luci.http.header("Content-Disposition", "attachment; filename=%s"
% fs.basename(argv[1]):gsub("%W+", ".") .. ".bin")
luci.http.prepare_content("application/octet-stream")
else
luci.http.header("Content-Disposition", "attachment; filename=%s"
% fs.basename(argv[1]):gsub("%W+", ".") .. ".txt")
luci.http.prepare_content("text/plain")
end
while chunk do
luci.http.write(chunk)
chunk = fd:read(4096)
end
fd:close()
else
luci.http.status(500, "Failed to execute command")
end
else
luci.http.status(404, "No such command")
end
end
function action_public(cmdid, args)
local disp = false
if string.sub(cmdid, -1) == "s" then
disp = true
cmdid = string.sub(cmdid, 1, -2)
end
local uci = require "luci.model.uci".cursor()
if cmdid and
uci:get("tgappstore", cmdid) == "command" and
uci:get("tgappstore", cmdid, "public") == "1"
then
if disp then
luci.util.perror("blah blah")
execute_command(return_html, cmdid, args)
else
action_download(cmdid, args)
end
else
luci.http.status(403, "Access to command denied")
end
end

@ -0,0 +1,30 @@
-- Copyright 2012 Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
local m, s
m = Map("luci", translate("Router Appstore"),
translate("This page allows you download apps to your router and deploy with custom settings."))
s = m:section(TypedSection, "command", "")
s.template = "cbi/tblsection"
s.anonymous = true
s.addremove = true
s:option(Value, "name", translate("Description"),
translate("A short textual description of the app"))
s:option(Value, "info", translate("App Info"),
translate("Provide Info about the app"))
s:option(Value, "command", translate("Command"),
translate("command to install luci app"))
s:option(Flag, "param", translate("Custom arguments"),
translate("Allow the user to provide additional command line arguments"))
s:option(Flag, "public", translate("Public access"),
translate("Allow executing the command and downloading its output without prior authentication"))
return m

@ -0,0 +1,183 @@
<%#
Copyright 2012 Jo-Philipp Wich <jow@openwrt.org>
Licensed to the public under the Apache License 2.0.
-%>
<% css = [[
.commandbox {
height: 20em;
width: 30%;
float: left;
height: 20em;
margin: 5px;
position: relative;
}
.commandbox h3 {
font-size: 1.5em !important;
line-height: 2em !important;
margin: 0 !important;
}
.commandbox input[type="text"] {
width: 50% !important;
}
.commandbox div {
position: absolute;
left: 0;
bottom: 1.5em;
}
]] -%>
<%+header%>
<script type="text/javascript">//<![CDATA[
var stxhr = new XHR();
function command_run(ev, id)
{
var args;
var field = document.getElementById(id);
if (field)
args = encodeURIComponent(field.value);
var legend = document.getElementById('command-rc-legend');
var output = document.getElementById('command-rc-output');
if (legend && output)
{
output.innerHTML =
'<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> ' +
'<%:Waiting for app install to complete...%>'
;
legend.parentNode.style.display = 'block';
legend.style.display = 'inline';
stxhr.get('<%=url('admin/apps/tgappstore/run')%>/' + id + (args ? '/' + args : ''), null,
function(x, st)
{
if (st)
{
if (st.binary)
st.stdout = '[<%:Binary data not displayed, download instead.%>]';
legend.style.display = 'none';
output.innerHTML = String.format(
'<pre><strong># %h\n</strong>%h<span style="color:red">%h</span></pre>' +
'<div class="alert-message warning">%s (<%:Code:%> %d)</div>',
st.command, st.stdout, st.stderr,
(st.exitcode == 0) ? '<%:Command successful%>' : '<%:Command failed%>',
st.exitcode);
}
else
{
legend.style.display = 'none';
output.innerHTML = '<span class="error"><%:Failed to execute command!%></span>';
}
location.hash = '#output';
}
);
}
ev.preventDefault();
}
function command_download(ev, id)
{
var args;
var field = document.getElementById(id);
if (field)
args = encodeURIComponent(field.value);
location.href = '<%=url('admin/apps/tgappstore/download')%>/' + id + (args ? '/' + args : '');
ev.preventDefault();
}
function command_link(ev, id)
{
var legend = document.getElementById('command-rc-legend');
var output = document.getElementById('command-rc-output');
var args;
var field = document.getElementById(id);
if (field)
args = encodeURIComponent(field.value);
if (legend && output)
{
var prefix = location.protocol + '//' + location.host + '<%=url('command')%>/';
var suffix = (args ? '/' + args : '');
var link = prefix + id + suffix;
var link_nodownload = prefix + id + "s" + suffix;
legend.style.display = 'none';
output.parentNode.style.display = 'block';
output.innerHTML = String.format(
'<div class="alert-message"><p><%:Download execution result%> <a href="%s">%s</a></p><p><%:Or display result%> <a href="%s">%s</a></p></div>',
link, link, link_nodownload, link_nodownload
);
location.hash = '#output';
}
ev.preventDefault();
}
//]]></script>
<%
local uci = require "luci.model.uci".cursor()
local commands = { }
uci:foreach("tgappstore", "command", function(s) commands[#commands+1] = s end)
%>
<form method="get" action="<%=pcdata(FULL_REQUEST_URI)%>">
<div class="cbi-map">
<h2 name="content"><%:Select App to Install%></h2>
<% if #commands == 0 then %>
<div class="cbi-section">
<div class="table cbi-section-table">
<div class="tr cbi-section-table-row">
<p>
<em><%:This section contains no values yet%></em>
</p>
</div>
</div>
</div>
<% else %>
<fieldset class="cbi-section">
<% local _, command; for _, command in ipairs(commands) do %>
<div class="commandbox">
<h3><%=pcdata(command.name)%></h3>
<p><%=pcdata(command.info)%></p>
<% if command.param == "1" then %>
<p><%:Arguments:%><input type="text" id="<%=command['.name']%>" /></p>
<% end %>
<div>
<button class="cbi-button cbi-button-apply" onclick="command_run(event, '<%=command['.name']%>')"><%:Install App%></button>
</div>
</div>
<% end %>
<br style="clear:both" /><br />
<a name="output"></a>
</fieldset>
<% end %>
</div>
<fieldset class="cbi-section" style="display:none">
<legend id="command-rc-legend"><%:Collecting data...%></legend>
<span id="command-rc-output"></span>
</fieldset>
</form>
<%+footer%>

@ -0,0 +1,11 @@
{
"tgappstore": {
"description": "Grant UCI access for tgappstore",
"read": {
"uci": [ "luci" ]
},
"write": {
"uci": [ "luci" ]
}
}
}
Loading…
Cancel
Save