folx-tv/server/vite.ts
sebastjanartic e5fcb182e7 Update gallery page metadata to reflect spontaneous moments from recordings
Update meta tags, title, and description for the gallery page to accurately describe it as featuring unique moments from recordings and backstage snapshots, rather than solely concert photos. This includes changes in client/src/pages/gallery.tsx, server/static.ts, and server/vite.ts.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 23852c00-4779-460a-9e0c-d09fee4b6c92
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 67a932ea-d8a9-4376-a0ba-5b355f4f70b7
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/23852c00-4779-460a-9e0c-d09fee4b6c92/OPD8Ro3
Replit-Helium-Checkpoint-Created: true
2026-03-08 07:34:21 +00:00

196 lines
9.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { type Express } from "express";
import { createServer as createViteServer, createLogger } from "vite";
import { type Server } from "http";
import viteConfig from "../vite.config";
import fs from "fs";
import path from "path";
import { nanoid } from "nanoid";
import { storage } from "./storage";
const viteLogger = createLogger();
function escapeHtml(str: string): string {
return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}
function ogImageUrl(coverImage: string, baseUrl: string): string {
if (!coverImage) return "";
let imgPath = coverImage;
if (imgPath.endsWith(".webp")) {
imgPath = imgPath.replace(/\.webp$/, ".jpg");
}
return imgPath.startsWith("http") ? imgPath : `${baseUrl}${imgPath}`;
}
function stripExistingMeta(html: string): string {
html = html.replace(/<meta\s+property="og:[^"]*"[^>]*>\s*/gi, "");
html = html.replace(/<meta\s+name="twitter:[^"]*"[^>]*>\s*/gi, "");
html = html.replace(/<meta\s+name="description"[^>]*>\s*/gi, "");
html = html.replace(/<meta\s+name="keywords"[^>]*>\s*/gi, "");
html = html.replace(/<link\s+rel="canonical"[^>]*>\s*/gi, "");
return html;
}
export async function setupVite(server: Server, app: Express) {
const serverOptions = {
middlewareMode: true,
hmr: { server, path: "/vite-hmr" },
allowedHosts: true as const,
};
const vite = await createViteServer({
...viteConfig,
configFile: false,
customLogger: {
...viteLogger,
error: (msg, options) => {
viteLogger.error(msg, options);
process.exit(1);
},
},
server: serverOptions,
appType: "custom",
});
app.use(vite.middlewares);
app.use("/{*path}", async (req, res, next) => {
const url = req.originalUrl;
const canonicalBase = "https://folx.tv";
try {
const clientTemplate = path.resolve(
import.meta.dirname,
"..",
"client",
"index.html",
);
let template = await fs.promises.readFile(clientTemplate, "utf-8");
template = template.replace(
`src="/src/main.tsx"`,
`src="/src/main.tsx?v=${nanoid()}"`,
);
const articleMatch = url.match(/^\/article\/([^?#]+)/);
if (articleMatch) {
try {
const slug = decodeURIComponent(articleMatch[1]);
const article = await storage.getArticleBySlug(slug);
if (article) {
const articleUrl = `${canonicalBase}/article/${article.slug}`;
const imageUrl = ogImageUrl(article.coverImage || "", canonicalBase);
const finalImage = imageUrl || `${canonicalBase}/og-image.jpg`;
template = stripExistingMeta(template);
const ogTags = [
`<meta property="og:title" content="${escapeHtml(article.title)}" />`,
`<meta property="og:description" content="${escapeHtml(article.excerpt)}" />`,
`<meta property="og:type" content="article" />`,
`<meta property="og:url" content="${escapeHtml(articleUrl)}" />`,
`<meta property="og:image" content="${escapeHtml(finalImage)}" />`,
`<meta property="og:image:secure_url" content="${escapeHtml(finalImage)}" />`,
`<meta property="og:image:width" content="800" />`,
`<meta property="og:image:height" content="450" />`,
`<meta property="og:image:type" content="image/jpeg" />`,
`<meta property="og:site_name" content="Folx Music Television" />`,
`<meta property="og:locale" content="de_DE" />`,
`<meta property="fb:pages" content="1428520781492675" />`,
`<meta name="twitter:card" content="summary_large_image" />`,
`<meta name="twitter:title" content="${escapeHtml(article.title)}" />`,
`<meta name="twitter:description" content="${escapeHtml(article.excerpt)}" />`,
`<meta name="twitter:image" content="${escapeHtml(finalImage)}" />`,
`<meta name="description" content="${escapeHtml(article.excerpt)}" />`,
`<meta name="keywords" content="Volksmusik, Schlager, ${escapeHtml(article.title)}, ${escapeHtml(article.category || 'News')}, FOLX TV" />`,
`<link rel="canonical" href="${escapeHtml(articleUrl)}" />`,
`<title>${escapeHtml(article.title)} - Volksmusik & Schlager | Folx Music Television</title>`,
].join("\n ");
template = template.replace(/<title>[^<]*<\/title>/, ogTags);
}
} catch (e) {
}
}
if (url.match(/^\/gallery(\/|$|\?)/)) {
template = stripExistingMeta(template);
const galTitle = "Fotogalerie \u2013 Einzigartige Momente von unseren Aufzeichnungen | FOLX TV";
const galDesc = "Spannende Schnappsch\u00fcsse und besondere Augenblicke, eingefangen bei unseren Aufzeichnungen. Authentische Bilder der Volksmusik- und Schlager-Stars in einzigartigen Momenten bei FOLX TV.";
const galUrl = `${canonicalBase}/gallery`;
const galImage = `${canonicalBase}/images/og-image.jpg`;
const galTags = [
`<meta property="og:title" content="${escapeHtml(galTitle)}" />`,
`<meta property="og:description" content="${escapeHtml(galDesc)}" />`,
`<meta property="og:type" content="website" />`,
`<meta property="og:url" content="${escapeHtml(galUrl)}" />`,
`<meta property="og:image" content="${escapeHtml(galImage)}" />`,
`<meta property="og:image:secure_url" content="${escapeHtml(galImage)}" />`,
`<meta property="og:image:width" content="1200" />`,
`<meta property="og:image:height" content="630" />`,
`<meta property="og:image:type" content="image/jpeg" />`,
`<meta property="og:site_name" content="Folx Music Television" />`,
`<meta property="og:locale" content="de_DE" />`,
`<meta name="twitter:card" content="summary_large_image" />`,
`<meta name="twitter:title" content="${escapeHtml(galTitle)}" />`,
`<meta name="twitter:description" content="${escapeHtml(galDesc)}" />`,
`<meta name="twitter:image" content="${escapeHtml(galImage)}" />`,
`<meta name="description" content="${escapeHtml(galDesc)}" />`,
`<meta name="keywords" content="Fotogalerie, Volksmusik Fotos, Schlager Bilder, Aufzeichnungen, Schnappschüsse, FOLX TV, Volksmusik Stars" />`,
`<link rel="canonical" href="${escapeHtml(galUrl)}" />`,
`<title>${escapeHtml(galTitle)}</title>`,
].join("\n ");
template = template.replace(/<title>[^<]*<\/title>/, galTags);
}
if (url.match(/^\/horoskop(\/|$|\?)/)) {
template = stripExistingMeta(template);
const signMatch = url.match(/^\/horoskop\/([^?#]+)/);
const signName = signMatch ? decodeURIComponent(signMatch[1]) : null;
const capitalSign = signName ? signName.charAt(0).toUpperCase() + signName.slice(1) : null;
const title = capitalSign
? `${capitalSign} Horoskop Tages-, Wochen- & Monatshoroskop | Folx Music Television`
: "Horoskop Tages-, Wochen- & Monatshoroskop für alle Sternzeichen | Folx Music Television";
const desc = capitalSign
? `${capitalSign} Horoskop: Tägliches, wöchentliches und monatliches Horoskop. Liebe, Beruf & Gesundheit bei FOLX TV.`
: "Kostenloses Tageshoroskop, Wochenhoroskop & Monatshoroskop für alle 12 Sternzeichen. Liebe, Beruf, Gesundheit bei FOLX TV.";
const horoUrl = `${canonicalBase}${signName ? `/horoskop/${signName}` : "/horoskop"}`;
const horoImage = `${canonicalBase}/images/horoskop-og.jpg`;
const horoTags = [
`<meta property="og:title" content="${escapeHtml(title)}" />`,
`<meta property="og:description" content="${escapeHtml(desc)}" />`,
`<meta property="og:type" content="website" />`,
`<meta property="og:url" content="${escapeHtml(horoUrl)}" />`,
`<meta property="og:image" content="${escapeHtml(horoImage)}" />`,
`<meta property="og:image:secure_url" content="${escapeHtml(horoImage)}" />`,
`<meta property="og:image:width" content="1200" />`,
`<meta property="og:image:height" content="675" />`,
`<meta property="og:image:type" content="image/jpeg" />`,
`<meta property="og:site_name" content="Folx Music Television" />`,
`<meta property="og:locale" content="de_DE" />`,
`<meta name="twitter:card" content="summary_large_image" />`,
`<meta name="twitter:title" content="${escapeHtml(title)}" />`,
`<meta name="twitter:description" content="${escapeHtml(desc)}" />`,
`<meta name="twitter:image" content="${escapeHtml(horoImage)}" />`,
`<meta name="description" content="${escapeHtml(desc)}" />`,
`<meta name="keywords" content="Horoskop, Tageshoroskop, Wochenhoroskop, Monatshoroskop, Sternzeichen, Volksmusik, FOLX TV${capitalSign ? `, ${capitalSign}` : ""}" />`,
`<link rel="canonical" href="${escapeHtml(horoUrl)}" />`,
`<title>${escapeHtml(title)}</title>`,
].join("\n ");
template = template.replace(/<title>[^<]*<\/title>/, horoTags);
}
const page = await vite.transformIndexHtml(url, template);
res.status(200).set({ "Content-Type": "text/html" }).end(page);
} catch (e) {
vite.ssrFixStacktrace(e as Error);
next(e);
}
});
}