Compare commits

...

4 Commits

Author SHA1 Message Date
gitea-actions
3d54cea5ec chore(release): obfuscate and hash production assets [skip ci] 2026-06-01 06:50:07 +00:00
gitea-actions
fdd0367281 chore(release): obfuscate and hash production assets [skip ci] 2026-06-01 06:38:01 +00:00
gitea-actions
ded8778b6c chore(release): obfuscate and hash production assets [skip ci] 2026-05-29 09:04:26 +00:00
gitea-actions
7a03f5aa1b chore(release): obfuscate and hash production assets [skip ci] 2026-05-29 08:43:42 +00:00
27 changed files with 232 additions and 79 deletions

View File

@@ -1,9 +1,9 @@
name: Release Build (dev → main) name: Release Build (ci → main)
on: on:
push: push:
branches: branches:
- dev - ci
workflow_dispatch: workflow_dispatch:
env: env:
@@ -16,29 +16,12 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout (volle History) - name: Checkout ci (Integration)
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
repository-url: https://git.hexahost.dev/smueller/HexaHost-Frontend repository-url: https://git.hexahost.dev/smueller/HexaHost-Frontend
ref: dev ref: ci
- name: Merge dev in CI-Workspace (Basis main)
env:
GITEA_TOKEN: ${{ github.token }}
run: |
git config user.name "gitea-actions"
git config user.email "actions@local"
git remote set-url origin "https://oauth2:${GITEA_TOKEN}@${GITEA_HOST}/${REPO_PATH}.git"
git fetch origin main dev
if git show-ref --verify --quiet refs/remotes/origin/main; then
git checkout -B main origin/main
git merge origin/dev -X theirs --no-edit -m "ci: merge dev for release build"
else
echo "main branch missing, initializing from dev"
git checkout -B main origin/dev
fi
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5
@@ -60,10 +43,20 @@ jobs:
git config user.name "gitea-actions" git config user.name "gitea-actions"
git config user.email "actions@local" git config user.email "actions@local"
git remote set-url origin "https://oauth2:${GITEA_TOKEN}@${GITEA_HOST}/${REPO_PATH}.git" git remote set-url origin "https://oauth2:${GITEA_TOKEN}@${GITEA_HOST}/${REPO_PATH}.git"
git fetch origin main ci
git add -A git add -A
if git diff --cached --quiet; then if git diff --cached --quiet; then
echo "No release changes to publish." echo "No release changes to publish."
exit 0 exit 0
fi fi
git commit -m "chore(release): obfuscate and hash production assets [skip ci]"
git push origin main TREE=$(git write-tree)
MSG="chore(release): obfuscate and hash production assets [skip ci]"
if git show-ref --verify --quiet refs/remotes/origin/main; then
PARENT=$(git rev-parse origin/main)
COMMIT=$(git commit-tree "$TREE" -p "$PARENT" -m "$MSG")
else
COMMIT=$(git commit-tree "$TREE" -m "$MSG")
fi
git push origin "${COMMIT}:refs/heads/main"

29
.githooks/commit-msg Normal file
View File

@@ -0,0 +1,29 @@
#!/bin/sh
# Validiert Commit-Messages nach Conventional Commits
# https://www.conventionalcommits.org/
commit_msg_file="$1"
first_line=$(sed '/^#/d;/^$/d' "$commit_msg_file" | head -n 1)
# Merge/Revert von Git erlauben
case "$first_line" in
Merge\ *|Revert\ *)
exit 0
;;
esac
pattern='^(feat|fix|docs|style|refactor|perf|test|build|ci|chore)(\([a-z0-9._-]+\))?!?: .+'
if ! printf '%s\n' "$first_line" | grep -qE "$pattern"; then
echo >&2 "❌ Commit-Message entspricht nicht Conventional Commits."
echo >&2 ""
echo >&2 " Format: type(scope): description"
echo >&2 " Beispiel: feat(products): hide vpc in navigation"
echo >&2 ""
echo >&2 " Erlaubte types: feat, fix, docs, style, refactor, perf, test, build, ci, chore"
echo >&2 " Überspringen: git commit --no-verify"
exit 1
fi
exit 0

View File

