Add OG meta tags for participant access pages
This commit is contained in:
@@ -6,7 +6,7 @@ import path from 'path';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
import { initializeDatabase, questionnaireOps } from './database.js';
|
import { initializeDatabase, questionnaireOps, participantOps } from './database.js';
|
||||||
import authRoutes from './routes/auth.js';
|
import authRoutes from './routes/auth.js';
|
||||||
import adminRoutes from './routes/admin.js';
|
import adminRoutes from './routes/admin.js';
|
||||||
import questionnaireRoutes from './routes/questionnaire.js';
|
import questionnaireRoutes from './routes/questionnaire.js';
|
||||||
@@ -76,6 +76,21 @@ if (isProduction) {
|
|||||||
const clientPath = path.join(__dirname, '../client');
|
const clientPath = path.join(__dirname, '../client');
|
||||||
app.use(express.static(clientPath));
|
app.use(express.static(clientPath));
|
||||||
|
|
||||||
|
// Helper function to generate OG tags HTML
|
||||||
|
function generateOgTags(baseUrl: string, pageUrl: string, title: string, description: string, ogImageUrl: string | null): string {
|
||||||
|
return `
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:url" content="${pageUrl}" />
|
||||||
|
<meta property="og:title" content="${title}" />
|
||||||
|
<meta property="og:description" content="${description}" />
|
||||||
|
${ogImageUrl ? `<meta property="og:image" content="${ogImageUrl}" />` : ''}
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:title" content="${title}" />
|
||||||
|
<meta name="twitter:description" content="${description}" />
|
||||||
|
${ogImageUrl ? `<meta name="twitter:image" content="${ogImageUrl}" />` : ''}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
// Dynamic OG meta tags for questionnaire pages
|
// Dynamic OG meta tags for questionnaire pages
|
||||||
app.get('/q/:slug', (req, res) => {
|
app.get('/q/:slug', (req, res) => {
|
||||||
const questionnaire = questionnaireOps.findBySlug(req.params.slug);
|
const questionnaire = questionnaireOps.findBySlug(req.params.slug);
|
||||||
@@ -101,17 +116,53 @@ if (isProduction) {
|
|||||||
ogImageUrl = `${baseUrl}${ogImageUrl}`;
|
ogImageUrl = `${baseUrl}${ogImageUrl}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ogTags = `
|
const ogTags = generateOgTags(baseUrl, pageUrl, title, description, ogImageUrl);
|
||||||
<meta property="og:type" content="website" />
|
|
||||||
<meta property="og:url" content="${pageUrl}" />
|
// Insert OG tags before </head>
|
||||||
<meta property="og:title" content="${title}" />
|
html = html.replace('</head>', `${ogTags}</head>`);
|
||||||
<meta property="og:description" content="${description}" />
|
|
||||||
${ogImageUrl ? `<meta property="og:image" content="${ogImageUrl}" />` : ''}
|
// Update title
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
html = html.replace(/<title>.*?<\/title>/, `<title>${title} - Activiteiten Inventaris</title>`);
|
||||||
<meta name="twitter:title" content="${title}" />
|
|
||||||
<meta name="twitter:description" content="${description}" />
|
res.send(html);
|
||||||
${ogImageUrl ? `<meta name="twitter:image" content="${ogImageUrl}" />` : ''}
|
});
|
||||||
`;
|
|
||||||
|
// Dynamic OG meta tags for participant access pages
|
||||||
|
app.get('/q/access/:token', (req, res) => {
|
||||||
|
const participant = participantOps.findByToken(req.params.token);
|
||||||
|
const indexPath = path.join(clientPath, 'index.html');
|
||||||
|
|
||||||
|
if (!participant) {
|
||||||
|
res.sendFile(indexPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const questionnaire = questionnaireOps.findById(participant.questionnaire_id);
|
||||||
|
if (!questionnaire) {
|
||||||
|
res.sendFile(indexPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the index.html template
|
||||||
|
let html = fs.readFileSync(indexPath, 'utf-8');
|
||||||
|
|
||||||
|
// Build meta tags
|
||||||
|
const baseUrl = `${req.protocol}://${req.get('host')}`;
|
||||||
|
const pageUrl = `${baseUrl}/q/access/${participant.token}`;
|
||||||
|
const title = questionnaire.title;
|
||||||
|
|
||||||
|
// Get first name from participant name
|
||||||
|
const firstName = participant.name.trim().split(/\s+/)[0] || participant.name;
|
||||||
|
const description = questionnaire.description
|
||||||
|
|| `Hoi ${firstName}! Voeg je ideeën toe en stem op activiteiten.`;
|
||||||
|
|
||||||
|
// Make image URL absolute if it's a relative path
|
||||||
|
let ogImageUrl = questionnaire.og_image;
|
||||||
|
if (ogImageUrl && ogImageUrl.startsWith('/')) {
|
||||||
|
ogImageUrl = `${baseUrl}${ogImageUrl}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ogTags = generateOgTags(baseUrl, pageUrl, title, description, ogImageUrl);
|
||||||
|
|
||||||
// Insert OG tags before </head>
|
// Insert OG tags before </head>
|
||||||
html = html.replace('</head>', `${ogTags}</head>`);
|
html = html.replace('</head>', `${ogTags}</head>`);
|
||||||
|
|||||||
Reference in New Issue
Block a user