diff options
-rw-r--r-- | server/controllers/api/videos/blacklist.ts | 6 | ||||
-rw-r--r-- | server/controllers/plugins.ts | 19 | ||||
-rw-r--r-- | server/helpers/utils.ts | 12 | ||||
-rw-r--r-- | server/lib/client-html.ts | 14 | ||||
-rw-r--r-- | server/lib/plugins/plugin-manager.ts | 3 | ||||
-rw-r--r-- | 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 | |||
100 | return res.type('json').status(204).end() | 100 | return res.type('json').status(204).end() |
101 | } | 101 | } |
102 | 102 | ||
103 | async function listBlacklist (req: express.Request, res: express.Response, next: express.NextFunction) { | 103 | async function listBlacklist (req: express.Request, res: express.Response) { |
104 | const resultList = await VideoBlacklistModel.listForApi(req.query.start, req.query.count, req.query.sort, req.query.type) | 104 | const resultList = await VideoBlacklistModel.listForApi(req.query.start, req.query.count, req.query.sort, req.query.type) |
105 | 105 | ||
106 | return res.json(getFormattedObjects<VideoBlacklist, VideoBlacklistModel>(resultList.data, resultList.total)) | 106 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
107 | } | 107 | } |
108 | 108 | ||
109 | async function removeVideoFromBlacklistController (req: express.Request, res: express.Response, next: express.NextFunction) { | 109 | async function removeVideoFromBlacklistController (req: express.Request, res: express.Response) { |
110 | const videoBlacklist = res.locals.videoBlacklist | 110 | const videoBlacklist = res.locals.videoBlacklist |
111 | const video = res.locals.video | 111 | const video = res.locals.video |
112 | 112 | ||
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' | |||
5 | import { servePluginStaticDirectoryValidator } from '../middlewares/validators/plugins' | 5 | import { servePluginStaticDirectoryValidator } from '../middlewares/validators/plugins' |
6 | import { serveThemeCSSValidator } from '../middlewares/validators/themes' | 6 | import { serveThemeCSSValidator } from '../middlewares/validators/themes' |
7 | import { PluginType } from '../../shared/models/plugins/plugin.type' | 7 | import { PluginType } from '../../shared/models/plugins/plugin.type' |
8 | import { isTestInstance } from '../helpers/core-utils' | ||
9 | |||
10 | const sendFileOptions = { | ||
11 | maxAge: '30 days', | ||
12 | immutable: !isTestInstance() | ||
13 | } | ||
8 | 14 | ||
9 | const pluginsRouter = express.Router() | 15 | const pluginsRouter = express.Router() |
10 | 16 | ||
@@ -46,7 +52,12 @@ export { | |||
46 | // --------------------------------------------------------------------------- | 52 | // --------------------------------------------------------------------------- |
47 | 53 | ||
48 | function servePluginGlobalCSS (req: express.Request, res: express.Response) { | 54 | function servePluginGlobalCSS (req: express.Request, res: express.Response) { |
49 | return res.sendFile(PLUGIN_GLOBAL_CSS_PATH) | 55 | // Only cache requests that have a ?hash=... query param |
56 | const globalCSSOptions = req.query.hash | ||
57 | ? sendFileOptions | ||
58 | : {} | ||
59 | |||
60 | return res.sendFile(PLUGIN_GLOBAL_CSS_PATH, globalCSSOptions) | ||
50 | } | 61 | } |
51 | 62 | ||
52 | function servePluginStaticDirectory (req: express.Request, res: express.Response) { | 63 | function servePluginStaticDirectory (req: express.Request, res: express.Response) { |
@@ -61,7 +72,7 @@ function servePluginStaticDirectory (req: express.Request, res: express.Response | |||
61 | } | 72 | } |
62 | 73 | ||
63 | const filepath = file.join('/') | 74 | const filepath = file.join('/') |
64 | return res.sendFile(join(plugin.path, staticPath, filepath)) | 75 | return res.sendFile(join(plugin.path, staticPath, filepath), sendFileOptions) |
65 | } | 76 | } |
66 | 77 | ||
67 | function servePluginClientScripts (req: express.Request, res: express.Response) { | 78 | function servePluginClientScripts (req: express.Request, res: express.Response) { |
@@ -73,7 +84,7 @@ function servePluginClientScripts (req: express.Request, res: express.Response) | |||
73 | return res.sendStatus(404) | 84 | return res.sendStatus(404) |
74 | } | 85 | } |
75 | 86 | ||
76 | return res.sendFile(join(plugin.path, staticEndpoint)) | 87 | return res.sendFile(join(plugin.path, staticEndpoint), sendFileOptions) |
77 | } | 88 | } |
78 | 89 | ||
79 | function serveThemeCSSDirectory (req: express.Request, res: express.Response) { | 90 | function serveThemeCSSDirectory (req: express.Request, res: express.Response) { |
@@ -84,5 +95,5 @@ function serveThemeCSSDirectory (req: express.Request, res: express.Response) { | |||
84 | return res.sendStatus(404) | 95 | return res.sendStatus(404) |
85 | } | 96 | } |
86 | 97 | ||
87 | return res.sendFile(join(plugin.path, staticEndpoint)) | 98 | return res.sendFile(join(plugin.path, staticEndpoint), sendFileOptions) |
88 | } | 99 | } |
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) { | |||
19 | return raw.toString('hex') | 19 | return raw.toString('hex') |
20 | } | 20 | } |
21 | 21 | ||
22 | interface FormattableToJSON { toFormattedJSON (args?: any) } | 22 | interface FormattableToJSON<U, V> { toFormattedJSON (args?: U): V } |
23 | function getFormattedObjects<U, T extends FormattableToJSON> (objects: T[], objectsTotal: number, formattedArg?: any) { | 23 | function getFormattedObjects<U, V, T extends FormattableToJSON<U, V>> (objects: T[], objectsTotal: number, formattedArg?: U) { |
24 | const formattedObjects: U[] = [] | 24 | const formattedObjects = objects.map(o => o.toFormattedJSON(formattedArg)) |
25 | |||
26 | objects.forEach(object => { | ||
27 | formattedObjects.push(object.toFormattedJSON(formattedArg)) | ||
28 | }) | ||
29 | 25 | ||
30 | return { | 26 | return { |
31 | total: objectsTotal, | 27 | total: objectsTotal, |
32 | data: formattedObjects | 28 | data: formattedObjects |
33 | } as ResultList<U> | 29 | } as ResultList<V> |
34 | } | 30 | } |
35 | 31 | ||
36 | const getServerActor = memoizee(async function () { | 32 | 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 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/models/i18n/i18n' | 2 | import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/models/i18n/i18n' |
3 | import { CUSTOM_HTML_TAG_COMMENTS, EMBED_SIZE, WEBSERVER } from '../initializers/constants' | 3 | import { CUSTOM_HTML_TAG_COMMENTS, EMBED_SIZE, PLUGIN_GLOBAL_CSS_PATH, WEBSERVER } from '../initializers/constants' |
4 | import { join } from 'path' | 4 | import { join } from 'path' |
5 | import { escapeHTML } from '../helpers/core-utils' | 5 | import { escapeHTML, sha256 } from '../helpers/core-utils' |
6 | import { VideoModel } from '../models/video/video' | 6 | import { VideoModel } from '../models/video/video' |
7 | import * as validator from 'validator' | 7 | import * as validator from 'validator' |
8 | import { VideoPrivacy } from '../../shared/models/videos' | 8 | import { VideoPrivacy } from '../../shared/models/videos' |
@@ -92,7 +92,7 @@ export class ClientHtml { | |||
92 | let html = buffer.toString() | 92 | let html = buffer.toString() |
93 | 93 | ||
94 | html = ClientHtml.addCustomCSS(html) | 94 | html = ClientHtml.addCustomCSS(html) |
95 | html = ClientHtml.addPluginCSS(html) | 95 | html = await ClientHtml.addAsyncPluginCSS(html) |
96 | 96 | ||
97 | ClientHtml.htmlCache[ path ] = html | 97 | ClientHtml.htmlCache[ path ] = html |
98 | 98 | ||
@@ -144,8 +144,12 @@ export class ClientHtml { | |||
144 | return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.CUSTOM_CSS, styleTag) | 144 | return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.CUSTOM_CSS, styleTag) |
145 | } | 145 | } |
146 | 146 | ||
147 | private static addPluginCSS (htmlStringPage: string) { | 147 | private static async addAsyncPluginCSS (htmlStringPage: string) { |
148 | const linkTag = `<link rel="stylesheet" href="/plugins/global.css" />` | 148 | const globalCSSContent = await readFile(PLUGIN_GLOBAL_CSS_PATH) |
149 | if (!globalCSSContent) return htmlStringPage | ||
150 | |||
151 | const fileHash = sha256(globalCSSContent) | ||
152 | const linkTag = `<link rel="stylesheet" href="/plugins/global.css?hash=${fileHash}" />` | ||
149 | 153 | ||
150 | return htmlStringPage.replace('</head>', linkTag + '</head>') | 154 | return htmlStringPage.replace('</head>', linkTag + '</head>') |
151 | } | 155 | } |
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 | |||
17 | import { getHookType, internalRunHook } from '../../../shared/core-utils/plugins/hooks' | 17 | import { getHookType, internalRunHook } from '../../../shared/core-utils/plugins/hooks' |
18 | import { RegisterOptions } from '../../typings/plugins/register-options.model' | 18 | import { RegisterOptions } from '../../typings/plugins/register-options.model' |
19 | import { PluginLibrary } from '../../typings/plugins' | 19 | import { PluginLibrary } from '../../typings/plugins' |
20 | import { ClientHtml } from '../client-html' | ||
20 | 21 | ||
21 | export interface RegisteredPlugin { | 22 | export interface RegisteredPlugin { |
22 | npmName: string | 23 | npmName: string |
@@ -323,6 +324,8 @@ export class PluginManager implements ServerHook { | |||
323 | for (const cssPath of cssRelativePaths) { | 324 | for (const cssPath of cssRelativePaths) { |
324 | await this.concatFiles(join(pluginPath, cssPath), PLUGIN_GLOBAL_CSS_PATH) | 325 | await this.concatFiles(join(pluginPath, cssPath), PLUGIN_GLOBAL_CSS_PATH) |
325 | } | 326 | } |
327 | |||
328 | ClientHtml.invalidCache() | ||
326 | } | 329 | } |
327 | 330 | ||
328 | private concatFiles (input: string, output: string) { | 331 | 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 @@ | |||
1 | // {hookType}:{api?}.{location}.{subLocation?}.{actionType}.{target} | ||
2 | |||
1 | export const serverFilterHookObject = { | 3 | export const serverFilterHookObject = { |
2 | 'filter:api.videos.list.params': true, | 4 | 'filter:api.videos.list.params': true, |
3 | 'filter:api.videos.list.result': true, | 5 | 'filter:api.videos.list.result': true, |