HEX
Server: LiteSpeed
System: Linux houston.panomity.com 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:40:06 UTC 2026 x86_64
User: nudepix (1011)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //proc/self/root/tmp/generate-static-campaign-hub.mjs
#!/usr/bin/env node
import fs from 'node:fs';
import path from 'node:path';

const webroot = process.env.DATEWEB_WEBROOT || process.env.DATEWEB_PUBLIC_HTML || '/home/dateweb.de/public_html';
const site = 'https://dateweb.de';
const route = path.join(webroot, 'kampagnen');
fs.mkdirSync(route, { recursive: true });

const guides = [
  ['Dating-Trends 2026', '/ratgeber/dating-trends-2026/', 'guide_dating_trends_2026_launch', 'dating_trends_2026', 'Clear Coding, Dating-Fatigue, KI mit Grenzen und sichere erste Treffen.'],
  ['Dating-Fatigue überwinden', '/ratgeber/dating-fatigue/', 'guide_dating_fatigue_launch', 'dating_fatigue', 'Weniger Swipen, weniger Rauschen und wieder mehr echte Begegnung.'],
  ['Ghosting beim Online-Dating', '/ratgeber/ghosting-online-dating/', 'guide_ghosting_launch', 'ghosting_online_dating', 'Funkstille ruhig einordnen und verbindlicher weiterdaten.'],
  ['Dating ab 40', '/ratgeber/dating-ab-40/', 'guide_dating_ab_40_launch', 'dating_ab_40', 'Profil, Erwartungen und erste Treffen für Singles ab 40 klarer gestalten.'],
  ['Dating nach Trennung', '/ratgeber/dating-nach-trennung/', 'guide_dating_nach_trennung_launch', 'dating_nach_trennung', 'Ruhig neu starten, ohne alte Muster mitzunehmen.'],
];
const cities = [
  ['Dating in Frankfurt', '/dating/frankfurt/', 'local_dating_frankfurt_launch', 'dating_frankfurt', 'After-Work, Museumsufer und kurze klare Treffen für volle Kalender.'],
  ['Dating in München', '/dating/muenchen/', 'local_dating_muenchen_launch', 'dating_muenchen', 'Klare Absicht zwischen Karriere, Alltag und sicheren Treffpunkten.'],
  ['Dating in Köln', '/dating/koeln/', 'local_dating_koeln_launch', 'dating_koeln', 'Locker ins Gespräch kommen, aber mit konkretem nächsten Schritt.'],
  ['Dating in Düsseldorf', '/dating/duesseldorf/', 'local_dating_duesseldorf_launch', 'dating_duesseldorf', 'Stil, Rhein-Nähe und klare Absicht verbinden.'],
  ['Dating in Stuttgart', '/dating/stuttgart/', 'local_dating_stuttgart_launch', 'dating_stuttgart', 'Volle Kalender und Qualitätsanspruch besser verbinden.'],
];

function esc(value) {
  return String(value).replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;').replaceAll("'", '&#39;');
}
function campaignUrl(pathname, campaign, source, medium, keyword) {
  const url = new URL(pathname, site);
  url.searchParams.set('mtm_campaign', campaign);
  url.searchParams.set('mtm_source', source);
  url.searchParams.set('mtm_medium', medium);
  url.searchParams.set('mtm_keyword', keyword);
  return url.toString();
}
function cards(items, type) {
  return items.map(([title, pathname, campaign, keyword, description]) => {
    const newsletter = campaignUrl(pathname, campaign, 'newsletter', 'owned', keyword);
    const social = campaignUrl(pathname, campaign, 'social', 'organic', keyword);
    const community = campaignUrl(pathname, campaign, 'community', 'organic', keyword);
    const feed = campaignUrl(pathname, campaign, 'feed', 'owned', keyword);
    const copyA = `${title}: ${description}`;
    const copyB = `Für Singles ab 30: ${description} dateweb.de zeigt den nächsten konkreten Schritt.`;
    return `<article class="campaign-card" data-growth-marker="campaign_hub_${esc(type)}_card"><h3>${esc(title)}</h3><p>${esc(description)}</p><dl><dt>Newsletter</dt><dd><a href="${esc(newsletter)}">${esc(newsletter)}</a></dd><dt>Social</dt><dd><a href="${esc(social)}">${esc(social)}</a></dd><dt>Community</dt><dd><a href="${esc(community)}">${esc(community)}</a></dd><dt>Feed</dt><dd><a href="${esc(feed)}">${esc(feed)}</a></dd></dl><div class="copy"><strong>Copy 1</strong><p>${esc(copyA)}</p><strong>Copy 2</strong><p>${esc(copyB)}</p></div></article>`;
  }).join('\n');
}

const schema = {
  '@context': 'https://schema.org',
  '@type': 'CollectionPage',
  name: 'dateweb.de Kampagnen-Hub',
  description: 'Messbare Kampagnenlinks und Copy-Varianten für dateweb.de Ratgeber und lokale Dating-Seiten.',
  url: `${site}/kampagnen/`,
  hasPart: [...guides, ...cities].map(([name, pathname]) => ({ '@type': 'WebPage', name, url: `${site}${pathname}` })),
};

