first commit
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…
Reference in New Issue