fix: remove duplicate page titles, add OG meta for review links
All checks were successful
Deploy / deploy (push) Successful in 11s
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:
@@ -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}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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, '&').replace(/"/g, '"').replace(/</g, '<');
|
||||
|
||||
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'));
|
||||
|
||||
Reference in New Issue
Block a user