From a8b666e9f1ed002230869606308749614390c82f Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 23 Jul 2019 09:48:48 +0200 Subject: [PATCH] Add plugin static files cache --- server/controllers/api/videos/blacklist.ts | 6 +++--- server/controllers/plugins.ts | 19 +++++++++++++++---- server/helpers/utils.ts | 12 ++++-------- server/lib/client-html.ts | 14 +++++++++----- server/lib/plugins/plugin-manager.ts | 3 +++ shared/models/plugins/server-hook.model.ts | 2 ++ 6 files changed, 36 insertions(+), 20 deletions(-) diff --git a/server/controllers/api/videos/blacklist.ts b/server/controllers/api/videos/blacklist.ts index 27dcfb761..0ec518e0d 100644 --- a/server/controllers/api/videos/blacklist.ts +++ b/server/controllers/api/videos/blacklist.ts @@ -100,13 +100,13 @@ async function updateVideoBlacklistController (req: express.Request, res: expres return res.type('json').status(204).end() } -async function listBlacklist (req: express.Request, res: express.Response, next: express.NextFunction) { +async function listBlacklist (req: express.Request, res: express.Response) { const resultList = await VideoBlacklistModel.listForApi(req.query.start, req.query.count, req.query.sort, req.query.type) - return res.json(getFormattedObjects(resultList.data, resultList.total)) + return res.json(getFormattedObjects(resultList.data, resultList.total)) } -async function removeVideoFromBlacklistController (req: express.Request, res: express.Response, next: express.NextFunction) { +async function removeVideoFromBlacklistController (req: express.Request, res: express.Response) { const videoBlacklist = res.locals.videoBlacklist const video = res.locals.video diff --git a/server/controllers/plugins.ts b/server/controllers/plugins.ts index f255d13e8..f5285ba3a 100644 --- a/server/controllers/plugins.ts +++ b/server/controllers/plugins.ts @@ -5,6 +5,12 @@ import { RegisteredPlugin } from '../lib/plugins/plugin-manager' import { servePluginStaticDirectoryValidator } from '../middlewares/validators/plugins' import { serveThemeCSSValidator } from '../middlewares/validators/themes' import { PluginType } from '../../shared/models/plugins/plugin.type' +import { isTestInstance } from '../helpers/core-utils' + +const sendFileOptions = { + maxAge: '30 days', + immutable: !isTestInstance() +} const pluginsRouter = express.Router() @@ -46,7 +52,12 @@ export { // --------------------------------------------------------------------------- function servePluginGlobalCSS (req: express.Request, res: express.Response) { - return res.sendFile(PLUGIN_GLOBAL_CSS_PATH) + // Only cache requests that have a ?hash=... query param + const globalCSSOptions = req.query.hash + ? sendFileOptions + : {} + + return res.sendFile(PLUGIN_GLOBAL_CSS_PATH, globalCSSOptions) } function servePluginStaticDirectory (req: express.Request, res: express.Response) { @@ -61,7 +72,7 @@ function servePluginStaticDirectory (req: express.Request, res: express.Response } const filepath = file.join('/') - return res.sendFile(join(plugin.path, staticPath, filepath)) + return res.sendFile(join(plugin.path, staticPath, filepath), sendFileOptions) } function servePluginClientScripts (req: express.Request, res: express.Response) { @@ -73,7 +84,7 @@ function servePluginClientScripts (req: express.Request, res: express.Response) return res.sendStatus(404) } - return res.sendFile(join(plugin.path, staticEndpoint)) + return res.sendFile(join(plugin.path, staticEndpoint), sendFileOptions) } function serveThemeCSSDirectory (req: express.Request, res: express.Response) { @@ -84,5 +95,5 @@ function serveThemeCSSDirectory (req: express.Request, res: express.Response) { return res.sendStatus(404) } - return res.sendFile(join(plugin.path, staticEndpoint)) + return res.sendFile(join(plugin.path, staticEndpoint), sendFileOptions) } diff --git a/server/helpers/utils.ts b/server/helpers/utils.ts index 94ceb15e0..1464b1477 100644 --- a/server/helpers/utils.ts +++ b/server/helpers/utils.ts @@ -19,18 +19,14 @@ async function generateRandomString (size: number) { return raw.toString('hex') } -interface FormattableToJSON { toFormattedJSON (args?: any) } -function getFormattedObjects (objects: T[], objectsTotal: number, formattedArg?: any) { - const formattedObjects: U[] = [] - - objects.forEach(object => { - formattedObjects.push(object.toFormattedJSON(formattedArg)) - }) +interface FormattableToJSON { toFormattedJSON (args?: U): V } +function getFormattedObjects> (objects: T[], objectsTotal: number, formattedArg?: U) { + const formattedObjects = objects.map(o => o.toFormattedJSON(formattedArg)) return { total: objectsTotal, data: formattedObjects - } as ResultList + } as ResultList } const getServerActor = memoizee(async function () { diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts index ccc963514..1e7897220 100644 --- a/server/lib/client-html.ts +++ b/server/lib/client-html.ts @@ -1,8 +1,8 @@ import * as express from 'express' import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/models/i18n/i18n' -import { CUSTOM_HTML_TAG_COMMENTS, EMBED_SIZE, WEBSERVER } from '../initializers/constants' +import { CUSTOM_HTML_TAG_COMMENTS, EMBED_SIZE, PLUGIN_GLOBAL_CSS_PATH, WEBSERVER } from '../initializers/constants' import { join } from 'path' -import { escapeHTML } from '../helpers/core-utils' +import { escapeHTML, sha256 } from '../helpers/core-utils' import { VideoModel } from '../models/video/video' import * as validator from 'validator' import { VideoPrivacy } from '../../shared/models/videos' @@ -92,7 +92,7 @@ export class ClientHtml { let html = buffer.toString() html = ClientHtml.addCustomCSS(html) - html = ClientHtml.addPluginCSS(html) + html = await ClientHtml.addAsyncPluginCSS(html) ClientHtml.htmlCache[ path ] = html @@ -144,8 +144,12 @@ export class ClientHtml { return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.CUSTOM_CSS, styleTag) } - private static addPluginCSS (htmlStringPage: string) { - const linkTag = `` + private static async addAsyncPluginCSS (htmlStringPage: string) { + const globalCSSContent = await readFile(PLUGIN_GLOBAL_CSS_PATH) + if (!globalCSSContent) return htmlStringPage + + const fileHash = sha256(globalCSSContent) + const linkTag = `` return htmlStringPage.replace('', linkTag + '') } diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts index cfe63e50d..c0b49c7c7 100644 --- a/server/lib/plugins/plugin-manager.ts +++ b/server/lib/plugins/plugin-manager.ts @@ -17,6 +17,7 @@ import { ServerHook, ServerHookName, serverHookObject } from '../../../shared/mo import { getHookType, internalRunHook } from '../../../shared/core-utils/plugins/hooks' import { RegisterOptions } from '../../typings/plugins/register-options.model' import { PluginLibrary } from '../../typings/plugins' +import { ClientHtml } from '../client-html' export interface RegisteredPlugin { npmName: string @@ -323,6 +324,8 @@ export class PluginManager implements ServerHook { for (const cssPath of cssRelativePaths) { await this.concatFiles(join(pluginPath, cssPath), PLUGIN_GLOBAL_CSS_PATH) } + + ClientHtml.invalidCache() } private concatFiles (input: string, output: string) { diff --git a/shared/models/plugins/server-hook.model.ts b/shared/models/plugins/server-hook.model.ts index f53e0ce59..fc4c51160 100644 --- a/shared/models/plugins/server-hook.model.ts +++ b/shared/models/plugins/server-hook.model.ts @@ -1,3 +1,5 @@ +// {hookType}:{api?}.{location}.{subLocation?}.{actionType}.{target} + export const serverFilterHookObject = { 'filter:api.videos.list.params': true, 'filter:api.videos.list.result': true,