diff --git a/.gitignore b/.gitignore index 7032eeb..f9f80d8 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ yarn-error.log /.vscode /.claude CLAUDE.md +/jabali-panel_*.deb +/jabali-deps_*.deb diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..ca05c9e --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,38 @@ +# Installation + +## Debian packages (no install.sh) + +Jabali ships as two Debian packages: + +- `jabali-deps` — system dependencies (nginx, PHP, DB, mail, DNS, etc.) +- `jabali-panel` — the panel application + systemd services + +### Build the packages + +From the repository root: + +``` +./scripts/build-jabali-deps-deb.sh +./scripts/build-jabali-panel-deb.sh +``` + +This produces: + +``` +jabali-deps__all.deb +jabali-panel__all.deb +``` + +### Install on a server + +``` +sudo dpkg -i ./jabali-deps__all.deb +sudo apt-get -f install -y +sudo dpkg -i ./jabali-panel__all.deb +``` + +After install, systemd services are enabled and started: + +- `jabali-agent` +- `jabali-queue` +- `jabali-health-monitor` diff --git a/packaging/jabali-panel/DEBIAN/control b/packaging/jabali-panel/DEBIAN/control new file mode 100644 index 0000000..9e96082 --- /dev/null +++ b/packaging/jabali-panel/DEBIAN/control @@ -0,0 +1,10 @@ +Package: jabali-panel +Version: 0.0.0 +Section: admin +Priority: optional +Architecture: all +Maintainer: Jabali Panel +Depends: jabali-deps (>= 0.9-rc36) +Description: Jabali Panel - Modern web hosting control panel + Jabali Panel is a modern web hosting control panel for WordPress and PHP + hosting. This package installs the application files and systemd services. diff --git a/packaging/jabali-panel/DEBIAN/postinst b/packaging/jabali-panel/DEBIAN/postinst new file mode 100755 index 0000000..a667212 --- /dev/null +++ b/packaging/jabali-panel/DEBIAN/postinst @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +set -euo pipefail + +APP_DIR="/var/www/jabali" +PHP_BIN="/usr/bin/php" + +if [ ! -d "$APP_DIR" ]; then + echo "Jabali install directory missing: $APP_DIR" >&2 + exit 1 +fi + +# Ensure runtime directories exist +mkdir -p \ + "$APP_DIR/storage/app" \ + "$APP_DIR/storage/framework/cache" \ + "$APP_DIR/storage/framework/sessions" \ + "$APP_DIR/storage/framework/views" \ + "$APP_DIR/storage/logs" \ + "$APP_DIR/bootstrap/cache" \ + "$APP_DIR/database" + +touch "$APP_DIR/database/database.sqlite" + +chown -R www-data:www-data "$APP_DIR/storage" "$APP_DIR/bootstrap/cache" +chown www-data:www-data "$APP_DIR/database/database.sqlite" +chmod -R ug+rwX "$APP_DIR/storage" "$APP_DIR/bootstrap/cache" +chmod 664 "$APP_DIR/database/database.sqlite" + +if [ ! -f "$APP_DIR/.env" ]; then + cp "$APP_DIR/.env.example" "$APP_DIR/.env" + sed -i \ + -e 's/^APP_NAME=.*/APP_NAME="Jabali"/' \ + -e 's/^APP_ENV=.*/APP_ENV=production/' \ + -e 's/^APP_DEBUG=.*/APP_DEBUG=false/' \ + -e "s|^APP_URL=.*|APP_URL=https://$(hostname -I | awk '{print $1}')|" \ + -e 's/^LOG_LEVEL=.*/LOG_LEVEL=error/' \ + "$APP_DIR/.env" +fi + +if ! grep -q '^APP_KEY=base64:' "$APP_DIR/.env"; then + su -s /bin/bash www-data -c "$PHP_BIN $APP_DIR/artisan key:generate --force" +fi + +su -s /bin/bash www-data -c "$PHP_BIN $APP_DIR/artisan migrate --force" || true +su -s /bin/bash www-data -c "$PHP_BIN $APP_DIR/artisan storage:link" || true + +if command -v systemctl >/dev/null 2>&1; then + systemctl daemon-reload + systemctl enable jabali-agent jabali-queue jabali-health-monitor >/dev/null 2>&1 || true + systemctl restart jabali-agent jabali-queue jabali-health-monitor >/dev/null 2>&1 || true +fi + +exit 0 diff --git a/packaging/jabali-panel/DEBIAN/postrm b/packaging/jabali-panel/DEBIAN/postrm new file mode 100755 index 0000000..d711275 --- /dev/null +++ b/packaging/jabali-panel/DEBIAN/postrm @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +if command -v systemctl >/dev/null 2>&1; then + systemctl daemon-reload >/dev/null 2>&1 || true +fi + +exit 0 diff --git a/packaging/jabali-panel/DEBIAN/prerm b/packaging/jabali-panel/DEBIAN/prerm new file mode 100755 index 0000000..d4c56b6 --- /dev/null +++ b/packaging/jabali-panel/DEBIAN/prerm @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +if command -v systemctl >/dev/null 2>&1; then + systemctl stop jabali-health-monitor jabali-queue jabali-agent >/dev/null 2>&1 || true + systemctl disable jabali-health-monitor jabali-queue jabali-agent >/dev/null 2>&1 || true +fi + +exit 0 diff --git a/packaging/jabali-panel/etc/systemd/system/jabali-agent.service b/packaging/jabali-panel/etc/systemd/system/jabali-agent.service new file mode 100644 index 0000000..3a13eaf --- /dev/null +++ b/packaging/jabali-panel/etc/systemd/system/jabali-agent.service @@ -0,0 +1,14 @@ +[Unit] +Description=Jabali Panel Agent +After=network.target + +[Service] +Type=simple +User=root +Group=root +ExecStart=/usr/bin/php /var/www/jabali/bin/jabali-agent +Restart=always +RestartSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/packaging/jabali-panel/etc/systemd/system/jabali-health-monitor.service b/packaging/jabali-panel/etc/systemd/system/jabali-health-monitor.service new file mode 100644 index 0000000..b5e43c6 --- /dev/null +++ b/packaging/jabali-panel/etc/systemd/system/jabali-health-monitor.service @@ -0,0 +1,23 @@ +[Unit] +Description=Jabali Health Monitor - Automatic service recovery +Documentation=https://github.com/shukiv/jabali-panel +After=network.target jabali-agent.service +Wants=jabali-agent.service + +[Service] +Type=simple +User=root +Group=root +ExecStart=/usr/bin/php /var/www/jabali/bin/jabali-health-monitor +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=jabali-health-monitor + +# Resource limits +LimitNOFILE=65535 +MemoryMax=128M + +[Install] +WantedBy=multi-user.target diff --git a/packaging/jabali-panel/etc/systemd/system/jabali-queue.service b/packaging/jabali-panel/etc/systemd/system/jabali-queue.service new file mode 100644 index 0000000..b5917fb --- /dev/null +++ b/packaging/jabali-panel/etc/systemd/system/jabali-queue.service @@ -0,0 +1,19 @@ +[Unit] +Description=Jabali Queue Worker +After=network.target jabali-agent.service +Wants=jabali-agent.service + +[Service] +Type=simple +User=www-data +Group=www-data +WorkingDirectory=/var/www/jabali +ExecStart=/usr/bin/php /var/www/jabali/artisan queue:work --sleep=3 --tries=1 --timeout=120 +Restart=always +RestartSec=5 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=jabali-queue + +[Install] +WantedBy=multi-user.target diff --git a/scripts/build-jabali-panel-deb.sh b/scripts/build-jabali-panel-deb.sh new file mode 100755 index 0000000..faae356 --- /dev/null +++ b/scripts/build-jabali-panel-deb.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +VERSION="$(cat "$ROOT_DIR/VERSION")" +if [[ "$VERSION" == VERSION=* ]]; then + VERSION="${VERSION#VERSION=}" +fi + +if [ ! -d "$ROOT_DIR/vendor" ]; then + echo "vendor directory missing. Run composer install before building the package." >&2 + exit 1 +fi + +if [ ! -d "$ROOT_DIR/public/build" ]; then + echo "public/build missing. Run npm install && npm run build before building the package." >&2 + exit 1 +fi + +BUILD_DIR="$(mktemp -d)" +PKG_DIR="$BUILD_DIR/jabali-panel" +APP_DIR="$PKG_DIR/var/www/jabali" + +mkdir -p "$PKG_DIR/DEBIAN" "$APP_DIR" + +cp -a "$ROOT_DIR/packaging/jabali-panel/DEBIAN/." "$PKG_DIR/DEBIAN/" + +sed -i "s/^Version:.*/Version: ${VERSION}/" "$PKG_DIR/DEBIAN/control" + +tar --exclude='.git' \ + --exclude='.env' \ + --exclude='.phpunit.result.cache' \ + --exclude='.git-credentials' \ + --exclude='.claude' \ + --exclude='node_modules' \ + --exclude='storage' \ + --exclude='database/database.sqlite' \ + --exclude='packaging' \ + --exclude='scripts' \ + --exclude='tests' \ + -C "$ROOT_DIR" -cf - . | tar -C "$APP_DIR" -xf - + +if [ -d "$ROOT_DIR/packaging/jabali-panel/etc" ]; then + cp -a "$ROOT_DIR/packaging/jabali-panel/etc" "$PKG_DIR/" +fi + +chmod 0755 "$PKG_DIR/DEBIAN/postinst" "$PKG_DIR/DEBIAN/prerm" "$PKG_DIR/DEBIAN/postrm" + +OUTPUT="$ROOT_DIR/jabali-panel_${VERSION}_all.deb" +dpkg-deb --build "$PKG_DIR" "$OUTPUT" + +echo "Built: $OUTPUT"