Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ All notable changes to CV Manager will be documented in this file.

Format follows [Keep a Changelog](https://keepachangelog.com/), versioning follows [Semantic Versioning](https://semver.org/).

## [1.49.3] - 2026-05-05

### Fixed
- **Public site was being deindexed by Google after the multi-language rollout.** Two issues combined into a strong "don't index" signal:
1. The admin "Preview Mode — This is a saved version and not publicly accessible" banner was hardcoded into `public-readonly/index.html` and only hidden via CSS. Crawlers read hidden text, so every public page advertised itself as not publicly accessible — a textbook soft-404 signal that caused Google to drop the URL even after re-indexing requests.
2. The public homepage was a near-empty shell server-side: `<h1>Loading…</h1>` with empty section containers. All CV content was hydrated client-side, but Googlebot frequently skips the second render pass on low-authority subdomains and treated the page as thin content.

The preview banner DOM is now built and inserted from JavaScript only when `window.DATASET_PREVIEW` is true (admin preview context), so its text never appears in HTML served to public visitors. `servePublicIndex` and `serveDatasetPage` in `src/server.js` now server-render the profile, about, experience, certifications, education, skills, and projects sections into the HTML before sending — the client JS still hydrates the same nodes on load, so behaviour is unchanged for users while crawlers see real CV content on first byte. Also set `<html lang>` correctly in the live-DB fallback path (it was already correct on the default-dataset path).

## [1.49.2] - 2026-05-04

### Fixed
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cv-manager",
"version": "1.49.2",
"version": "1.49.3",
"description": "Professional CV Management System",
"main": "src/server.js",
"scripts": {
Expand Down
36 changes: 22 additions & 14 deletions public-readonly/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,6 @@
<div class="public-lang-dropdown" id="publicLangDropdown"></div>
</div>

<!-- Preview Banner (only shown for versioned dataset URLs) -->
<div class="preview-banner no-print" id="previewBanner" style="display: none;">
<div class="preview-banner-content">
<span class="material-symbols-outlined" style="font-size:18px">visibility</span>
<span class="preview-banner-text">Preview Mode — This is a saved version and not publicly accessible</span>
</div>
<a href="/" class="preview-banner-btn">
<span class="material-symbols-outlined" style="font-size:14px">arrow_back</span>
Back to Admin
</a>
</div>

<div class="container" role="main">
<!-- ATS-friendly hidden text version -->
<div id="ats-content" class="ats-only" aria-hidden="true"></div>
Expand Down Expand Up @@ -295,9 +283,11 @@ <h2 class="section-title" data-i18n="section.projects">Featured Projects</h2>
return;
}

// Show preview banner only in admin preview mode (not on public-facing versioned URLs)
// Show preview banner only in admin preview mode (not on public-facing versioned URLs).
// Banner DOM is built here rather than shipped in the HTML so the banner copy
// never appears in raw HTML on canonical public pages (a soft-404 SEO signal).
if (window.DATASET_PREVIEW) {
document.getElementById('previewBanner').style.display = 'flex';
injectPreviewBanner();
document.body.classList.add('has-preview-banner');
}

Expand Down Expand Up @@ -373,6 +363,24 @@ <h2 class="section-title" data-i18n="section.projects">Featured Projects</h2>
}
}

function injectPreviewBanner() {
if (document.getElementById('previewBanner')) return;
const banner = document.createElement('div');
banner.className = 'preview-banner no-print';
banner.id = 'previewBanner';
banner.style.display = 'flex';
banner.innerHTML = `
<div class="preview-banner-content">
<span class="material-symbols-outlined" style="font-size:18px">visibility</span>
<span class="preview-banner-text">${escapeHtml(t('preview.banner_text'))}</span>
</div>
<a href="/" class="preview-banner-btn">
<span class="material-symbols-outlined" style="font-size:14px">arrow_back</span>
${escapeHtml(t('preview.back_to_admin'))}
</a>`;
document.body.insertBefore(banner, document.body.firstChild);
}

