feat: webtrees docker build setup with patched PHP 8.4.26 (CVE-2026-109618 / CVE-2026-109619)
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
RewriteEngine On
|
||||||
|
|
||||||
|
# https://webtrees.net/faq/urls/
|
||||||
|
# GIT config files can contain credentials or other sensitive data.
|
||||||
|
RewriteRule \.git - [F]
|
||||||
|
|
||||||
|
# User data is stored here by default.
|
||||||
|
RewriteRule ^data(/|$) - [F]
|
||||||
|
|
||||||
|
# Nothing sensitive here, but there is no need to publish it.
|
||||||
|
RewriteRule ^app(/|$) - [F]
|
||||||
|
RewriteRule ^modules - [F]
|
||||||
|
RewriteRule ^resources(/|$) - [F]
|
||||||
|
RewriteRule ^vendor(/|$) - [F]
|
||||||
|
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
|
||||||
|
RewriteRule ^ index.php [L]
|
||||||
+78
@@ -0,0 +1,78 @@
|
|||||||
|
ARG PHP_VERSION=8.4
|
||||||
|
|
||||||
|
FROM docker.io/library/php:$PHP_VERSION-apache
|
||||||
|
|
||||||
|
# https://hub.docker.com/_/php
|
||||||
|
# https://github.com/NathanVaughn/webtrees-docker/issues/160
|
||||||
|
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
|
||||||
|
|
||||||
|
ENV WEBTREES_HOME="/var/www/webtrees"
|
||||||
|
WORKDIR $WEBTREES_HOME
|
||||||
|
|
||||||
|
# install pre-reqs
|
||||||
|
# postgresql-client provides pg_isready
|
||||||
|
# mariadb-client provides mysqladmin
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y \
|
||||||
|
curl \
|
||||||
|
libmagickwand-dev \
|
||||||
|
libpq-dev \
|
||||||
|
libzip-dev \
|
||||||
|
postgresql-client \
|
||||||
|
mariadb-client \
|
||||||
|
patch \
|
||||||
|
python3 \
|
||||||
|
unzip \
|
||||||
|
--no-install-recommends \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# install php extensions
|
||||||
|
RUN pecl install imagick \
|
||||||
|
&& docker-php-ext-enable imagick \
|
||||||
|
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
|
||||||
|
&& docker-php-ext-install -j"$(nproc)" pdo pdo_mysql pdo_pgsql zip intl gd exif opcache
|
||||||
|
|
||||||
|
# remove old apt stuff
|
||||||
|
RUN apt-get purge gcc g++ make -y \
|
||||||
|
&& apt-get autoremove -y \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/tmp/* /etc/apache2/sites-enabled/000-*.conf
|
||||||
|
|
||||||
|
ARG WEBTREES_VERSION
|
||||||
|
RUN curl -s -L https://github.com/fisharebest/webtrees/releases/download/${WEBTREES_VERSION}/webtrees-${WEBTREES_VERSION}.zip -o webtrees.zip \
|
||||||
|
&& unzip -q webtrees.zip -d /var/www/ && rm webtrees.zip \
|
||||||
|
&& rm $WEBTREES_HOME/*.md
|
||||||
|
|
||||||
|
# Disable version update prompt. Webtrees should not be upgrading itself,
|
||||||
|
# users should be using tagged container versions
|
||||||
|
ARG UPGRADE_PATCH_VERSION
|
||||||
|
COPY patches/UpgradeService${UPGRADE_PATCH_VERSION}.patch /UpgradeService.patch
|
||||||
|
RUN patch app/Services/UpgradeService.php /UpgradeService.patch \
|
||||||
|
&& rm /UpgradeService.patch \
|
||||||
|
# Delete file that caused email issues
|
||||||
|
# https://www.webtrees.net/index.php/fr/forum/help-for-2-0/36616-email-error-after-update-to-2-0-21#89985
|
||||||
|
# https://github.com/NathanVaughn/webtrees-docker/issues/88
|
||||||
|
&& rm vendor/egulias/email-validator/src/Validation/MessageIDValidation.php
|
||||||
|
|
||||||
|
# enable apache modules
|
||||||
|
RUN a2enmod rewrite && a2enmod ssl && rm -rf /var/www/html
|
||||||
|
|
||||||
|
# copy apache/php configs
|
||||||
|
RUN mkdir -p /bak/webtrees/data/ \
|
||||||
|
&& cp $WEBTREES_HOME/data/.htaccess /bak/webtrees/data/.htaccess
|
||||||
|
COPY .htaccess ./
|
||||||
|
COPY apache/ /etc/apache2/sites-available/
|
||||||
|
|
||||||
|
# entrypoint
|
||||||
|
COPY docker-entrypoint.py /
|
||||||
|
|
||||||
|
# healthcheck
|
||||||
|
COPY docker-healthcheck.sh /
|
||||||
|
RUN chmod +x /docker-healthcheck.sh
|
||||||
|
|
||||||
|
# final Docker config
|
||||||
|
EXPOSE 80 443
|
||||||
|
VOLUME ["$WEBTREES_HOME/data"]
|
||||||
|
|
||||||
|
HEALTHCHECK CMD /docker-healthcheck.sh
|
||||||
|
ENTRYPOINT ["python3", "/docker-entrypoint.py"]
|
||||||
@@ -4,10 +4,10 @@
|
|||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1 align="center">Name des Projekts</h1>
|
<h1 align="center">Webtrees Docker (PHP 8.4 Security Patched)</h1>
|
||||||
|
|
||||||
<h4 align="center">
|
<h4 align="center">
|
||||||
Kurzbeschreibung des Projekts/Anwendung, um die es geht
|
Webtrees Docker Image mit gepatchtem PHP 8.4.26 (Schutz vor CVE-2026-109618 / CVE-2026-109619 "PHP2Shell")
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<h6 align="center">
|
<h6 align="center">
|
||||||
@@ -21,9 +21,115 @@
|
|||||||
</h6>
|
</h6>
|
||||||
<br><br>
|
<br><br>
|
||||||
|
|
||||||
|
## 📌 Übersicht & Hintergrund
|
||||||
|
|
||||||
CONTENT BEREICH
|
Dieses Repository stellt vorgefertigte Docker-Images für die führende Open-Source-Genealogie-Software **[webtrees](https://webtrees.net/)** bereit.
|
||||||
|
|
||||||
|
### 🛡️ Warum dieser Fork / Rebuild?
|
||||||
|
Am 21. Oktober 2026 wurde die kritische Zero-Day-Schwachstelle **"PHP2Shell"** ([CVE-2026-109618](https://nvd.nist.gov/vuln/detail/CVE-2026-109618) & CVE-2026-109619, CVSS 9.2) im PHP-Kern bekannt.
|
||||||
|
|
||||||
|
Da bisherige INI-Workarounds (`session.upload_progress.enabled = Off`) laut Sicherheitsberichten (u. a. Heise Security) unvollständig sind bzw. umgangen werden können, ist der Einsatz der offiziell gepatchten PHP-Binaries zwingend erforderlich.
|
||||||
|
|
||||||
|
Da das beliebte Upstream-Image von [NathanVaughn/webtrees-docker](https://github.com/NathanVaughn/webtrees-docker) nur bei neuen Webtrees-Releases automatisiert baut, wurde dieses Image direkt auf Basis des heute veröffentlichten Upstream-Patches **`php:8.4-apache` (PHP 8.4.26, Build: 21.10.2026 00:32:44 UTC)** gebaut und getestet.
|
||||||
|
|
||||||
|
* **Upstream-Basis:** 1:1 kompatibel zu [NathanVaughn/webtrees-docker](https://github.com/NathanVaughn/webtrees-docker)
|
||||||
|
* **Webtrees-Versionen:** `2.2.6` und `2.2.1`
|
||||||
|
* **PHP-Version:** `PHP 8.4.26` (Offizielles Security-Patched Release vom 21.10.2026)
|
||||||
|
* **Enthaltene Module:** `imagick`, `gd`, `pdo_mysql`, `pdo_pgsql`, `zip`, `intl`, `exif`, `opcache` uvm.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 Verfügbare Container Tags
|
||||||
|
|
||||||
|
Die Images können direkt aus der Techniverse Gitea Container Registry bezogen werden:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Webtrees 2.2.6 (PHP 8.4.26)
|
||||||
|
docker pull git.techniverse.net/scriptos/webtrees:2.2.6-php8.4.26
|
||||||
|
docker pull git.techniverse.net/scriptos/webtrees:latest-php8.4.26
|
||||||
|
|
||||||
|
# Webtrees 2.2.1 (PHP 8.4.26)
|
||||||
|
docker pull git.techniverse.net/scriptos/webtrees:2.2.1-php8.4.26
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Schnellstart mit Docker Compose
|
||||||
|
|
||||||
|
Beispiel für eine `docker-compose.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
webtrees-app:
|
||||||
|
image: git.techniverse.net/scriptos/webtrees:2.2.6-php8.4.26
|
||||||
|
container_name: webtrees-app
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- webtrees-db
|
||||||
|
environment:
|
||||||
|
PRETTY_URLS: "1"
|
||||||
|
HTTPS: "0"
|
||||||
|
HTTPS_REDIRECT: "0"
|
||||||
|
LANG: "de"
|
||||||
|
TZ: "Europe/Berlin"
|
||||||
|
BASE_URL: "https://stammbaum.deine-domain.de"
|
||||||
|
DB_TYPE: "mysql"
|
||||||
|
DB_HOST: "webtrees-db"
|
||||||
|
DB_PORT: "3306"
|
||||||
|
DB_USER: "webtrees"
|
||||||
|
DB_PASS: "DeinSicheresPasswort"
|
||||||
|
DB_NAME: "webtrees"
|
||||||
|
DB_PREFIX: "wt_"
|
||||||
|
PUID: "33"
|
||||||
|
PGID: "33"
|
||||||
|
PHP_UPLOAD_MAX_FILE_SIZE: "50M"
|
||||||
|
PHP_POST_MAX_SIZE: "50M"
|
||||||
|
PHP_MEMORY_LIMIT: "1024M"
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
volumes:
|
||||||
|
- ./data/app_data:/var/www/webtrees/data/
|
||||||
|
- ./data/modules_v4:/var/www/webtrees/modules_v4
|
||||||
|
|
||||||
|
webtrees-db:
|
||||||
|
image: mariadb:11
|
||||||
|
container_name: webtrees-db
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
MARIADB_DATABASE: "webtrees"
|
||||||
|
MARIADB_USER: "webtrees"
|
||||||
|
MARIADB_PASSWORD: "DeinSicheresPasswort"
|
||||||
|
MARIADB_ROOT_PASSWORD: "DeinRootPasswort"
|
||||||
|
volumes:
|
||||||
|
- ./data/db_data:/var/lib/mysql
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 Verifikation des Sicherheits-Patches
|
||||||
|
|
||||||
|
Im laufenden Container lässt sich der Patch-Stand jederzeit über die PHP-CLI prüfen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec webtrees-app php -v
|
||||||
|
```
|
||||||
|
|
||||||
|
Ausgabe:
|
||||||
|
```text
|
||||||
|
PHP 8.4.26 (cli) (built: Oct 21 2026 00:32:44) (NTS)
|
||||||
|
Copyright (c) The PHP Group
|
||||||
|
Built by https://github.com/docker-library/php
|
||||||
|
Zend Engine v4.4.26, Copyright (c) Zend Technologies
|
||||||
|
with Zend OPcache v8.4.26, Copyright (c), by Zend Technologies
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔗 Danksagung & Upstream-Referenzen
|
||||||
|
|
||||||
|
* **webtrees:** Offizielles Projekt unter [https://webtrees.net/](https://webtrees.net/) und auf [GitHub (fisharebest/webtrees)](https://github.com/fisharebest/webtrees)
|
||||||
|
* **Docker Image Vorlage:** Nathan Vaughn ([NathanVaughn/webtrees-docker](https://github.com/NathanVaughn/webtrees-docker))
|
||||||
|
* **PHP Security:** PHP Group ([php.net](https://www.php.net/))
|
||||||
|
|
||||||
<br><br>
|
<br><br>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@@ -32,6 +138,6 @@ CONTENT BEREICH
|
|||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<sub>
|
<sub>
|
||||||
© Patrick Asmus · Techniverse Network · <a href="./LICENSE">Lizenz</a>
|
© Patrick Asmus · Techniverse Network · <a href="./LICENSE.gnu">GPL-3.0 Lizenz</a>
|
||||||
</sub>
|
</sub>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<VirtualHost *:80>
|
||||||
|
ServerName webtrees
|
||||||
|
DocumentRoot "/var/www/webtrees/"
|
||||||
|
|
||||||
|
RewriteEngine On
|
||||||
|
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
|
||||||
|
|
||||||
|
<Directory "/var/www/webtrees/">
|
||||||
|
Options FollowSymLinks MultiViews
|
||||||
|
AllowOverride All
|
||||||
|
Require all granted
|
||||||
|
</Directory>
|
||||||
|
</VirtualHost>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<VirtualHost *:443>
|
||||||
|
ServerName webtrees
|
||||||
|
DocumentRoot "/var/www/webtrees/"
|
||||||
|
|
||||||
|
SSLEngine on
|
||||||
|
SSLCertificateFile /certs/webtrees.crt
|
||||||
|
SSLCertificateKeyFile /certs/webtrees.key
|
||||||
|
|
||||||
|
<Directory "/var/www/webtrees/">
|
||||||
|
Options FollowSymLinks MultiViews
|
||||||
|
AllowOverride All
|
||||||
|
Require all granted
|
||||||
|
</Directory>
|
||||||
|
</VirtualHost>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<VirtualHost *:80>
|
||||||
|
ServerName webtrees
|
||||||
|
DocumentRoot "/var/www/webtrees/"
|
||||||
|
|
||||||
|
<Directory "/var/www/webtrees/">
|
||||||
|
Options FollowSymLinks MultiViews
|
||||||
|
AllowOverride All
|
||||||
|
Require all granted
|
||||||
|
</Directory>
|
||||||
|
</VirtualHost>
|
||||||
@@ -0,0 +1,602 @@
|
|||||||
|
import filecmp
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import urllib.error
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Any, List, Literal, Optional, TypeVar, Union, overload
|
||||||
|
from urllib import request
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
|
|
||||||
|
class NoRedirect(request.HTTPRedirectHandler):
|
||||||
|
def redirect_request(self, req, fp, code, msg, headers, newurl):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class DBType(Enum):
|
||||||
|
mysql = "mysql"
|
||||||
|
pgsql = "pgsql"
|
||||||
|
sqlite = "sqlite"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EnvVars:
|
||||||
|
prettyurls: bool
|
||||||
|
https: bool
|
||||||
|
httpsredirect: bool
|
||||||
|
sslcertfile: str
|
||||||
|
sslcertkeyfile: str
|
||||||
|
lang: str
|
||||||
|
baseurl: Optional[str]
|
||||||
|
dbtype: DBType
|
||||||
|
dbhost: Optional[str]
|
||||||
|
dbport: str
|
||||||
|
dbuser: str
|
||||||
|
dbpass: Optional[str]
|
||||||
|
dbname: str
|
||||||
|
tblpfx: str
|
||||||
|
wtuser: Optional[str]
|
||||||
|
wtname: Optional[str]
|
||||||
|
wtpass: Optional[str]
|
||||||
|
wtemail: Optional[str]
|
||||||
|
# https://github.com/fisharebest/webtrees/blob/f9a3af650116d75f1a87f454cabff5e9047e43f3/app/Http/Middleware/UseDatabase.php#L71-L82
|
||||||
|
dbkey: Optional[str]
|
||||||
|
dbcert: Optional[str]
|
||||||
|
dbca: Optional[str]
|
||||||
|
dbverify: bool
|
||||||
|
# php settings
|
||||||
|
phpmemorylimit: str
|
||||||
|
phpmaxexecutiontime: str
|
||||||
|
phppostmaxsize: str
|
||||||
|
phpuploadmaxfilesize: str
|
||||||
|
# user/group ID
|
||||||
|
puid: str
|
||||||
|
pgid: str
|
||||||
|
|
||||||
|
|
||||||
|
def truish(value: Optional[str]) -> bool:
|
||||||
|
"""
|
||||||
|
Check if a value is close enough to true
|
||||||
|
"""
|
||||||
|
if value is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return value.lower().strip() in ["true", "yes", "1"]
|
||||||
|
|
||||||
|
|
||||||
|
def print2(msg: Any) -> None:
|
||||||
|
"""
|
||||||
|
Print a message to stderr.
|
||||||
|
"""
|
||||||
|
print(f"[NV_INIT] {msg}", file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def get_environment_variable(
|
||||||
|
key: str, default: None = None, alternates: Optional[List[str]] = None
|
||||||
|
) -> Optional[str]: ...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def get_environment_variable(
|
||||||
|
key: str, default: T = None, alternates: Optional[List[str]] = None
|
||||||
|
) -> T: ...
|
||||||
|
|
||||||
|
|
||||||
|
def get_environment_variable(
|
||||||
|
key: str, default: Optional[T] = None, alternates: Optional[List[str]] = None
|
||||||
|
) -> Union[Optional[str], T]:
|
||||||
|
"""
|
||||||
|
Try to find the value of an environment variable.
|
||||||
|
"""
|
||||||
|
key = key.upper()
|
||||||
|
|
||||||
|
# try to find variable in env
|
||||||
|
if key in os.environ:
|
||||||
|
value = os.environ[key]
|
||||||
|
|
||||||
|
print2(f"{key} found in environment variables")
|
||||||
|
return value
|
||||||
|
|
||||||
|
# try to find file version of variable
|
||||||
|
file_key = f"{key}_FILE"
|
||||||
|
|
||||||
|
if file_key in os.environ:
|
||||||
|
# file name does not exist
|
||||||
|
if not os.path.isfile(os.environ[file_key]):
|
||||||
|
print(f"WARNING: {file_key} is not a file: {os.environ[file_key]}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# read data from file
|
||||||
|
with open(os.environ[file_key], "r") as f:
|
||||||
|
value = f.read().strip()
|
||||||
|
|
||||||
|
print2(f"{file_key} found in environment variables")
|
||||||
|
return value
|
||||||
|
|
||||||
|
# try to find alternate variable
|
||||||
|
if alternates is not None:
|
||||||
|
for a in alternates:
|
||||||
|
a_value = get_environment_variable(a)
|
||||||
|
if a_value is not None:
|
||||||
|
return a_value
|
||||||
|
|
||||||
|
# return default value
|
||||||
|
print2(f"{key} NOT found in environment variables, using default: {default}")
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
ENV = EnvVars(
|
||||||
|
prettyurls=truish(get_environment_variable("PRETTY_URLS")),
|
||||||
|
https=truish(get_environment_variable("HTTPS", alternates=["SSL"])),
|
||||||
|
httpsredirect=truish(
|
||||||
|
get_environment_variable("HTTPS_REDIRECT", alternates=["SSL_REDIRECT"])
|
||||||
|
),
|
||||||
|
sslcertfile=get_environment_variable("SSL_CERT_FILE", "/certs/webtrees.crt"),
|
||||||
|
sslcertkeyfile=get_environment_variable("SSL_CERT_KEY_FILE", "/certs/webtrees.key"),
|
||||||
|
baseurl=get_environment_variable("BASE_URL"),
|
||||||
|
lang=get_environment_variable("LANG", "en-US"),
|
||||||
|
dbtype=DBType[get_environment_variable("DB_TYPE", "mysql")],
|
||||||
|
dbhost=get_environment_variable("DB_HOST"),
|
||||||
|
dbport=get_environment_variable("DB_PORT", "3306"),
|
||||||
|
dbuser=get_environment_variable(
|
||||||
|
"DB_USER",
|
||||||
|
"webtrees",
|
||||||
|
alternates=["MYSQL_USER", "MARIADB_USER", "POSTGRES_USER"],
|
||||||
|
),
|
||||||
|
dbpass=get_environment_variable(
|
||||||
|
"DB_PASS",
|
||||||
|
alternates=["MYSQL_PASSWORD", "MARIADB_PASSWORD", "POSTGRES_PASSWORD"],
|
||||||
|
),
|
||||||
|
dbname=get_environment_variable(
|
||||||
|
"DB_NAME",
|
||||||
|
default="webtrees",
|
||||||
|
alternates=["MYSQL_DATABASE", "MARIADB_DATABASE", "POSTGRES_DB"],
|
||||||
|
),
|
||||||
|
tblpfx=get_environment_variable("DB_PREFIX", "wt_"),
|
||||||
|
wtuser=get_environment_variable("WT_USER"),
|
||||||
|
wtname=get_environment_variable("WT_NAME"),
|
||||||
|
wtpass=get_environment_variable("WT_PASS"),
|
||||||
|
wtemail=get_environment_variable("WT_EMAIL"),
|
||||||
|
dbkey=get_environment_variable("DB_KEY"),
|
||||||
|
dbcert=get_environment_variable("DB_CERT"),
|
||||||
|
dbca=get_environment_variable("DB_CA"),
|
||||||
|
dbverify=truish(get_environment_variable("DB_VERIFY")),
|
||||||
|
phpmemorylimit=get_environment_variable("PHP_MEMORY_LIMIT", "1024M"),
|
||||||
|
phpmaxexecutiontime=get_environment_variable("PHP_MAX_EXECUTION_TIME", "90"),
|
||||||
|
phppostmaxsize=get_environment_variable("PHP_POST_MAX_SIZE", "50M"),
|
||||||
|
phpuploadmaxfilesize=get_environment_variable("PHP_UPLOAD_MAX_FILE_SIZE", "50M"),
|
||||||
|
puid=get_environment_variable("PUID", "33"), # www-data user
|
||||||
|
pgid=get_environment_variable("PGID", "33"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = "/var/www/webtrees"
|
||||||
|
DATA_DIR = os.path.join(ROOT, "data")
|
||||||
|
CONFIG_FILE = os.path.join(DATA_DIR, "config.ini.php")
|
||||||
|
PHP_INI_FILE = "/usr/local/etc/php/php.ini"
|
||||||
|
|
||||||
|
os.chdir(ROOT)
|
||||||
|
|
||||||
|
|
||||||
|
def retry_urlopen(url: str, data: bytes) -> None:
|
||||||
|
"""
|
||||||
|
Retry a request until a postiive repsonse code is reached. Raises error if it fails.
|
||||||
|
"""
|
||||||
|
opener = request.build_opener(NoRedirect)
|
||||||
|
request.install_opener(opener)
|
||||||
|
|
||||||
|
for try_ in range(10):
|
||||||
|
try:
|
||||||
|
# make request
|
||||||
|
print2(f"Attempt {try_} for {url}")
|
||||||
|
resp = request.urlopen(url, data)
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
# capture error as well
|
||||||
|
resp = e
|
||||||
|
print2(f"Recieved HTTP {resp.status} response")
|
||||||
|
|
||||||
|
# check status code
|
||||||
|
# 302 is also accpetable in case the user selected something other than port 80
|
||||||
|
if resp.status in (200, 302):
|
||||||
|
return
|
||||||
|
|
||||||
|
# backoff
|
||||||
|
time.sleep(try_)
|
||||||
|
|
||||||
|
raise RuntimeError(f"Could not send a request to {url}")
|
||||||
|
|
||||||
|
|
||||||
|
def add_line_to_file(filename: str, newline: str) -> None:
|
||||||
|
"""
|
||||||
|
Add a new line to a file. If an existing line is found with the same
|
||||||
|
starting string, it will be replaced.
|
||||||
|
"""
|
||||||
|
newline += "\n"
|
||||||
|
|
||||||
|
# read file
|
||||||
|
with open(filename, "r") as fp:
|
||||||
|
lines = fp.readlines()
|
||||||
|
|
||||||
|
key = newline.split("=")[0]
|
||||||
|
|
||||||
|
# replace matching line
|
||||||
|
found = False
|
||||||
|
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
if line.startswith(key):
|
||||||
|
if line == newline:
|
||||||
|
return
|
||||||
|
|
||||||
|
lines[i] = newline
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
lines.append(newline)
|
||||||
|
|
||||||
|
# write new contents
|
||||||
|
with open(filename, "w") as fp:
|
||||||
|
fp.writelines(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def set_config_value(key: str, value: Optional[str]) -> None:
|
||||||
|
"""
|
||||||
|
In the config file, make sure the given key is set to the given value.
|
||||||
|
"""
|
||||||
|
if value is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
print2(f"Setting value for {key} in config")
|
||||||
|
|
||||||
|
if not os.path.isfile(CONFIG_FILE):
|
||||||
|
print2(f"WARNING: {CONFIG_FILE} does not exist")
|
||||||
|
return
|
||||||
|
|
||||||
|
add_line_to_file(CONFIG_FILE, f'{key}="{value}"')
|
||||||
|
|
||||||
|
|
||||||
|
def set_php_ini_value(key: str, value: str) -> None:
|
||||||
|
"""
|
||||||
|
In the php.ini file, make sure the given key is set to the given value.
|
||||||
|
"""
|
||||||
|
print2(f"Setting value for {key} in php.ini")
|
||||||
|
add_line_to_file(PHP_INI_FILE, f"{key} = {value}")
|
||||||
|
|
||||||
|
|
||||||
|
def enable_apache_site(
|
||||||
|
enable_sites: List[Literal["webtrees", "webtrees-redir", "webtrees-ssl"]],
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Enable an Apache site.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# update ssl apache config with cert path from env
|
||||||
|
ssl_site_file = "/etc/apache2/sites-available/webtrees-ssl.conf"
|
||||||
|
|
||||||
|
# make paths absolute
|
||||||
|
if not os.path.isabs(ENV.sslcertfile):
|
||||||
|
ENV.sslcertfile = os.path.join(DATA_DIR, ENV.sslcertfile)
|
||||||
|
|
||||||
|
if not os.path.isabs(ENV.sslcertkeyfile):
|
||||||
|
ENV.sslcertkeyfile = os.path.join(DATA_DIR, ENV.sslcertkeyfile)
|
||||||
|
|
||||||
|
# update file
|
||||||
|
with open(ssl_site_file, "r") as fp:
|
||||||
|
ssl_site_file_lines = fp.readlines()
|
||||||
|
|
||||||
|
new_ssl_site_file_lines = []
|
||||||
|
for line in ssl_site_file_lines:
|
||||||
|
if line.strip().startswith("SSLCertificateFile"):
|
||||||
|
line = line.replace(line.split()[1], ENV.sslcertfile)
|
||||||
|
elif line.strip().startswith("SSLCertificateKeyFile"):
|
||||||
|
line = line.replace(line.split()[1], ENV.sslcertkeyfile)
|
||||||
|
|
||||||
|
new_ssl_site_file_lines.append(line)
|
||||||
|
|
||||||
|
with open(ssl_site_file, "w") as fp:
|
||||||
|
fp.writelines(new_ssl_site_file_lines)
|
||||||
|
|
||||||
|
all_sites = ["webtrees", "webtrees-redir", "webtrees-ssl"]
|
||||||
|
|
||||||
|
# perl complains about locale to stderr, so disable that
|
||||||
|
|
||||||
|
# disable the other sites
|
||||||
|
for s in all_sites:
|
||||||
|
if s not in enable_sites:
|
||||||
|
print2(f"Disabling site {s}")
|
||||||
|
subprocess.check_call(
|
||||||
|
["a2dissite", s], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
||||||
|
)
|
||||||
|
|
||||||
|
# enable the desired sites
|
||||||
|
for s in enable_sites:
|
||||||
|
print2(f"Enabling site {s}")
|
||||||
|
subprocess.check_call(
|
||||||
|
["a2ensite", s], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def perms() -> None:
|
||||||
|
"""
|
||||||
|
Set up folder permissions
|
||||||
|
"""
|
||||||
|
|
||||||
|
print2("Setting up folder permissions for uploads")
|
||||||
|
# https://github.com/linuxserver/docker-baseimage-alpine/blob/bef0f4cee208396c92c0fdd1426613de02698301/root/etc/s6-overlay/s6-rc.d/init-adduser/run#L4-L9
|
||||||
|
subprocess.check_call(["groupmod", "-o", "-g", ENV.pgid, "www-data"])
|
||||||
|
subprocess.check_call(["usermod", "-o", "-u", ENV.puid, "www-data"])
|
||||||
|
subprocess.check_call(["chown", "-R", "www-data:www-data", DATA_DIR])
|
||||||
|
|
||||||
|
if os.path.isfile(CONFIG_FILE):
|
||||||
|
subprocess.check_call(["chmod", "700", CONFIG_FILE])
|
||||||
|
|
||||||
|
|
||||||
|
def php_ini() -> None:
|
||||||
|
"""
|
||||||
|
Update PHP .ini file
|
||||||
|
"""
|
||||||
|
print2("Updating php.ini")
|
||||||
|
|
||||||
|
if not os.path.isfile(PHP_INI_FILE):
|
||||||
|
print2("Creating php.ini")
|
||||||
|
|
||||||
|
os.makedirs(os.path.dirname(PHP_INI_FILE), exist_ok=True)
|
||||||
|
with open(PHP_INI_FILE, "w") as fp:
|
||||||
|
fp.writelines(["[PHP]\n", "\n"])
|
||||||
|
|
||||||
|
set_php_ini_value("memory_limit", ENV.phpmemorylimit)
|
||||||
|
set_php_ini_value("max_execution_time", ENV.phpmaxexecutiontime)
|
||||||
|
set_php_ini_value("post_max_size", ENV.phppostmaxsize)
|
||||||
|
set_php_ini_value("upload_max_filesize", ENV.phpuploadmaxfilesize)
|
||||||
|
|
||||||
|
# https://webtrees.net/admin/performance/
|
||||||
|
set_php_ini_value("opcache.enable", "1")
|
||||||
|
set_php_ini_value(
|
||||||
|
"opcache.revalidate_freq", "60"
|
||||||
|
) # re check changed files every 60 seconds
|
||||||
|
set_php_ini_value("opcache.revalidate_path", "0")
|
||||||
|
|
||||||
|
|
||||||
|
def check_db_variables() -> bool:
|
||||||
|
"""
|
||||||
|
Check if all required database variables are present
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
assert ENV.dbtype is not None
|
||||||
|
assert ENV.dbname is not None
|
||||||
|
assert ENV.tblpfx is not None
|
||||||
|
|
||||||
|
if ENV.dbtype == DBType.sqlite:
|
||||||
|
ENV.dbhost = ""
|
||||||
|
ENV.dbport = ""
|
||||||
|
ENV.dbuser = ""
|
||||||
|
ENV.dbpass = ""
|
||||||
|
|
||||||
|
assert ENV.dbhost is not None
|
||||||
|
assert ENV.dbport is not None
|
||||||
|
assert ENV.dbuser is not None
|
||||||
|
assert ENV.dbpass is not None
|
||||||
|
|
||||||
|
except AssertionError:
|
||||||
|
print2("WARNING: Not all database variables are set")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def setup_wizard() -> None:
|
||||||
|
"""
|
||||||
|
Run the setup wizard
|
||||||
|
"""
|
||||||
|
|
||||||
|
if os.path.isfile(CONFIG_FILE):
|
||||||
|
return
|
||||||
|
|
||||||
|
print2("Attempting to automate setup wizard")
|
||||||
|
|
||||||
|
# make sure all the variables we need are not set to None
|
||||||
|
if not check_db_variables():
|
||||||
|
return
|
||||||
|
|
||||||
|
if any(
|
||||||
|
v is None
|
||||||
|
for v in [ENV.baseurl, ENV.wtname, ENV.wtuser, ENV.wtpass, ENV.wtemail]
|
||||||
|
):
|
||||||
|
print2("WARNING: Not all required variables were found for setup wizard")
|
||||||
|
return
|
||||||
|
|
||||||
|
assert ENV.baseurl is not None
|
||||||
|
if not ENV.baseurl.startswith("http"):
|
||||||
|
print2(
|
||||||
|
"WARNING: BASE_URL does not start with 'http'. This is likely not what you want."
|
||||||
|
)
|
||||||
|
|
||||||
|
print2("Automating setup wizard")
|
||||||
|
print2("Starting Apache in background")
|
||||||
|
# set us up to a known HTTP state
|
||||||
|
enable_apache_site(["webtrees"])
|
||||||
|
# run apache in the background
|
||||||
|
apache_proc = subprocess.Popen(["apache2-foreground"], stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
if ENV.dbtype in [DBType.mysql, DBType.pgsql]:
|
||||||
|
# for typing, check_db_variables already does this
|
||||||
|
assert ENV.dbhost is not None
|
||||||
|
|
||||||
|
# try to resolve the host
|
||||||
|
# most common error is wrong hostname
|
||||||
|
try:
|
||||||
|
socket.gethostbyname(ENV.dbhost)
|
||||||
|
except socket.gaierror:
|
||||||
|
print2(f"ERROR: Could not resolve database host '{ENV.dbhost}'")
|
||||||
|
print2(
|
||||||
|
"ERROR: You likely have the DBHOST environment variable set incorrectly."
|
||||||
|
)
|
||||||
|
print2("ERROR: Exiting.")
|
||||||
|
|
||||||
|
# stop apache
|
||||||
|
apache_proc.terminate()
|
||||||
|
# die
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# wait until database is ready
|
||||||
|
if ENV.dbtype == DBType.mysql:
|
||||||
|
# https://dev.mysql.com/doc/refman/8.0/en/mysqladmin.html#option_mysqladmin_user
|
||||||
|
# don't miss the capital P
|
||||||
|
cmd = ["mysqladmin", "ping", "-h", ENV.dbhost, "-P", ENV.dbport, "--silent"]
|
||||||
|
name = "MySQL"
|
||||||
|
elif ENV.dbtype == DBType.pgsql:
|
||||||
|
# https://www.postgresql.org/docs/current/app-pg-isready.html
|
||||||
|
cmd = ["pg_isready", "-h", ENV.dbhost, "-p", ENV.dbport, "--quiet"]
|
||||||
|
name = "PostgreSQL"
|
||||||
|
|
||||||
|
while subprocess.run(cmd).returncode != 0: # type: ignore
|
||||||
|
print2(f"Waiting for {name} server {ENV.dbhost}:{ENV.dbport} to be ready") # type: ignore
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# sqlite
|
||||||
|
# let Apache start up
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# send it
|
||||||
|
url = "http://127.0.0.1:80/"
|
||||||
|
print2(f"Sending setup wizard request to {url}")
|
||||||
|
|
||||||
|
retry_urlopen(
|
||||||
|
url,
|
||||||
|
urlencode(
|
||||||
|
{
|
||||||
|
"lang": ENV.lang,
|
||||||
|
"tblpfx": ENV.tblpfx,
|
||||||
|
"baseurl": ENV.baseurl,
|
||||||
|
"dbtype": ENV.dbtype.value,
|
||||||
|
"dbhost": ENV.dbhost,
|
||||||
|
"dbport": ENV.dbport,
|
||||||
|
"dbuser": ENV.dbuser,
|
||||||
|
"dbpass": ENV.dbpass,
|
||||||
|
"dbname": ENV.dbname,
|
||||||
|
"wtname": ENV.wtname,
|
||||||
|
"wtuser": ENV.wtuser,
|
||||||
|
"wtpass": ENV.wtpass,
|
||||||
|
"wtemail": ENV.wtemail,
|
||||||
|
"step": "6",
|
||||||
|
}
|
||||||
|
).encode("ascii"),
|
||||||
|
)
|
||||||
|
|
||||||
|
print2("Stopping Apache")
|
||||||
|
apache_proc.terminate()
|
||||||
|
|
||||||
|
|
||||||
|
def update_config_file() -> None:
|
||||||
|
"""
|
||||||
|
Update the config file with items set via environment variables
|
||||||
|
"""
|
||||||
|
print2("Updating config file")
|
||||||
|
|
||||||
|
if not os.path.isfile(CONFIG_FILE):
|
||||||
|
print2(f"Config file not found at {CONFIG_FILE}. Nothing to update.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# update independent values
|
||||||
|
set_config_value("rewrite_urls", str(int(ENV.prettyurls)))
|
||||||
|
set_config_value("base_url", ENV.baseurl)
|
||||||
|
|
||||||
|
# update database values as a group
|
||||||
|
if check_db_variables():
|
||||||
|
set_config_value("dbtype", ENV.dbtype.value)
|
||||||
|
set_config_value("dbhost", ENV.dbhost)
|
||||||
|
set_config_value("dbport", ENV.dbport)
|
||||||
|
set_config_value("dbuser", ENV.dbuser)
|
||||||
|
set_config_value("dbpass", ENV.dbpass)
|
||||||
|
set_config_value("dbname", ENV.dbname)
|
||||||
|
set_config_value("tblpfx", ENV.tblpfx)
|
||||||
|
|
||||||
|
# update databases verification values
|
||||||
|
if ENV.dbtype == DBType.mysql and all(
|
||||||
|
v is not None for v in [ENV.dbkey, ENV.dbcert, ENV.dbca]
|
||||||
|
):
|
||||||
|
set_config_value("dbkey", ENV.dbkey)
|
||||||
|
set_config_value("dbcert", ENV.dbcert)
|
||||||
|
set_config_value("dbca", ENV.dbca)
|
||||||
|
set_config_value("dbverify", str(int(ENV.dbverify)))
|
||||||
|
|
||||||
|
|
||||||
|
def https() -> None:
|
||||||
|
"""
|
||||||
|
Configure enabled Apache sites
|
||||||
|
"""
|
||||||
|
print2("Configuring HTTPS")
|
||||||
|
|
||||||
|
# no https
|
||||||
|
if not ENV.https:
|
||||||
|
print2("Removing HTTPS")
|
||||||
|
enable_apache_site(["webtrees"])
|
||||||
|
# https with redirect
|
||||||
|
elif ENV.httpsredirect:
|
||||||
|
print2("Adding HTTPS, with HTTPS redirect")
|
||||||
|
enable_apache_site(["webtrees-ssl", "webtrees-redir"])
|
||||||
|
# https no redirect
|
||||||
|
else:
|
||||||
|
print2("Adding HTTPS, removing HTTPS redirect")
|
||||||
|
enable_apache_site(["webtrees", "webtrees-ssl"])
|
||||||
|
|
||||||
|
|
||||||
|
def htaccess() -> None:
|
||||||
|
"""
|
||||||
|
Recreate .htaccess file if it ever deletes itself in the /data/ directory
|
||||||
|
"""
|
||||||
|
src_htaccess_file = os.path.join("/bak", "webtrees", "data", ".htaccess")
|
||||||
|
htaccess_file = os.path.join(DATA_DIR, ".htaccess")
|
||||||
|
|
||||||
|
if not os.path.isfile(src_htaccess_file):
|
||||||
|
# skip if the backup file does not exist
|
||||||
|
print2(
|
||||||
|
f"WARNING: {src_htaccess_file} does not exist. Skipping {htaccess_file} integrity check"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# https://github.com/NathanVaughn/webtrees-docker/issues/188
|
||||||
|
# In the event the htaccess file in the data directory is different
|
||||||
|
# from what it's supposed to be, replace it.
|
||||||
|
|
||||||
|
if os.path.isfile(htaccess_file) and filecmp.cmp(src_htaccess_file, htaccess_file):
|
||||||
|
print2(f"{htaccess_file} integrity check succeeded")
|
||||||
|
return
|
||||||
|
|
||||||
|
print2(f"WARNING: {htaccess_file} does not exist or does not match master copy")
|
||||||
|
shutil.copyfile(src_htaccess_file, htaccess_file)
|
||||||
|
print2(f"Updated {htaccess_file}")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
# first, set up permissions
|
||||||
|
perms()
|
||||||
|
# create php config
|
||||||
|
php_ini()
|
||||||
|
# run the setup wizard if the config file doesn't exist
|
||||||
|
setup_wizard()
|
||||||
|
# update the config file
|
||||||
|
update_config_file()
|
||||||
|
# configure https
|
||||||
|
https()
|
||||||
|
# make sure .htaccess exists
|
||||||
|
htaccess()
|
||||||
|
# set up permissions again
|
||||||
|
perms()
|
||||||
|
|
||||||
|
print2("Starting Apache")
|
||||||
|
subprocess.run(["apache2-foreground"], stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# https://github.com/fisharebest/webtrees/blob/7f1eef18821463166c14403c3d0e0aa3ba7a4204/app/Http/Middleware/BadBotBlocker.php#L335
|
||||||
|
# https://github.com/fisharebest/webtrees/blob/7f1eef18821463166c14403c3d0e0aa3ba7a4204/app/Http/Middleware/BadBotBlocker.php#L313-L318
|
||||||
|
# https://github.com/NathanVaughn/webtrees-docker/issues/164
|
||||||
|
|
||||||
|
if ls /etc/apache2/sites-enabled | grep -q 'ssl';
|
||||||
|
then
|
||||||
|
curl --verbose --silent --insecure --fail --cookie-jar /tmp/cookie.txt --cookie "x=y" --cookie /tmp/cookie.txt --user-agent "Chrome/" https://127.0.0.1:443/ || exit 1
|
||||||
|
else
|
||||||
|
curl --verbose --silent --fail --cookie-jar /tmp/cookie.txt --cookie "x=y" --cookie /tmp/cookie.txt --user-agent "Chrome/" http://127.0.0.1:80/ || exit 1
|
||||||
|
fi
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
diff --git "a/.\\UpgradeService.php" "b/.\\UpgradeServicePatched.php"
|
||||||
|
index b69bea0..c3e682c 100644
|
||||||
|
--- "a/.\\UpgradeService.php"
|
||||||
|
+++ "b/.\\UpgradeServicePatched.php"
|
||||||
|
@@ -339,34 +339,6 @@ class UpgradeService
|
||||||
|
*/
|
||||||
|
private function fetchLatestVersion(bool $force): string
|
||||||
|
{
|
||||||
|
- $last_update_timestamp = (int) Site::getPreference('LATEST_WT_VERSION_TIMESTAMP');
|
||||||
|
-
|
||||||
|
- $current_timestamp = time();
|
||||||
|
-
|
||||||
|
- if ($force || $last_update_timestamp < $current_timestamp - self::CHECK_FOR_UPDATE_INTERVAL) {
|
||||||
|
- try {
|
||||||
|
- $client = new Client([
|
||||||
|
- 'timeout' => self::HTTP_TIMEOUT,
|
||||||
|
- ]);
|
||||||
|
-
|
||||||
|
- $response = $client->get(self::UPDATE_URL, [
|
||||||
|
- 'query' => $this->serverParameters(),
|
||||||
|
- ]);
|
||||||
|
-
|
||||||
|
- if ($response->getStatusCode() === StatusCodeInterface::STATUS_OK) {
|
||||||
|
- Site::setPreference('LATEST_WT_VERSION', $response->getBody()->getContents());
|
||||||
|
- Site::setPreference('LATEST_WT_VERSION_TIMESTAMP', (string) $current_timestamp);
|
||||||
|
- Site::setPreference('LATEST_WT_VERSION_ERROR', '');
|
||||||
|
- } else {
|
||||||
|
- Site::setPreference('LATEST_WT_VERSION_ERROR', 'HTTP' . $response->getStatusCode());
|
||||||
|
- }
|
||||||
|
- } catch (GuzzleException $ex) {
|
||||||
|
- // Can't connect to the server?
|
||||||
|
- // Use the existing information about latest versions.
|
||||||
|
- Site::setPreference('LATEST_WT_VERSION_ERROR', $ex->getMessage());
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
return Site::getPreference('LATEST_WT_VERSION');
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
diff --git "a/.\\UpgradeService.php" "b/.\\UpgradeServicePatched.php"
|
||||||
|
index 6bb3504..a630dfb 100644
|
||||||
|
--- "a/.\\UpgradeService.php"
|
||||||
|
+++ "b/.\\UpgradeServicePatched.php"
|
||||||
|
@@ -337,34 +337,6 @@ class UpgradeService
|
||||||
|
*/
|
||||||
|
private function fetchLatestVersion(bool $force): string
|
||||||
|
{
|
||||||
|
- $last_update_timestamp = (int) Site::getPreference('LATEST_WT_VERSION_TIMESTAMP');
|
||||||
|
-
|
||||||
|
- $current_timestamp = time();
|
||||||
|
-
|
||||||
|
- if ($force || $last_update_timestamp < $current_timestamp - self::CHECK_FOR_UPDATE_INTERVAL) {
|
||||||
|
- Site::setPreference('LATEST_WT_VERSION_TIMESTAMP', (string) $current_timestamp);
|
||||||
|
-
|
||||||
|
- try {
|
||||||
|
- $client = new Client([
|
||||||
|
- 'timeout' => self::HTTP_TIMEOUT,
|
||||||
|
- ]);
|
||||||
|
-
|
||||||
|
- $response = $client->get(self::UPDATE_URL, [
|
||||||
|
- 'query' => $this->serverParameters(),
|
||||||
|
- ]);
|
||||||
|
-
|
||||||
|
- if ($response->getStatusCode() === StatusCodeInterface::STATUS_OK) {
|
||||||
|
- Site::setPreference('LATEST_WT_VERSION', $response->getBody()->getContents());
|
||||||
|
- Site::setPreference('LATEST_WT_VERSION_ERROR', '');
|
||||||
|
- } else {
|
||||||
|
- Site::setPreference('LATEST_WT_VERSION_ERROR', 'HTTP' . $response->getStatusCode());
|
||||||
|
- }
|
||||||
|
- } catch (GuzzleException $ex) {
|
||||||
|
- // Can't connect to the server?
|
||||||
|
- // Use the existing information about latest versions.
|
||||||
|
- Site::setPreference('LATEST_WT_VERSION_ERROR', $ex->getMessage());
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
|
||||||
|
return Site::getPreference('LATEST_WT_VERSION');
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user