const html = `<!doctype html>
<html lang="de">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>dateweb.de Kampagnen-Hub: Ratgeber und lokale Dating-Links</title>
  <meta name="description" content="Messbare dateweb.de Kampagnenlinks für Ratgeber, lokale Dating-Seiten, Newsletter, Social und Community-Ausspielung.">
  <link rel="canonical" href="${site}/kampagnen/">
  <meta property="og:type" content="website">
  <meta property="og:title" content="dateweb.de Kampagnen-Hub">
  <meta property="og:description" content="Ratgeber- und lokale Dating-Kampagnen mit Matomo-Parametern und Copy-Varianten.">
  <meta property="og:url" content="${site}/kampagnen/">
  <meta property="og:image" content="${site}/media/generated/dateweb-hero-editorial-optimized.webp">
  <meta property="og:image:width" content="1200">
  <meta property="og:image:height" content="630">
  <meta property="og:image:alt" content="dateweb.de Kampagnen-Hub">
  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:image:alt" content="dateweb.de Kampagnen-Hub">
  <style>
    :root{--ink:#1f2423;--muted:#5f6a66;--line:#e7d8c7;--paper:#fffaf2;--teal:#1f716c;--white:#fffefb}*{box-sizing:border-box}body{margin:0;color:var(--ink);font-family:Georgia,'Times New Roman',serif;background:linear-gradient(135deg,#fffaf2,#edf8f4);line-height:1.62}main{width:min(1120px,calc(100% - 32px));margin:auto;padding:42px 0 70px}.brand{font:900 15px/1 ui-sans-serif,system-ui,sans-serif;text-decoration:none}h1{font-size:clamp(42px,7vw,78px);line-height:.92;letter-spacing:-.06em;margin:32px 0 18px}.lead{font-size:22px;color:#3f4945;max-width:820px}.meta{font:800 13px/1 ui-sans-serif,system-ui,sans-serif;color:var(--teal);letter-spacing:.08em;text-transform:uppercase}.grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px;margin:24px 0 42px}.campaign-card{background:#fff;border:1px solid var(--line);border-radius:24px;padding:22px;box-shadow:0 12px 34px rgba(42,35,24,.08)}h2{font-size:clamp(30px,4vw,48px);line-height:1;letter-spacing:-.04em;margin:38px 0 14px}h3{font-size:25px;line-height:1.08;margin:0 0 8px}.campaign-card p{color:var(--muted);margin:.35rem 0 1rem}dl{display:grid;gap:9px;margin:18px 0}dt{font:900 12px/1 ui-sans-serif,system-ui,sans-serif;color:var(--teal);text-transform:uppercase;letter-spacing:.06em}dd{margin:0;overflow-wrap:anywhere;font:13px/1.45 ui-sans-serif,system-ui,sans-serif}.copy{border-top:1px solid var(--line);padding-top:14px}.copy strong{font:900 12px/1 ui-sans-serif,system-ui,sans-serif}.cta{display:inline-flex;padding:14px 20px;border-radius:999px;background:var(--ink);color:#fff;text-decoration:none;font:800 15px/1 ui-sans-serif,system-ui,sans-serif}@media(max-width:780px){.grid{grid-template-columns:1fr}}
  </style>
  <script type="application/ld+json">${JSON.stringify(schema).replaceAll('</', '<\\/')}</script>
</head>
<body data-growth-marker="campaign_hub_growth">
<main>
  <a class="brand" href="/">dateweb.de</a>
  <p class="meta">campaign_hub_growth</p>
  <h1>Kampagnen-Hub für messbare Dating-Impulse</h1>
  <p class="lead">Diese Seite bündelt die wichtigsten Ratgeber- und lokalen Dating-Links mit Matomo-Parametern. Sie macht Ausspielung einfacher und sorgt dafür, dass Newsletter-, Social-, Community- und Feed-Klicks im Growth-Report sichtbar werden.</p>
  <a class="cta" href="/feed/?from=campaign_hub_feed">Zum öffentlichen Feed</a>
  <h2>Ratgeber-Kampagnen</h2>
  <section class="grid" data-growth-marker="campaign_hub_guide_links">${cards(guides, 'guide')}</section>
  <h2>Lokale Kampagnen</h2>
  <section class="grid" data-growth-marker="campaign_hub_local_links">${cards(cities, 'local')}</section>
</main>
</body>
</html>`;

fs.writeFileSync(path.join(route, 'index.html'), html, 'utf8');
fs.writeFileSync(path.join(route, 'index.php'), `<?php\nheader_remove('X-Powered-By');\nheader('X-Frame-Options: SAMEORIGIN');\nheader('X-Content-Type-Options: nosniff');\nheader('Referrer-Policy: strict-origin-when-cross-origin');\nheader('Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=()');\nheader('Strict-Transport-Security: max-age=31536000; includeSubDomains; preload');\nheader('X-Permitted-Cross-Domain-Policies: none');\nheader('Content-Type: text/html; charset=UTF-8');\nreadfile(__DIR__ . '/index.html');\n`, 'utf8');

const sitemapPath = path.join(webroot, 'sitemap.xml');
if (fs.existsSync(sitemapPath)) {
  let sitemap = fs.readFileSync(sitemapPath, 'utf8');
  const url = `${site}/kampagnen/`;
  if (!sitemap.includes(url)) {
    sitemap = sitemap.replace('</urlset>', `  <url>\n    <loc>${url}</loc>\n    <changefreq>weekly</changefreq>\n    <priority>0.64</priority>\n  </url>\n</urlset>`);
    fs.writeFileSync(sitemapPath, sitemap, 'utf8');
    console.log('Added campaign hub URL to sitemap.xml');
  }
}
console.log('generated campaign hub: /kampagnen/');