diff --git a/README.md b/README.md index 82d39494..e582b804 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ python -m leads_vec -r config run This will generate a default "config.json" file under the current directory. -##### Register as a Systemd Service +##### Register a Systemd Service ```shell python -m leads_vec -r systemd run @@ -157,6 +157,12 @@ systemctl daemon-reload systemctl enable leads_vec ``` +##### Run Reverse Proxy + +```shell +python -m leads_vec -r reverse_proxy run +``` + ##### Specify a Theme ```shell @@ -235,13 +241,48 @@ Ubuntu 22.04 LTS on a Raspberry Pi 4 Model B 8GB. After the OS is set up, just r You may also choose to clone the repository or download the scripts from [releases](https://github.com/ProjectNeura/LEADS/releases) (only stable releases provide scripts). -You can simply run "[setup.sh](scripts/setup.sh)" and it will install everything including LEADS for you. If anything -goes wrong, you can also manually install everything. +These scripts currently only support Ubuntu. + +### Python and LEADS + +You can simply run "[setup.sh](scripts/setup.sh)" and it will install everything including all the optional dependencies +of LEADS for you. ```shell /bin/sh "setup.sh$(wget -O setup.sh https://raw.githubusercontent.com/ProjectNeura/LEADS/master/scripts/setup.sh)" && rm setup.sh || rm setup.sh ``` +### Python Only + +[python-install.sh](scripts/python-install.sh) will only install Python 3.12 and Tcl/Tk. + +```shell +/bin/sh "python-install.sh$(wget -O python-install.sh https://raw.githubusercontent.com/ProjectNeura/LEADS/master/scripts/python-install.sh)" && rm python-install.sh || rm python-install.sh +``` + +### frp + +We use frp for reverse proxy. This is optional if you do not need public connections. If you want, install it through +"[frp-install.sh](scripts/frp-install.sh)". + +```shell +/bin/sh "frp-install.sh$(wget -O frp-install.sh https://raw.githubusercontent.com/ProjectNeura/LEADS/master/scripts/frp-install.sh)" && rm frp-install.sh || rm frp-install.sh +``` + +To configure frp, use "[frp-config.sh](scripts/frp-config.sh)". + +```shell +/bin/sh "frp-config.sh$(wget -O frp-config.sh https://raw.githubusercontent.com/ProjectNeura/LEADS/master/scripts/frp-config.sh)" && rm frp-config.sh || rm frp-config.sh +``` + +There are 4 arguments for this script, of which the first 2 are required. + +```shell +/bin/sh "frp-config.sh$(...)" ${frp server IP} ${frp token} ${frp port} ${LEADS comm port} && rm frp-config.sh || rm frp-config.sh +``` + +### OBS Studio + We also use OBS Studio for streaming, but it is not required. If you want to install, run "[obs-install.sh](scripts/obs-install.sh)". diff --git a/leads_gui/system.py b/leads_gui/system.py index f580a09d..b73408e2 100644 --- a/leads_gui/system.py +++ b/leads_gui/system.py @@ -2,7 +2,7 @@ from platform import system as _system -def get_system_platform() -> str: +def get_system_kernel() -> str: return _system().lower() diff --git a/leads_raspberry_pi/led_group.py b/leads_raspberry_pi/led_group.py index 07886eb1..3df4f3b9 100644 --- a/leads_raspberry_pi/led_group.py +++ b/leads_raspberry_pi/led_group.py @@ -39,7 +39,7 @@ def async_do(self, command: LEDCommand, *leds: LED) -> None: @_override def do(self, command: LEDCommand, *leds: LED) -> None: - _Thread(name="transition", target=self.async_do, args=(command, *leds)).start() + _Thread(name="transition", target=self.async_do, args=(command, *leds), daemon=True).start() @_dataclass diff --git a/leads_vec/__main__.py b/leads_vec/__main__.py index 14eb2d9e..f340823d 100644 --- a/leads_vec/__main__.py +++ b/leads_vec/__main__.py @@ -13,7 +13,7 @@ L as _L, load_config as _load_config, register_config as _register_config, reset as _reset from leads.data_persistence import Dataset as _Dataset from leads_gui import Config as _Config -from leads_gui.system import get_system_platform as _get_system_platform +from leads_gui.system import get_system_kernel as _get_system_platform if __name__ == "__main__": _filterwarnings("ignore") @@ -26,7 +26,8 @@ parser.add_argument("-c", "--config", default=None, help="specify a configuration file") parser.add_argument("-d", "--devices", default=f"{_abspath(__file__)[:-11]}devices.py", help="specify a devices module") - parser.add_argument("-r", "--register", choices=("systemd", "config"), default=None, help="register a service") + parser.add_argument("-r", "--register", choices=("systemd", "config", "reverse_proxy"), default=None, + help="register a service") parser.add_argument("-t", "--theme", default=None, help="specify a theme") parser.add_argument("-mfs", "--magnify-font-sizes", type=float, default=None, help="magnify font sizes by a factor") parser.add_argument("--emu", action=_BooleanOptionalAction, default=False, help="use emulator") @@ -54,9 +55,9 @@ f.write(str(_Config({}))) _L.info("Using \"/usr/local/leads/config.json\"") _chmod("/usr/local/leads/config.json", 777) - from ._bootloader import create_service + from ._bootloader import create_service as _create_service - create_service() + _create_service() _L.info("Service registered") elif args.register == "config": if _exists("config.json"): @@ -66,6 +67,11 @@ with open("config.json", "w") as f: f.write(str(_Config({}))) _L.info("Configuration file saved to \"config.json\"") + elif args.register == "reverse_proxy": + from ._bootloader import start_frpc as _start_frpc + + _start_frpc() + _L.info("`frpc` started") config = _load_config(args.config, _Config) if args.config else _Config({}) _L.debug("Configuration loaded:", str(config)) if t := args.theme: diff --git a/leads_vec/_bootloader/__init__.py b/leads_vec/_bootloader/__init__.py index 72fd48fd..752916d7 100644 --- a/leads_vec/_bootloader/__init__.py +++ b/leads_vec/_bootloader/__init__.py @@ -1 +1,2 @@ +from leads_vec._bootloader.frp import * from leads_vec._bootloader.systemd import * diff --git a/leads_vec/_bootloader/frp.py b/leads_vec/_bootloader/frp.py new file mode 100644 index 00000000..c18a8b4b --- /dev/null +++ b/leads_vec/_bootloader/frp.py @@ -0,0 +1,17 @@ +from os.path import exists as _exists +from subprocess import run as _run, PIPE as _PIPE +from threading import Thread as _Thread + +from leads import L as _L + + +def start_frpc() -> None: + if not _exists("/usr/local/frp/frpc") or not _exists("/usr/local/frp/frpc.toml"): + raise FileNotFoundError("frp not found") + + def wrapper() -> None: + r = _run(("/usr/local/frp/frpc", "-c", "/usr/local/frp/frpc.toml"), stdout=_PIPE, stderr=_PIPE) + _L.error("`frpc` exits prematurely") + _L.debug(f"Console output:\n{r.stdout.decode()}") + + _Thread(name="frpc", target=wrapper, daemon=True).start() diff --git a/scripts/frp-config.sh b/scripts/frp-config.sh new file mode 100644 index 00000000..21ccebce --- /dev/null +++ b/scripts/frp-config.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +abort() { + printf "%s\n" "$@" >&2 + exit 1 +} + +if [ "${EUID:-$(id -u)}" -ne 0 ]; +then abort "Error: This script requires root permission" +fi + +execute() { + if ! "$@"; + then abort "$(printf "Failed: %s" "$@")" + fi +} + +execute_root() { + execute "sudo" "$@" +} + +require_argument() { + if [ -n "$1" ]; + then echo "$1" + else abort "Required argument $2 does not exist" + fi +} + +argument_exists_or() { + if [ -n "$1" ]; + then echo "$1" + else echo "$2" + fi +} +if ! test -d "/usr/local/frp"; +then abort "frp not found" +fi +echo "Configuring client..." +echo "serverAddr = \"$(require_argument "$1" "frp server IP")\"" > "/usr/local/frp/frpc.toml" +{ + echo "serverPort = $(argument_exists_or "$3" "7000")" + echo "auth.method = \"token\"" + echo "auth.token = \"$(require_argument "$2" "frp token")\"" + echo "[[proxies]]" + echo "name = \"leads-vec-comm\"" + echo "type = \"tcp\"" + echo "localIP = \"127.0.0.1\"" + echo "localPort = $(argument_exists_or "$4" "16900")" + echo "remotePort = $(argument_exists_or "$4" "16900")" +} >> "/usr/local/frp/frpc.toml" +echo "Configuring server..." +echo "bindPort = $(argument_exists_or "$3" "7000")" > "/usr/local/frp/frps.toml" +{ + echo "auth.method = \"token\"" + echo "auth.token = \"$(require_argument "$2" "frp token")\"" + echo "vhostHTTPPort = 80" + echo "vhostHTTPSPort = 443" +} >> "/usr/local/frp/frps.toml" \ No newline at end of file diff --git a/scripts/frp-install.sh b/scripts/frp-install.sh new file mode 100644 index 00000000..3ab0ab73 --- /dev/null +++ b/scripts/frp-install.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +abort() { + printf "%s\n" "$@" >&2 + exit 1 +} + +if [ "${EUID:-$(id -u)}" -ne 0 ]; +then abort "Error: This script requires root permission" +fi + +execute() { + if ! "$@"; + then abort "$(printf "Failed: %s" "$@")" + fi +} + +execute_root() { + execute "sudo" "$@" +} + +if test -d "/usr/local/frp"; +then abort "/usr/local/frp already exists" +fi +if ! command -v curl &> /dev/null; +then + echo "cURL is not available, installing..." + execute_root "apt" "install" "-y" "curl" +fi +latest_release=$(curl -s "https://api.github.com/repos/fatedier/frp/releases/latest" | grep -o '"tag_name": "[^"]*' | grep -o '[^"]*$' | cut -c 2-) +if [ -z "$latest_release" ]; +then abort "Failed to retrieve the latest release" +fi +filename="frp_${latest_release}_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m)" +echo "Downloading ${filename}.tar.gz..." +execute_root "wget" "-O" "frp.tar.gz" "https://github.com/fatedier/frp/releases/download/v${latest_release}/${filename}.tar.gz" +echo "Extracting..." +execute_root "tar" "-xzvf" "frp.tar.gz" +echo "Moving ${filename} to /usr/local/frp..." +execute_root "mv" "${filename}" "/usr/local/frp" +echo "Cleaning up..." +execute_root "rm" "frp.tar.gz" \ No newline at end of file diff --git a/scripts/obs-install.sh b/scripts/obs-install.sh index 2568b83e..7ac81fa6 100644 --- a/scripts/obs-install.sh +++ b/scripts/obs-install.sh @@ -1,3 +1,5 @@ +#!/bin/sh + abort() { printf "%s\n" "$@" >&2 exit 1 diff --git a/scripts/obs-run.sh b/scripts/obs-run.sh index 67ab6ef0..269788a2 100644 --- a/scripts/obs-run.sh +++ b/scripts/obs-run.sh @@ -1,3 +1,5 @@ +#!/bin/sh + abort() { printf "%s\n" "$@" >&2 exit 1 diff --git a/scripts/python-install.sh b/scripts/python-install.sh index e8f78651..90d4fbac 100644 --- a/scripts/python-install.sh +++ b/scripts/python-install.sh @@ -1,3 +1,5 @@ +#!/bin/sh + abort() { printf "%s\n" "$@" >&2 exit 1 diff --git a/scripts/setup.sh b/scripts/setup.sh index b5ebbeb0..1da863fa 100644 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -1,3 +1,5 @@ +#!/bin/sh + abort() { printf "%s\n" "$@" >&2 exit 1