diff options
Diffstat (limited to 'server/lib/client-html.ts')
-rw-r--r-- | server/lib/client-html.ts | 80 |
1 files changed, 60 insertions, 20 deletions
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts index b2c376e20..516827a05 100644 --- a/server/lib/client-html.ts +++ b/server/lib/client-html.ts | |||
@@ -1,7 +1,6 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import * as Bluebird from 'bluebird' | ||
3 | import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/models/i18n/i18n' | 2 | import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/models/i18n/i18n' |
4 | import { CONFIG, CUSTOM_HTML_TAG_COMMENTS, EMBED_SIZE } from '../initializers' | 3 | import { CUSTOM_HTML_TAG_COMMENTS, EMBED_SIZE, WEBSERVER } from '../initializers/constants' |
5 | import { join } from 'path' | 4 | import { join } from 'path' |
6 | import { escapeHTML } from '../helpers/core-utils' | 5 | import { escapeHTML } from '../helpers/core-utils' |
7 | import { VideoModel } from '../models/video/video' | 6 | import { VideoModel } from '../models/video/video' |
@@ -9,10 +8,14 @@ import * as validator from 'validator' | |||
9 | import { VideoPrivacy } from '../../shared/models/videos' | 8 | import { VideoPrivacy } from '../../shared/models/videos' |
10 | import { readFile } from 'fs-extra' | 9 | import { readFile } from 'fs-extra' |
11 | import { getActivityStreamDuration } from '../models/video/video-format-utils' | 10 | import { getActivityStreamDuration } from '../models/video/video-format-utils' |
11 | import { AccountModel } from '../models/account/account' | ||
12 | import { VideoChannelModel } from '../models/video/video-channel' | ||
13 | import * as Bluebird from 'bluebird' | ||
14 | import { CONFIG } from '../initializers/config' | ||
12 | 15 | ||
13 | export class ClientHtml { | 16 | export class ClientHtml { |
14 | 17 | ||
15 | private static htmlCache: { [path: string]: string } = {} | 18 | private static htmlCache: { [ path: string ]: string } = {} |
16 | 19 | ||
17 | static invalidCache () { | 20 | static invalidCache () { |
18 | ClientHtml.htmlCache = {} | 21 | ClientHtml.htmlCache = {} |
@@ -28,18 +31,14 @@ export class ClientHtml { | |||
28 | } | 31 | } |
29 | 32 | ||
30 | static async getWatchHTMLPage (videoId: string, req: express.Request, res: express.Response) { | 33 | static async getWatchHTMLPage (videoId: string, req: express.Request, res: express.Response) { |
31 | let videoPromise: Bluebird<VideoModel> | ||
32 | |||
33 | // Let Angular application handle errors | 34 | // Let Angular application handle errors |
34 | if (validator.isInt(videoId) || validator.isUUID(videoId, 4)) { | 35 | if (!validator.isInt(videoId) && !validator.isUUID(videoId, 4)) { |
35 | videoPromise = VideoModel.loadAndPopulateAccountAndServerAndTags(videoId) | ||
36 | } else { | ||
37 | return ClientHtml.getIndexHTML(req, res) | 36 | return ClientHtml.getIndexHTML(req, res) |
38 | } | 37 | } |
39 | 38 | ||
40 | const [ html, video ] = await Promise.all([ | 39 | const [ html, video ] = await Promise.all([ |
41 | ClientHtml.getIndexHTML(req, res), | 40 | ClientHtml.getIndexHTML(req, res), |
42 | videoPromise | 41 | VideoModel.loadAndPopulateAccountAndServerAndTags(videoId) |
43 | ]) | 42 | ]) |
44 | 43 | ||
45 | // Let Angular application handle errors | 44 | // Let Angular application handle errors |
@@ -49,14 +48,44 @@ export class ClientHtml { | |||
49 | 48 | ||
50 | let customHtml = ClientHtml.addTitleTag(html, escapeHTML(video.name)) | 49 | let customHtml = ClientHtml.addTitleTag(html, escapeHTML(video.name)) |
51 | customHtml = ClientHtml.addDescriptionTag(customHtml, escapeHTML(video.description)) | 50 | customHtml = ClientHtml.addDescriptionTag(customHtml, escapeHTML(video.description)) |
52 | customHtml = ClientHtml.addOpenGraphAndOEmbedTags(customHtml, video) | 51 | customHtml = ClientHtml.addVideoOpenGraphAndOEmbedTags(customHtml, video) |
52 | |||
53 | return customHtml | ||
54 | } | ||
55 | |||
56 | static async getAccountHTMLPage (nameWithHost: string, req: express.Request, res: express.Response) { | ||
57 | return this.getAccountOrChannelHTMLPage(() => AccountModel.loadByNameWithHost(nameWithHost), req, res) | ||
58 | } | ||
59 | |||
60 | static async getVideoChannelHTMLPage (nameWithHost: string, req: express.Request, res: express.Response) { | ||
61 | return this.getAccountOrChannelHTMLPage(() => VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithHost), req, res) | ||
62 | } | ||
63 | |||
64 | private static async getAccountOrChannelHTMLPage ( | ||
65 | loader: () => Bluebird<AccountModel | VideoChannelModel>, | ||
66 | req: express.Request, | ||
67 | res: express.Response | ||
68 | ) { | ||
69 | const [ html, entity ] = await Promise.all([ | ||
70 | ClientHtml.getIndexHTML(req, res), | ||
71 | loader() | ||
72 | ]) | ||
73 | |||
74 | // Let Angular application handle errors | ||
75 | if (!entity) { | ||
76 | return ClientHtml.getIndexHTML(req, res) | ||
77 | } | ||
78 | |||
79 | let customHtml = ClientHtml.addTitleTag(html, escapeHTML(entity.getDisplayName())) | ||
80 | customHtml = ClientHtml.addDescriptionTag(customHtml, escapeHTML(entity.description)) | ||
81 | customHtml = ClientHtml.addAccountOrChannelMetaTags(customHtml, entity) | ||
53 | 82 | ||
54 | return customHtml | 83 | return customHtml |
55 | } | 84 | } |
56 | 85 | ||
57 | private static async getIndexHTML (req: express.Request, res: express.Response, paramLang?: string) { | 86 | private static async getIndexHTML (req: express.Request, res: express.Response, paramLang?: string) { |
58 | const path = ClientHtml.getIndexPath(req, res, paramLang) | 87 | const path = ClientHtml.getIndexPath(req, res, paramLang) |
59 | if (ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path] | 88 | if (ClientHtml.htmlCache[ path ]) return ClientHtml.htmlCache[ path ] |
60 | 89 | ||
61 | const buffer = await readFile(path) | 90 | const buffer = await readFile(path) |
62 | 91 | ||
@@ -64,7 +93,7 @@ export class ClientHtml { | |||
64 | 93 | ||
65 | html = ClientHtml.addCustomCSS(html) | 94 | html = ClientHtml.addCustomCSS(html) |
66 | 95 | ||
67 | ClientHtml.htmlCache[path] = html | 96 | ClientHtml.htmlCache[ path ] = html |
68 | 97 | ||
69 | return html | 98 | return html |
70 | } | 99 | } |
@@ -78,7 +107,7 @@ export class ClientHtml { | |||
78 | 107 | ||
79 | // Save locale in cookies | 108 | // Save locale in cookies |
80 | res.cookie('clientLanguage', lang, { | 109 | res.cookie('clientLanguage', lang, { |
81 | secure: CONFIG.WEBSERVER.SCHEME === 'https', | 110 | secure: WEBSERVER.SCHEME === 'https', |
82 | sameSite: true, | 111 | sameSite: true, |
83 | maxAge: 1000 * 3600 * 24 * 90 // 3 months | 112 | maxAge: 1000 * 3600 * 24 * 90 // 3 months |
84 | }) | 113 | }) |
@@ -114,13 +143,13 @@ export class ClientHtml { | |||
114 | return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.CUSTOM_CSS, styleTag) | 143 | return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.CUSTOM_CSS, styleTag) |
115 | } | 144 | } |
116 | 145 | ||
117 | private static addOpenGraphAndOEmbedTags (htmlStringPage: string, video: VideoModel) { | 146 | private static addVideoOpenGraphAndOEmbedTags (htmlStringPage: string, video: VideoModel) { |
118 | const previewUrl = CONFIG.WEBSERVER.URL + video.getPreviewStaticPath() | 147 | const previewUrl = WEBSERVER.URL + video.getPreviewStaticPath() |
119 | const videoUrl = CONFIG.WEBSERVER.URL + video.getWatchStaticPath() | 148 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() |
120 | 149 | ||
121 | const videoNameEscaped = escapeHTML(video.name) | 150 | const videoNameEscaped = escapeHTML(video.name) |
122 | const videoDescriptionEscaped = escapeHTML(video.description) | 151 | const videoDescriptionEscaped = escapeHTML(video.description) |
123 | const embedUrl = CONFIG.WEBSERVER.URL + video.getEmbedStaticPath() | 152 | const embedUrl = WEBSERVER.URL + video.getEmbedStaticPath() |
124 | 153 | ||
125 | const openGraphMetaTags = { | 154 | const openGraphMetaTags = { |
126 | 'og:type': 'video', | 155 | 'og:type': 'video', |
@@ -152,7 +181,7 @@ export class ClientHtml { | |||
152 | const oembedLinkTags = [ | 181 | const oembedLinkTags = [ |
153 | { | 182 | { |
154 | type: 'application/json+oembed', | 183 | type: 'application/json+oembed', |
155 | href: CONFIG.WEBSERVER.URL + '/services/oembed?url=' + encodeURIComponent(videoUrl), | 184 | href: WEBSERVER.URL + '/services/oembed?url=' + encodeURIComponent(videoUrl), |
156 | title: videoNameEscaped | 185 | title: videoNameEscaped |
157 | } | 186 | } |
158 | ] | 187 | ] |
@@ -174,7 +203,7 @@ export class ClientHtml { | |||
174 | 203 | ||
175 | // Opengraph | 204 | // Opengraph |
176 | Object.keys(openGraphMetaTags).forEach(tagName => { | 205 | Object.keys(openGraphMetaTags).forEach(tagName => { |
177 | const tagValue = openGraphMetaTags[tagName] | 206 | const tagValue = openGraphMetaTags[ tagName ] |
178 | 207 | ||
179 | tagsString += `<meta property="${tagName}" content="${tagValue}" />` | 208 | tagsString += `<meta property="${tagName}" content="${tagValue}" />` |
180 | }) | 209 | }) |
@@ -190,6 +219,17 @@ export class ClientHtml { | |||
190 | // SEO, use origin video url so Google does not index remote videos | 219 | // SEO, use origin video url so Google does not index remote videos |
191 | tagsString += `<link rel="canonical" href="${video.url}" />` | 220 | tagsString += `<link rel="canonical" href="${video.url}" />` |
192 | 221 | ||
193 | return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.OPENGRAPH_AND_OEMBED, tagsString) | 222 | return this.addOpenGraphAndOEmbedTags(htmlStringPage, tagsString) |
223 | } | ||
224 | |||
225 | private static addAccountOrChannelMetaTags (htmlStringPage: string, entity: AccountModel | VideoChannelModel) { | ||
226 | // SEO, use origin account or channel URL | ||
227 | const metaTags = `<link rel="canonical" href="${entity.Actor.url}" />` | ||
228 | |||
229 | return this.addOpenGraphAndOEmbedTags(htmlStringPage, metaTags) | ||
230 | } | ||
231 | |||
232 | private static addOpenGraphAndOEmbedTags (htmlStringPage: string, metaTags: string) { | ||
233 | return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.META_TAGS, metaTags) | ||
194 | } | 234 | } |
195 | } | 235 | } |