File: //tmp/dateweb_add_local_growth_automation.cjs
const fs = require('fs');
const localDistribution = String.raw`#!/usr/bin/env node
import fs from 'node:fs';
import path from 'node:path';
const baseUrl = process.env.DATEWEB_BASE_URL || 'https://dateweb.de';
const outDir = process.env.DATEWEB_LOCAL_DISTRIBUTION_DIR || '/home/dateweb.de/logs/dateweb-local-distribution';
const stamp = new Date().toISOString().replace(/[-:]/g, '').replace(/\..+/, 'Z');
fs.mkdirSync(outDir, { recursive: true });
const cities = [
['muenchen', 'München', 'dating_muenchen', 'Dating in München', 'Singles ab 30 mit vollem Kalender und klaren Ansprüchen'],
['hamburg', 'Hamburg', 'dating_hamburg', 'Dating in Hamburg', 'Singles, die entspannte Treffen am Wasser und weniger Chat-Leerlauf suchen'],
['berlin', 'Berlin', 'dating_berlin', 'Dating in Berlin', 'Singles, die trotz großer Auswahl verbindlicher filtern wollen'],
['koeln', 'Köln', 'dating_koeln', 'Dating in Köln', 'Singles, die locker bleiben und trotzdem konkret treffen wollen'],
['frankfurt', 'Frankfurt', 'dating_frankfurt', 'Dating in Frankfurt', 'Zeitbewusste Singles mit beruflich dichtem Alltag'],
['duesseldorf', 'Düsseldorf', 'dating_duesseldorf', 'Dating in Düsseldorf', 'Singles, die Stil, Rhein-Nähe und klare Absicht verbinden wollen'],
['stuttgart', 'Stuttgart', 'dating_stuttgart', 'Dating in Stuttgart', 'Singles mit vollem Alltag und Qualitätsanspruch'],
['leipzig', 'Leipzig', 'dating_leipzig', 'Dating in Leipzig', 'Singles, die kreativ und entspannt, aber verbindlich starten wollen'],
['dortmund', 'Dortmund', 'dating_dortmund', 'Dating in Dortmund', 'Singles, die bodenständig und direkt kennenlernen wollen'],
['essen', 'Essen', 'dating_essen', 'Dating in Essen', 'Singles im Ruhrgebiet, die konkrete Treffen statt Chat-Schleifen suchen'],
['bremen', 'Bremen', 'dating_bremen', 'Dating in Bremen', 'Singles, die überschaubarer und persönlicher daten wollen'],
['dresden', 'Dresden', 'dating_dresden', 'Dating in Dresden', 'Singles, die Kultur und sichere erste Treffen verbinden wollen'],
['hannover', 'Hannover', 'dating_hannover', 'Dating in Hannover', 'Singles, die unaufgeregt und ehrlich kennenlernen wollen'],
['nuernberg', 'Nürnberg', 'dating_nuernberg', 'Dating in Nürnberg', 'Singles, die kurze sichere Treffen und ehrliche Profile suchen'],
['karlsruhe', 'Karlsruhe', 'dating_karlsruhe', 'Dating in Karlsruhe', 'Singles zwischen Studium, Technik, Beruf und Alltag'],
];
function campaignUrl(slug, source, medium, keyword) {
const url = new URL('/dating/' + slug + '/', baseUrl);
url.searchParams.set('mtm_campaign', 'local_' + keyword + '_launch');
url.searchParams.set('mtm_source', source);
url.searchParams.set('mtm_medium', medium);
url.searchParams.set('mtm_keyword', keyword);
return url.toString();
}
const lines = [];
lines.push('# dateweb.de Local Growth Distribution Plan');
lines.push('');
lines.push('- Zeitpunkt: ' + new Date().toISOString());
lines.push('- Basis-URL: ' + baseUrl);
lines.push('- Lokale URLs: ' + cities.length);
lines.push('');
lines.push('## IndexNow-/Sitemap-URLs');
lines.push('');
for (const [slug] of cities) lines.push('- ' + new URL('/dating/' + slug + '/', baseUrl).toString());
lines.push('');
lines.push('## Lokale Kampagnenlinks');
for (const [slug, name, keyword, title, audience] of cities) {
lines.push('');
lines.push('### ' + title);
lines.push('- Stadt: ' + name);
lines.push('- Zielgruppe: ' + audience);
lines.push('- Direkt: ' + new URL('/dating/' + slug + '/', baseUrl).toString());
lines.push('- Newsletter: ' + campaignUrl(slug, 'newsletter', 'owned', keyword));
lines.push('- Social: ' + campaignUrl(slug, 'social', 'organic', keyword));
lines.push('- Community: ' + campaignUrl(slug, 'community', 'organic', keyword));
lines.push('- Anchor-Texte: Dating in ' + name + ' · Singles ab 30 in ' + name + ' · erstes Date in ' + name + ' planen');
lines.push('Copy-Varianten:');
lines.push('- Dating in ' + name + ': lokale Date-Ideen, klare Profilabsicht und ein ruhiger Weg vom Match zum ersten Treffen.');
lines.push('- Für Singles ab 30 in ' + name + ': weniger Chat-Leerlauf, bessere erste Nachrichten und öffentliche Treffen mit klarer Dauer.');
}
lines.push('');
lines.push('## Operative Checks');
lines.push('');
lines.push('1. Sitemap muss alle lokalen URLs enthalten.');
lines.push('2. IndexNow muss die lokale URL-Liste nach Deploy mit einreichen.');
lines.push('3. Nach 48-72 Stunden Local Watch prüfen: Stadtseiten mit Requests priorisieren, Stadtseiten ohne Requests stärker intern und extern verteilen.');
lines.push('4. Städte mit Register-Referrern bekommen eigene CTA-Varianten auf Home, Tools und passenden Ratgebern.');
const reportPath = path.join(outDir, 'local-distribution-' + stamp + '.md');
fs.writeFileSync(reportPath, lines.join('\n') + '\n', 'utf8');
console.log(reportPath);
`;
const localWatch = String.raw`#!/usr/bin/env bash
set -euo pipefail
INSIGHTS_DIR="${DATEWEB_INSIGHTS_DIR:-/home/dateweb.de/logs/dateweb-growth-insights}"
DISTRIBUTION_DIR="${DATEWEB_LOCAL_DISTRIBUTION_DIR:-/home/dateweb.de/logs/dateweb-local-distribution}"
BACKLOG_DIR="${DATEWEB_BACKLOG_DIR:-/home/dateweb.de/logs/dateweb-growth-backlog}"
OUT_DIR="${DATEWEB_LOCAL_WATCH_DIR:-/home/dateweb.de/logs/dateweb-local-watch}"
mkdir -p "$OUT_DIR"
RUN_ID="$(date -u +%Y%m%d_%H%M%S)"
OUT="$OUT_DIR/local-watch-$RUN_ID.md"
LATEST_INSIGHTS="$(ls -t "$INSIGHTS_DIR"/growth-insights-*.md 2>/dev/null | head -1 || true)"
LATEST_DISTRIBUTION="$(ls -t "$DISTRIBUTION_DIR"/local-distribution-*.md 2>/dev/null | head -1 || true)"
LATEST_BACKLOG="$(ls -t "$BACKLOG_DIR"/growth-backlog-*.md 2>/dev/null | head -1 || true)"
cities=(muenchen hamburg berlin koeln frankfurt duesseldorf stuttgart leipzig dortmund essen bremen dresden hannover nuernberg karlsruhe)
city_names=(München Hamburg Berlin Köln Frankfurt Düsseldorf Stuttgart Leipzig Dortmund Essen Bremen Dresden Hannover Nürnberg Karlsruhe)
count_route() {
local route="$1"
local file="$2"
if [ -z "$file" ] || [ ! -s "$file" ]; then printf '0'; return; fi
awk -v route="\`$route\`" '$0 ~ /^\| / && $0 ~ route {gsub(/ /,"",$3); print $3; found=1} END{if(!found) print 0}' "$file" | head -1
}
{
echo '# dateweb.de Local Watch'
echo
echo "- Zeitpunkt: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "- Insights: ${LATEST_INSIGHTS:-fehlt}"
echo "- Distribution: ${LATEST_DISTRIBUTION:-fehlt}"
echo "- Backlog: ${LATEST_BACKLOG:-fehlt}"
echo
echo '## Lokale Seiten: Requests'
echo '| Stadt | Route | Requests | Status |'
echo '| --- |--- |--- |--- |'
total=0
active=0
for i in "${!cities[@]}"; do
route="/dating/${cities[$i]}/"
count="$(count_route "$route" "$LATEST_INSIGHTS")"
total=$((total + count))
if [ "$count" -gt 0 ]; then active=$((active + 1)); status='aktiv'; else status='beobachten'; fi
echo "| ${city_names[$i]} | \`$route\` | $count | $status |"
done
echo
echo '## Zusammenfassung'
echo "- Lokale Gesamtrequests: $total"
echo "- Städte mit Requests: $active / ${#cities[@]}"
echo
echo '## Nächste Aktionen'
if [ "$total" -eq 0 ]; then
echo '- Alle neuen lokalen Seiten weiter über Kampagnenlinks, Sitemap und interne Hubs verteilen; erstes Trafficfenster abwarten.'
else
echo '- Städte mit ersten Requests priorisieren: lokale CTA-Brücken, interne Links und Ratgeber-Verweise für diese Städte ausbauen.'
fi
echo '- Nach 48-72 Stunden Such- und Referrer-Signale prüfen; Städte ohne Signal stärker im Feed, Tools-Hub und passenden Ratgebern verlinken.'
} > "$OUT"
echo "local watch written: $OUT"
`;
fs.writeFileSync('scripts/local-distribution-plan.mjs', localDistribution, 'utf8');
fs.writeFileSync('scripts/local-pages-watch.sh', localWatch, 'utf8');
fs.chmodSync('scripts/local-pages-watch.sh', 0o755);
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
pkg.scripts['ops:local-distribution'] = 'node scripts/local-distribution-plan.mjs';
pkg.scripts['ops:local-watch'] = 'bash scripts/local-pages-watch.sh';
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n', 'utf8');
let cron = fs.readFileSync('scripts/install-growth-cron.sh', 'utf8');
if (!cron.includes('ops:local-distribution')) {
cron = cron.replace('npm run ops:growth-distribution', 'npm run ops:growth-distribution && npm run ops:local-distribution');
}
if (!cron.includes('ops:local-watch')) {
cron = cron.replace('npm run ops:cluster-watch', 'npm run ops:cluster-watch && npm run ops:local-watch');
}
fs.writeFileSync('scripts/install-growth-cron.sh', cron, 'utf8');