diff options
Diffstat (limited to 'server/lib/client-html.ts')
-rw-r--r-- | server/lib/client-html.ts | 48 |
1 files changed, 39 insertions, 9 deletions
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts index 203bd3893..72194416d 100644 --- a/server/lib/client-html.ts +++ b/server/lib/client-html.ts | |||
@@ -2,12 +2,14 @@ import * as express from 'express' | |||
2 | import { readFile } from 'fs-extra' | 2 | import { readFile } from 'fs-extra' |
3 | import { join } from 'path' | 3 | import { join } from 'path' |
4 | import validator from 'validator' | 4 | import validator from 'validator' |
5 | import { escapeHTML } from '@shared/core-utils/renderer' | ||
6 | import { HTMLServerConfig } from '@shared/models' | ||
5 | import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n' | 7 | import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n' |
6 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' | 8 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' |
7 | import { VideoPlaylistPrivacy, VideoPrivacy } from '../../shared/models/videos' | 9 | import { VideoPlaylistPrivacy, VideoPrivacy } from '../../shared/models/videos' |
8 | import { isTestInstance, sha256 } from '../helpers/core-utils' | 10 | import { isTestInstance, sha256 } from '../helpers/core-utils' |
9 | import { escapeHTML } from '@shared/core-utils/renderer' | ||
10 | import { logger } from '../helpers/logger' | 11 | import { logger } from '../helpers/logger' |
12 | import { mdToPlainText } from '../helpers/markdown' | ||
11 | import { CONFIG } from '../initializers/config' | 13 | import { CONFIG } from '../initializers/config' |
12 | import { | 14 | import { |
13 | ACCEPT_HEADERS, | 15 | ACCEPT_HEADERS, |
@@ -19,12 +21,13 @@ import { | |||
19 | WEBSERVER | 21 | WEBSERVER |
20 | } from '../initializers/constants' | 22 | } from '../initializers/constants' |
21 | import { AccountModel } from '../models/account/account' | 23 | import { AccountModel } from '../models/account/account' |
24 | import { getActivityStreamDuration } from '../models/video/formatter/video-format-utils' | ||
22 | import { VideoModel } from '../models/video/video' | 25 | import { VideoModel } from '../models/video/video' |
23 | import { VideoChannelModel } from '../models/video/video-channel' | 26 | import { VideoChannelModel } from '../models/video/video-channel' |
24 | import { getActivityStreamDuration } from '../models/video/video-format-utils' | ||
25 | import { VideoPlaylistModel } from '../models/video/video-playlist' | 27 | import { VideoPlaylistModel } from '../models/video/video-playlist' |
26 | import { MAccountActor, MChannelActor } from '../types/models' | 28 | import { MAccountActor, MChannelActor } from '../types/models' |
27 | import { mdToPlainText } from '../helpers/markdown' | 29 | import { ServerConfigManager } from './server-config-manager' |
30 | import { toCompleteUUID } from '@server/helpers/custom-validators/misc' | ||
28 | 31 | ||
29 | type Tags = { | 32 | type Tags = { |
30 | ogType: string | 33 | ogType: string |
@@ -76,7 +79,9 @@ class ClientHtml { | |||
76 | return customHtml | 79 | return customHtml |
77 | } | 80 | } |
78 | 81 | ||
79 | static async getWatchHTMLPage (videoId: string, req: express.Request, res: express.Response) { | 82 | static async getWatchHTMLPage (videoIdArg: string, req: express.Request, res: express.Response) { |
83 | const videoId = toCompleteUUID(videoIdArg) | ||
84 | |||
80 | // Let Angular application handle errors | 85 | // Let Angular application handle errors |
81 | if (!validator.isInt(videoId) && !validator.isUUID(videoId, 4)) { | 86 | if (!validator.isInt(videoId) && !validator.isUUID(videoId, 4)) { |
82 | res.status(HttpStatusCode.NOT_FOUND_404) | 87 | res.status(HttpStatusCode.NOT_FOUND_404) |
@@ -134,7 +139,9 @@ class ClientHtml { | |||
134 | return customHtml | 139 | return customHtml |
135 | } | 140 | } |
136 | 141 | ||
137 | static async getWatchPlaylistHTMLPage (videoPlaylistId: string, req: express.Request, res: express.Response) { | 142 | static async getWatchPlaylistHTMLPage (videoPlaylistIdArg: string, req: express.Request, res: express.Response) { |
143 | const videoPlaylistId = toCompleteUUID(videoPlaylistIdArg) | ||
144 | |||
138 | // Let Angular application handle errors | 145 | // Let Angular application handle errors |
139 | if (!validator.isInt(videoPlaylistId) && !validator.isUUID(videoPlaylistId, 4)) { | 146 | if (!validator.isInt(videoPlaylistId) && !validator.isUUID(videoPlaylistId, 4)) { |
140 | res.status(HttpStatusCode.NOT_FOUND_404) | 147 | res.status(HttpStatusCode.NOT_FOUND_404) |
@@ -196,11 +203,22 @@ class ClientHtml { | |||
196 | } | 203 | } |
197 | 204 | ||
198 | static async getAccountHTMLPage (nameWithHost: string, req: express.Request, res: express.Response) { | 205 | static async getAccountHTMLPage (nameWithHost: string, req: express.Request, res: express.Response) { |
199 | return this.getAccountOrChannelHTMLPage(() => AccountModel.loadByNameWithHost(nameWithHost), req, res) | 206 | const accountModelPromise = AccountModel.loadByNameWithHost(nameWithHost) |
207 | return this.getAccountOrChannelHTMLPage(() => accountModelPromise, req, res) | ||
200 | } | 208 | } |
201 | 209 | ||
202 | static async getVideoChannelHTMLPage (nameWithHost: string, req: express.Request, res: express.Response) { | 210 | static async getVideoChannelHTMLPage (nameWithHost: string, req: express.Request, res: express.Response) { |
203 | return this.getAccountOrChannelHTMLPage(() => VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithHost), req, res) | 211 | const videoChannelModelPromise = VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithHost) |
212 | return this.getAccountOrChannelHTMLPage(() => videoChannelModelPromise, req, res) | ||
213 | } | ||
214 | |||
215 | static async getActorHTMLPage (nameWithHost: string, req: express.Request, res: express.Response) { | ||
216 | const [ account, channel ] = await Promise.all([ | ||
217 | AccountModel.loadByNameWithHost(nameWithHost), | ||
218 | VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithHost) | ||
219 | ]) | ||
220 | |||
221 | return this.getAccountOrChannelHTMLPage(() => Promise.resolve(account || channel), req, res) | ||
204 | } | 222 | } |
205 | 223 | ||
206 | static async getEmbedHTML () { | 224 | static async getEmbedHTML () { |
@@ -209,11 +227,14 @@ class ClientHtml { | |||
209 | if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path] | 227 | if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path] |
210 | 228 | ||
211 | const buffer = await readFile(path) | 229 | const buffer = await readFile(path) |
230 | const serverConfig = await ServerConfigManager.Instance.getHTMLServerConfig() | ||
212 | 231 | ||
213 | let html = buffer.toString() | 232 | let html = buffer.toString() |
214 | html = await ClientHtml.addAsyncPluginCSS(html) | 233 | html = await ClientHtml.addAsyncPluginCSS(html) |
215 | html = ClientHtml.addCustomCSS(html) | 234 | html = ClientHtml.addCustomCSS(html) |
216 | html = ClientHtml.addTitleTag(html) | 235 | html = ClientHtml.addTitleTag(html) |
236 | html = ClientHtml.addDescriptionTag(html) | ||
237 | html = ClientHtml.addServerConfig(html, serverConfig) | ||
217 | 238 | ||
218 | ClientHtml.htmlCache[path] = html | 239 | ClientHtml.htmlCache[path] = html |
219 | 240 | ||
@@ -275,6 +296,7 @@ class ClientHtml { | |||
275 | if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path] | 296 | if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path] |
276 | 297 | ||
277 | const buffer = await readFile(path) | 298 | const buffer = await readFile(path) |
299 | const serverConfig = await ServerConfigManager.Instance.getHTMLServerConfig() | ||
278 | 300 | ||
279 | let html = buffer.toString() | 301 | let html = buffer.toString() |
280 | 302 | ||
@@ -283,6 +305,7 @@ class ClientHtml { | |||
283 | html = ClientHtml.addFaviconContentHash(html) | 305 | html = ClientHtml.addFaviconContentHash(html) |
284 | html = ClientHtml.addLogoContentHash(html) | 306 | html = ClientHtml.addLogoContentHash(html) |
285 | html = ClientHtml.addCustomCSS(html) | 307 | html = ClientHtml.addCustomCSS(html) |
308 | html = ClientHtml.addServerConfig(html, serverConfig) | ||
286 | html = await ClientHtml.addAsyncPluginCSS(html) | 309 | html = await ClientHtml.addAsyncPluginCSS(html) |
287 | 310 | ||
288 | ClientHtml.htmlCache[path] = html | 311 | ClientHtml.htmlCache[path] = html |
@@ -355,6 +378,13 @@ class ClientHtml { | |||
355 | return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.CUSTOM_CSS, styleTag) | 378 | return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.CUSTOM_CSS, styleTag) |
356 | } | 379 | } |
357 | 380 | ||
381 | private static addServerConfig (htmlStringPage: string, serverConfig: HTMLServerConfig) { | ||
382 | const serverConfigString = JSON.stringify(serverConfig) | ||
383 | const configScriptTag = `<script type="application/javascript">window.PeerTubeServerConfig = '${serverConfigString}'</script>` | ||
384 | |||
385 | return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.SERVER_CONFIG, configScriptTag) | ||
386 | } | ||
387 | |||
358 | private static async addAsyncPluginCSS (htmlStringPage: string) { | 388 | private static async addAsyncPluginCSS (htmlStringPage: string) { |
359 | const globalCSSContent = await readFile(PLUGIN_GLOBAL_CSS_PATH) | 389 | const globalCSSContent = await readFile(PLUGIN_GLOBAL_CSS_PATH) |
360 | if (globalCSSContent.byteLength === 0) return htmlStringPage | 390 | if (globalCSSContent.byteLength === 0) return htmlStringPage |
@@ -524,11 +554,11 @@ async function serveIndexHTML (req: express.Request, res: express.Response) { | |||
524 | return | 554 | return |
525 | } catch (err) { | 555 | } catch (err) { |
526 | logger.error('Cannot generate HTML page.', err) | 556 | logger.error('Cannot generate HTML page.', err) |
527 | return res.sendStatus(HttpStatusCode.INTERNAL_SERVER_ERROR_500) | 557 | return res.status(HttpStatusCode.INTERNAL_SERVER_ERROR_500).end() |
528 | } | 558 | } |
529 | } | 559 | } |
530 | 560 | ||
531 | return res.sendStatus(HttpStatusCode.NOT_ACCEPTABLE_406) | 561 | return res.status(HttpStatusCode.NOT_ACCEPTABLE_406).end() |
532 | } | 562 | } |
533 | 563 | ||
534 | // --------------------------------------------------------------------------- | 564 | // --------------------------------------------------------------------------- |