diff --git a/Panel/CHANGELOG.md b/Panel/CHANGELOG.md index e696b3e7..bd6962af 100644 --- a/Panel/CHANGELOG.md +++ b/Panel/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 2026-05-13 +- **Root Apache vhost template for split Panel/Website layout:** Added `SITES_AVAILABLE_EXAMPLE.conf` at repo root with ready-to-copy `sites-available` examples for both `Panel/` and `Website/`, covering HTTP→HTTPS redirects, SSL vhosts, example domains/paths, required Apache modules, Certbot (`--apache` and `--webroot`) commands, and verification/reload steps. +- **Per-site Apache examples for direct deployment:** Added `examples/apache/panel.example.com.conf` and `examples/apache/website.example.com.conf` so each site can be copied directly into `/etc/apache2/sites-available/` with minimal edits. +- **Apache install helper script:** Added `examples/apache/install-sites-available.sh` to copy vhost examples into `sites-available`, optionally run `a2enmod`, `a2ensite`, `apache2ctl configtest`, and reload Apache in one flow. + ## 2026-05-09 - **Billing OS-variant rollback + exact-service provisioning:** Removed storefront canonical Linux/Windows deduping and order-time OS service auto-switching so each enabled `billing_services` row is sold as its own variant. Checkout/provision now preserve the exact selected `service_id`/`home_cfg_id`/XML, enforce location OS compatibility without silent swapping, log selected XML context in provisioning traces, and show “Server installation is in progress.” instead of immediate executable-missing wording while update installs are active. Also replaced deprecated `utf8_encode()` usage in `modules/gamemanager/view_server_log.php` for PHP 8.3 compatibility. - **Mandatory provisioning trace log + existing-home install retry:** Added visible fail-closed trace logging to `modules/billing/logs/provisioning_trace.log`, threaded detailed per-order provisioning results into checkout success flows, and changed billing provisioning so `Active` orders with an existing `home_id` only skip when the install is already complete. Incomplete existing homes now keep allocating missing IP/mod data and re-use `gamemanager_trigger_update_install()` with traced inputs/outputs instead of silently requiring a manual Game Monitor update. diff --git a/SITES_AVAILABLE_EXAMPLE.conf b/SITES_AVAILABLE_EXAMPLE.conf new file mode 100644 index 00000000..6bd08972 --- /dev/null +++ b/SITES_AVAILABLE_EXAMPLE.conf @@ -0,0 +1,138 @@ +# GameServerPanel (GSP) Apache vhost examples +# +# Copy these into real files under /etc/apache2/sites-available/, for example: +# /etc/apache2/sites-available/panel.example.com.conf +# /etc/apache2/sites-available/website.example.com.conf +# +# Example deployment paths: +# Panel -> /var/www/gsp/Panel +# Website -> /var/www/gsp/Website +# +# Required Apache modules: +# sudo a2enmod ssl rewrite headers +# sudo systemctl reload apache2 +# +# Initial activation (HTTP first): +# sudo a2ensite panel.example.com.conf website.example.com.conf +# sudo apache2ctl configtest +# sudo systemctl reload apache2 +# +# Certbot (Apache plugin): +# sudo certbot --apache -d panel.example.com -d www.panel.example.com +# sudo certbot --apache -d gsp.example.com -d www.gsp.example.com +# +# Certbot dry run / renewal test: +# sudo certbot renew --dry-run +# +# Notes: +# - Replace all example domains and paths. +# - Keep all user-facing website routes root-relative in Website/. +# - If you use Cloudflare or another proxy, ensure TLS mode and DNS records are correct. + +###################################################################### +# PANEL SITE (Panel/) +###################################################################### + +# HTTP: allow ACME challenge, redirect everything else to HTTPS + + ServerName panel.example.com + ServerAlias www.panel.example.com + DocumentRoot /var/www/gsp/Panel + + + Options FollowSymLinks + AllowOverride All + Require all granted + + + RewriteEngine On + RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/ + RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] + + ErrorLog ${APACHE_LOG_DIR}/panel_example_error.log + CustomLog ${APACHE_LOG_DIR}/panel_example_access.log combined + + +# HTTPS: panel site with Let's Encrypt cert paths + + ServerName panel.example.com + ServerAlias www.panel.example.com + DocumentRoot /var/www/gsp/Panel + + + Options FollowSymLinks + AllowOverride All + Require all granted + + + SSLEngine on + SSLCertificateFile /etc/letsencrypt/live/panel.example.com/fullchain.pem + SSLCertificateKeyFile /etc/letsencrypt/live/panel.example.com/privkey.pem + + Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" + + ErrorLog ${APACHE_LOG_DIR}/panel_example_ssl_error.log + CustomLog ${APACHE_LOG_DIR}/panel_example_ssl_access.log combined + + +###################################################################### +# WEBSITE SITE (Website/ standalone storefront) +###################################################################### + +# HTTP: allow ACME challenge, redirect everything else to HTTPS + + ServerName gsp.example.com + ServerAlias www.gsp.example.com + DocumentRoot /var/www/gsp/Website + + + Options FollowSymLinks + AllowOverride All + Require all granted + + + RewriteEngine On + RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/ + RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] + + ErrorLog ${APACHE_LOG_DIR}/website_example_error.log + CustomLog ${APACHE_LOG_DIR}/website_example_access.log combined + + +# HTTPS: website site with Let's Encrypt cert paths + + ServerName gsp.example.com + ServerAlias www.gsp.example.com + DocumentRoot /var/www/gsp/Website + + + Options FollowSymLinks + AllowOverride All + Require all granted + + + SSLEngine on + SSLCertificateFile /etc/letsencrypt/live/gsp.example.com/fullchain.pem + SSLCertificateKeyFile /etc/letsencrypt/live/gsp.example.com/privkey.pem + + Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" + + ErrorLog ${APACHE_LOG_DIR}/website_example_ssl_error.log + CustomLog ${APACHE_LOG_DIR}/website_example_ssl_access.log combined + + +###################################################################### +# Optional webroot-based Certbot alternative (instead of --apache): +# +# sudo certbot certonly --webroot \ +# -w /var/www/gsp/Panel \ +# -d panel.example.com -d www.panel.example.com +# +# sudo certbot certonly --webroot \ +# -w /var/www/gsp/Website \ +# -d gsp.example.com -d www.gsp.example.com +# +# Then verify and reload: +# sudo apache2ctl configtest +# sudo systemctl reload apache2 +###################################################################### diff --git a/examples/apache/install-sites-available.sh b/examples/apache/install-sites-available.sh new file mode 100644 index 00000000..34693ab9 --- /dev/null +++ b/examples/apache/install-sites-available.sh @@ -0,0 +1,218 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Installs example Apache vhost configs for GSP Panel + Website. +# +# Usage (run as root or with sudo): +# sudo ./install-sites-available.sh \ +# --source-dir /path/to/repo/examples/apache \ +# --dest-dir /etc/apache2/sites-available \ +# --panel-conf panel.example.com.conf \ +# --website-conf website.example.com.conf \ +# --enable-mods \ +# --enable-sites \ +# --reload +# +# Defaults are suitable for Debian/Ubuntu Apache layout. + +SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DEST_DIR="/etc/apache2/sites-available" +PANEL_CONF="panel.example.com.conf" +WEBSITE_CONF="website.example.com.conf" +ENABLE_MODS=0 +ENABLE_SITES=0 +RELOAD=0 +FORCE=0 +RUN_CERTBOT=0 +PANEL_DOMAINS="" +WEBSITE_DOMAINS="" + +print_help() { + cat <<'EOF' +Install GSP example Apache vhost files. + +Options: + --source-dir Directory containing *.conf files. + --dest-dir Apache sites-available directory. + --panel-conf Panel conf filename in source-dir. + --website-conf Website conf filename in source-dir. + --enable-mods Run: a2enmod ssl rewrite headers + --enable-sites Run: a2ensite + --reload Run apache2ctl configtest and reload apache2 + --run-certbot Run certbot --apache for provided domains + --panel-domains Comma-separated domains for panel certbot run + --website-domains Comma-separated domains for website certbot run + --force Overwrite destination files if they exist + -h, --help Show this help text + +Examples: + sudo ./install-sites-available.sh --enable-mods --enable-sites --reload + sudo ./install-sites-available.sh --force --enable-sites --reload + sudo ./install-sites-available.sh --enable-mods --enable-sites --run-certbot \ + --panel-domains panel.example.com,www.panel.example.com \ + --website-domains gsp.example.com,www.gsp.example.com --reload +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --source-dir) + SOURCE_DIR="$2" + shift 2 + ;; + --dest-dir) + DEST_DIR="$2" + shift 2 + ;; + --panel-conf) + PANEL_CONF="$2" + shift 2 + ;; + --website-conf) + WEBSITE_CONF="$2" + shift 2 + ;; + --enable-mods) + ENABLE_MODS=1 + shift + ;; + --enable-sites) + ENABLE_SITES=1 + shift + ;; + --reload) + RELOAD=1 + shift + ;; + --run-certbot) + RUN_CERTBOT=1 + shift + ;; + --panel-domains) + PANEL_DOMAINS="$2" + shift 2 + ;; + --website-domains) + WEBSITE_DOMAINS="$2" + shift 2 + ;; + --force) + FORCE=1 + shift + ;; + -h|--help) + print_help + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + print_help + exit 1 + ;; + esac +done + +if [[ $EUID -ne 0 ]]; then + echo "Run this script as root (or via sudo)." >&2 + exit 1 +fi + +PANEL_SRC="$SOURCE_DIR/$PANEL_CONF" +WEBSITE_SRC="$SOURCE_DIR/$WEBSITE_CONF" +PANEL_DST="$DEST_DIR/$PANEL_CONF" +WEBSITE_DST="$DEST_DIR/$WEBSITE_CONF" + +if [[ ! -f "$PANEL_SRC" ]]; then + echo "Panel conf not found: $PANEL_SRC" >&2 + exit 1 +fi +if [[ ! -f "$WEBSITE_SRC" ]]; then + echo "Website conf not found: $WEBSITE_SRC" >&2 + exit 1 +fi +if [[ ! -d "$DEST_DIR" ]]; then + echo "Destination directory does not exist: $DEST_DIR" >&2 + exit 1 +fi + +if [[ "$RUN_CERTBOT" -eq 1 ]]; then + if [[ -z "$PANEL_DOMAINS" || -z "$WEBSITE_DOMAINS" ]]; then + echo "--run-certbot requires both --panel-domains and --website-domains" >&2 + exit 1 + fi + if ! command -v certbot >/dev/null 2>&1; then + echo "certbot not found in PATH. Install certbot first." >&2 + exit 1 + fi +fi + +copy_file() { + local src="$1" + local dst="$2" + + if [[ -f "$dst" && "$FORCE" -ne 1 ]]; then + echo "Skipping existing file: $dst (use --force to overwrite)" + return + fi + + install -m 0644 "$src" "$dst" + echo "Installed: $dst" +} + +copy_file "$PANEL_SRC" "$PANEL_DST" +copy_file "$WEBSITE_SRC" "$WEBSITE_DST" + +build_domain_args() { + local list="$1" + local normalized + local domain + normalized="${list//,/ }" + for domain in $normalized; do + if [[ -n "$domain" ]]; then + printf '%s\n' "-d" "$domain" + fi + done +} + +if [[ "$ENABLE_MODS" -eq 1 ]]; then + echo "Enabling Apache modules: ssl rewrite headers" + a2enmod ssl rewrite headers +fi + +if [[ "$ENABLE_SITES" -eq 1 ]]; then + echo "Enabling Apache sites: $PANEL_CONF $WEBSITE_CONF" + a2ensite "$PANEL_CONF" "$WEBSITE_CONF" +fi + +if [[ "$RELOAD" -eq 1 ]]; then + echo "Validating Apache config" + apache2ctl configtest + echo "Reloading Apache" + systemctl reload apache2 +fi + +if [[ "$RUN_CERTBOT" -eq 1 ]]; then + echo "Running Certbot for panel domains: $PANEL_DOMAINS" + mapfile -t panel_domain_args < <(build_domain_args "$PANEL_DOMAINS") + certbot --apache "${panel_domain_args[@]}" + + echo "Running Certbot for website domains: $WEBSITE_DOMAINS" + mapfile -t website_domain_args < <(build_domain_args "$WEBSITE_DOMAINS") + certbot --apache "${website_domain_args[@]}" + + echo "Re-validating Apache config after Certbot changes" + apache2ctl configtest + echo "Reloading Apache after Certbot changes" + systemctl reload apache2 +fi + +echo "Done." +echo "Next steps:" +echo " 1) Edit ServerName/ServerAlias/DocumentRoot values in $PANEL_DST and $WEBSITE_DST" +if [[ "$RUN_CERTBOT" -eq 1 ]]; then + echo " 2) Certbot has been run for the supplied domains." +else + echo " 2) Run Certbot for real domains, e.g.:" + echo " certbot --apache -d panel.example.com -d www.panel.example.com" + echo " certbot --apache -d gsp.example.com -d www.gsp.example.com" +fi diff --git a/examples/apache/panel.example.com.conf b/examples/apache/panel.example.com.conf new file mode 100644 index 00000000..e9a9c24a --- /dev/null +++ b/examples/apache/panel.example.com.conf @@ -0,0 +1,60 @@ +# Apache vhost example for GSP Panel +# Copy to: /etc/apache2/sites-available/panel.example.com.conf +# +# Enable modules once: +# sudo a2enmod ssl rewrite headers +# +# Enable site: +# sudo a2ensite panel.example.com.conf +# sudo apache2ctl configtest +# sudo systemctl reload apache2 +# +# Issue certificate (Apache plugin): +# sudo certbot --apache -d panel.example.com -d www.panel.example.com +# +# Alternative webroot issuance: +# sudo certbot certonly --webroot -w /var/www/gsp/Panel \ +# -d panel.example.com -d www.panel.example.com +# +# Renewal test: +# sudo certbot renew --dry-run + + + ServerName panel.example.com + ServerAlias www.panel.example.com + DocumentRoot /var/www/html/Panel + + + Options FollowSymLinks + AllowOverride All + Require all granted + + + RewriteEngine On + RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/ + RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] + + ErrorLog ${APACHE_LOG_DIR}/panel_example_error.log + CustomLog ${APACHE_LOG_DIR}/panel_example_access.log combined + + + + ServerName panel.example.com + ServerAlias www.panel.example.com + DocumentRoot /var/www/html/Panel + + + Options FollowSymLinks + AllowOverride All + Require all granted + + + SSLEngine on + SSLCertificateFile /etc/letsencrypt/live/panel.example.com/fullchain.pem + SSLCertificateKeyFile /etc/letsencrypt/live/panel.example.com/privkey.pem + + Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" + + ErrorLog ${APACHE_LOG_DIR}/panel_example_ssl_error.log + CustomLog ${APACHE_LOG_DIR}/panel_example_ssl_access.log combined + diff --git a/examples/apache/website.example.com.conf b/examples/apache/website.example.com.conf new file mode 100644 index 00000000..6adc5fa3 --- /dev/null +++ b/examples/apache/website.example.com.conf @@ -0,0 +1,60 @@ +# Apache vhost example for GSP Website storefront +# Copy to: /etc/apache2/sites-available/website.example.com.conf +# +# Enable modules once: +# sudo a2enmod ssl rewrite headers +# +# Enable site: +# sudo a2ensite website.example.com.conf +# sudo apache2ctl configtest +# sudo systemctl reload apache2 +# +# Issue certificate (Apache plugin): +# sudo certbot --apache -d gsp.example.com -d www.gsp.example.com +# +# Alternative webroot issuance: +# sudo certbot certonly --webroot -w /var/www/gsp/Website \ +# -d gsp.example.com -d www.gsp.example.com +# +# Renewal test: +# sudo certbot renew --dry-run + + + ServerName gsp.example.com + ServerAlias www.gsp.example.com + DocumentRoot /var/www/html/Website + + + Options FollowSymLinks + AllowOverride All + Require all granted + + + RewriteEngine On + RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/ + RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] + + ErrorLog ${APACHE_LOG_DIR}/website_example_error.log + CustomLog ${APACHE_LOG_DIR}/website_example_access.log combined + + + + ServerName gsp.example.com + ServerAlias www.gsp.example.com + DocumentRoot /var/www/html/Website + + + Options FollowSymLinks + AllowOverride All + Require all granted + + + SSLEngine on + SSLCertificateFile /etc/letsencrypt/live/gsp.example.com/fullchain.pem + SSLCertificateKeyFile /etc/letsencrypt/live/gsp.example.com/privkey.pem + + Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" + + ErrorLog ${APACHE_LOG_DIR}/website_example_ssl_error.log + CustomLog ${APACHE_LOG_DIR}/website_example_ssl_access.log combined +