Add PnPaaS application and browser installer

This commit is contained in:
Hermes Agent
2026-09-20 13:41:15 +00:00
parent 498f3308d4
commit 2e20e5472f
18 changed files with 1890 additions and 0 deletions
Executable
+154
View File
@@ -0,0 +1,154 @@
#!/usr/bin/env bash
set -Eeuo pipefail
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
APP_SOURCE="$SCRIPT_DIR/app"
TARGET="/var/www/html/pnpaas"
DB_NAME="pnpaas"
DB_USER="pnpaas"
DB_HOST="localhost"
ADMIN_USER="admin"
ADMIN_EMAIL=""
APP_URL=""
PORTAINER_URL=""
DRY_RUN=0
usage() {
printf '%s\n' \
'Usage: sudo ./install.sh [options]' \
' --target PATH Application target (default: /var/www/html/pnpaas)' \
' --db-name NAME MariaDB database (default: pnpaas)' \
' --db-user NAME MariaDB application user (default: pnpaas)' \
' --db-host HOST MariaDB host (default: localhost)' \
' --admin-user NAME Initial administrator username (default: admin)' \
' --admin-email EMAIL Initial administrator email' \
' --app-url URL Public HTTPS URL, e.g. https://example/pnpaas' \
' --portainer-url URL Backend Portainer URL' \
' --check Validate prerequisites without changes' \
' --help Show this help'
}
while (($#)); do
case "$1" in
--target) TARGET="$2"; shift 2;;
--db-name) DB_NAME="$2"; shift 2;;
--db-user) DB_USER="$2"; shift 2;;
--db-host) DB_HOST="$2"; shift 2;;
--admin-user) ADMIN_USER="$2"; shift 2;;
--admin-email) ADMIN_EMAIL="$2"; shift 2;;
--app-url) APP_URL="$2"; shift 2;;
--portainer-url) PORTAINER_URL="$2"; shift 2;;
--check) DRY_RUN=1; shift;;
--help) usage; exit 0;;
*) printf 'Unknown option: %s\n' "$1" >&2; usage >&2; exit 2;;
esac
done
fail() { printf 'ERROR: %s\n' "$1" >&2; exit 1; }
require_command() { command -v "$1" >/dev/null 2>&1 || fail "Required command not found: $1"; }
sql_escape() { local value="$1"; printf "%s" "${value//\'/\'\'}"; }
[[ "$(id -u)" -eq 0 ]] || fail 'Run this installer as root, for example: sudo ./install.sh'
[[ -d "$APP_SOURCE" ]] || fail "Application source not found: $APP_SOURCE"
require_command php
require_command mariadb
require_command openssl
require_command apache2ctl
php -m | grep -qi '^pdo_mysql$' || fail 'PHP extension pdo_mysql is required.'
php -m | grep -qi '^curl$' || fail 'PHP extension curl is required.'
php -m | grep -qi '^mbstring$' || fail 'PHP extension mbstring is required.'
[[ "$DB_NAME" =~ ^[A-Za-z0-9_]{1,64}$ ]] || fail 'Database name must contain only letters, numbers, and underscores.'
[[ "$DB_USER" =~ ^[A-Za-z0-9_]{1,32}$ ]] || fail 'Database user must contain only letters, numbers, and underscores.'
[[ "$DB_HOST" =~ ^[A-Za-z0-9_.:-]{1,253}$ ]] || fail 'Database host contains invalid characters.'
[[ "$ADMIN_USER" =~ ^[A-Za-z0-9_.-]{3,80}$ ]] || fail 'Admin username must contain 3-80 ASCII letters, numbers, dot, dash, or underscore.'
[[ -z "$APP_URL" || "$APP_URL" == https://* ]] || fail '--app-url must use HTTPS.'
[[ -z "$PORTAINER_URL" || "$PORTAINER_URL" == https://* ]] || fail '--portainer-url must use HTTPS.'
if [[ -n "$ADMIN_EMAIL" ]]; then
php -r 'exit(filter_var($argv[1], FILTER_VALIDATE_EMAIL) ? 0 : 1);' "$ADMIN_EMAIL" || fail 'Invalid administrator email address.'
fi
if ((DRY_RUN)); then
printf 'Prerequisite check passed. No files, services, or database objects were changed.\n'
exit 0
fi
[[ -n "$ADMIN_EMAIL" ]] || read -r -p 'Administrator email: ' ADMIN_EMAIL
php -r 'exit(filter_var($argv[1], FILTER_VALIDATE_EMAIL) ? 0 : 1);' "$ADMIN_EMAIL" || fail 'Invalid administrator email address.'
read -r -s -p 'Initial administrator password (empty = generate one): ' ADMIN_PASSWORD; printf '\n'
if [[ -z "$ADMIN_PASSWORD" ]]; then
ADMIN_PASSWORD="$(openssl rand -hex 24)"
GENERATED_ADMIN_PASSWORD=1
else
GENERATED_ADMIN_PASSWORD=0
fi
[[ ${#ADMIN_PASSWORD} -ge 12 ]] || fail 'Administrator password must be at least 12 characters.'
DB_PASSWORD="$(openssl rand -hex 32)"
ADMIN_HASH="$(php -r 'echo password_hash($argv[1], PASSWORD_DEFAULT);' "$ADMIN_PASSWORD")"
install -d -m 0755 "$TARGET"
cp -a "$APP_SOURCE/." "$TARGET/"
rm -f "$TARGET/.env"
SQL_TMP="$(mktemp)"
cleanup() { rm -f "$SQL_TMP"; }
trap cleanup EXIT
{
printf 'CREATE DATABASE IF NOT EXISTS `%s` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n' "$(sql_escape "$DB_NAME")"
printf "CREATE USER IF NOT EXISTS '%s'@'%s' IDENTIFIED BY '%s';\n" "$(sql_escape "$DB_USER")" "$(sql_escape "$DB_HOST")" "$(sql_escape "$DB_PASSWORD")"
printf "ALTER USER '%s'@'%s' IDENTIFIED BY '%s';\n" "$(sql_escape "$DB_USER")" "$(sql_escape "$DB_HOST")" "$(sql_escape "$DB_PASSWORD")"
printf 'GRANT ALL PRIVILEGES ON `%s`.* TO '\''%s'\''@'\''%s'\'';\n' "$(sql_escape "$DB_NAME")" "$(sql_escape "$DB_USER")" "$(sql_escape "$DB_HOST")"
printf 'FLUSH PRIVILEGES;\n'
} > "$SQL_TMP"
mariadb < "$SQL_TMP"
mariadb "$DB_NAME" < "$TARGET/database/schema.sql"
for migration in "$TARGET"/database/migrations/*.sql; do
mariadb "$DB_NAME" < "$migration"
done
ADMIN_SQL_TMP="$(mktemp)"
trap 'rm -f "$SQL_TMP" "$ADMIN_SQL_TMP"' EXIT
printf "INSERT INTO users (username, email, password_hash, role, status) VALUES ('%s','%s','%s','admin','active') ON DUPLICATE KEY UPDATE email=VALUES(email), password_hash=VALUES(password_hash), role='admin', status='active';\n" \
"$(sql_escape "$ADMIN_USER")" "$(sql_escape "$ADMIN_EMAIL")" "$(sql_escape "$ADMIN_HASH")" > "$ADMIN_SQL_TMP"
mariadb "$DB_NAME" < "$ADMIN_SQL_TMP"
umask 027
cat > "$TARGET/.env" <<EOF
PNPAAS_ADMIN_USER=$ADMIN_USER
PNPAAS_ADMIN_PASSWORD_HASH=$ADMIN_HASH
PNPAAS_DB_DSN=mysql:host=$DB_HOST;dbname=$DB_NAME;charset=utf8mb4
PNPAAS_DB_USER=$DB_USER
PNPAAS_DB_PASSWORD=$DB_PASSWORD
PNPAAS_APP_URL=$APP_URL
PNPAAS_FORCE_SECURE_COOKIES=1
PORTAINER_URL=$PORTAINER_URL
EOF
chown root:www-data "$TARGET/.env"
chmod 640 "$TARGET/.env"
chown -R root:root "$TARGET"
chown root:www-data "$TARGET/.env"
chmod 640 "$TARGET/.env"
APACHE_CONF="/etc/apache2/conf-available/pnpaas.conf"
cat > "$APACHE_CONF" <<EOF
<Directory $TARGET>
Options -Indexes
AllowOverride None
Require all granted
<FilesMatch "^(?:\\.pnpaas-setup-token\\.sha256|\\.env(?:\\..*)?|.*\\.(?:sql|log|ini|conf|sh|bak|old|orig)|config(?:\\..*)?)$">
Require all denied
</FilesMatch>
</Directory>
<DirectoryMatch "$TARGET/(?:includes|database)">
Require all denied
</DirectoryMatch>
EOF
if command -v a2enconf >/dev/null 2>&1; then a2enconf pnpaas >/dev/null; fi
apache2ctl configtest >/dev/null
systemctl reload apache2
printf '\nPnPaaS installation completed.\nTarget: %s\nDatabase: %s\nAdministrator: %s\n' "$TARGET" "$DB_NAME" "$ADMIN_USER"
if ((GENERATED_ADMIN_PASSWORD)); then
printf 'Initial administrator password (displayed once): %s\n' "$ADMIN_PASSWORD"
fi
printf 'Next steps: configure Portainer and legal-document URLs in %s/.env, then configure HTTPS.\n' "$TARGET"