fix: remove duplicate page titles, add OG meta for review links
All checks were successful
Deploy / deploy (push) Successful in 11s

- Removed h1 titles from Settings and Brands (already in Header)
- Server injects og:title, og:description, og:image meta tags for
  /review-post/:token URLs so WhatsApp/Slack show proper previews

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
fahed
2026-03-05 17:50:45 +03:00
parent 96fb838388
commit f4cf2e39a4
3 changed files with 48 additions and 15 deletions

View File

@@ -129,13 +129,7 @@ export default function Brands() {
<div className="space-y-6 animate-fade-in">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-text-primary flex items-center gap-3">
<Tag className="w-7 h-7 text-brand-primary" />
{t('brands.title')}
</h1>
<p className="text-sm text-text-tertiary mt-1">{t('brands.manageBrands')}</p>
</div>
<p className="text-sm text-text-tertiary">{t('brands.manageBrands')}</p>
{isSuperadminOrManager && (
<button
onClick={openNewBrand}

View File

@@ -62,14 +62,7 @@ export default function Settings() {
return (
<div className="space-y-6 animate-fade-in max-w-3xl">
{/* Header */}
<div>
<h1 className="text-2xl font-bold text-text-primary flex items-center gap-3">
<SettingsIcon className="w-7 h-7 text-brand-primary" />
{t('settings.title')}
</h1>
<p className="text-sm text-text-tertiary mt-1">{t('settings.preferences')}</p>
</div>
<p className="text-sm text-text-tertiary">{t('settings.preferences')}</p>
{/* General Settings */}
<div className="bg-white rounded-xl border border-border overflow-hidden">

View File

@@ -4695,6 +4695,52 @@ async function startServer() {
if (process.env.NODE_ENV === 'production') {
const clientDist = path.join(__dirname, '..', 'client', 'dist');
app.use(express.static(clientDist));
// OG meta tags for public review links (WhatsApp, Slack, etc.)
app.get('/review-post/:token', async (req, res) => {
try {
const indexHtml = require('fs').readFileSync(path.join(clientDist, 'index.html'), 'utf-8');
const posts = await nocodb.list('Posts', {
where: `(approval_token,eq,${sanitizeWhereValue(req.params.token)})`,
limit: 1,
});
if (posts.length === 0) return res.send(indexHtml);
const post = posts[0];
// Find first image attachment for thumbnail
let ogImage = '';
try {
const attachments = await nocodb.list('PostAttachments', {
where: `(post_id,eq,${post.Id})`,
limit: 10,
});
const img = attachments.find(a => (a.mime_type || '').startsWith('image/'));
if (img) {
const baseUrl = `${req.protocol}://${req.get('host')}`;
ogImage = img.url?.startsWith('http') ? img.url : `${baseUrl}${img.url}`;
}
} catch (e) { /* no attachments */ }
const title = post.title || 'Post Review';
const desc = post.description ? post.description.slice(0, 200) : 'Review and approve this post';
const escape = s => s.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;');
const ogTags = `
<meta property="og:title" content="${escape(title)}" />
<meta property="og:description" content="${escape(desc)}" />
<meta property="og:type" content="article" />
<meta property="og:url" content="${req.protocol}://${req.get('host')}${req.originalUrl}" />${ogImage ? `
<meta property="og:image" content="${escape(ogImage)}" />` : ''}
<meta name="twitter:card" content="${ogImage ? 'summary_large_image' : 'summary'}" />`;
const html = indexHtml.replace('</head>', ogTags + '\n </head>');
res.send(html);
} catch (err) {
console.error('OG meta error:', err.message);
res.sendFile(path.join(clientDist, 'index.html'));
}
});
app.get('*', (req, res) => {
if (!req.path.startsWith('/api')) {
res.sendFile(path.join(clientDist, 'index.html'));