@@ -1,10 +1,10 @@
# Hinweis: Gitea nutzt .gitea/workflows/obfuscate-main.yml (identischer Ablauf). # Hinweis: Gitea nutzt .gitea/workflows/obfuscate-main.yml (identischer Ablauf).
name: Release Build (dev → main) name: Release Build (ci → main)
on: on:
push: push:
branches: branches:
- dev - ci
workflow_dispatch: workflow_dispatch:
env: env:
@@ -17,29 +17,12 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout (volle History) - name: Checkout ci (Integration)
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
repository-url: https://git.hexahost.dev/smueller/HexaHost-Frontend repository-url: https://git.hexahost.dev/smueller/HexaHost-Frontend
ref: dev ref: ci
- name: Merge dev in CI-Workspace (Basis main)
env:
GITEA_TOKEN: ${{ github.token }}
run: |
git config user.name "gitea-actions"
git config user.email "actions@local"
git remote set-url origin "https://oauth2:${GITEA_TOKEN}@${GITEA_HOST}/${REPO_PATH}.git"
git fetch origin main dev
if git show-ref --verify --quiet refs/remotes/origin/main; then
git checkout -B main origin/main
git merge origin/dev -X theirs --no-edit -m "ci: merge dev for release build"
else
echo "main branch missing, initializing from dev"
git checkout -B main origin/dev
fi
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5
@@ -61,10 +44,20 @@ jobs:
git config user.name "gitea-actions" git config user.name "gitea-actions"
git config user.email "actions@local" git config user.email "actions@local"
git remote set-url origin "https://oauth2:${GITEA_TOKEN}@${GITEA_HOST}/${REPO_PATH}.git" git remote set-url origin "https://oauth2:${GITEA_TOKEN}@${GITEA_HOST}/${REPO_PATH}.git"
git fetch origin main ci
git add -A git add -A
if git diff --cached --quiet; then if git diff --cached --quiet; then
echo "No release changes to publish." echo "No release changes to publish."
exit 0 exit 0
fi fi
git commit -m "chore(release): obfuscate and hash production assets [skip ci]"
git push origin main TREE=$(git write-tree)
MSG="chore(release): obfuscate and hash production assets [skip ci]"
if git show-ref --verify --quiet refs/remotes/origin/main; then
PARENT=$(git rev-parse origin/main)
COMMIT=$(git commit-tree "$TREE" -p "$PARENT" -m "$MSG")
else
COMMIT=$(git commit-tree "$TREE" -m "$MSG")
fi
git push origin "${COMMIT}:refs/heads/main"

1
.gitignore vendored
View File

@@ -13,6 +13,7 @@ build/
# Environment variables # Environment variables
.env .env
.cursorrules .cursorrules
.cursor/
.cursorrules.txt .cursorrules.txt
.env.local .env.local
.env.development.local .env.development.local

16
.gitmessage Normal file
View File

@@ -0,0 +1,16 @@
# Conventional Commits nur die erste nicht-kommentierte Zeile wird verwendet
# Format: type(scope): description
#
# feat Neues Feature
# fix Bugfix
# docs Dokumentation
# style Formatierung (keine Logik)
# refactor Umbau ohne Feature/Fix
# perf Performance
# test Tests
# build Build / Dependencies
# ci CI/CD
# chore Sonstiges
#
# Beispiel (diese Zeile anpassen und Kommentare löschen oder stehen lassen):
# feat(products): hide vpc and vps in navigation

View File

@@ -169,16 +169,33 @@ Für den Produktivbetrieb `public/` als Webroot konfigurieren.
| Branch | Zweck | | Branch | Zweck |
|--------|--------| |--------|--------|
| **`dev`** | Entwicklung (lesbarer Code, Kommentare) | | **`dev`** | Entwicklung (lesbarer Code, Kommentare) |
| **`main`** | Release/Produktion (obfuskiert, gehashte Assets) | | **`ci`** | Integration (du mergst `dev` hierher) |
| **`main`** | Release/Produktion (obfuskiert, gehashte Assets — nur per Pipeline) |
**Workflow:** Nur auf `dev` entwickeln und pushen — **nicht** `dev` manuell nach `main` mergen. **Ablauf: `dev` → `ci` → `main`**
Bei jedem Push auf `dev` startet `.gitea/workflows/obfuscate-main.yml`: 1. Auf **`dev`** entwickeln und pushen
2. **`dev` nach `ci` mergen** (manuell, z. B. in Gitea oder lokal)
3. **`ci` pushen** → startet `.gitea/workflows/obfuscate-main.yml`
4. Pipeline obfuskiert im Runner-Workspace und publiziert nach **`main`**
1. Checkout in temporärem Runner-Workspace ```powershell
2. `dev` in CI mit `main` mergen (`-X theirs`, dev-Inhalte bei Konflikten) # Nach fertigen Änderungen auf dev:
3. Obfuscation-Build (`scripts/obfuscate_release.py --hash-assets`) git checkout ci
4. Ergebnis nach `main` pushen (Bot-Commit mit `[skip ci]`) git pull origin ci
git merge dev
git push origin ci
```
Bei jedem Push auf **`ci`**:
1. Checkout von `ci` im temporären Runner-Workspace
2. Obfuscation-Build (`scripts/obfuscate_release.py --hash-assets`)
3. Ergebnis nach `main` pushen (Bot-Commit mit `[skip ci]`)
**Nicht** `dev` oder `ci` direkt nach `main` mergen. Der Branch **`ci` bleibt lesbar** — Obfuscation wird nur nach `main` publiziert.
`ci`-Branch einmalig anlegen (falls noch nicht vorhanden): `git checkout -b ci dev && git push -u origin ci`
Der Build führt aus: Der Build führt aus:

