mirror of
https://git.hexahost.dev/smueller/HexaHost-Frontend.git
synced 2026-06-02 10:08:44 +00:00
Compare commits
28 Commits
ci
...
b113bdeaa2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b113bdeaa2 | ||
|
|
5d2be60dfa | ||
|
|
62d0076799 | ||
|
|
e920fdfc8e | ||
|
|
5d953fda7b | ||
|
|
6ca4786955 | ||
|
|
b9bd339607 | ||
|
|
b893272d64 | ||
|
|
3dd707ab93 | ||
|
|
cc1a48943a | ||
|
|
dfc781f3ed | ||
|
|
d0e5baa443 | ||
|
|
8afba16905 | ||
|
|
96a5977283 | ||
|
|
ec8686761c | ||
|
|
d3da589a1d | ||
|
|
b6e268855e | ||
|
|
d02377c735 | ||
|
|
d62d6b576d | ||
|
|
2c0138f55d | ||
|
|
2074707c9d | ||
|
|
55f9fdd957 | ||
|
|
ab81d1c49f | ||
|
|
b40ad53d9c | ||
|
|
e5402189ea | ||
|
|
e544720900 | ||
| a5bba86db0 | |||
| d34dbbb079 |
@@ -2,7 +2,8 @@
|
||||
/**
|
||||
* HexaHost.de Produkt-Konfiguration
|
||||
*
|
||||
* Hier können Sie alle Preise und Produktinformationen zentral verwalten.
|
||||
* Hier können Sie alle Preise, Shop-Links und Produktinformationen zentral verwalten.
|
||||
* Pro Paket: shop_url (WHMCS/Warenkorb-Link, z. B. https://shop.hexahost.de/cart.php?a=add&pid=123)
|
||||
* Nach Änderungen: npm run build && npm run deploy
|
||||
*
|
||||
* Verwendung in PHP-Seiten:
|
||||
@@ -30,6 +31,7 @@ $PRODUCTS['vpc'] = [
|
||||
'starter' => [
|
||||
'name' => 'VPC Starter',
|
||||
'price' => '4,99',
|
||||
'shop_url' => '',
|
||||
'featured' => false,
|
||||
'specs' => [
|
||||
['label' => 'CPU Kerne', 'value' => '1 vCore'],
|
||||
@@ -49,6 +51,7 @@ $PRODUCTS['vpc'] = [
|
||||
'business' => [
|
||||
'name' => 'VPC Business',
|
||||
'price' => '9,99',
|
||||
'shop_url' => '',
|
||||
'featured' => true,
|
||||
'specs' => [
|
||||
['label' => 'CPU Kerne', 'value' => '2 vCores'],
|
||||
@@ -69,6 +72,7 @@ $PRODUCTS['vpc'] = [
|
||||
'professional' => [
|
||||
'name' => 'VPC Professional',
|
||||
'price' => '19,99',
|
||||
'shop_url' => '',
|
||||
'featured' => false,
|
||||
'specs' => [
|
||||
['label' => 'CPU Kerne', 'value' => '4 vCores'],
|
||||
@@ -90,6 +94,7 @@ $PRODUCTS['vpc'] = [
|
||||
'enterprise' => [
|
||||
'name' => 'VPC Enterprise',
|
||||
'price' => '39,99',
|
||||
'shop_url' => '',
|
||||
'featured' => false,
|
||||
'specs' => [
|
||||
['label' => 'CPU Kerne', 'value' => '8 vCores'],
|
||||
@@ -132,6 +137,7 @@ $PRODUCTS['vps'] = [
|
||||
'starter' => [
|
||||
'name' => 'VPS Starter',
|
||||
'price' => '9,99',
|
||||
'shop_url' => '',
|
||||
'featured' => false,
|
||||
'specs' => [
|
||||
['label' => 'CPU Kerne', 'value' => '1 vCore'],
|
||||
@@ -151,6 +157,7 @@ $PRODUCTS['vps'] = [
|
||||
'business' => [
|
||||
'name' => 'VPS Business',
|
||||
'price' => '19,99',
|
||||
'shop_url' => '',
|
||||
'featured' => true,
|
||||
'specs' => [
|
||||
['label' => 'CPU Kerne', 'value' => '2 vCores'],
|
||||
@@ -171,6 +178,7 @@ $PRODUCTS['vps'] = [
|
||||
'professional' => [
|
||||
'name' => 'VPS Professional',
|
||||
'price' => '39,99',
|
||||
'shop_url' => '',
|
||||
'featured' => false,
|
||||
'specs' => [
|
||||
['label' => 'CPU Kerne', 'value' => '4 vCores'],
|
||||
@@ -192,6 +200,7 @@ $PRODUCTS['vps'] = [
|
||||
'enterprise' => [
|
||||
'name' => 'VPS Enterprise',
|
||||
'price' => '79,99',
|
||||
'shop_url' => '',
|
||||
'featured' => false,
|
||||
'specs' => [
|
||||
['label' => 'CPU Kerne', 'value' => '8 vCores'],
|
||||
@@ -234,6 +243,7 @@ $PRODUCTS['mail-gateway'] = [
|
||||
'starter' => [
|
||||
'name' => 'Mail Starter',
|
||||
'price' => '4,99',
|
||||
'shop_url' => '',
|
||||
'featured' => false,
|
||||
'specs' => [
|
||||
['label' => 'Postfächer', 'value' => '5'],
|
||||
@@ -252,6 +262,7 @@ $PRODUCTS['mail-gateway'] = [
|
||||
'business' => [
|
||||
'name' => 'Mail Business',
|
||||
'price' => '14,99',
|
||||
'shop_url' => '',
|
||||
'featured' => true,
|
||||
'specs' => [
|
||||
['label' => 'Postfächer', 'value' => '25'],
|
||||
@@ -272,6 +283,7 @@ $PRODUCTS['mail-gateway'] = [
|
||||
'professional' => [
|
||||
'name' => 'Mail Professional',
|
||||
'price' => '29,99',
|
||||
'shop_url' => '',
|
||||
'featured' => false,
|
||||
'specs' => [
|
||||
['label' => 'Postfächer', 'value' => '100'],
|
||||
@@ -293,6 +305,7 @@ $PRODUCTS['mail-gateway'] = [
|
||||
'enterprise' => [
|
||||
'name' => 'Mail Enterprise',
|
||||
'price' => '59,99',
|
||||
'shop_url' => '',
|
||||
'featured' => false,
|
||||
'specs' => [
|
||||
['label' => 'Postfächer', 'value' => 'Unbegrenzt'],
|
||||
@@ -336,6 +349,7 @@ $PRODUCTS['webhosting'] = [
|
||||
'starter' => [
|
||||
'name' => 'Webhosting Starter',
|
||||
'price' => '4,99',
|
||||
'shop_url' => 'https://shop.hexahost.de/store/webserver/webhosting-starter',
|
||||
'featured' => false,
|
||||
'specs' => [
|
||||
['label' => 'Webspace', 'value' => '10 GB'],
|
||||
@@ -358,6 +372,7 @@ $PRODUCTS['webhosting'] = [
|
||||
'business' => [
|
||||
'name' => 'Webhosting Business',
|
||||
'price' => '7,99',
|
||||
'shop_url' => 'https://shop.hexahost.de/store/webserver/webhosting-business',
|
||||
'featured' => true,
|
||||
'specs' => [
|
||||
['label' => 'Webspace', 'value' => '30 GB'],
|
||||
@@ -380,6 +395,7 @@ $PRODUCTS['webhosting'] = [
|
||||
'professional' => [
|
||||
'name' => 'Webhosting Professional',
|
||||
'price' => '9,99',
|
||||
'shop_url' => 'https://shop.hexahost.de/store/webserver/webhosting-professional',
|
||||
'featured' => false,
|
||||
'specs' => [
|
||||
['label' => 'Webspace', 'value' => '50 GB'],
|
||||
@@ -402,6 +418,7 @@ $PRODUCTS['webhosting'] = [
|
||||
'enterprise' => [
|
||||
'name' => 'Webhosting Enterprise',
|
||||
'price' => '29,99',
|
||||
'shop_url' => '',
|
||||
'featured' => false,
|
||||
'specs' => [
|
||||
['label' => 'Webspace', 'value' => '200 GB'],
|
||||
@@ -485,6 +502,38 @@ function formatPrice($price, $withCurrency = true) {
|
||||
return $withCurrency ? $price . '€' : $price;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bestell-Link für ein Paket (Online-Shop oder Kontaktformular)
|
||||
*/
|
||||
function getOrderUrl($productId, $packageId) {
|
||||
$package = getPackage($productId, $packageId);
|
||||
if ($package && !empty($package['shop_url'])) {
|
||||
return $package['shop_url'];
|
||||
}
|
||||
|
||||
return sprintf('contact.php?package=%s-%s', $productId, $packageId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bestell-Link für CTA (beliebtes Paket oder erstes Paket)
|
||||
*/
|
||||
function getProductOrderUrl($productId) {
|
||||
$packages = getProductPackages($productId);
|
||||
|
||||
foreach ($packages as $packageId => $package) {
|
||||
if (!empty($package['featured'])) {
|
||||
return getOrderUrl($productId, $packageId);
|
||||
}
|
||||
}
|
||||
|
||||
$firstPackageId = array_key_first($packages);
|
||||
if ($firstPackageId !== null) {
|
||||
return getOrderUrl($productId, $firstPackageId);
|
||||
}
|
||||
|
||||
return sprintf('contact.php?product=%s', $productId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generiert HTML für eine Paket-Karte
|
||||
*/
|
||||
@@ -522,7 +571,7 @@ function renderPackageCard($productId, $packageId, $package) {
|
||||
<div class="package-features">
|
||||
%s
|
||||
</div>
|
||||
<a href="contact.php?package=%s-%s" class="btn btn-primary">Jetzt bestellen</a>
|
||||
<a href="%s" class="btn btn-primary">Jetzt bestellen</a>
|
||||
</div>',
|
||||
$featuredClass,
|
||||
$featuredBadge,
|
||||
@@ -530,8 +579,7 @@ function renderPackageCard($productId, $packageId, $package) {
|
||||
$package['price'],
|
||||
$specsHtml,
|
||||
$featuresHtml,
|
||||
$productId,
|
||||
$packageId
|
||||
htmlspecialchars(getOrderUrl($productId, $packageId), ENT_QUOTES, 'UTF-8')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,3 +15,94 @@
|
||||
justify-content: center;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
/* Legal pages: plain white content with black text */
|
||||
.legal-hero,
|
||||
.legal-content {
|
||||
background: #ffffff;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.legal-hero {
|
||||
margin-top: 70px;
|
||||
padding: 2rem 0 1.5rem;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.legal-content {
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
||||
.legal-hero-title {
|
||||
background: none;
|
||||
-webkit-text-fill-color: #000000;
|
||||
color: #000000;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.legal-hero-description,
|
||||
.legal-section h2,
|
||||
.legal-section h3,
|
||||
.legal-block p,
|
||||
.legal-block li,
|
||||
.breadcrumb,
|
||||
.breadcrumb span {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.legal-section,
|
||||
.legal-section.glass-card {
|
||||
background: transparent;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
border-radius: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.legal-section:hover,
|
||||
.legal-section.glass-card:hover {
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.legal-content .glass-card:hover {
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.legal-section h2 {
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
padding-bottom: 0.5rem;
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
.legal-block a,
|
||||
.breadcrumb a {
|
||||
color: #0b57d0;
|
||||
}
|
||||
|
||||
.legal-block a:hover,
|
||||
.breadcrumb a:hover {
|
||||
color: #0b57d0;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Ensure absolutely no hover effects on legal text/content */
|
||||
.legal-hero *,
|
||||
.legal-content *,
|
||||
.legal-hero *:hover,
|
||||
.legal-content *:hover,
|
||||
.legal-hero *:focus,
|
||||
.legal-content *:focus,
|
||||
.legal-hero *:active,
|
||||
.legal-content *:active {
|
||||
transform: none !important;
|
||||
box-shadow: none !important;
|
||||
text-shadow: none !important;
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
|
||||
@@ -166,8 +166,8 @@ includeHeader($page_title, $page_description, $current_page);
|
||||
<h2><?php echo htmlspecialchars($product['cta_title'] ?? ('Bereit für ' . $product['name'] . '?')); ?></h2>
|
||||
<p><?php echo htmlspecialchars($product['cta_description'] ?? ('Starten Sie noch heute mit ' . $product['name'])); ?></p>
|
||||
<div class="cta-actions">
|
||||
<a href="contact.php?product=mail-gateway" class="btn btn-primary">Jetzt bestellen</a>
|
||||
<a href="/contact" class="btn btn-secondary">Beratung anfordern</a>
|
||||
<a href="<?php echo htmlspecialchars(getProductOrderUrl('mail-gateway'), ENT_QUOTES, 'UTF-8'); ?>" class="btn btn-primary">Jetzt bestellen</a>
|
||||
<a href="contact.php" class="btn btn-secondary">Beratung anfordern</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -166,8 +166,8 @@ includeHeader($page_title, $page_description, $current_page);
|
||||
<h2><?php echo htmlspecialchars($product['cta_title'] ?? ('Bereit für ' . $product['name'] . '?')); ?></h2>
|
||||
<p><?php echo htmlspecialchars($product['cta_description'] ?? ('Starten Sie noch heute mit ' . $product['name'])); ?></p>
|
||||
<div class="cta-actions">
|
||||
<a href="contact.php?product=vpc" class="btn btn-primary">Jetzt bestellen</a>
|
||||
<a href="/contact" class="btn btn-secondary">Beratung anfordern</a>
|
||||
<a href="<?php echo htmlspecialchars(getProductOrderUrl('vpc'), ENT_QUOTES, 'UTF-8'); ?>" class="btn btn-primary">Jetzt bestellen</a>
|
||||
<a href="contact.php" class="btn btn-secondary">Beratung anfordern</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -171,8 +171,8 @@ includeHeader($page_title, $page_description, $current_page);
|
||||
<h2><?php echo htmlspecialchars($product['cta_title'] ?? ('Bereit für ' . $product['name'] . '?')); ?></h2>
|
||||
<p><?php echo htmlspecialchars($product['cta_description'] ?? ('Starten Sie noch heute mit ' . $product['name'])); ?></p>
|
||||
<div class="cta-actions">
|
||||
<a href="contact.php?product=vps" class="btn btn-primary">Jetzt bestellen</a>
|
||||
<a href="/contact" class="btn btn-secondary">Beratung anfordern</a>
|
||||
<a href="<?php echo htmlspecialchars(getProductOrderUrl('vps'), ENT_QUOTES, 'UTF-8'); ?>" class="btn btn-primary">Jetzt bestellen</a>
|
||||
<a href="contact.php" class="btn btn-secondary">Beratung anfordern</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -95,7 +95,7 @@ includeHeader($page_title, $page_description, $current_page);
|
||||
<polyline points="10,9 9,9 8,9"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3>cPanel/Webmin</h3>
|
||||
<h3>Plesk</h3>
|
||||
<p>Benutzerfreundliche Verwaltungsoberfläche für einfache Website-Verwaltung und E-Mail-Konfiguration.</p>
|
||||
</div>
|
||||
<div class="detail-card glass-card">
|
||||
@@ -170,8 +170,8 @@ includeHeader($page_title, $page_description, $current_page);
|
||||
<h2><?php echo htmlspecialchars($product['cta_title'] ?? ('Bereit für ' . $product['name'] . '?')); ?></h2>
|
||||
<p><?php echo htmlspecialchars($product['cta_description'] ?? ('Starten Sie noch heute mit ' . $product['name'])); ?></p>
|
||||
<div class="cta-actions">
|
||||
<a href="contact.php?product=webhosting" class="btn btn-primary">Jetzt bestellen</a>
|
||||
<a href="/contact" class="btn btn-secondary">Beratung anfordern</a>
|
||||
<a href="<?php echo htmlspecialchars(getProductOrderUrl('webhosting'), ENT_QUOTES, 'UTF-8'); ?>" class="btn btn-primary">Jetzt bestellen</a>
|
||||
<a href="contact.php" class="btn btn-secondary">Beratung anfordern</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Erstellt einen Production-Build und veroeffentlicht ihn auf den Branch main.
|
||||
|
||||
.DESCRIPTION
|
||||
1. Wechselt auf main und setzt ihn auf den Stand von dev
|
||||
2. Entfernt Kommentare, minifiziert CSS, obfuskiert JavaScript
|
||||
3. Committet und pusht main (optional)
|
||||
4. Wechselt zurueck auf dev (Quellcode bleibt unveraendert)
|
||||
|
||||
.PARAMETER Push
|
||||
Pusht main nach origin (Standard: nur lokaler Commit)
|
||||
|
||||
.PARAMETER DryRun
|
||||
Fuehrt Git-Schritte nur simuliert aus (Build wird trotzdem erstellt)
|
||||
|
||||
.PARAMETER AllowDirty
|
||||
Erlaubt uncommittete Aenderungen
|
||||
|
||||
.PARAMETER Message
|
||||
Commit-Nachricht fuer den Production-Build
|
||||
|
||||
.EXAMPLE
|
||||
.\scripts\publish-to-main.ps1
|
||||
|
||||
.EXAMPLE
|
||||
.\scripts\publish-to-main.ps1 -Push
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[switch]$Push,
|
||||
[switch]$DryRun,
|
||||
[switch]$AllowDirty,
|
||||
[string]$Message = ""
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$Root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
||||
$BuildDir = Join-Path $Root "scripts\build"
|
||||
$OriginalBranch = ""
|
||||
|
||||
function Write-Step([string]$Text) {
|
||||
Write-Host ""
|
||||
Write-Host "==> $Text" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
function Ensure-GitClean {
|
||||
$status = git -C $Root status --porcelain
|
||||
if ($status) {
|
||||
throw "Uncommittete Aenderungen im Repository. Bitte zuerst committen oder stashen."
|
||||
}
|
||||
}
|
||||
|
||||
function Resolve-NodeTool([string]$ToolName) {
|
||||
$command = Get-Command $ToolName -ErrorAction SilentlyContinue
|
||||
if ($command) {
|
||||
return $command.Source
|
||||
}
|
||||
|
||||
$candidates = @(
|
||||
(Join-Path $env:ProgramFiles "nodejs\$ToolName.cmd"),
|
||||
(Join-Path ${env:ProgramFiles(x86)} "nodejs\$ToolName.cmd"),
|
||||
(Join-Path $env:LOCALAPPDATA "Programs\nodejs\$ToolName.cmd"),
|
||||
"c:\Program Files\cursor\resources\app\resources\helpers\node.exe"
|
||||
)
|
||||
|
||||
foreach ($candidate in $candidates) {
|
||||
if ($ToolName -eq "node" -and (Test-Path $candidate)) {
|
||||
return $candidate
|
||||
}
|
||||
if ($ToolName -ne "node" -and (Test-Path $candidate)) {
|
||||
return $candidate
|
||||
}
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
function Ensure-Node {
|
||||
$script:NodeExe = Resolve-NodeTool "node"
|
||||
$script:NpmExe = Resolve-NodeTool "npm"
|
||||
|
||||
if (-not $script:NodeExe) {
|
||||
throw "Node.js ist nicht installiert. Bitte Node.js 18+ installieren: https://nodejs.org/"
|
||||
}
|
||||
if (-not $script:NpmExe) {
|
||||
throw "npm wurde nicht gefunden. Bitte Node.js inkl. npm installieren und PATH setzen."
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Set-Location $Root
|
||||
Ensure-Node
|
||||
if (-not $AllowDirty) {
|
||||
Ensure-GitClean
|
||||
} else {
|
||||
Write-Warning "AllowDirty aktiv - uncommittete Aenderungen werden mit veroeffentlicht."
|
||||
}
|
||||
|
||||
$OriginalBranch = (git branch --show-current).Trim()
|
||||
if ($OriginalBranch -ne "dev") {
|
||||
Write-Warning "Empfohlen: Auf Branch 'dev' starten (aktuell: $OriginalBranch)"
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($Message)) {
|
||||
$Message = "chore(release): production build $(Get-Date -Format 'yyyy-MM-dd HH:mm')"
|
||||
}
|
||||
|
||||
Write-Step "Installiere Build-Abhaengigkeiten"
|
||||
Set-Location $BuildDir
|
||||
if (-not $DryRun) {
|
||||
& $NpmExe ci --no-fund --no-audit
|
||||
if ($LASTEXITCODE -ne 0) { throw "npm ci fehlgeschlagen" }
|
||||
}
|
||||
|
||||
Write-Step "Wechsle auf main und synchronisiere mit dev"
|
||||
Set-Location $Root
|
||||
if ($DryRun) {
|
||||
Write-Host '[DryRun] git checkout main'
|
||||
Write-Host '[DryRun] git reset --hard dev'
|
||||
} else {
|
||||
git checkout main
|
||||
git reset --hard dev
|
||||
}
|
||||
|
||||
Write-Step "Production-Build (Kommentare entfernen, JS obfuscaten)"
|
||||
Set-Location $BuildDir
|
||||
if ($DryRun) {
|
||||
Write-Host '[DryRun] npm run build:in-place'
|
||||
} else {
|
||||
& $NpmExe run build:in-place
|
||||
if ($LASTEXITCODE -ne 0) { throw "Production-Build fehlgeschlagen" }
|
||||
}
|
||||
|
||||
Write-Step "Production-Build committen"
|
||||
Set-Location $Root
|
||||
if ($DryRun) {
|
||||
Write-Host '[DryRun] git add -A'
|
||||
Write-Host ('[DryRun] git commit -m "' + $Message + '"')
|
||||
} else {
|
||||
git add -A
|
||||
$null = git diff --cached --quiet
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Warning "Keine Build-Aenderungen - nichts zu committen."
|
||||
} else {
|
||||
git commit -m $Message
|
||||
}
|
||||
}
|
||||
|
||||
if ($Push) {
|
||||
Write-Step "Push nach origin/main"
|
||||
if ($DryRun) {
|
||||
Write-Host '[DryRun] git push origin main'
|
||||
} else {
|
||||
git push origin main
|
||||
}
|
||||
} else {
|
||||
Write-Host "Hinweis: Ohne -Push wurde nur lokal auf main gebaut." -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
Write-Step "Zurueck auf $OriginalBranch"
|
||||
if (-not $DryRun) {
|
||||
if ([string]::IsNullOrWhiteSpace($OriginalBranch)) {
|
||||
git checkout dev
|
||||
} else {
|
||||
git checkout $OriginalBranch
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Production-Release abgeschlossen." -ForegroundColor Green
|
||||
if (-not $Push -and -not $DryRun) {
|
||||
Write-Host "Zum Veroeffentlichen: git push origin main" -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Host ""
|
||||
Write-Host ('FEHLER: ' + $_.Exception.Message) -ForegroundColor Red
|
||||
Set-Location $Root
|
||||
if ($OriginalBranch -and -not $DryRun) {
|
||||
git checkout $OriginalBranch 2>$null
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Erstellt einen Production-Build und veröffentlicht ihn auf den Branch main.
|
||||
#
|
||||
# 1. Wechselt auf main und setzt ihn auf den Stand von dev
|
||||
# 2. Entfernt Kommentare, minifiziert CSS, obfuskiert JavaScript
|
||||
# 3. Committet und pusht main (optional)
|
||||
# 4. Wechselt zurück auf den ursprünglichen Branch (dev bleibt unverändert)
|
||||
#
|
||||
# Nutzung:
|
||||
# ./scripts/publish-to-main.sh
|
||||
# ./scripts/publish-to-main.sh --push
|
||||
# ./scripts/publish-to-main.sh --dry-run
|
||||
# ./scripts/publish-to-main.sh --allow-dirty --message "chore(release): v1.2"
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PUSH=false
|
||||
DRY_RUN=false
|
||||
ALLOW_DIRTY=false
|
||||
MESSAGE=""
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: publish-to-main.sh [OPTIONS]
|
||||
|
||||
Options:
|
||||
--push Push nach origin/main
|
||||
--dry-run Git-Schritte nur anzeigen (Build wird ausgeführt)
|
||||
--allow-dirty Uncommittete Änderungen erlauben
|
||||
--message TEXT Commit-Nachricht
|
||||
-h, --help Hilfe anzeigen
|
||||
EOF
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--push)
|
||||
PUSH=true
|
||||
shift
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN=true
|
||||
shift
|
||||
;;
|
||||
--allow-dirty)
|
||||
ALLOW_DIRTY=true
|
||||
shift
|
||||
;;
|
||||
--message)
|
||||
MESSAGE="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unbekannte Option: $1" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
BUILD_DIR="$ROOT/scripts/build"
|
||||
ORIGINAL_BRANCH=""
|
||||
|
||||
step() {
|
||||
echo ""
|
||||
echo "==> $1"
|
||||
}
|
||||
|
||||
require_command() {
|
||||
if ! command -v "$1" >/dev/null 2>&1; then
|
||||
echo "FEHLER: '$1' nicht gefunden. Bitte installieren und PATH setzen." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_git_clean() {
|
||||
if [[ -n "$(git -C "$ROOT" status --porcelain)" ]]; then
|
||||
echo "FEHLER: Uncommittete Änderungen. Bitte zuerst committen oder stashen." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup_on_error() {
|
||||
echo ""
|
||||
echo "FEHLER: Abgebrochen." >&2
|
||||
cd "$ROOT" || true
|
||||
if [[ -n "$ORIGINAL_BRANCH" && "$DRY_RUN" == false ]]; then
|
||||
git checkout "$ORIGINAL_BRANCH" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
trap cleanup_on_error ERR
|
||||
|
||||
require_command node
|
||||
require_command npm
|
||||
require_command git
|
||||
|
||||
cd "$ROOT"
|
||||
|
||||
if [[ "$ALLOW_DIRTY" == false ]]; then
|
||||
ensure_git_clean
|
||||
else
|
||||
echo "WARNUNG: --allow-dirty aktiv – uncommittete Änderungen werden mit veröffentlicht." >&2
|
||||
fi
|
||||
|
||||
ORIGINAL_BRANCH="$(git branch --show-current | tr -d '[:space:]')"
|
||||
if [[ "$ORIGINAL_BRANCH" != "dev" ]]; then
|
||||
echo "WARNUNG: Empfohlen auf Branch 'dev' zu starten (aktuell: ${ORIGINAL_BRANCH:-detached})" >&2
|
||||
fi
|
||||
|
||||
if [[ -z "$MESSAGE" ]]; then
|
||||
MESSAGE="chore(release): production build $(date '+%Y-%m-%d %H:%M')"
|
||||
fi
|
||||
|
||||
step "Installiere Build-Abhängigkeiten"
|
||||
cd "$BUILD_DIR"
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
npm ci --no-fund --no-audit
|
||||
fi
|
||||
|
||||
step "Wechsle auf main und synchronisiere mit dev"
|
||||
cd "$ROOT"
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
echo "[DryRun] git checkout main"
|
||||
echo "[DryRun] git reset --hard dev"
|
||||
else
|
||||
git checkout main
|
||||
git reset --hard dev
|
||||
fi
|
||||
|
||||
step "Production-Build (Kommentare entfernen, JS obfuscaten)"
|
||||
cd "$BUILD_DIR"
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
echo "[DryRun] npm run build:in-place"
|
||||
else
|
||||
npm run build:in-place
|
||||
fi
|
||||
|
||||
step "Production-Build committen"
|
||||
cd "$ROOT"
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
echo "[DryRun] git add -A"
|
||||
echo "[DryRun] git commit -m \"$MESSAGE\""
|
||||
else
|
||||
git add -A
|
||||
if git diff --cached --quiet; then
|
||||
echo "WARNUNG: Keine Build-Änderungen – nichts zu committen." >&2
|
||||
else
|
||||
git commit -m "$MESSAGE"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$PUSH" == true ]]; then
|
||||
step "Push nach origin/main"
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
echo "[DryRun] git push origin main"
|
||||
else
|
||||
git push origin main
|
||||
fi
|
||||
else
|
||||
echo "Hinweis: Ohne --push wurde nur lokal auf main gebaut."
|
||||
fi
|
||||
|
||||
step "Zurück auf ${ORIGINAL_BRANCH:-dev}"
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
if [[ -n "$ORIGINAL_BRANCH" ]]; then
|
||||
git checkout "$ORIGINAL_BRANCH"
|
||||
else
|
||||
git checkout dev
|
||||
fi
|
||||
fi
|
||||
|
||||
trap - ERR
|
||||
|
||||
echo ""
|
||||
echo "Production-Release abgeschlossen."
|
||||
if [[ "$PUSH" == false && "$DRY_RUN" == false ]]; then
|
||||
echo "Zum Veröffentlichen: git push origin main"
|
||||
fi
|
||||
@@ -1,46 +0,0 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Erstellt ein Production-Bundle unter dist/ (ohne Branch-Wechsel).
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[switch]$InPlace
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$Root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
||||
$BuildDir = Join-Path $Root "scripts\build"
|
||||
|
||||
function Resolve-NodeTool([string]$ToolName) {
|
||||
$command = Get-Command $ToolName -ErrorAction SilentlyContinue
|
||||
if ($command) { return $command.Source }
|
||||
|
||||
$candidates = @(
|
||||
(Join-Path $env:ProgramFiles "nodejs\$ToolName.cmd"),
|
||||
(Join-Path ${env:ProgramFiles(x86)} "nodejs\$ToolName.cmd")
|
||||
)
|
||||
|
||||
foreach ($candidate in $candidates) {
|
||||
if (Test-Path $candidate) { return $candidate }
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
$npm = Resolve-NodeTool "npm"
|
||||
if (-not $npm) {
|
||||
throw "npm nicht gefunden. Bitte Node.js installieren."
|
||||
}
|
||||
|
||||
Set-Location $BuildDir
|
||||
& $npm ci --no-fund --no-audit
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
|
||||
if ($InPlace) {
|
||||
& $npm run build:in-place
|
||||
} else {
|
||||
& $npm run build
|
||||
}
|
||||
|
||||
exit $LASTEXITCODE
|
||||
@@ -1,58 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Erstellt ein Production-Bundle unter dist/ (ohne Branch-Wechsel).
|
||||
#
|
||||
# Nutzung:
|
||||
# ./scripts/run-build.sh
|
||||
# ./scripts/run-build.sh --in-place
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
IN_PLACE=false
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: run-build.sh [OPTIONS]
|
||||
|
||||
Options:
|
||||
--in-place Build direkt im Repository (statt dist/)
|
||||
-h, --help Hilfe anzeigen
|
||||
EOF
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--in-place)
|
||||
IN_PLACE=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unbekannte Option: $1" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
BUILD_DIR="$ROOT/scripts/build"
|
||||
|
||||
if ! command -v npm >/dev/null 2>&1; then
|
||||
echo "FEHLER: npm nicht gefunden. Bitte Node.js installieren." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$BUILD_DIR"
|
||||
npm ci --no-fund --no-audit
|
||||
|
||||
if [[ "$IN_PLACE" == true ]]; then
|
||||
npm run build:in-place
|
||||
else
|
||||
npm run build
|
||||
fi
|
||||
@@ -1,48 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* HexaHost.de E-Mail Test (nur CLI oder lokale Entwicklung)
|
||||
*/
|
||||
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
$remoteAddr = $_SERVER['REMOTE_ADDR'] ?? '';
|
||||
$isLocal = in_array($remoteAddr, ['127.0.0.1', '::1'], true)
|
||||
|| filter_var($remoteAddr, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false;
|
||||
|
||||
if (!$isLocal) {
|
||||
http_response_code(403);
|
||||
exit('Forbidden');
|
||||
}
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../backend/config/mail-config.php';
|
||||
|
||||
function testEmail() {
|
||||
$config = getHexaHostConfig();
|
||||
|
||||
$subject = '[HexaHost.de] Test-E-Mail';
|
||||
$message = "Test-E-Mail von HexaHost.de\n\n";
|
||||
$message .= "Zeitstempel: " . date('d.m.Y H:i:s') . "\n";
|
||||
|
||||
$headers = [
|
||||
'From: ' . $config['from_name'] . ' <' . $config['from_email'] . '>',
|
||||
'MIME-Version: 1.0',
|
||||
'Content-Type: text/plain; charset=UTF-8',
|
||||
'X-Mailer: HexaHost Test Email',
|
||||
];
|
||||
|
||||
return mail($config['to_email'], $subject, $message, implode("\r\n", $headers));
|
||||
}
|
||||
|
||||
if (PHP_SAPI === 'cli') {
|
||||
echo testEmail() ? "Test-E-Mail gesendet.\n" : "Fehler beim Senden.\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($_GET['test'])) {
|
||||
echo testEmail()
|
||||
? 'Test-E-Mail wurde gesendet.'
|
||||
: 'Fehler beim Senden der Test-E-Mail.';
|
||||
} else {
|
||||
echo '<h1>HexaHost.de E-Mail Test</h1>';
|
||||
echo '<p><a href="?test=1">Test-E-Mail senden</a></p>';
|
||||
}
|
||||
Reference in New Issue
Block a user