initiatione
This commit is contained in:
commit
627caccdaf
9 changed files with 346 additions and 0 deletions
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/vendor/
|
||||
/node_modules/
|
||||
/.idea/
|
||||
/.vscode/
|
||||
/var/
|
||||
*.log
|
||||
98
README.md
Normal file
98
README.md
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
# SumediaStage
|
||||
|
||||
Shopware 6 Plugin zur Nutzung einer lizenzierten Installation unter einem URL-Unterverzeichnis (z. B. `/stage/`, `/local/`), ohne dass Plugin-Lizenzprüfungen oder die interne Shop-Identifikation fehlschlagen.
|
||||
|
||||
## Hintergrund
|
||||
|
||||
Shopware speichert die `APP_URL` zusammen mit der Shop-ID (`core.app.shopId`) in der Systemkonfiguration. Bei jedem Request vergleicht `ShopIdProvider` die aktuelle `APP_URL` mit dem gespeicherten Wert. Weichen Live (`https://example.com`) und Stage (`https://example.com/stage/`) ab, wirft Shopware eine `AppUrlChangeDetectedException` – Plugins melden daraufhin fehlende Lizenzen.
|
||||
|
||||
Das Plugin registriert einen `EnvironmentHelperTransformer`, der bei jedem internen `APP_URL`-Lesezugriff den Pfad-Anteil automatisch entfernt:
|
||||
|
||||
```
|
||||
https://www.example.com/stage/ → https://www.example.com
|
||||
https://www.example.com/local/ → https://www.example.com
|
||||
https://www.example.com → https://www.example.com (no-op)
|
||||
```
|
||||
|
||||
## Installation auf dem Stage-System
|
||||
|
||||
### Voraussetzungen
|
||||
|
||||
- Shopware 6.6.x
|
||||
- SSH-Zugang zum Stage-Server
|
||||
- `APP_ENV` muss `dev` oder `staging` sein – **nicht** `prod`
|
||||
- `APP_URL` in `.env.local` ist auf das Unterverzeichnis gesetzt, z. B.:
|
||||
```
|
||||
APP_URL=https://www.example.com/stage/
|
||||
```
|
||||
|
||||
> **Wichtig:** Das Plugin lässt sich in `APP_ENV=prod` nicht aktivieren und registriert den Transformer auch dann nicht, falls es doch installiert sein sollte. Es ist ausschliesslich fuer Staging- und lokale Umgebungen gedacht.
|
||||
|
||||
### 1. Plugin übertragen
|
||||
|
||||
Plugin-Verzeichnis auf den Stage-Server kopieren (z. B. per rsync oder git):
|
||||
|
||||
```bash
|
||||
rsync -av custom/plugins/SumediaStage/ user@stage-server:/var/www/shopware/custom/plugins/SumediaStage/
|
||||
```
|
||||
|
||||
Oder per git, wenn das Stage-System dasselbe Repository nutzt – dann ist das Plugin bereits vorhanden.
|
||||
|
||||
### 2. Plugin installieren und aktivieren
|
||||
|
||||
```bash
|
||||
php bin/console plugin:refresh
|
||||
php bin/console plugin:install --activate SumediaStage
|
||||
php bin/console cache:clear
|
||||
```
|
||||
|
||||
### 3. Konfiguration prüfen
|
||||
|
||||
Im Shopware-Admin unter **Einstellungen → Erweiterungen → SumediaStage**:
|
||||
|
||||
| Einstellung | Empfohlener Wert |
|
||||
|---|---|
|
||||
| Normalize APP_URL | aktiv (true) |
|
||||
| Base URL override | leer lassen |
|
||||
|
||||
### 4. Verifizieren
|
||||
|
||||
Nach der Installation sollte der Admin-Login funktionieren und keine Plugin-Lizenzfehler mehr erscheinen. Zur Kontrolle:
|
||||
|
||||
```bash
|
||||
php bin/console system:config:get core.app.shopId
|
||||
```
|
||||
|
||||
Der gespeicherte `app_url`-Wert sollte nun ohne den Unterverzeichnis-Pfad gespeichert werden.
|
||||
|
||||
## Deinstallation
|
||||
|
||||
```bash
|
||||
php bin/console plugin:uninstall SumediaStage
|
||||
php bin/console cache:clear
|
||||
```
|
||||
|
||||
## Konfigurationsoptionen
|
||||
|
||||
**Admin (Einstellungen → Erweiterungen → SumediaStage):**
|
||||
|
||||
| Schlüssel | Typ | Default | Beschreibung |
|
||||
|---|---|---|---|
|
||||
| `normalizeAppUrl` | bool | `true` | Transformer aktiv/inaktiv |
|
||||
| `baseUrl` | string | leer | Explizite Basis-URL statt Auto-Erkennung (z. B. hinter Proxy mit falschem Host-Header) |
|
||||
|
||||
**Umgebungsvariablen (`.env.local`):**
|
||||
|
||||
| Variable | Wert | Beschreibung |
|
||||
|---|---|---|
|
||||
| `SUMEDIA_STAGE_ENABLED` | `0` | Transformer deaktivieren, ohne das Plugin zu deinstallieren |
|
||||
| `APP_ENV` | `prod` | Transformer wird automatisch blockiert, Aktivierung wird verweigert |
|
||||
|
||||
Beispiel `.env.local` um das Plugin temporaer zu deaktivieren:
|
||||
```
|
||||
SUMEDIA_STAGE_ENABLED=0
|
||||
```
|
||||
|
||||
## Hinweis
|
||||
|
||||
Das Plugin muss auf **allen** Instanzen (live, stage, local) installiert und aktiviert sein. Auf Live-Instanzen, bei denen `APP_URL` keinen Pfad-Anteil enthält, ist der Transformer ein No-Op.
|
||||
23
composer.json
Normal file
23
composer.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "sumedia-webdesign/sumedia-stage",
|
||||
"description": "Normalizes APP_URL for staging environments served under a subdirectory",
|
||||
"type": "shopware-platform-plugin",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"shopware/core": "~6.6.0"
|
||||
},
|
||||
"extra": {
|
||||
"shopware-plugin-class": "SumediaStage\\SumediaStage",
|
||||
"plugin-icon": "src/Resources/config/plugin.png",
|
||||
"label": {
|
||||
"de-DE": "Sumedia Stage",
|
||||
"en-GB": "Sumedia Stage"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SumediaStage\\": "src/"
|
||||
}
|
||||
}
|
||||
}
|
||||
57
src/DevOps/AppUrlNormalizer.php
Normal file
57
src/DevOps/AppUrlNormalizer.php
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace SumediaStage\DevOps;
|
||||
|
||||
use Shopware\Core\DevOps\Environment\EnvironmentHelperTransformerData;
|
||||
use Shopware\Core\DevOps\Environment\EnvironmentHelperTransformerInterface;
|
||||
|
||||
/**
|
||||
* Normalizes APP_URL by stripping the path component.
|
||||
*
|
||||
* This allows a Shopware installation licensed for "https://example.com" to run
|
||||
* under a subdirectory like "https://example.com/stage/" without triggering
|
||||
* AppUrlChangeDetectedException or plugin license violations.
|
||||
*
|
||||
* Configuration is injected via configure() by the SystemConfigSubscriber.
|
||||
*/
|
||||
class AppUrlNormalizer implements EnvironmentHelperTransformerInterface
|
||||
{
|
||||
private static bool $enabled = true;
|
||||
|
||||
private static ?string $baseUrl = null;
|
||||
|
||||
public static function configure(bool $enabled, ?string $baseUrl): void
|
||||
{
|
||||
self::$enabled = $enabled;
|
||||
self::$baseUrl = $baseUrl ?: null;
|
||||
}
|
||||
|
||||
public static function transform(EnvironmentHelperTransformerData $data): void
|
||||
{
|
||||
if ($data->getKey() !== 'APP_URL' || !self::$enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self::$baseUrl !== null) {
|
||||
$data->setValue(rtrim(self::$baseUrl, '/'));
|
||||
return;
|
||||
}
|
||||
|
||||
$url = $data->getValue();
|
||||
if (!\is_string($url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parsed = parse_url($url);
|
||||
if ($parsed === false || empty($parsed['host'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$normalized = ($parsed['scheme'] ?? 'https') . '://' . $parsed['host'];
|
||||
if (isset($parsed['port'])) {
|
||||
$normalized .= ':' . $parsed['port'];
|
||||
}
|
||||
|
||||
$data->setValue($normalized);
|
||||
}
|
||||
}
|
||||
62
src/Resources/config/config.xml
Normal file
62
src/Resources/config/config.xml
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/shopware/shopware/trunk/src/Core/System/SystemConfig/Schema/config.xsd">
|
||||
|
||||
<card>
|
||||
<title>Sumedia Stage</title>
|
||||
<title lang="de-DE">Sumedia Stage</title>
|
||||
|
||||
<input-field type="bool">
|
||||
<name>normalizeAppUrl</name>
|
||||
<label>Normalize APP_URL (strip subdirectory)</label>
|
||||
<label lang="de-DE">APP_URL normalisieren (Unterverzeichnis entfernen)</label>
|
||||
<helpText>
|
||||
TECHNICAL BACKGROUND: Shopware stores APP_URL alongside the Shop ID (core.app.shopId) and compares it on every request via ShopIdProvider. If live uses "https://example.com" but stage uses "https://example.com/stage/", Shopware throws AppUrlChangeDetectedException and plugins report license errors.
|
||||
|
||||
HOW IT WORKS: This plugin registers an EnvironmentHelperTransformer that intercepts every internal APP_URL read and strips the path component before Shopware sees it. Both live and stage then present the same base URL to Shopware.
|
||||
|
||||
Example: https://example.com/stage/ becomes https://example.com
|
||||
|
||||
Safe to enable on live instances – if APP_URL has no path, the transformer is a no-op.
|
||||
|
||||
Changes take effect after cache:clear.
|
||||
</helpText>
|
||||
<helpText lang="de-DE">
|
||||
TECHNISCHER HINTERGRUND: Shopware speichert APP_URL zusammen mit der Shop-ID (core.app.shopId) und vergleicht sie bei jedem Request via ShopIdProvider. Weichen Live ("https://example.com") und Stage ("https://example.com/stage/") ab, wirft Shopware eine AppUrlChangeDetectedException und Plugins melden Lizenzfehler.
|
||||
|
||||
FUNKTIONSWEISE: Das Plugin registriert einen EnvironmentHelperTransformer, der jeden internen APP_URL-Lesezugriff abfängt und den Pfad-Anteil entfernt, bevor Shopware ihn verarbeitet. Damit sehen Live und Stage dieselbe Basis-URL.
|
||||
|
||||
Beispiel: https://example.com/stage/ wird zu https://example.com
|
||||
|
||||
Auf Live-Instanzen ohne Pfad ist der Transformer wirkungslos.
|
||||
|
||||
Aenderungen greifen nach cache:clear.
|
||||
</helpText>
|
||||
<defaultValue>true</defaultValue>
|
||||
</input-field>
|
||||
|
||||
<input-field type="text">
|
||||
<name>baseUrl</name>
|
||||
<label>Base URL override (optional)</label>
|
||||
<label lang="de-DE">Basis-URL manuell setzen (optional)</label>
|
||||
<helpText>
|
||||
Leave empty for automatic detection – the transformer extracts scheme and host from APP_URL automatically.
|
||||
|
||||
Set this only if auto-detection produces a wrong result, e.g. behind a reverse proxy that alters the Host header.
|
||||
|
||||
Format: https://www.example.com (no trailing slash)
|
||||
</helpText>
|
||||
<helpText lang="de-DE">
|
||||
Leer lassen fuer automatische Erkennung – der Transformer extrahiert Scheme und Host automatisch aus APP_URL.
|
||||
|
||||
Nur setzen, wenn die automatische Erkennung ein falsches Ergebnis liefert, z. B. hinter einem Reverse-Proxy, der den Host-Header veraendert.
|
||||
|
||||
Format: https://www.example.com (kein abschliessender Slash)
|
||||
</helpText>
|
||||
<defaultValue></defaultValue>
|
||||
</input-field>
|
||||
|
||||
</card>
|
||||
|
||||
</config>
|
||||
BIN
src/Resources/config/plugin.png
Normal file
BIN
src/Resources/config/plugin.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
16
src/Resources/config/services.xml
Normal file
16
src/Resources/config/services.xml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<services>
|
||||
|
||||
<service id="SumediaStage\Subscriber\SystemConfigSubscriber">
|
||||
<argument type="service" id="Shopware\Core\System\SystemConfig\SystemConfigService" />
|
||||
<tag name="kernel.event_subscriber" />
|
||||
</service>
|
||||
|
||||
</services>
|
||||
|
||||
</container>
|
||||
39
src/Subscriber/SystemConfigSubscriber.php
Normal file
39
src/Subscriber/SystemConfigSubscriber.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace SumediaStage\Subscriber;
|
||||
|
||||
use Shopware\Core\System\SystemConfig\SystemConfigService;
|
||||
use SumediaStage\DevOps\AppUrlNormalizer;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
class SystemConfigSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
private bool $configured = false;
|
||||
|
||||
public function __construct(private readonly SystemConfigService $systemConfigService)
|
||||
{
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
KernelEvents::REQUEST => ['onKernelRequest', 512],
|
||||
];
|
||||
}
|
||||
|
||||
public function onKernelRequest(RequestEvent $event): void
|
||||
{
|
||||
if ($this->configured) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->configured = true;
|
||||
|
||||
$enabled = (bool) ($this->systemConfigService->get('SumediaStage.config.normalizeAppUrl') ?? true);
|
||||
$baseUrl = (string) ($this->systemConfigService->get('SumediaStage.config.baseUrl') ?? '');
|
||||
|
||||
AppUrlNormalizer::configure($enabled, $baseUrl ?: null);
|
||||
}
|
||||
}
|
||||
45
src/SumediaStage.php
Normal file
45
src/SumediaStage.php
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace SumediaStage;
|
||||
|
||||
use Shopware\Core\DevOps\Environment\EnvironmentHelper;
|
||||
use Shopware\Core\Framework\Plugin;
|
||||
use Shopware\Core\Framework\Plugin\Context\ActivateContext;
|
||||
use SumediaStage\DevOps\AppUrlNormalizer;
|
||||
|
||||
class SumediaStage extends Plugin
|
||||
{
|
||||
public function boot(): void
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
if (!$this->isAllowed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EnvironmentHelper::addTransformer(AppUrlNormalizer::class);
|
||||
}
|
||||
|
||||
public function activate(ActivateContext $activateContext): void
|
||||
{
|
||||
if ((string) EnvironmentHelper::getVariable('APP_ENV') === 'prod') {
|
||||
throw new \RuntimeException(
|
||||
'SumediaStage must not be activated in production (APP_ENV=prod). '
|
||||
. 'This plugin is intended for staging and local environments only.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function isAllowed(): bool
|
||||
{
|
||||
if ((string) EnvironmentHelper::getVariable('APP_ENV') === 'prod') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((string) EnvironmentHelper::getVariable('SUMEDIA_STAGE_ENABLED', '1') === '0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue