diff --git a/Makefile b/Makefile index 6336ba4..76ba224 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,9 @@ SASS_SRCS = $(shell find scss/ -name '*.scss') INSTALLDIR := TSC := npx tsc TSC_OPTIONS := --noEmitOnError --module es2015 +OGUSER := $(USER) + +.PHONY: docs # TODO: automatically compile sass instead of manual @@ -33,7 +36,6 @@ all: mkdir -p assets/css assets/js $(SASS) scss/custom.scss:assets/css/custom_bootstrap.css $(SASS_OPTIONS) $(TSC) $(TSC_OPTIONS) --outDir assets/js js/*.ts -# $(MAKE) -C js ssvplwc: cd srv/ssvplwc; cargo run --release @@ -41,4 +43,10 @@ ssvplwc: install: mkdir -p $(INSTALLDIR) cp -a . $(INSTALLDIR) - chown -R $(USER):$(USER) $(INSTALLDIR) \ No newline at end of file + chown -R $(OGUSER):$(OGUSER) $(INSTALLDIR) + +clean: + rm -rf .sass-cache assets node_modules venv docs/_build + +docs: + make -C docs html \ No newline at end of file diff --git a/docs/configuration.rst b/docs/configuration.rst index d38966e..b51cf20 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -1,7 +1,18 @@ Configuration ============= -Here's all of the configuration options: +SSVP is very versatile, and designed to run in a wide variety of environments. + +Configuration file +------------------ + +The SSVP configuration file must be stored at :code:`srv/ssvp-config.json`. A new one can be created by: + +- Manually entering the options seen below +- Running :code:`python3 installer/gen_config.py` +- Copying and editing :code:`srv/ssvp-config.json.example` + +The file options are: - :code:`enable_host_ipv6`: Determines whether to enable IPv6 support. On non-Linux OSes, having this option enabled may disable IPv4 support. (optional) - Allowed values: :code:`true`, :code:`false` @@ -26,6 +37,8 @@ Here's all of the configuration options: "args": "list of module arguments" } + - The :code:`module` can be one of :code:`ping`, :code:`http`, :code:`tcp`, :code:`ssvplwc` + - :code:`services`: List of services, set as an object - Allowed values: must fit the servers schema - :code:`database`: Database information @@ -39,4 +52,38 @@ Here's all of the configuration options: - :code:`username`: database username - :code:`password`: database password - :code:`database`: name of database in the DB system - - :code:`prefix`: prefix for tables \ No newline at end of file + - :code:`prefix`: prefix for tables + +Systemd +------- + +If you're using systemd, there's four main options: + +- **Enabling**: setting a unit to run at boot (:code:`systemctl enable unit_name`) +- **Disabling**: removing a unit from the at-boot list (:code:`systemctl disable unit_name`) +- **Starting**: launching a unit for the current boot (:code:`systemctl start unit_name`) +- **Stopping**: stopping a unit for the current boot (:code:`systemctl stop unit_name`) + + It should be noted that, to start a unit at the same time as enabling it, you should pass the :code:`--now` flag, as in + :code:`systemctl enable --now unit_name` + +SSVP uses three main units for operation: + +- :code:`ssvp-gunicorn.service`: Production web server +- :code:`ssvp-werkzeug.service`: Development web server +- :code:`ssvp-interval.timer`: Server uptime checking timer + +Some important notes: + +1. :code:`ssvp-gunicorn.service` and :code:`ssvp-werkzeug.service` should not be run at the same time; you only need one web server. + If you try and run both at once, systemd will kill the first one. +2. For those with some experience in systemd, it is very important that :code:`ssvp-interval.timer` not have its suffix left off (running as :code:`systemctl start ssvp-interval`). + Doing so will just run the server uptime checker just once, and not periodically. +3. Do not remove :code:`ssvp-interval.service`; it gets called by :code:`ssvp-interval.timer`. + +So, for instance, to set up a standard configuration: + +.. code-block:: bash + + systemctl enable --now ssvp-gunicorn.service + systemctl enable --now ssvp-interval.timer diff --git a/docs/installing.rst b/docs/installing.rst index e500fa2..1fe421c 100644 --- a/docs/installing.rst +++ b/docs/installing.rst @@ -64,8 +64,8 @@ If you use the defaults for databases, you should use the defaults in the config fail to run without the entries. The full installation is meant for server installs, when you want to segregate the installations and have automation set up for you automatically. If you're a developer, -this often is not necessary. Cron is currently the only supported mode for autoinstallation; please note your cron setup needs to be able to support `@reboot` (most do, but some old editions don't). -Systemd (and OpenRC) support will be added eventually. +this often is not necessary. Cron is one option for installation; systemd is another. Read the `configuration guide `_ for more information on setting up systemd. +The "enable on boot" option for systemd will take care of everything. OpenRC is not currently supported, but may be added in the future; the same goes for other systems (sysvinit, upstart, runit, shepherd). You're now done with the installation. You can run the server by running `srv/tmux.sh`. It should print :code:`no current client` when done. You can check on the server by running `tmux attach`, and disconnect from it by pressing `CTRL-b d`. diff --git a/install.sh b/install.sh index 5b2bea8..351d4ab 100755 --- a/install.sh +++ b/install.sh @@ -1,3 +1,4 @@ +#!/usr/bin/bash # SPDX-License-Identifier: AGPL-3.0-or-later # # ssvp: server statistics viewer project @@ -27,8 +28,13 @@ cd "$(dirname "$0")" if [ $(id -u) -ne 0 ]; then which sudo > /dev/null if [ $? -ne 0 ]; then - echo "Either run as root, or have sudo installed" - exit 1 + which doas > /dev/null + if [ $? -ne 0 ]; then + echo "Either run as root, or have sudo installed" + exit 1 + else + PREROOT="doas" + fi else PREROOT="sudo" fi @@ -113,25 +119,60 @@ fi echo -n "Installation directory [current directory]: " read INSDIR if [ "$INSDIR" != "" ]; then - $PREROOT make install INSTALLDIR=$INSDIR + $PREROOT make install INSTALLDIR=$INSDIR OGUSER=$(whoami) + # handle SELinux + if [ -f "/usr/sbin/restorecon" ]; then + $PREROOT restorecon -rv $INSDIR + fi else INSDIR="$(pwd)" fi -echo -n "Choose an autorunner method (cron/[none]): " +# TODO: switch to case +echo -n "Choose an autorunner method (systemd/cron/[none]): " read ATRMTH if [ "$ATRMTH" == "cron" ]; then (crontab -l; echo "*/5 * * * * $INSDIR/venv/bin/python3 $INSDIR/srv/interval.py") | crontab - echo "Cron runner installed" +elif [ "$ATRMTH" == "systemd" ]; then + for i in ssvp-gunicorn.service ssvp-werkzeug.service; do + sed -e "s//$(echo $INSDIR | sed 's_/_\\/_g')/g" srv/systemd/$i | $PREROOT tee $INSDIR/srv/systemd/$i > /dev/null + $PREROOT ln -sf $INSDIR/srv/systemd/$i /usr/lib/systemd/system/$i + done + $PREROOT systemctl daemon-reload + echo -n "Enable on boot? prod/dev/[none] " + read ATRACN + case $ATRACN in + "prod") + $PREROOT systemctl enable --now ssvp-gunicorn.service + ;; + "dev") + $PREROOT systemctl enable --now ssvp-werkzeug.service + ;; + *) + echo "To load on boot or start SSVP later, read https://ssvp.docs.amyip.net/configuration.html#systemd" + ;; + esac fi -echo -n "Choose a server reboot launch method (cron/[none]): " +# TODO: switch to case +echo -n "Choose a server reboot launch method (systemd/cron/[none]): " read RBTLNC if [ "$RBTLNC" == "cron" ]; then (crontab -l; echo "@reboot $INSDIR/srv/tmux.sh") | crontab - +elif [ "$RBTLNC" == "systemd" ]; then + for i in ssvp-interval.service ssvp-interval.timer; do + sed -e "s//$(echo $INSDIR | sed 's_/_\\/_g')/g" srv/systemd/$i | $PREROOT tee $INSDIR/srv/systemd/$i > /dev/null + $PREROOT ln -sf $INSDIR/srv/systemd/$i /usr/lib/systemd/system/$i + done + $PREROOT systemctl daemon-reload + echo -n "Enable timer now? n/[y] " + read ENATIM + if [ "$ENATIM" != "n" ]; then + $PREROOT systemctl enable --now systemctl-interval.timer + else + echo "Timer loading information available at https://ssvp.docs.amyip.net/configuration.html#systemd" + fi fi -echo "Installation finished." - -# next todo: cron (also implement systemd timers) -# next todo: systemd service for web server? \ No newline at end of file +echo "Installation finished." \ No newline at end of file diff --git a/srv/systemd/ssvp-gunicorn.service b/srv/systemd/ssvp-gunicorn.service new file mode 100644 index 0000000..6d7e5fa --- /dev/null +++ b/srv/systemd/ssvp-gunicorn.service @@ -0,0 +1,12 @@ +[Unit] +Description=ssvp (server statistics viewer project) production server +Documentation=https://ssvp.docs.amyip.net +Conflicts=ssvp-werkzeug + +[Service] +Type=simple +ExecStart=/venv-wrapper.sh srv/gunicorn.sh +Restart=always + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/srv/systemd/ssvp-interval.service b/srv/systemd/ssvp-interval.service new file mode 100644 index 0000000..e904923 --- /dev/null +++ b/srv/systemd/ssvp-interval.service @@ -0,0 +1,10 @@ +[Unit] +Description=ssvp (server statistics viewer project) server uptime checker +Documentation=https://ssvp.docs.amyip.net + +[Service] +Type=oneshot +ExecStart=/venv-wrapper.sh python3 srv/interval.py + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/srv/systemd/ssvp-interval.timer b/srv/systemd/ssvp-interval.timer new file mode 100644 index 0000000..edfdebb --- /dev/null +++ b/srv/systemd/ssvp-interval.timer @@ -0,0 +1,11 @@ +[Unit] +Description=ssvp (server statistics viewer project) server uptime checker +Documentation=https://ssvp.docs.amyip.net +Requires=ssvp-interval.service + +[Timer] +Unit=ssvp-interval.service +OnUnitActiveSec=5min + +[Install] +WantedBy=timers.target \ No newline at end of file diff --git a/srv/systemd/ssvp-werkzeug.service b/srv/systemd/ssvp-werkzeug.service new file mode 100644 index 0000000..da81a2f --- /dev/null +++ b/srv/systemd/ssvp-werkzeug.service @@ -0,0 +1,12 @@ +[Unit] +Description=ssvp (server statistics viewer project) development server +Documentation=https://ssvp.docs.amyip.net +Conflicts=ssvp-gunicorn + +[Service] +Type=simple +ExecStart=/venv-wrapper.sh python3 srv/app.py +Restart=always + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/srv/tmux.sh b/srv/tmux.sh index e990a7b..b8e0a67 100755 --- a/srv/tmux.sh +++ b/srv/tmux.sh @@ -20,8 +20,8 @@ # License version 3 is available at, for your convenience, # https://www.gnu.org/licenses/agpl-3.0.en.html. +# Should be run with venv-wrapper or the venv natively cd "$(dirname "$0")" -source ../venv/bin/activate tmux new-session -d -s ENTER tmux detach -s ENTER tmux send-keys -t 0 "./gunicorn.sh" ENTER \ No newline at end of file diff --git a/uninstall.sh b/uninstall.sh new file mode 100755 index 0000000..f343373 --- /dev/null +++ b/uninstall.sh @@ -0,0 +1,67 @@ +#!/usr/bin/bash +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# ssvp: server statistics viewer project +# Copyright (C) 2023 Amy Parker +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA or visit the +# GNU Project at https://gnu.org/licenses. The GNU Affero General Public +# License version 3 is available at, for your convenience, +# https://www.gnu.org/licenses/agpl-3.0.en.html. + +set -e + +cd "$(dirname "$0")" + +echo "SSVP Uninstaller - cleanly uninstalls SSVP" + +if [ $(id -u) -ne 0 ]; then + which sudo > /dev/null + if [ $? -ne 0 ]; then + which doas > /dev/null + if [ $? -ne 0 ]; then + echo "Either run as root, or have sudo installed" + exit 1 + else + PREROOT="doas" + fi + else + PREROOT="sudo" + fi +fi + +echo -n "Installation directory: [none] " +read INSDIR +if [ "$INSDIR" != "" ]; then + $PREROOT rm -rf $INSDIR +fi + +which systemctl > /dev/null +if [ $? -eq 0 ]; then + echo -n "Clear systemd? n/[y] " + read CLRSMD + if [ "$CLRSMD" != "n" ]; then + set +e + for i in ssvp-gunicorn.service ssvp-interval.service ssvp-interval.timer ssvp-werkzeug.service; do + # --now was causing systemctl to hang, so do it manually + $PREROOT systemctl disable $i + $PREROOT systemctl stop $i + $PREROOT rm -f /usr/lib/systemd/system/$i + done + set -e + fi +fi + +echo "Uninstall complete." \ No newline at end of file diff --git a/venv-wrapper.sh b/venv-wrapper.sh new file mode 100755 index 0000000..5b2ce2e --- /dev/null +++ b/venv-wrapper.sh @@ -0,0 +1,30 @@ +#!/usr/bin/bash +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# ssvp: server statistics viewer project +# Copyright (C) 2023 Amy Parker +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA or visit the +# GNU Project at https://gnu.org/licenses. The GNU Affero General Public +# License version 3 is available at, for your convenience, +# https://www.gnu.org/licenses/agpl-3.0.en.html. + +set -e + +cd "$(dirname "$0")" + +source venv/bin/activate + +${@: 1} \ No newline at end of file