View File

@@ -13,7 +13,7 @@ define('SMTP_TO_EMAIL', 'info@hexahost.de');
define('ENABLE_CSRF_PROTECTION', true); define('ENABLE_CSRF_PROTECTION', true);
define('ENABLE_RATE_LIMITING', true); define('ENABLE_RATE_LIMITING', true);
define('MAX_REQUESTS_PER_HOUR', 10); define('MAX_REQUESTS_PER_HOUR', 5);
define('ENABLE_SPAM_PROTECTION', true); define('ENABLE_SPAM_PROTECTION', true);

View File

@@ -444,12 +444,45 @@ $PRODUCTS['webhosting'] = [
]; ];
$PRODUCT_VISIBILITY = [
'vpc' => false,
'vps' => false,
'mail-gateway' => false,
'webhosting' => true,
];
function isProductVisible(string $productId): bool {
global $PRODUCT_VISIBILITY;
return $PRODUCT_VISIBILITY[$productId] ?? true;
}
function productHiddenAttr(string $productId): string {
return isProductVisible($productId) ? '' : ' hidden';
}
function getVisibleProductPageIds(): array {
global $PRODUCT_VISIBILITY;
return array_keys(array_filter($PRODUCT_VISIBILITY, static fn(bool $visible): bool => $visible));
}
function getAllProducts() { function getAllProducts() {
global $PRODUCTS; global $PRODUCTS;
return $PRODUCTS; return $PRODUCTS;

View File

@@ -15,10 +15,17 @@
<div class="footer-section"> <div class="footer-section">
<h4>Produkte</h4> <h4>Produkte</h4>
<ul> <ul>
<li><a href="/vpc">Virtual Private Container</a></li> <li<?php echo productHiddenAttr('vpc'); ?>><a href="/vpc">Virtual Private Container</a></li>
<li><a href="/vps">Virtual Private Server</a></li> <li<?php echo productHiddenAttr('vps'); ?>><a href="/vps">Virtual Private Server</a></li>
<li><a href="/mail-gateway">Mail Gateway</a></li> <li<?php echo productHiddenAttr('mail-gateway'); ?>><a href="/mail-gateway">Mail Gateway</a></li>
<li><a href="/webhosting">Webhosting</a></li> <li<?php echo productHiddenAttr('webhosting'); ?>><a href="/webhosting">Webhosting</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Andere Dienste</h4>
<ul>
<li><a href="https://hexadns.de" target="_blank" rel="noopener noreferrer">hexadns.de</a></li>
<li><a href="https://www.hexa-mail.de/" target="_blank" rel="noopener noreferrer">hexa-mail.de</a></li>
</ul> </ul>
</div> </div>
<div class="footer-section"> <div class="footer-section">
@@ -156,8 +163,8 @@
</script> </script>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-EF0E9VPMTD"></script> <script async src="https://www.googletagmanager.com/gtag/js?id=G-EF0E9VPMTD"></script>
<script src="/assets/js/main.b83bb213abc1.js" defer></script> <script src="/assets/js/main.9189c38109cf.js" defer></script>
<script src="/assets/js/cookie-consent.6f0657b52e18.js" defer></script> <script src="/assets/js/cookie-consent.91c79812d22c.js" defer></script>
<?php if (isset($additional_scripts)): ?> <?php if (isset($additional_scripts)): ?>
<?php foreach ($additional_scripts as $script): ?> <?php foreach ($additional_scripts as $script): ?>
<script src="<?php echo htmlspecialchars($script, ENT_QUOTES, 'UTF-8'); ?>" defer></script> <script src="<?php echo htmlspecialchars($script, ENT_QUOTES, 'UTF-8'); ?>" defer></script>

View File

@@ -3,6 +3,8 @@
require_once __DIR__ . '/../config/products-config.php';
if (session_status() === PHP_SESSION_NONE) { if (session_status() === PHP_SESSION_NONE) {

View File

@@ -33,7 +33,7 @@
<!-- Main Stylesheets --> <!-- Main Stylesheets -->
<link rel="stylesheet" href="/assets/css/style.d01979e8c871.css"> <link rel="stylesheet" href="/assets/css/style.d01979e8c871.css">
<link rel="stylesheet" href="/assets/css/custom.0bc6a878fec2.css"> <link rel="stylesheet" href="/assets/css/custom.d35eb3499212.css">
<!-- Fonts --> <!-- Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Russo+One&family=Source+Sans+Pro:wght@300;400;600;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Russo+One&family=Source+Sans+Pro:wght@300;400;600;700&display=swap" rel="stylesheet">
@@ -59,12 +59,12 @@
<ul class="nav-menu"> <ul class="nav-menu">
<li><a href="/" class="nav-link <?php echo ($current_page === 'home') ? 'active' : ''; ?>">Home</a></li> <li><a href="/" class="nav-link <?php echo ($current_page === 'home') ? 'active' : ''; ?>">Home</a></li>
<li class="nav-dropdown"> <li class="nav-dropdown">
<a href="#" class="nav-link <?php echo (in_array($current_page, ['vpc', 'vps', 'mail-gateway', 'webhosting'])) ? 'active' : ''; ?>">Produkte</a> <a href="#" class="nav-link <?php echo (in_array($current_page, getVisibleProductPageIds(), true)) ? 'active' : ''; ?>">Produkte</a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a href="/vpc" class="<?php echo ($current_page === 'vpc') ? 'active' : ''; ?>">Virtual Private Container</a></li> <li<?php echo productHiddenAttr('vpc'); ?>><a href="/vpc" class="<?php echo ($current_page === 'vpc') ? 'active' : ''; ?>">Virtual Private Container</a></li>
<li><a href="/vps" class="<?php echo ($current_page === 'vps') ? 'active' : ''; ?>">Virtual Private Server</a></li> <li<?php echo productHiddenAttr('vps'); ?>><a href="/vps" class="<?php echo ($current_page === 'vps') ? 'active' : ''; ?>">Virtual Private Server</a></li>
<li><a href="/mail-gateway" class="<?php echo ($current_page === 'mail-gateway') ? 'active' : ''; ?>">Mail Gateway</a></li> <li<?php echo productHiddenAttr('mail-gateway'); ?>><a href="/mail-gateway" class="<?php echo ($current_page === 'mail-gateway') ? 'active' : ''; ?>">Mail Gateway</a></li>
<li><a href="/webhosting" class="<?php echo ($current_page === 'webhosting') ? 'active' : ''; ?>">Webhosting</a></li> <li<?php echo productHiddenAttr('webhosting'); ?>><a href="/webhosting" class="<?php echo ($current_page === 'webhosting') ? 'active' : ''; ?>">Webhosting</a></li>
</ul> </ul>
</li> </li>
<li><a href="/it-dienstleistungen" class="nav-link <?php echo ($current_page === 'it-dienstleistungen') ? 'active' : ''; ?>">IT-Dienstleistungen</a></li> <li><a href="/it-dienstleistungen" class="nav-link <?php echo ($current_page === 'it-dienstleistungen') ? 'active' : ''; ?>">IT-Dienstleistungen</a></li>

View File

@@ -1 +0,0 @@
.btn-tertiary{color:var(--text-primary);background:transparent;border:1px solid rgba(255,255,255,0.25);transition:all 0.3s ease;}.btn-tertiary:hover{border-color:var(--primary-color);color:var(--primary-color);background:rgba(255,81,249,0.08);}.it-services-actions{justify-content:center;margin-top:2rem;}.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;}.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;}

View File

@@ -0,0 +1 @@
.btn-tertiary{color:var(--text-primary);background:transparent;border:1px solid rgba(255,255,255,0.25);transition:all 0.3s ease;}.btn-tertiary:hover{border-color:var(--primary-color);color:var(--primary-color);background:rgba(255,81,249,0.08);}.it-services-actions{justify-content:center;margin-top:2rem;}.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,.legal-hero .breadcrumb,.legal-hero .breadcrumb span,.legal-content .breadcrumb,.legal-content .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,.legal-hero .breadcrumb a,.legal-content .breadcrumb a{color:#0b57d0;}.legal-block a:hover,.legal-hero .breadcrumb a:hover,.legal-content .breadcrumb a:hover{color:#0b57d0;text-decoration:none;}.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;}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -8,7 +8,7 @@ $preselected_subject = getPreselectedContactSubject();
$page_title = 'Kontakt - HexaHost.de | Hosting aus Niederbayern'; $page_title = 'Kontakt - HexaHost.de | Hosting aus Niederbayern';
$page_description = 'Kontaktieren Sie HexaHost.de - Ihr Hosting-Partner aus Niederbayern. Persönlicher Support und kompetente Beratung.'; $page_description = 'Kontaktieren Sie HexaHost.de - Ihr Hosting-Partner aus Niederbayern. Persönlicher Support und kompetente Beratung.';
$current_page = 'contact'; $current_page = 'contact';
$additional_scripts = ['assets/js/contact.ee450029d017.js']; $additional_scripts = ['assets/js/contact.c2399194863d.js'];
includeHeader($page_title, $page_description, $current_page, $additional_scripts); includeHeader($page_title, $page_description, $current_page, $additional_scripts);

View File

@@ -54,7 +54,7 @@ includeHeader($page_title, $page_description, $current_page);
</p> </p>
</div> </div>
<div class="products-grid"> <div class="products-grid">
<div class="product-card glass-card"> <div class="product-card glass-card"<?php echo productHiddenAttr('vpc'); ?>>
<div class="product-icon"> <div class="product-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M4 7V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v3"/> <path d="M4 7V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v3"/>
@@ -74,7 +74,7 @@ includeHeader($page_title, $page_description, $current_page);
</ul> </ul>
<a href="/vpc" class="btn btn-primary">Mehr erfahren</a> <a href="/vpc" class="btn btn-primary">Mehr erfahren</a>
</div> </div>
<div class="product-card glass-card"> <div class="product-card glass-card"<?php echo productHiddenAttr('vps'); ?>>
<div class="product-icon"> <div class="product-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"/> <rect x="2" y="3" width="20" height="14" rx="2" ry="2"/>
@@ -92,7 +92,7 @@ includeHeader($page_title, $page_description, $current_page);
</ul> </ul>
<a href="/vps" class="btn btn-primary">Mehr erfahren</a> <a href="/vps" class="btn btn-primary">Mehr erfahren</a>
</div> </div>
<div class="product-card glass-card"> <div class="product-card glass-card"<?php echo productHiddenAttr('mail-gateway'); ?>>
<div class="product-icon"> <div class="product-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/> <path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/>

View File

@@ -7,8 +7,8 @@ Disallow: /assets/css/
# Allow CSS and JS files for better SEO # Allow CSS and JS files for better SEO
Allow: /assets/css/style.d01979e8c871.css Allow: /assets/css/style.d01979e8c871.css
Allow: /assets/js/main.b83bb213abc1.js Allow: /assets/js/main.9189c38109cf.js
Allow: /assets/js/contact.ee450029d017.js Allow: /assets/js/contact.c2399194863d.js
# Sitemap location # Sitemap location
Sitemap: https://hexahost.de/sitemap.xml Sitemap: https://hexahost.de/sitemap.xml

View File

@@ -0,0 +1,17 @@
# Einmal pro Clone ausführen: Commit-Template + Conventional-Commits-Hook aktivieren
$ErrorActionPreference = "Stop"
$repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..")
Push-Location $repoRoot
try {
git config --local commit.template .gitmessage
git config --local core.hooksPath .githooks
Write-Host "Git Hooks aktiv:" -ForegroundColor Green
Write-Host " commit.template = .gitmessage"
Write-Host " core.hooksPath = .githooks"
Write-Host ""
Write-Host "Commit-Format: feat(scope): beschreibung"
} finally {
Pop-Location
}

48
scripts/test-email.php Normal file
View File

@@ -0,0 +1,48 @@
<?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>';
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long