mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 08:36:12 +01:00
feat: closes #12656, only send required meta/link tags on /api calls
This commit is contained in:
117
src/meta/tags.js
117
src/meta/tags.js
@@ -14,8 +14,10 @@ const relative_path = nconf.get('relative_path');
|
|||||||
const upload_url = nconf.get('upload_url');
|
const upload_url = nconf.get('upload_url');
|
||||||
|
|
||||||
Tags.parse = async (req, data, meta, link) => {
|
Tags.parse = async (req, data, meta, link) => {
|
||||||
|
const isAPI = req.res && req.res.locals && req.res.locals.isAPI;
|
||||||
|
|
||||||
// Meta tags
|
// Meta tags
|
||||||
const defaultTags = [{
|
const defaultTags = isAPI ? [] : [{
|
||||||
name: 'viewport',
|
name: 'viewport',
|
||||||
content: 'width=device-width, initial-scale=1.0',
|
content: 'width=device-width, initial-scale=1.0',
|
||||||
}, {
|
}, {
|
||||||
@@ -40,14 +42,14 @@ Tags.parse = async (req, data, meta, link) => {
|
|||||||
content: Meta.config.themeColor || '#ffffff',
|
content: Meta.config.themeColor || '#ffffff',
|
||||||
}];
|
}];
|
||||||
|
|
||||||
if (Meta.config.keywords) {
|
if (Meta.config.keywords && !isAPI) {
|
||||||
defaultTags.push({
|
defaultTags.push({
|
||||||
name: 'keywords',
|
name: 'keywords',
|
||||||
content: Meta.config.keywords,
|
content: Meta.config.keywords,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Meta.config['brand:logo']) {
|
if (Meta.config['brand:logo'] && !isAPI) {
|
||||||
defaultTags.push({
|
defaultTags.push({
|
||||||
name: 'msapplication-square150x150logo',
|
name: 'msapplication-square150x150logo',
|
||||||
content: Meta.config['brand:logo'],
|
content: Meta.config['brand:logo'],
|
||||||
@@ -59,7 +61,7 @@ Tags.parse = async (req, data, meta, link) => {
|
|||||||
const cacheBuster = `${Meta.config['cache-buster'] ? `?${Meta.config['cache-buster']}` : ''}`;
|
const cacheBuster = `${Meta.config['cache-buster'] ? `?${Meta.config['cache-buster']}` : ''}`;
|
||||||
|
|
||||||
// Link Tags
|
// Link Tags
|
||||||
const defaultLinks = [{
|
const defaultLinks = isAPI ? [] : [{
|
||||||
rel: 'icon',
|
rel: 'icon',
|
||||||
type: 'image/x-icon',
|
type: 'image/x-icon',
|
||||||
href: `${faviconPath}${cacheBuster}`,
|
href: `${faviconPath}${cacheBuster}`,
|
||||||
@@ -69,7 +71,7 @@ Tags.parse = async (req, data, meta, link) => {
|
|||||||
crossorigin: `use-credentials`,
|
crossorigin: `use-credentials`,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
if (plugins.hooks.hasListeners('filter:search.query')) {
|
if (plugins.hooks.hasListeners('filter:search.query') && !isAPI) {
|
||||||
defaultLinks.push({
|
defaultLinks.push({
|
||||||
rel: 'search',
|
rel: 'search',
|
||||||
type: 'application/opensearchdescription+xml',
|
type: 'application/opensearchdescription+xml',
|
||||||
@@ -78,7 +80,59 @@ Tags.parse = async (req, data, meta, link) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Touch icons for mobile-devices
|
if (!isAPI) {
|
||||||
|
addTouchIcons(defaultLinks);
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = await utils.promiseParallel({
|
||||||
|
tags: plugins.hooks.fire('filter:meta.getMetaTags', { req: req, data: data, tags: defaultTags }),
|
||||||
|
links: plugins.hooks.fire('filter:meta.getLinkTags', { req: req, data: data, links: defaultLinks }),
|
||||||
|
});
|
||||||
|
|
||||||
|
meta = results.tags.tags.concat(meta || []).map((tag) => {
|
||||||
|
if (!tag || typeof tag.content !== 'string') {
|
||||||
|
winston.warn('Invalid meta tag. ', tag);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tag.noEscape) {
|
||||||
|
const attributes = Object.keys(tag);
|
||||||
|
attributes.forEach((attr) => {
|
||||||
|
tag[attr] = utils.escapeHTML(String(tag[attr]));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return tag;
|
||||||
|
});
|
||||||
|
|
||||||
|
await addSiteOGImage(meta);
|
||||||
|
|
||||||
|
addIfNotExists(meta, 'property', 'og:title', Meta.config.title || 'NodeBB');
|
||||||
|
const ogUrl = url + (req.originalUrl !== '/' ? stripRelativePath(req.originalUrl) : '');
|
||||||
|
addIfNotExists(meta, 'property', 'og:url', ogUrl);
|
||||||
|
addIfNotExists(meta, 'name', 'description', Meta.config.description);
|
||||||
|
addIfNotExists(meta, 'property', 'og:description', Meta.config.description);
|
||||||
|
|
||||||
|
link = results.links.links.concat(link || []);
|
||||||
|
if (isAPI) {
|
||||||
|
const whitelist = ['canonical', 'alternate', 'up'];
|
||||||
|
link = link.filter(link => whitelist.some(val => val === link.rel));
|
||||||
|
}
|
||||||
|
link = link.map((tag) => {
|
||||||
|
if (!tag.noEscape) {
|
||||||
|
const attributes = Object.keys(tag);
|
||||||
|
attributes.forEach((attr) => {
|
||||||
|
tag[attr] = utils.escapeHTML(String(tag[attr]));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return tag;
|
||||||
|
});
|
||||||
|
|
||||||
|
return { meta, link };
|
||||||
|
};
|
||||||
|
|
||||||
|
function addTouchIcons(defaultLinks) {
|
||||||
if (Meta.config['brand:touchIcon']) {
|
if (Meta.config['brand:touchIcon']) {
|
||||||
defaultLinks.push({
|
defaultLinks.push({
|
||||||
rel: 'apple-touch-icon',
|
rel: 'apple-touch-icon',
|
||||||
@@ -142,59 +196,16 @@ Tags.parse = async (req, data, meta, link) => {
|
|||||||
href: `${relative_path}/assets/images/touch/512.png`,
|
href: `${relative_path}/assets/images/touch/512.png`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const results = await utils.promiseParallel({
|
|
||||||
tags: plugins.hooks.fire('filter:meta.getMetaTags', { req: req, data: data, tags: defaultTags }),
|
|
||||||
links: plugins.hooks.fire('filter:meta.getLinkTags', { req: req, data: data, links: defaultLinks }),
|
|
||||||
});
|
|
||||||
|
|
||||||
meta = results.tags.tags.concat(meta || []).map((tag) => {
|
|
||||||
if (!tag || typeof tag.content !== 'string') {
|
|
||||||
winston.warn('Invalid meta tag. ', tag);
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tag.noEscape) {
|
|
||||||
const attributes = Object.keys(tag);
|
|
||||||
attributes.forEach((attr) => {
|
|
||||||
tag[attr] = utils.escapeHTML(String(tag[attr]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return tag;
|
|
||||||
});
|
|
||||||
|
|
||||||
await addSiteOGImage(meta);
|
|
||||||
|
|
||||||
addIfNotExists(meta, 'property', 'og:title', Meta.config.title || 'NodeBB');
|
|
||||||
const ogUrl = url + (req.originalUrl !== '/' ? stripRelativePath(req.originalUrl) : '');
|
|
||||||
addIfNotExists(meta, 'property', 'og:url', ogUrl);
|
|
||||||
addIfNotExists(meta, 'name', 'description', Meta.config.description);
|
|
||||||
addIfNotExists(meta, 'property', 'og:description', Meta.config.description);
|
|
||||||
|
|
||||||
link = results.links.links.concat(link || []).map((tag) => {
|
|
||||||
if (!tag.noEscape) {
|
|
||||||
const attributes = Object.keys(tag);
|
|
||||||
attributes.forEach((attr) => {
|
|
||||||
tag[attr] = utils.escapeHTML(String(tag[attr]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return tag;
|
|
||||||
});
|
|
||||||
|
|
||||||
return { meta, link };
|
|
||||||
};
|
|
||||||
|
|
||||||
function addIfNotExists(meta, keyName, tagName, value) {
|
function addIfNotExists(meta, keyName, tagName, value) {
|
||||||
const exists = meta.some(tag => tag[keyName] === tagName);
|
const exists = meta.some(tag => tag[keyName] === tagName);
|
||||||
|
|
||||||
if (!exists && value) {
|
if (!exists && value) {
|
||||||
const data = {
|
meta.push({
|
||||||
content: utils.escapeHTML(String(value)),
|
content: utils.escapeHTML(String(value)),
|
||||||
};
|
[keyName]: tagName,
|
||||||
data[keyName] = tagName;
|
});
|
||||||
meta.push(data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user