mirror of
https://github.com/wassname/talk.git
synced 2026-06-29 14:56:43 +08:00
[CORL-127] Custom CSS (#2194)
* feat: moved html-webpack-plugin to custom server templates in production * fix: fixed templates * fix: removed sri for the time being * fix: fixed up tests for new field name
This commit is contained in:
Generated
+81
-49
@@ -2101,10 +2101,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/clean-css": {
|
||||
"version": "3.4.30",
|
||||
"resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-3.4.30.tgz",
|
||||
"integrity": "sha1-AFLBNvUkgAJCjjY4s33ko5gYZB0=",
|
||||
"dev": true
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.0.tgz",
|
||||
"integrity": "sha512-P+gDCIBAXZ/Q5e9d/Z9Rtn16P5Pr1YIO3gZcY7ZvaQ9ErgmOYtFQlLHZ2P/xcrIyN8TEZrI03EZmdmv1154sjA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/commander": {
|
||||
"version": "2.12.2",
|
||||
@@ -2193,12 +2196,6 @@
|
||||
"@types/enzyme": "*"
|
||||
}
|
||||
},
|
||||
"@types/escape-string-regexp": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/escape-string-regexp/-/escape-string-regexp-1.0.0.tgz",
|
||||
"integrity": "sha512-KAruqgk9/340M4MYYasdBET+lyYN8KMXUuRKWO72f4SbmIMMFp9nnJiXUkJS0HC2SFe4x0R/fLozXIzqoUfSjA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/eventemitter2": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/eventemitter2/-/eventemitter2-4.1.0.tgz",
|
||||
@@ -2746,6 +2743,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@types/webpack-assets-manifest": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/webpack-assets-manifest/-/webpack-assets-manifest-3.0.0.tgz",
|
||||
"integrity": "sha512-KDwIPcC3uwTownU5pIIm1BiWXvDKnqnv0HisAw3z3eiI/cFAJGi1ryUGnOQwy22lXDPsPmMkK4Os/PtF2LjGrQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/tapable": "*",
|
||||
"@types/webpack": "*"
|
||||
}
|
||||
},
|
||||
"@types/webpack-bundle-analyzer": {
|
||||
"version": "2.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.13.0.tgz",
|
||||
@@ -2767,15 +2774,6 @@
|
||||
"@types/webpack": "*"
|
||||
}
|
||||
},
|
||||
"@types/webpack-manifest-plugin": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/webpack-manifest-plugin/-/webpack-manifest-plugin-1.3.2.tgz",
|
||||
"integrity": "sha512-ythLsrDoSLkEOrmKF22MbrweS4hHdMaM1C/2Fp1OOh7jPawy8ah4ajJPrLeEim8uAfZVe/V/jTEDc7VtSogU/w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/webpack": "*"
|
||||
}
|
||||
},
|
||||
"@types/ws": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-5.1.2.tgz",
|
||||
@@ -6399,7 +6397,6 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
|
||||
"integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"no-case": "^2.2.0",
|
||||
"upper-case": "^1.1.1"
|
||||
@@ -6714,12 +6711,18 @@
|
||||
"dev": true
|
||||
},
|
||||
"clean-css": {
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.11.tgz",
|
||||
"integrity": "sha1-Ls3xRaujj1R0DybO/Q/z4D4SXWo=",
|
||||
"dev": true,
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
|
||||
"integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==",
|
||||
"requires": {
|
||||
"source-map": "0.5.x"
|
||||
"source-map": "~0.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli-boxes": {
|
||||
@@ -13213,25 +13216,28 @@
|
||||
"dev": true
|
||||
},
|
||||
"html-minifier": {
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.17.tgz",
|
||||
"integrity": "sha512-O+StuKL0UWfwX5Zv4rFxd60DPcT5DVjGq1AlnP6VQ8wzudft/W4hx5Wl98aSYNwFBHY6XWJreRw/BehX4l+diQ==",
|
||||
"dev": true,
|
||||
"version": "3.5.21",
|
||||
"resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz",
|
||||
"integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==",
|
||||
"requires": {
|
||||
"camel-case": "3.0.x",
|
||||
"clean-css": "4.1.x",
|
||||
"commander": "2.15.x",
|
||||
"he": "1.1.x",
|
||||
"clean-css": "4.2.x",
|
||||
"commander": "2.17.x",
|
||||
"he": "1.2.x",
|
||||
"param-case": "2.1.x",
|
||||
"relateurl": "0.2.x",
|
||||
"uglify-js": "3.4.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "2.15.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
|
||||
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
|
||||
"dev": true
|
||||
"version": "2.17.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
|
||||
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
|
||||
},
|
||||
"he": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -17040,6 +17046,12 @@
|
||||
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.has": {
|
||||
"version": "4.5.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz",
|
||||
"integrity": "sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.includes": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||
@@ -17265,8 +17277,7 @@
|
||||
"lower-case": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
|
||||
"integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=",
|
||||
"dev": true
|
||||
"integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw="
|
||||
},
|
||||
"lowercase-keys": {
|
||||
"version": "1.0.1",
|
||||
@@ -18138,7 +18149,6 @@
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
|
||||
"integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lower-case": "^1.1.1"
|
||||
}
|
||||
@@ -18932,7 +18942,6 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
|
||||
"integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"no-case": "^2.2.0"
|
||||
}
|
||||
@@ -23644,8 +23653,7 @@
|
||||
"relateurl": {
|
||||
"version": "0.2.7",
|
||||
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
|
||||
"integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
|
||||
"dev": true
|
||||
"integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk="
|
||||
},
|
||||
"relay-compiler": {
|
||||
"version": "1.7.0-rc.1",
|
||||
@@ -27095,7 +27103,6 @@
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.2.tgz",
|
||||
"integrity": "sha512-/kVQDzwiE9Vy7Y63eMkMozF4jIt0C2+xHctF9YpqNWdE/NLOuMurshkpoYGUlAbeYhACPv0HJPIHJul0Ak4/uw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commander": "~2.15.0",
|
||||
"source-map": "~0.6.1"
|
||||
@@ -27104,14 +27111,12 @@
|
||||
"commander": {
|
||||
"version": "2.15.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
|
||||
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
|
||||
"dev": true
|
||||
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -27512,8 +27517,7 @@
|
||||
"upper-case": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
|
||||
"integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=",
|
||||
"dev": true
|
||||
"integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg="
|
||||
},
|
||||
"uri-js": {
|
||||
"version": "4.2.2",
|
||||
@@ -27968,6 +27972,34 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"webpack-assets-manifest": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack-assets-manifest/-/webpack-assets-manifest-3.1.1.tgz",
|
||||
"integrity": "sha512-JV9V2QKc5wEWQptdIjvXDUL1ucbPLH2f27toAY3SNdGZp+xSaStAgpoMcvMZmqtFrBc9a5pTS1058vxyMPOzRQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.0",
|
||||
"lodash.get": "^4.0",
|
||||
"lodash.has": "^4.0",
|
||||
"mkdirp": "^0.5",
|
||||
"schema-utils": "^1.0.0",
|
||||
"tapable": "^1.0.0",
|
||||
"webpack-sources": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"schema-utils": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
|
||||
"integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "^6.1.0",
|
||||
"ajv-errors": "^1.0.0",
|
||||
"ajv-keywords": "^3.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"webpack-bundle-analyzer": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.0.3.tgz",
|
||||
|
||||
+6
-5
@@ -75,11 +75,12 @@
|
||||
"graphql-playground-html": "^1.6.0",
|
||||
"graphql-redis-subscriptions": "^1.5.0",
|
||||
"graphql-tools": "^3.0.5",
|
||||
"html-minifier": "^3.5.21",
|
||||
"html-to-text": "^4.0.0",
|
||||
"ioredis": "^3.2.2",
|
||||
"joi": "^13.4.0",
|
||||
"jsonwebtoken": "^8.3.0",
|
||||
"jsdom": "^11.12.0",
|
||||
"jsonwebtoken": "^8.3.0",
|
||||
"jwks-rsa": "^1.3.0",
|
||||
"linkify-it": "^2.1.0",
|
||||
"linkifyjs": "^2.1.7",
|
||||
@@ -139,11 +140,11 @@
|
||||
"@types/dotenv": "^4.0.3",
|
||||
"@types/enzyme": "^3.1.15",
|
||||
"@types/enzyme-adapter-react-16": "^1.0.3",
|
||||
"@types/escape-string-regexp": "^1.0.0",
|
||||
"@types/eventemitter2": "^4.1.0",
|
||||
"@types/express": "^4.16.0",
|
||||
"@types/fs-extra": "^5.0.4",
|
||||
"@types/graphql": "^0.13.3",
|
||||
"@types/html-minifier": "^3.5.2",
|
||||
"@types/html-to-text": "^1.4.31",
|
||||
"@types/html-webpack-plugin": "^3.2.0",
|
||||
"@types/ioredis": "^3.2.12",
|
||||
@@ -189,9 +190,9 @@
|
||||
"@types/verror": "^1.10.3",
|
||||
"@types/vinyl": "^2.0.2",
|
||||
"@types/webpack": "^4.4.7",
|
||||
"@types/webpack-assets-manifest": "^3.0.0",
|
||||
"@types/webpack-bundle-analyzer": "^2.13.0",
|
||||
"@types/webpack-dev-server": "^2.9.5",
|
||||
"@types/webpack-manifest-plugin": "^1.3.2",
|
||||
"@types/ws": "^5.1.2",
|
||||
"autoprefixer": "^8.6.5",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
@@ -301,11 +302,11 @@
|
||||
"typescript-snapshots-plugin": "^1.2.0",
|
||||
"wait-for-expect": "^1.1.0",
|
||||
"webpack": "^4.27.1",
|
||||
"webpack-assets-manifest": "^3.1.1",
|
||||
"webpack-bundle-analyzer": "^3.0.3",
|
||||
"webpack-cli": "^3.1.2",
|
||||
"webpack-dev-server": "^3.1.14",
|
||||
"webpack-hot-client": "^4.1.1",
|
||||
"webpack-manifest-plugin": "^2.0.4",
|
||||
"whatwg-fetch": "^2.0.4"
|
||||
},
|
||||
"husky": {
|
||||
@@ -314,7 +315,7 @@
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,ts,tsx}": [
|
||||
"*.{j,t}s{,x}": [
|
||||
"tslint --fix",
|
||||
"git add"
|
||||
]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import OptimizeCssnanoPlugin from "@intervolga/optimize-cssnano-plugin";
|
||||
import CaseSensitivePathsPlugin from "case-sensitive-paths-webpack-plugin";
|
||||
import CompressionPlugin from "compression-webpack-plugin";
|
||||
import HtmlWebpackPlugin, { Options } from "html-webpack-plugin";
|
||||
import HtmlWebpackPlugin from "html-webpack-plugin";
|
||||
import { identity } from "lodash";
|
||||
import MiniCssExtractPlugin from "mini-css-extract-plugin";
|
||||
import path from "path";
|
||||
@@ -9,14 +9,12 @@ import WatchMissingNodeModulesPlugin from "react-dev-utils/WatchMissingNodeModul
|
||||
import TerserPlugin from "terser-webpack-plugin";
|
||||
import TsconfigPathsPlugin from "tsconfig-paths-webpack-plugin";
|
||||
import webpack, { Configuration, Plugin } from "webpack";
|
||||
import WebpackAssetsManifest from "webpack-assets-manifest";
|
||||
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
|
||||
import ManifestPlugin from "webpack-manifest-plugin";
|
||||
|
||||
import { Config } from "./config";
|
||||
import { createClientEnv } from "./config";
|
||||
import paths from "./paths";
|
||||
import InterpolateHtmlPlugin from "./plugins/InterpolateHtmlPlugin";
|
||||
import PublicURIWebpackPlugin from "./plugins/PublicURIWebpackPlugin";
|
||||
|
||||
/**
|
||||
* filterPlugins will filter out null values from the array of plugins, allowing
|
||||
@@ -59,23 +57,16 @@ export default function createWebpackConfig(
|
||||
* ifProduction will only include the nodes if we're in production mode.
|
||||
*/
|
||||
const ifProduction = isProduction
|
||||
? <T extends {}>(...nodes: T[]) => nodes
|
||||
: <T extends {}>(...nodes: T[]) => [];
|
||||
? (...nodes: any[]) => nodes
|
||||
: (...nodes: any[]) => [];
|
||||
|
||||
const htmlWebpackConfig: Options = {
|
||||
minify: minimize && {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeRedundantAttributes: true,
|
||||
useShortDoctype: true,
|
||||
removeEmptyAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
keepClosingSlash: true,
|
||||
minifyJS: true,
|
||||
minifyCSS: true,
|
||||
minifyURLs: true,
|
||||
},
|
||||
};
|
||||
/**
|
||||
* ifNotProduction will only include the nodes if we're not in production
|
||||
* mode.
|
||||
*/
|
||||
const ifNotProduction = !isProduction
|
||||
? (...nodes: any[]) => nodes
|
||||
: (...nodes: any[]) => [];
|
||||
|
||||
const styleLoader = {
|
||||
loader: require.resolve("style-loader"),
|
||||
@@ -532,66 +523,50 @@ export default function createWebpackConfig(
|
||||
},
|
||||
plugins: filterPlugins([
|
||||
...baseConfig.plugins!,
|
||||
// Generates an `stream.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "stream.html",
|
||||
template: paths.appStreamHTML,
|
||||
chunks: ["stream"],
|
||||
inject: "body",
|
||||
...htmlWebpackConfig,
|
||||
}),
|
||||
// Generates an `auth.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "auth.html",
|
||||
template: paths.appAuthHTML,
|
||||
chunks: ["auth"],
|
||||
inject: "body",
|
||||
...htmlWebpackConfig,
|
||||
}),
|
||||
// Generates an `auth-callback.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "auth-callback.html",
|
||||
template: paths.appAuthCallbackHTML,
|
||||
chunks: ["authCallback"],
|
||||
inject: "body",
|
||||
...htmlWebpackConfig,
|
||||
}),
|
||||
// Generates an `install.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "install.html",
|
||||
template: paths.appInstallHTML,
|
||||
chunks: ["install"],
|
||||
inject: "body",
|
||||
...htmlWebpackConfig,
|
||||
}),
|
||||
// Generates an `admin.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "admin.html",
|
||||
template: paths.appAdminHTML,
|
||||
chunks: ["admin"],
|
||||
inject: "body",
|
||||
...htmlWebpackConfig,
|
||||
}),
|
||||
...ifProduction(
|
||||
// Inject the pieces we need here to resolve all the now relative url's
|
||||
// against the CDN if it's provided. It will inject the following into
|
||||
// the configuration blob on the page.
|
||||
new PublicURIWebpackPlugin(
|
||||
"{{ staticURI | dump | safe }}",
|
||||
"{{ staticURI }}"
|
||||
)
|
||||
...ifNotProduction(
|
||||
// Generates an `stream.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "stream.html",
|
||||
template: paths.appStreamHTML,
|
||||
chunks: ["stream"],
|
||||
inject: "body",
|
||||
}),
|
||||
// Generates an `auth.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "auth.html",
|
||||
template: paths.appAuthHTML,
|
||||
chunks: ["auth"],
|
||||
inject: "body",
|
||||
}),
|
||||
// Generates an `auth-callback.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "auth-callback.html",
|
||||
template: paths.appAuthCallbackHTML,
|
||||
chunks: ["authCallback"],
|
||||
inject: "body",
|
||||
}),
|
||||
// Generates an `install.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "install.html",
|
||||
template: paths.appInstallHTML,
|
||||
chunks: ["install"],
|
||||
inject: "body",
|
||||
}),
|
||||
// Generates an `admin.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "admin.html",
|
||||
template: paths.appAdminHTML,
|
||||
chunks: ["admin"],
|
||||
inject: "body",
|
||||
})
|
||||
),
|
||||
...ifProduction(
|
||||
new WebpackAssetsManifest({
|
||||
output: "asset-manifest.json",
|
||||
entrypoints: true,
|
||||
integrity: true,
|
||||
})
|
||||
),
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In development, this will be an empty string.
|
||||
new InterpolateHtmlPlugin(env),
|
||||
// Generate a manifest file which contains a mapping of all asset filenames
|
||||
// to their corresponding output file so that tools can pick it up without
|
||||
// having to parse `index.html`.
|
||||
new ManifestPlugin({
|
||||
fileName: "asset-manifest.json",
|
||||
}),
|
||||
]),
|
||||
},
|
||||
/* Webpack config for our embed */
|
||||
@@ -622,40 +597,31 @@ export default function createWebpackConfig(
|
||||
},
|
||||
plugins: filterPlugins([
|
||||
...baseConfig.plugins!,
|
||||
...(isProduction
|
||||
? []
|
||||
: [
|
||||
// Generates an `embed.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "embed.html",
|
||||
template: paths.appEmbedHTML,
|
||||
inject: "head",
|
||||
...htmlWebpackConfig,
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "story.html",
|
||||
template: paths.appEmbedStoryHTML,
|
||||
inject: "head",
|
||||
...htmlWebpackConfig,
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "storyButton.html",
|
||||
template: paths.appEmbedStoryButtonHTML,
|
||||
inject: "head",
|
||||
...htmlWebpackConfig,
|
||||
}),
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In development, this will be an empty string.
|
||||
new InterpolateHtmlPlugin(env),
|
||||
]),
|
||||
// Generate a manifest file which contains a mapping of all asset filenames
|
||||
// to their corresponding output file so that tools can pick it up without
|
||||
// having to parse `index.html`.
|
||||
new ManifestPlugin({
|
||||
fileName: "embed-manifest.json",
|
||||
}),
|
||||
...ifNotProduction(
|
||||
// Generates an `embed.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "embed.html",
|
||||
template: paths.appEmbedHTML,
|
||||
inject: "head",
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "story.html",
|
||||
template: paths.appEmbedStoryHTML,
|
||||
inject: "head",
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "storyButton.html",
|
||||
template: paths.appEmbedStoryButtonHTML,
|
||||
inject: "head",
|
||||
})
|
||||
),
|
||||
...ifProduction(
|
||||
new WebpackAssetsManifest({
|
||||
output: "embed-asset-manifest.json",
|
||||
entrypoints: true,
|
||||
integrity: true,
|
||||
})
|
||||
),
|
||||
]),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
// This Webpack plugin lets us interpolate custom variables into `index.html`.
|
||||
// Usage: `new InterpolateHtmlPlugin({ 'MY_VARIABLE': 42 })`
|
||||
// Then, you can use %MY_VARIABLE% in your `index.html`.
|
||||
|
||||
// It works in tandem with HtmlWebpackPlugin.
|
||||
// Learn more about creating plugins like this:
|
||||
// https://github.com/ampedandwired/html-webpack-plugin#events
|
||||
|
||||
import escapeStringRegexp from "escape-string-regexp";
|
||||
import HtmlWebpackPlugin from "html-webpack-plugin";
|
||||
import { Compiler, Plugin } from "webpack";
|
||||
|
||||
export default class InterpolateHtmlPlugin implements Plugin {
|
||||
private replacements: Record<string, string>;
|
||||
|
||||
constructor(replacements: Record<string, string>) {
|
||||
this.replacements = replacements;
|
||||
}
|
||||
|
||||
public apply(compiler: Compiler) {
|
||||
// The following was modified from the original source:
|
||||
// https://github.com/jussikinnula/react-dev-utils/blob/9c290281877c026774c909b2900000c653f431a4/InterpolateHtmlPlugin.js
|
||||
// to support Webpack 4.
|
||||
compiler.hooks.compilation.tap("InterpolateHtmlPlugin", compilation => {
|
||||
const hooks = (HtmlWebpackPlugin as any).getHooks(compilation);
|
||||
hooks.afterTemplateExecution.tap("InterpolateHtmlPlugin", (data: any) => {
|
||||
// Run HTML through a series of user-specified string replacements.
|
||||
Object.keys(this.replacements).forEach(key => {
|
||||
const value = this.replacements[key];
|
||||
data.html = data.html.replace(
|
||||
new RegExp("%" + escapeStringRegexp(key) + "%", "g"),
|
||||
value
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
import HtmlWebpackPlugin from "html-webpack-plugin";
|
||||
import { AsyncSeriesWaterfallHook } from "tapable";
|
||||
import { Compiler, Plugin } from "webpack";
|
||||
|
||||
// Copied from @types/html-webpack-plugin
|
||||
interface HtmlTagObject {
|
||||
/**
|
||||
* Attributes of the html tag
|
||||
* E.g. `{'disabled': true, 'value': 'demo'}`
|
||||
*/
|
||||
attributes: {
|
||||
[attributeName: string]: string | boolean;
|
||||
};
|
||||
/**
|
||||
* Wether this html must not contain innerHTML
|
||||
* @see https://www.w3.org/TR/html5/syntax.html#void-elements
|
||||
*/
|
||||
voidTag: boolean;
|
||||
/**
|
||||
* The tag name e.g. `'div'`
|
||||
*/
|
||||
tagName: string;
|
||||
/**
|
||||
* Inner HTML The
|
||||
*/
|
||||
innerHTML?: string;
|
||||
}
|
||||
|
||||
type AlterAssetTagGroupsHook = AsyncSeriesWaterfallHook<{
|
||||
headTags: Array<HtmlTagObject | HtmlTagObject>;
|
||||
bodyTags: Array<HtmlTagObject | HtmlTagObject>;
|
||||
outputName: string;
|
||||
plugin: HtmlWebpackPlugin;
|
||||
}>;
|
||||
|
||||
export default class PublicURIWebpackPlugin implements Plugin {
|
||||
private configTemplate: string;
|
||||
private prefixTemplate: string;
|
||||
|
||||
constructor(configTemplate: string, prefixTemplate: string) {
|
||||
this.configTemplate = configTemplate;
|
||||
this.prefixTemplate = prefixTemplate;
|
||||
}
|
||||
|
||||
private prefixAttribute(attr: string | boolean) {
|
||||
if (!attr || typeof attr !== "string" || !attr.startsWith("/")) {
|
||||
return attr;
|
||||
}
|
||||
|
||||
return this.prefixTemplate + attr;
|
||||
}
|
||||
|
||||
private prefixTag = (tag: {
|
||||
tagName: string;
|
||||
attributes: Record<string, string | boolean>;
|
||||
}) => {
|
||||
switch (tag.tagName) {
|
||||
case "link":
|
||||
tag.attributes.href = this.prefixAttribute(tag.attributes.href);
|
||||
break;
|
||||
case "script":
|
||||
tag.attributes.src = this.prefixAttribute(tag.attributes.src);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
public apply = (compiler: Compiler) => {
|
||||
compiler.hooks.compilation.tap("PublicURIWebpackPlugin", compilation => {
|
||||
const hooks = (HtmlWebpackPlugin as any).getHooks(compilation);
|
||||
(hooks.alterAssetTagGroups as AlterAssetTagGroupsHook).tapAsync(
|
||||
"PublicURIWebpackPlugin",
|
||||
(htmlPluginData, cb) => {
|
||||
// Prefix all the asset's url's with the template.
|
||||
htmlPluginData.headTags.forEach(this.prefixTag);
|
||||
htmlPluginData.bodyTags.forEach(this.prefixTag);
|
||||
|
||||
// Insert the public path reference.
|
||||
htmlPluginData.bodyTags.unshift({
|
||||
tagName: "script",
|
||||
attributes: {
|
||||
type: "application/json",
|
||||
id: "config",
|
||||
},
|
||||
innerHTML: this.configTemplate,
|
||||
voidTag: false,
|
||||
});
|
||||
|
||||
return cb(null, htmlPluginData);
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
}
|
||||
+2
-2
@@ -20,7 +20,7 @@ const CustomCSSConfig: StatelessComponent<Props> = ({ disabled }) => (
|
||||
<FormField>
|
||||
<HorizontalGutter size="full">
|
||||
<Localized id="configure-advanced-customCSS">
|
||||
<Header container={<label htmlFor="configure-advanced-customCssUrl" />}>
|
||||
<Header container={<label htmlFor="configure-advanced-customCSSURL" />}>
|
||||
Custom CSS
|
||||
</Header>
|
||||
</Localized>
|
||||
@@ -33,7 +33,7 @@ const CustomCSSConfig: StatelessComponent<Props> = ({ disabled }) => (
|
||||
styles. Can be internal or external.
|
||||
</Typography>
|
||||
</Localized>
|
||||
<Field name="customCssUrl">
|
||||
<Field name="customCSSURL">
|
||||
{({ input, meta }) => (
|
||||
<>
|
||||
<TextField
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ class CustomCSSConfigContainer extends React.Component<Props> {
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment CustomCSSConfigContainer_settings on Settings {
|
||||
customCssUrl
|
||||
customCSSURL
|
||||
}
|
||||
`,
|
||||
})(CustomCSSConfigContainer);
|
||||
|
||||
@@ -115,7 +115,7 @@ exports[`renders configure advanced 1`] = `
|
||||
>
|
||||
<label
|
||||
className="Typography-root Typography-heading1 Typography-colorTextPrimary Header-root"
|
||||
htmlFor="configure-advanced-customCssUrl"
|
||||
htmlFor="configure-advanced-customCSSURL"
|
||||
>
|
||||
Custom CSS
|
||||
</label>
|
||||
@@ -133,8 +133,8 @@ exports[`renders configure advanced 1`] = `
|
||||
autoCorrect="off"
|
||||
className="TextField-input TextField-colorRegular"
|
||||
disabled={false}
|
||||
id="configure-advanced-customCssUrl"
|
||||
name="customCssUrl"
|
||||
id="configure-advanced-customCSSURL"
|
||||
name="customCSSURL"
|
||||
onChange={[Function]}
|
||||
placeholder=""
|
||||
spellCheck={false}
|
||||
|
||||
@@ -72,7 +72,7 @@ it("change custom css", async () => {
|
||||
let settingsRecord = cloneDeep(settings);
|
||||
const updateSettingsStub = createSinonStub(s =>
|
||||
s.onFirstCall().callsFake((_: any, data: any) => {
|
||||
expect(data.input.settings.customCssUrl).toEqual("./custom.css");
|
||||
expect(data.input.settings.customCSSURL).toEqual("./custom.css");
|
||||
settingsRecord = merge(settingsRecord, data.input.settings);
|
||||
return {
|
||||
settings: settingsRecord,
|
||||
|
||||
@@ -17,7 +17,7 @@ export const settings = {
|
||||
closedTimeout: 604800,
|
||||
autoCloseStream: false,
|
||||
closedMessage: null,
|
||||
customCssUrl: null,
|
||||
customCSSURL: null,
|
||||
domains: ["localhost:8080"],
|
||||
editCommentWindowLength: 30000,
|
||||
communityGuidelines: {
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import fs from "fs";
|
||||
import logger from "talk-server/logger";
|
||||
|
||||
export interface Asset {
|
||||
src: string;
|
||||
integrity: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entrypoint is the version of the entrypoint that has collected entries with
|
||||
* integrity values.
|
||||
*/
|
||||
export type Entrypoint = Record<string, Asset[]>;
|
||||
|
||||
/**
|
||||
* RawEntrypoint is the entrypoint entry generated by the webpack plugin.
|
||||
*/
|
||||
export type RawEntrypoint = Record<string, string[]>;
|
||||
|
||||
/**
|
||||
* Manifest is the full raw manifest that is generated by the webpack plugin.
|
||||
*/
|
||||
export type Manifest = {
|
||||
/**
|
||||
* entrypoints are generated by the webpack plugin for each of the entrypoints
|
||||
* with their required chunks.
|
||||
*/
|
||||
entrypoints: Record<string, RawEntrypoint>;
|
||||
} & Record<string, Asset>;
|
||||
|
||||
/**
|
||||
* Entrypoints will parse the manifest provided by the `webpack-assets-manifest`
|
||||
* plugin and make their assets available for each entrypoint.
|
||||
*/
|
||||
export default class Entrypoints {
|
||||
private entrypoints = new Map<string, Entrypoint>();
|
||||
|
||||
constructor(manifest: Manifest) {
|
||||
for (const entry in manifest.entrypoints) {
|
||||
if (!manifest.entrypoints.hasOwnProperty(entry)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Grab the entrypoint that contains the list of all the assets
|
||||
// for this entrypoint.
|
||||
const entrypoint: Entrypoint = {};
|
||||
|
||||
// Itterate over the extension's in the entrypoint.
|
||||
for (const extension in manifest.entrypoints[entry]) {
|
||||
if (!manifest.entrypoints[entry].hasOwnProperty(extension)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create the extension in the entrypoint.
|
||||
entrypoint[extension] = [];
|
||||
|
||||
// Grab the files in the extension.
|
||||
const assets = manifest.entrypoints[entry][extension];
|
||||
|
||||
// Itterate over the src field for each of the files.
|
||||
for (const src of assets) {
|
||||
// Search for the entry in the assets.
|
||||
for (const name in manifest) {
|
||||
if (name !== "entrypoints" && !manifest.hasOwnProperty(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Grab the asset.
|
||||
const asset = manifest[name];
|
||||
|
||||
// Check to see if the asset is a match.
|
||||
if (asset.src === src) {
|
||||
entrypoint[extension].push({
|
||||
integrity: asset.integrity,
|
||||
// Prefix all the sources with a `/`.
|
||||
src: "/" + asset.src,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.entrypoints.set(entry, entrypoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get(name: string): Readonly<Entrypoint> {
|
||||
const entrypoint = this.entrypoints.get(name);
|
||||
if (!entrypoint) {
|
||||
throw new Error(`Entrypoint ${name} does not exist in the manifest`);
|
||||
}
|
||||
|
||||
return entrypoint;
|
||||
}
|
||||
|
||||
public static fromFile(filepath: string): Entrypoints | null {
|
||||
try {
|
||||
// Load the manifest.
|
||||
const manifest = JSON.parse(
|
||||
fs.readFileSync(filepath, { encoding: "utf8" })
|
||||
);
|
||||
|
||||
// Create and return the entrypoints.
|
||||
return new Entrypoints(manifest);
|
||||
} catch (err) {
|
||||
logger.error({ err }, "could not load the manifest");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,15 +102,8 @@ function configureApplication(options: AppOptions) {
|
||||
function setupViews(options: AppOptions) {
|
||||
const { parent } = options;
|
||||
|
||||
// Configure the default views directories.
|
||||
const views = [
|
||||
// Load the templates compiled by Webpack.
|
||||
path.resolve(
|
||||
path.join(__dirname, "..", "..", "..", "..", "dist", "static")
|
||||
),
|
||||
// Load the templates generated by the server.
|
||||
path.join(__dirname, "views"),
|
||||
];
|
||||
// Configure the default views directory.
|
||||
const views = path.join(__dirname, "views");
|
||||
parent.set("views", views);
|
||||
|
||||
// Reconfigure nunjucks.
|
||||
@@ -119,13 +112,9 @@ function setupViews(options: AppOptions) {
|
||||
// caching.
|
||||
watch: options.config.get("env") === "development",
|
||||
noCache: options.config.get("env") === "development",
|
||||
// Trim blocks of whitespace.
|
||||
trimBlocks: true,
|
||||
lstripBlocks: true,
|
||||
});
|
||||
|
||||
// assign the nunjucks engine to .njk and .html files.
|
||||
parent.engine("njk", cons.nunjucks);
|
||||
parent.engine("html", cons.nunjucks);
|
||||
|
||||
// set .html as the default extension.
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import express from "express";
|
||||
import { minify } from "html-minifier";
|
||||
|
||||
import { cacheHeadersMiddleware } from "talk-server/app/middleware/cacheHeaders";
|
||||
import { Entrypoint } from "../helpers/entrypoints";
|
||||
|
||||
export interface ClientTargetHandlerOptions {
|
||||
/**
|
||||
* view is the name of the template to render.
|
||||
* entrypoint is the entrypoint entry to load.
|
||||
*/
|
||||
view: string;
|
||||
entrypoint: Entrypoint;
|
||||
|
||||
/**
|
||||
* enableCustomCSS will insert the custom CSS into the template if it is
|
||||
* available on the Tenant.
|
||||
*/
|
||||
enableCustomCSS?: boolean;
|
||||
|
||||
/**
|
||||
* cacheDuration is the cache duration that a given request should be cached for.
|
||||
@@ -22,7 +30,8 @@ export interface ClientTargetHandlerOptions {
|
||||
|
||||
export function createClientTargetRouter({
|
||||
staticURI,
|
||||
view,
|
||||
entrypoint,
|
||||
enableCustomCSS = false,
|
||||
cacheDuration = "1h",
|
||||
}: ClientTargetHandlerOptions) {
|
||||
// Create a router.
|
||||
@@ -32,7 +41,25 @@ export function createClientTargetRouter({
|
||||
router.use(cacheHeadersMiddleware(cacheDuration));
|
||||
|
||||
// Wildcard display all the client routes under the provided prefix.
|
||||
router.get("/*", (req, res) => res.render(view, { staticURI }));
|
||||
router.get("/*", (req, res, next) =>
|
||||
res.render(
|
||||
"client",
|
||||
{ staticURI, entrypoint, enableCustomCSS },
|
||||
(err, html) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
// Send back the HTML minified.
|
||||
res.send(
|
||||
minify(html, {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import express, { Router } from "express";
|
||||
import path from "path";
|
||||
|
||||
import { AppOptions } from "talk-server/app";
|
||||
import { noCacheMiddleware } from "talk-server/app/middleware/cacheHeaders";
|
||||
@@ -9,6 +10,7 @@ import logger from "talk-server/logger";
|
||||
|
||||
import { cspTenantMiddleware } from "talk-server/app/middleware/csp/tenant";
|
||||
import { tenantMiddleware } from "talk-server/app/middleware/tenant";
|
||||
import Entrypoints from "../helpers/entrypoints";
|
||||
import { createAPIRouter } from "./api";
|
||||
import { createClientTargetRouter } from "./client";
|
||||
|
||||
@@ -28,67 +30,89 @@ export async function createRouter(app: AppOptions, options: RouterOptions) {
|
||||
|
||||
const staticURI = app.config.get("static_uri");
|
||||
|
||||
// Add the embed targets.
|
||||
router.use(
|
||||
"/embed/stream",
|
||||
createClientTargetRouter({
|
||||
staticURI,
|
||||
view: "stream",
|
||||
})
|
||||
);
|
||||
router.use(
|
||||
"/embed/auth",
|
||||
createClientTargetRouter({
|
||||
staticURI,
|
||||
view: "auth",
|
||||
cacheDuration: false,
|
||||
})
|
||||
);
|
||||
router.use(
|
||||
"/embed/auth/callback",
|
||||
createClientTargetRouter({
|
||||
staticURI,
|
||||
view: "auth-callback",
|
||||
cacheDuration: false,
|
||||
})
|
||||
// Load the entrypoint manifest.
|
||||
const manifest = path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"dist",
|
||||
"static",
|
||||
"asset-manifest.json"
|
||||
);
|
||||
const entrypoints = Entrypoints.fromFile(manifest);
|
||||
|
||||
// Add the standalone targets.
|
||||
router.use(
|
||||
"/admin",
|
||||
// If we aren't already installed, redirect the user to the install page.
|
||||
installedMiddleware({
|
||||
tenantCache: app.tenantCache,
|
||||
}),
|
||||
createClientTargetRouter({
|
||||
staticURI,
|
||||
view: "admin",
|
||||
cacheDuration: false,
|
||||
})
|
||||
);
|
||||
router.use(
|
||||
"/install",
|
||||
// If we're already installed, redirect the user to the admin page.
|
||||
installedMiddleware({
|
||||
redirectIfInstalled: true,
|
||||
redirectURL: "/admin",
|
||||
tenantCache: app.tenantCache,
|
||||
}),
|
||||
createClientTargetRouter({
|
||||
staticURI,
|
||||
view: "install",
|
||||
cacheDuration: false,
|
||||
})
|
||||
);
|
||||
if (entrypoints) {
|
||||
// Add the embed targets.
|
||||
router.use(
|
||||
"/embed/stream",
|
||||
createClientTargetRouter({
|
||||
staticURI,
|
||||
enableCustomCSS: true,
|
||||
entrypoint: entrypoints.get("stream"),
|
||||
})
|
||||
);
|
||||
router.use(
|
||||
"/embed/auth",
|
||||
createClientTargetRouter({
|
||||
staticURI,
|
||||
cacheDuration: false,
|
||||
entrypoint: entrypoints.get("auth"),
|
||||
})
|
||||
);
|
||||
router.use(
|
||||
"/embed/auth/callback",
|
||||
createClientTargetRouter({
|
||||
staticURI,
|
||||
cacheDuration: false,
|
||||
entrypoint: entrypoints.get("authCallback"),
|
||||
})
|
||||
);
|
||||
|
||||
// Handle the root path.
|
||||
router.get(
|
||||
"/",
|
||||
// Redirect the user to the install page if they are not, otherwise redirect
|
||||
// them to the admin.
|
||||
installedMiddleware({ tenantCache: app.tenantCache }),
|
||||
(req, res, next) => res.redirect("/admin")
|
||||
);
|
||||
// Add the standalone targets.
|
||||
router.use(
|
||||
"/admin",
|
||||
// If we aren't already installed, redirect the user to the install page.
|
||||
installedMiddleware({
|
||||
tenantCache: app.tenantCache,
|
||||
}),
|
||||
createClientTargetRouter({
|
||||
staticURI,
|
||||
cacheDuration: false,
|
||||
entrypoint: entrypoints.get("admin"),
|
||||
})
|
||||
);
|
||||
router.use(
|
||||
"/install",
|
||||
// If we're already installed, redirect the user to the admin page.
|
||||
installedMiddleware({
|
||||
redirectIfInstalled: true,
|
||||
redirectURL: "/admin",
|
||||
tenantCache: app.tenantCache,
|
||||
}),
|
||||
createClientTargetRouter({
|
||||
staticURI,
|
||||
cacheDuration: false,
|
||||
entrypoint: entrypoints.get("install"),
|
||||
})
|
||||
);
|
||||
|
||||
// Handle the root path.
|
||||
router.get(
|
||||
"/",
|
||||
// Redirect the user to the install page if they are not, otherwise redirect
|
||||
// them to the admin.
|
||||
installedMiddleware({ tenantCache: app.tenantCache }),
|
||||
(req, res, next) => res.redirect("/admin")
|
||||
);
|
||||
} else {
|
||||
logger.warn(
|
||||
{ manifest },
|
||||
"could not load the generated manifest, client routes will remain un-mounted"
|
||||
);
|
||||
}
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
{% import "macros.html" as macros %}
|
||||
|
||||
{% extends "templates/base.html" %}
|
||||
|
||||
{% block title %}Talk{% endblock %}
|
||||
|
||||
{% block meta %}
|
||||
<script type="application/javascript" id="config">{{ staticURI | dump | safe }}</script>
|
||||
{% endblock %}
|
||||
|
||||
{# Include all the styles from the entrypoint #}
|
||||
{% if entrypoint.css or enableCustomCSS %}
|
||||
{% block css %}
|
||||
{% if entrypoint.css %}
|
||||
{% for asset in entrypoint.css %}
|
||||
{{ macros.css(asset.src, asset.integrity, staticURI) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if enableCustomCSS %}
|
||||
{# Custom CSS is included after the CSS block so that its overrides will apply #}
|
||||
{% include "partials/customCSS.html" %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
|
||||
{% block html %}
|
||||
<div id="app"></div>
|
||||
{% endblock %}
|
||||
|
||||
{# Include all the scripts from the entrypoint #}
|
||||
{% if entrypoint.js %}
|
||||
{% block js %}
|
||||
{% for asset in entrypoint.js %}
|
||||
{{ macros.js(asset.src, asset.integrity, staticURI) }}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,19 @@
|
||||
{% macro css(src, integrity = '', prefix = '') %}
|
||||
<link type="text/css" rel="stylesheet" href="{{ prefix }}{{ src }}"/>
|
||||
{# TODO: evaluate when to enable SRI, non-SSL connections cause issues #}
|
||||
{# {% if integrity %}
|
||||
<link type="text/css" rel="stylesheet" href="{{ prefix }}{{ src }}" integrity="{{ integrity }}" crossorigin="anonymous"/>
|
||||
{% else %}
|
||||
<link type="text/css" rel="stylesheet" href="{{ prefix }}{{ src }}"/>
|
||||
{% endif %} #}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro js(src, integrity = '', prefix = '') %}
|
||||
<script type="application/javascript" src="{{ prefix }}{{ src }}"></script>
|
||||
{# TODO: evaluate when to enable SRI, non-SSL connections cause issues #}
|
||||
{# {% if false %}
|
||||
<script type="application/javascript" src="{{ prefix }}{{ src }}" integrity="{{ integrity }}" crossorigin="anonymous"></script>
|
||||
{% else %}
|
||||
<script type="application/javascript" src="{{ prefix }}{{ src }}"></script>
|
||||
{% endif %} #}
|
||||
{% endmacro %}
|
||||
@@ -0,0 +1,5 @@
|
||||
{% import "../macros.html" as macros %}
|
||||
|
||||
{% if tenant and tenant.customCSSURL %}
|
||||
{{ macros.css(tenant.customCSSURL) }}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
{# Meta tags #}
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no" />
|
||||
{% block meta %}{% endblock %}
|
||||
|
||||
{# Title #}
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
|
||||
{# CSS #}
|
||||
{% block css %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{% block body %}
|
||||
{% block html %}
|
||||
<div id="root"></div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
{% block js %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -935,9 +935,9 @@ type Settings {
|
||||
autoCloseStream: Boolean! @auth(roles: [ADMIN])
|
||||
|
||||
"""
|
||||
customCssUrl is the URL of the custom CSS used to display on the frontend.
|
||||
customCSSURL is the URL of the custom CSS used to display on the frontend.
|
||||
"""
|
||||
customCssUrl: String
|
||||
customCSSURL: String
|
||||
|
||||
"""
|
||||
closedTimeout is the amount of time (in seconds) from the createdAt timestamp
|
||||
@@ -2201,9 +2201,9 @@ input SettingsInput {
|
||||
autoCloseStream: Boolean
|
||||
|
||||
"""
|
||||
customCssUrl is the URL of the custom CSS used to display on the frontend.
|
||||
customCSSURL is the URL of the custom CSS used to display on the frontend.
|
||||
"""
|
||||
customCssUrl: String
|
||||
customCSSURL: String
|
||||
|
||||
"""
|
||||
closedTimeout is the amount of seconds from the createdAt timestamp that a
|
||||
|
||||
@@ -80,7 +80,11 @@ export interface Auth {
|
||||
}
|
||||
|
||||
export interface Settings extends ModerationSettings {
|
||||
customCssUrl?: string;
|
||||
/**
|
||||
* customCSSURL is the URL that can be specified by the Tenant to describe a
|
||||
* URL that contains custom styles to be applied to the Stream.
|
||||
*/
|
||||
customCSSURL?: string;
|
||||
|
||||
/**
|
||||
* editCommentWindowLength is the length of time (in seconds) after a comment
|
||||
|
||||
Reference in New Issue
Block a user