Add Windows test-server setup behind nginx
- docker-compose.server.yml: app on APP_BIND for an existing nginx proxy, QLO_SSL passed to the container - docker/setup-testserver.ps1: one-shot setup for Docker Desktop/WSL (random .env secrets, CLI install that never reinstalls over an existing shop, German texts, optional demo content, hardening) - docker/harden-test-server.php: delete demo customer pub@qloapps.com, disable webservice API keys, set SSL mode, deny module log folders - docker/nginx-qloapps.conf: HTTPS + basic auth + X-Forwarded-Proto - apache: deny /modules/*/logs/ (qlocmconnector logs API secrets) - docker/TESTSERVER.md: Windows Server guide (German) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,3 +8,12 @@ DB_ROOT_PASSWORD=qlo_local_root
|
|||||||
# Local admin created by the CLI installer (test values only)
|
# Local admin created by the CLI installer (test values only)
|
||||||
ADMIN_EMAIL=admin@hotel.test
|
ADMIN_EMAIL=admin@hotel.test
|
||||||
ADMIN_PASSWORD=change-me
|
ADMIN_PASSWORD=change-me
|
||||||
|
|
||||||
|
# Test server only (docker-compose.server.yml)
|
||||||
|
# Public host name as nginx serves it, without https:// (QloApps stores it in the DB)
|
||||||
|
SHOP_DOMAIN=test.example.de
|
||||||
|
# Address:port the app listens on for nginx. Use the LAN IP of the Windows server
|
||||||
|
# if nginx runs on another machine, or 127.0.0.1:8080 if nginx runs on the same host.
|
||||||
|
APP_BIND=0.0.0.0:8080
|
||||||
|
# 1 when nginx serves HTTPS
|
||||||
|
QLO_SSL=1
|
||||||
|
|||||||
16
docker-compose.server.yml
Normal file
16
docker-compose.server.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Test-server overlay (Windows Server + Docker Desktop, or Docker in WSL).
|
||||||
|
# Your existing nginx reverse proxy terminates HTTPS and asks for a password;
|
||||||
|
# the app only listens on APP_BIND (default: all interfaces, port 8080).
|
||||||
|
# docker compose -f docker-compose.yml -f docker-compose.server.yml up -d --build
|
||||||
|
# See docker/TESTSERVER.md and docker/nginx-qloapps.conf.
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
ports: !override
|
||||||
|
- "${APP_BIND:-0.0.0.0:8080}:80"
|
||||||
|
environment:
|
||||||
|
QLO_SSL: ${QLO_SSL:-1}
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
db:
|
||||||
|
restart: unless-stopped
|
||||||
87
docker/TESTSERVER.md
Normal file
87
docker/TESTSERVER.md
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
# Testserver auf Windows Server
|
||||||
|
|
||||||
|
Shop mit Wasserrad-Design auf einem Windows Server, erreichbar über den vorhandenen nginx-Reverse-Proxy.
|
||||||
|
nginx übernimmt HTTPS und schützt die ganze Seite mit einem Passwort.
|
||||||
|
|
||||||
|
```
|
||||||
|
Browser ──HTTPS──> nginx (Zertifikat, Passwort) ──HTTP──> Windows Server :8080 ──> Docker: app (Apache/PHP) + db (MariaDB)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 1. Docker auf dem Windows Server
|
||||||
|
|
||||||
|
**Variante A: Docker Desktop** (bevorzugt)
|
||||||
|
|
||||||
|
1. WSL 2 aktivieren (PowerShell als Administrator), danach neu starten:
|
||||||
|
```powershell
|
||||||
|
wsl --install --no-distribution
|
||||||
|
```
|
||||||
|
2. Docker Desktop installieren, Einstellung „Use the WSL 2 based engine“ aktiv lassen.
|
||||||
|
3. Unter *Settings → General* „Start Docker Desktop when you sign in“ aktivieren.
|
||||||
|
|
||||||
|
Hinweis: Docker Desktop ist offiziell für Windows 10/11 gedacht; auf Windows Server 2022/2025 läuft es mit WSL 2 in der Regel,
|
||||||
|
wird aber von Docker nicht unterstützt. Docker Desktop startet erst, wenn sich ein Benutzer anmeldet.
|
||||||
|
Ab 250 Mitarbeitenden oder 10 Mio. $ Umsatz braucht Docker Desktop eine kostenpflichtige Lizenz.
|
||||||
|
|
||||||
|
**Variante B: Docker Engine direkt in WSL** (falls Docker Desktop nicht startet)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
wsl --install -d Ubuntu-24.04
|
||||||
|
```
|
||||||
|
In Ubuntu: `curl -fsSL https://get.docker.com | sh`, dann die Schritte unten in Ubuntu mit `docker compose …` ausführen
|
||||||
|
(statt des PowerShell-Skripts die Befehle aus `docker-compose.yml` nutzen).
|
||||||
|
|
||||||
|
## 2. Code holen
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
git clone -b hotel/no-telemetry-docker https://gitea.hephex.de/Daniel/hotel.git C:\hotel
|
||||||
|
cd C:\hotel
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Einrichten (ein Befehl)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell -ExecutionPolicy Bypass -File docker\setup-testserver.ps1 -ShopDomain test.example.de
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Skript
|
||||||
|
- legt `.env` mit zufälligen Passwörtern an (Admin-Login steht dort: `ADMIN_EMAIL` / `ADMIN_PASSWORD`),
|
||||||
|
- baut und startet die Container,
|
||||||
|
- installiert QloApps auf Deutsch (überspringt das, wenn schon installiert, also kein versehentliches Löschen),
|
||||||
|
- ergänzt fehlende deutsche Texte, lädt die Wasserrad-Demo (`-NoDemo` zum Weglassen),
|
||||||
|
- räumt auf: Demo-Kunde gelöscht, API-Schlüssel abgeschaltet, Log-Ordner gesperrt.
|
||||||
|
|
||||||
|
Ohne Zertifikat im LAN: `-NoSsl`.
|
||||||
|
|
||||||
|
Port und Adresse der App stehen in `.env` unter `APP_BIND` (Standard `0.0.0.0:8080`).
|
||||||
|
Läuft nginx auf demselben Server: `APP_BIND=127.0.0.1:8080`. Sonst die Windows-Firewall für Port 8080 nur für die IP des nginx-Servers öffnen:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
New-NetFirewallRule -DisplayName "QloApps from nginx" -Direction Inbound -Protocol TCP -LocalPort 8080 -RemoteAddress <IP-des-nginx> -Action Allow
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. nginx
|
||||||
|
|
||||||
|
Vorlage: [`nginx-qloapps.conf`](nginx-qloapps.conf). Domain, Zertifikat und `APP_HOST` anpassen, Passwortdatei anlegen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
htpasswd -c /etc/nginx/.htpasswd-hotel tester
|
||||||
|
nginx -t && systemctl reload nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
Wichtig ist `proxy_set_header X-Forwarded-Proto $scheme;` – daran erkennt QloApps HTTPS. Ohne die Zeile gibt es Umleitungsschleifen.
|
||||||
|
|
||||||
|
## Betrieb
|
||||||
|
|
||||||
|
| Aufgabe | Befehl (im Repo-Ordner) |
|
||||||
|
|---|---|
|
||||||
|
| Status | `docker compose -f docker-compose.yml -f docker-compose.server.yml ps` |
|
||||||
|
| Logs | `docker compose -f docker-compose.yml -f docker-compose.server.yml logs -f app` |
|
||||||
|
| Update | `git pull`, dann das Setup-Skript erneut ausführen |
|
||||||
|
| Stoppen | `docker compose -f docker-compose.yml -f docker-compose.server.yml down` (Daten bleiben im Volume `db_data`) |
|
||||||
|
| Backup DB | `docker compose exec db mariadb-dump -uroot -p<DB_ROOT_PASSWORD> qloapps > backup.sql` |
|
||||||
|
|
||||||
|
## Vor echtem Betrieb
|
||||||
|
|
||||||
|
Der Testserver ist für Design- und Ablauftests gedacht. Für echte Buchungen fehlen noch: eigene Rechtstexte
|
||||||
|
(Impressum, Datenschutz, AGB), E-Mail-Versand (SMTP), Zahlungsart, regelmäßige Backups und QloApps-Updates
|
||||||
|
(der automatische Update-Hinweis ist abgeschaltet).
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
# The repo is bind-mounted as web root: never serve dotfiles (.env, .git, ...) or docker files.
|
# The repo is bind-mounted as web root: never serve dotfiles (.env, .git, ...) or docker files.
|
||||||
RedirectMatch 404 "/\.(?!well-known/)"
|
RedirectMatch 404 "/\.(?!well-known/)"
|
||||||
RedirectMatch 404 "^/(docker/|docker-compose\.yml)"
|
RedirectMatch 404 "^/(docker/|docker-compose\.yml)"
|
||||||
|
# Module log folders (qlocmconnector logs full API requests incl. client_secret)
|
||||||
|
RedirectMatch 404 "^/modules/[^/]+/logs/"
|
||||||
# Installation is done via CLI (see docker-compose.yml); a web re-install would wipe the database.
|
# Installation is done via CLI (see docker-compose.yml); a web re-install would wipe the database.
|
||||||
RedirectMatch 404 "^/install-dev"
|
RedirectMatch 404 "^/install-dev"
|
||||||
ServerTokens Prod
|
ServerTokens Prod
|
||||||
|
|||||||
67
docker/harden-test-server.php
Normal file
67
docker/harden-test-server.php
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Clean up demo leftovers before a QloApps install is reachable from the network.
|
||||||
|
* Idempotent; safe to run again.
|
||||||
|
* docker compose exec -u www-data app php docker/harden-test-server.php
|
||||||
|
*
|
||||||
|
* - deletes the demo customer pub@qloapps.com (its password is public in the QloApps fixtures)
|
||||||
|
* - disables the webservice API and every API key (the installer creates an active key)
|
||||||
|
* - makes sure developer mode is off and the shop runs in the expected SSL mode
|
||||||
|
* - protects module log folders (qlocmconnector logs API secrets) with a deny file
|
||||||
|
* QLO_SSL=1 in the environment (docker-compose.server.yml) switches the shop to HTTPS.
|
||||||
|
*/
|
||||||
|
if (PHP_SAPI !== 'cli') {
|
||||||
|
exit("CLI only\n");
|
||||||
|
}
|
||||||
|
define('_PS_ADMIN_DIR_', __DIR__);
|
||||||
|
require __DIR__.'/../config/config.inc.php';
|
||||||
|
|
||||||
|
$ok = function ($msg) { echo ' [ok] '.$msg.PHP_EOL; };
|
||||||
|
$warn = function ($msg) { echo ' [!!] '.$msg.PHP_EOL; };
|
||||||
|
|
||||||
|
echo "Demo customer\n";
|
||||||
|
$ids = Db::getInstance()->executeS('SELECT id_customer FROM '._DB_PREFIX_."customer WHERE email = 'pub@qloapps.com'");
|
||||||
|
foreach ($ids as $row) {
|
||||||
|
$c = new Customer((int)$row['id_customer']);
|
||||||
|
$c->delete();
|
||||||
|
}
|
||||||
|
$ok(count($ids) ? 'pub@qloapps.com deleted' : 'no demo customer present');
|
||||||
|
|
||||||
|
echo "Webservice API\n";
|
||||||
|
Configuration::updateValue('PS_WEBSERVICE', 0);
|
||||||
|
Db::getInstance()->update('webservice_account', array('active' => 0));
|
||||||
|
$ok('API disabled, all keys deactivated');
|
||||||
|
|
||||||
|
echo "Developer mode\n";
|
||||||
|
if (_PS_MODE_DEV_) {
|
||||||
|
$warn('_PS_MODE_DEV_ is on: set it to false in config/defines.inc.php');
|
||||||
|
} else {
|
||||||
|
$ok('off');
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "SSL\n";
|
||||||
|
$ssl = getenv('QLO_SSL') === '1';
|
||||||
|
Configuration::updateValue('PS_SSL_ENABLED', (int)$ssl);
|
||||||
|
Configuration::updateValue('PS_SSL_ENABLED_EVERYWHERE', (int)$ssl);
|
||||||
|
$ok($ssl ? 'HTTPS everywhere' : 'HTTP (LAN test, no certificate)');
|
||||||
|
|
||||||
|
echo "Module log folders\n";
|
||||||
|
$deny = "<IfModule mod_authz_core.c>\n Require all denied\n</IfModule>\n";
|
||||||
|
foreach (glob(_PS_MODULE_DIR_.'*', GLOB_ONLYDIR) as $dir) {
|
||||||
|
$logs = $dir.'/logs';
|
||||||
|
if (basename($dir) === 'qlocmconnector' && !is_dir($logs)) {
|
||||||
|
@mkdir($logs, 0755, true);
|
||||||
|
}
|
||||||
|
if (is_dir($logs) && !is_file($logs.'/.htaccess')) {
|
||||||
|
file_put_contents($logs.'/.htaccess', $deny);
|
||||||
|
$ok(basename($dir).'/logs protected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Admin accounts\n";
|
||||||
|
foreach (Db::getInstance()->executeS('SELECT email, active FROM '._DB_PREFIX_.'employee') as $e) {
|
||||||
|
echo ' - '.$e['email'].($e['active'] ? '' : ' (inactive)').PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tools::clearSmartyCache();
|
||||||
|
echo "Done.\n";
|
||||||
37
docker/nginx-qloapps.conf
Normal file
37
docker/nginx-qloapps.conf
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# nginx server block for the QloApps test server (HTTPS + password for the whole site).
|
||||||
|
# Replace test.example.de, the certificate paths and APP_HOST:8080 (the Windows server running Docker).
|
||||||
|
# Password file: htpasswd -c /etc/nginx/.htpasswd-hotel <user>
|
||||||
|
# (or: printf "user:$(openssl passwd -apr1)\n" > /etc/nginx/.htpasswd-hotel)
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name test.example.de;
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
http2 on;
|
||||||
|
server_name test.example.de;
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/test.example.de/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/test.example.de/privkey.pem;
|
||||||
|
|
||||||
|
# whole site (shop + /admin-dev) behind a password while testing
|
||||||
|
auth_basic "Hotel Test";
|
||||||
|
auth_basic_user_file /etc/nginx/.htpasswd-hotel;
|
||||||
|
|
||||||
|
client_max_body_size 20m; # image uploads in the back office
|
||||||
|
add_header X-Robots-Tag "noindex, nofollow" always;
|
||||||
|
add_header X-Content-Type-Options nosniff always;
|
||||||
|
add_header Referrer-Policy strict-origin-when-cross-origin always;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://APP_HOST:8080;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme; # QloApps uses this to detect HTTPS
|
||||||
|
proxy_read_timeout 300s; # installer / image regeneration
|
||||||
|
}
|
||||||
|
}
|
||||||
115
docker/setup-testserver.ps1
Normal file
115
docker/setup-testserver.ps1
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Sets up the QloApps test server on Windows (Docker Desktop or Docker in WSL).
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Run from the repository root in PowerShell:
|
||||||
|
powershell -ExecutionPolicy Bypass -File docker\setup-testserver.ps1 -ShopDomain test.example.de
|
||||||
|
|
||||||
|
- creates .env with random passwords (only if .env does not exist yet)
|
||||||
|
- builds and starts the containers (docker-compose.yml + docker-compose.server.yml)
|
||||||
|
- installs QloApps in German via CLI (skipped if config/settings.inc.php exists: no accidental wipe)
|
||||||
|
- fills German shop texts, optionally loads the Wasserrad demo content, then hardens the install
|
||||||
|
Re-running is safe: it never reinstalls over an existing shop.
|
||||||
|
|
||||||
|
.PARAMETER ShopDomain
|
||||||
|
Host name nginx serves the shop under, without https:// (e.g. test.example.de).
|
||||||
|
.PARAMETER NoSsl
|
||||||
|
nginx serves plain HTTP (LAN test without certificate).
|
||||||
|
.PARAMETER NoDemo
|
||||||
|
Skip the "Zum alten Wasserrad" demo content.
|
||||||
|
#>
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)][string]$ShopDomain,
|
||||||
|
[switch]$NoSsl,
|
||||||
|
[switch]$NoDemo
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$root = Split-Path -Parent $PSScriptRoot
|
||||||
|
Set-Location $root
|
||||||
|
$compose = @('compose', '-f', 'docker-compose.yml', '-f', 'docker-compose.server.yml')
|
||||||
|
|
||||||
|
function Step($text) { Write-Host "`n==> $text" -ForegroundColor Cyan }
|
||||||
|
function New-Secret([int]$bytes = 18) {
|
||||||
|
$b = New-Object byte[] $bytes
|
||||||
|
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($b)
|
||||||
|
return ([Convert]::ToBase64String($b) -replace '[^A-Za-z0-9]', '')
|
||||||
|
}
|
||||||
|
function Invoke-Docker {
|
||||||
|
& docker @args
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "docker $($args -join ' ') failed ($LASTEXITCODE)" }
|
||||||
|
}
|
||||||
|
function Read-EnvFile {
|
||||||
|
$h = @{}
|
||||||
|
foreach ($line in Get-Content .env) {
|
||||||
|
if ($line -match '^\s*([A-Z_]+)=(.*)$') { $h[$Matches[1]] = $Matches[2].Trim() }
|
||||||
|
}
|
||||||
|
return $h
|
||||||
|
}
|
||||||
|
|
||||||
|
Step 'Docker'
|
||||||
|
docker version --format '{{.Server.Version}}' | Out-Null
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw 'Docker is not running (start Docker Desktop or the Docker engine in WSL).' }
|
||||||
|
|
||||||
|
Step '.env'
|
||||||
|
$ssl = if ($NoSsl) { '0' } else { '1' }
|
||||||
|
if (-not (Test-Path .env)) {
|
||||||
|
$content = (Get-Content .env.example -Raw)
|
||||||
|
$content = $content -replace '(?m)^DB_PASSWORD=.*$', "DB_PASSWORD=$(New-Secret)"
|
||||||
|
$content = $content -replace '(?m)^DB_ROOT_PASSWORD=.*$', "DB_ROOT_PASSWORD=$(New-Secret)"
|
||||||
|
$content = $content -replace '(?m)^ADMIN_EMAIL=.*$', "ADMIN_EMAIL=admin@$ShopDomain"
|
||||||
|
$content = $content -replace '(?m)^ADMIN_PASSWORD=.*$', "ADMIN_PASSWORD=$(New-Secret 15)"
|
||||||
|
$content = $content -replace '(?m)^SHOP_DOMAIN=.*$', "SHOP_DOMAIN=$ShopDomain"
|
||||||
|
$content = $content -replace '(?m)^QLO_SSL=.*$', "QLO_SSL=$ssl"
|
||||||
|
[System.IO.File]::WriteAllText((Join-Path $root '.env'), $content)
|
||||||
|
Write-Host '.env created with random passwords (keep it private, it is git-ignored).'
|
||||||
|
} else {
|
||||||
|
Write-Host '.env exists, keeping its values.'
|
||||||
|
# older .env files lack the server keys: add them from the parameters
|
||||||
|
$existing = Get-Content .env -Raw
|
||||||
|
$add = @{ 'SHOP_DOMAIN' = $ShopDomain; 'APP_BIND' = '0.0.0.0:8080'; 'QLO_SSL' = $ssl }
|
||||||
|
foreach ($k in $add.Keys) {
|
||||||
|
if ($existing -notmatch "(?m)^$k=") {
|
||||||
|
Add-Content -Path .env -Value "$k=$($add[$k])"
|
||||||
|
Write-Host " added $k=$($add[$k])"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$envVars = Read-EnvFile
|
||||||
|
|
||||||
|
Step 'Build and start containers'
|
||||||
|
Invoke-Docker @compose up -d --build
|
||||||
|
|
||||||
|
Step 'File permissions for www-data'
|
||||||
|
Invoke-Docker @compose exec -T app sh -c 'cd /var/www/html && chown -R www-data:www-data img mails modules translations cache config upload download themes override log'
|
||||||
|
|
||||||
|
if (Test-Path config/settings.inc.php) {
|
||||||
|
Step 'QloApps already installed (config/settings.inc.php exists) - skipping install'
|
||||||
|
} else {
|
||||||
|
Step 'Install QloApps (German, newsletter off)'
|
||||||
|
Invoke-Docker @compose exec -T -u www-data app php install-dev/index_cli.php `
|
||||||
|
--language=de --country=de --timezone=Europe/Berlin `
|
||||||
|
"--domain=$($envVars['SHOP_DOMAIN'])" "--ssl=$($envVars['QLO_SSL'])" `
|
||||||
|
--db_server=db "--db_user=$($envVars['DB_USER'])" "--db_password=$($envVars['DB_PASSWORD'])" "--db_name=$($envVars['DB_NAME'])" `
|
||||||
|
"--name=Zum alten Wasserrad" --firstname=Admin --lastname=Hotel `
|
||||||
|
"--email=$($envVars['ADMIN_EMAIL'])" "--password=$($envVars['ADMIN_PASSWORD'])" `
|
||||||
|
--newsletter=0
|
||||||
|
}
|
||||||
|
|
||||||
|
Step 'German shop texts'
|
||||||
|
Invoke-Docker @compose exec -T -u www-data app php docker/apply-translations.php de
|
||||||
|
|
||||||
|
if (-not $NoDemo) {
|
||||||
|
Step 'Wasserrad demo content'
|
||||||
|
Invoke-Docker @compose exec -T -u www-data app php docker/seed-wasserrad-demo.php
|
||||||
|
}
|
||||||
|
|
||||||
|
Step 'Harden'
|
||||||
|
Invoke-Docker @compose exec -T -u www-data app php docker/harden-test-server.php
|
||||||
|
|
||||||
|
$scheme = if ($envVars['QLO_SSL'] -eq '1') { 'https' } else { 'http' }
|
||||||
|
Write-Host "`nDone." -ForegroundColor Green
|
||||||
|
Write-Host "Shop: $scheme`://$($envVars['SHOP_DOMAIN'])/"
|
||||||
|
Write-Host "Admin: $scheme`://$($envVars['SHOP_DOMAIN'])/admin-dev/ (login: ADMIN_EMAIL / ADMIN_PASSWORD in .env)"
|
||||||
|
Write-Host "App listens on $($envVars['APP_BIND']) - point nginx there (docker/nginx-qloapps.conf)."
|
||||||
Reference in New Issue
Block a user