// Render functions for dataset data
function renderProfileFromData(p) {
if (!p) return;
Expand Down
2 changes: 2 additions & 0 deletions public/shared/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,8 @@
"datasets.make_private": "Privat machen",
"datasets.make_shared": "Freigeben",
"datasets.preview": "Vorschau",
"preview.banner_text": "Vorschaumodus — Dies ist eine gespeicherte Version und nicht öffentlich zugänglich",
"preview.back_to_admin": "Zurück zur Verwaltung",
"datasets.copy_url": "URL kopieren",
"ats.english_headers": "Abschnittsüberschriften auf Englisch",
"datasets.legend_default": "Wird auf der öffentlichen Seite angezeigt",
Expand Down
2 changes: 2 additions & 0 deletions public/shared/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@
"datasets.make_private": "Make private",
"datasets.make_shared": "Make shared",
"datasets.preview": "Preview",
"preview.banner_text": "Preview Mode — This is a saved version and not publicly accessible",
"preview.back_to_admin": "Back to Admin",
"datasets.copy_url": "Copy URL",
"datasets.legend.radio": "select which dataset visitors see at your root URL",
"datasets.legend.toggle": "share other datasets at their own /v/slug URL",
Expand Down
2 changes: 2 additions & 0 deletions public/shared/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,8 @@
"datasets.make_private": "Hacer privado",
"datasets.make_shared": "Compartir",
"datasets.preview": "Vista previa",
"preview.banner_text": "Modo vista previa — Esta es una versión guardada y no es accesible públicamente",
"preview.back_to_admin": "Volver a la administración",
"datasets.copy_url": "Copiar URL",
"ats.english_headers": "Encabezados de sección en inglés",
"datasets.legend_default": "Publicado en el sitio público",
Expand Down
2 changes: 2 additions & 0 deletions public/shared/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,8 @@
"datasets.make_private": "Rendre privé",
"datasets.make_shared": "Partager",
"datasets.preview": "Aperçu",
"preview.banner_text": "Mode aperçu — Ceci est une version enregistrée et n'est pas accessible publiquement",
"preview.back_to_admin": "Retour à l'administration",
"datasets.copy_url": "Copier l'URL",
"ats.english_headers": "Titres de section en anglais",
"datasets.legend_default": "Affiché sur le site public",
Expand Down
2 changes: 2 additions & 0 deletions public/shared/i18n/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,8 @@
"datasets.make_private": "Rendi privato",
"datasets.make_shared": "Condividi",
"datasets.preview": "Anteprima",
"preview.banner_text": "Modalità anteprima — Questa è una versione salvata e non è accessibile pubblicamente",
"preview.back_to_admin": "Torna all'amministrazione",
"datasets.copy_url": "Copia URL",
"ats.english_headers": "Intestazioni sezione in inglese",
"datasets.legend_default": "Mostrato sul sito pubblico",
Expand Down
2 changes: 2 additions & 0 deletions public/shared/i18n/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,8 @@
"datasets.make_private": "Privé maken",
"datasets.make_shared": "Delen",
"datasets.preview": "Voorbeeld",
"preview.banner_text": "Voorbeeldmodus — Dit is een opgeslagen versie en niet publiekelijk toegankelijk",
"preview.back_to_admin": "Terug naar beheer",
"datasets.copy_url": "URL kopiëren",
"ats.english_headers": "Sectiekoppen in het Engels",
"datasets.legend_default": "Weergegeven op de publieke site",
Expand Down
2 changes: 2 additions & 0 deletions public/shared/i18n/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,8 @@
"datasets.make_private": "Tornar privado",
"datasets.make_shared": "Compartilhar",
"datasets.preview": "Visualizar",
"preview.banner_text": "Modo de visualização — Esta é uma versão salva e não está acessível publicamente",
"preview.back_to_admin": "Voltar à administração",
"datasets.copy_url": "Copiar URL",
"ats.english_headers": "Cabeçalhos de secção em inglês",
"datasets.legend_default": "Exibido no site público",
Expand Down
2 changes: 2 additions & 0 deletions public/shared/i18n/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,8 @@
"datasets.make_private": "设为私密",
"datasets.make_shared": "设为共享",
"datasets.preview": "预览",
"preview.banner_text": "预览模式 — 这是已保存的版本,未公开访问",
"preview.back_to_admin": "返回管理后台",
"datasets.copy_url": "复制链接",
"ats.english_headers": "章节标题使用英文",
"datasets.legend_default": "显示在公开网站上",
Expand Down
Loading
Loading