mirror of
https://github.com/wassname/talk.git
synced 2026-09-13 13:10:43 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d331bdee18 | ||
|
|
d6f6fbcd9c | ||
|
|
1ed5df71da | ||
|
|
f1a3a5ca28 | ||
|
|
8ef44dc6f5 | ||
|
|
362f29f77e | ||
|
|
08d342ea2e |
@@ -41,9 +41,9 @@ integration_job: &integration_job
|
||||
- attach_workspace:
|
||||
at: ~/coralproject/talk
|
||||
- <<: *create_indexes
|
||||
- run:
|
||||
name: Setup the database with defaults
|
||||
command: ./bin/cli setup --defaults
|
||||
# - run:
|
||||
# name: Setup the database with defaults
|
||||
# command: ./bin/cli setup --defaults
|
||||
- run:
|
||||
name: Run the integration tests
|
||||
command: bash .circleci/e2e.sh
|
||||
|
||||
@@ -11,6 +11,7 @@ ONBUILD ARG TALK_DEFAULT_LANG=en
|
||||
ONBUILD ARG TALK_WHITELISTED_LANGUAGES
|
||||
ONBUILD ARG TALK_PLUGINS_JSON
|
||||
ONBUILD ARG TALK_WEBPACK_SOURCE_MAP
|
||||
ONBUILD ARG TALK_DEFAULT_LAZY_RENDER
|
||||
|
||||
# Bundle app source
|
||||
ONBUILD COPY . /usr/src/app
|
||||
|
||||
+37
-31
@@ -45,11 +45,10 @@ const performSetup = async () => {
|
||||
} catch (err) {
|
||||
// If the error is `not init`, then we're good, otherwise, it's something
|
||||
// else.
|
||||
if (err instanceof ErrSettingsNotInit) {
|
||||
if (!err instanceof ErrSettingsNotInit) {
|
||||
throw err;
|
||||
return;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (program.defaults) {
|
||||
@@ -138,7 +137,7 @@ const performSetup = async () => {
|
||||
|
||||
console.log("\nWe'll ask you some questions about your first admin user.\n");
|
||||
|
||||
let user = await inquirer.prompt([
|
||||
let { username, email } = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'username',
|
||||
@@ -161,39 +160,46 @@ const performSetup = async () => {
|
||||
return 'Email is required';
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'password',
|
||||
message: 'Password',
|
||||
type: 'password',
|
||||
filter: password => {
|
||||
return UsersService.isValidPassword(password).catch(err => {
|
||||
throw err.message;
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'confirmPassword',
|
||||
message: 'Confirm Password',
|
||||
type: 'password',
|
||||
filter: (confirmPassword, { password }) => {
|
||||
if (password !== confirmPassword) {
|
||||
return Promise.reject(new Error('Passwords do not match'));
|
||||
}
|
||||
|
||||
return UsersService.isValidPassword(confirmPassword).catch(err => {
|
||||
throw err.message;
|
||||
});
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
let password = '';
|
||||
while (!password) {
|
||||
answers = await inquirer.prompt([
|
||||
{
|
||||
name: 'password',
|
||||
message: 'Password',
|
||||
type: 'password',
|
||||
filter: password => {
|
||||
try {
|
||||
UsersService.isValidPassword(password);
|
||||
} catch (err) {
|
||||
throw err.message;
|
||||
}
|
||||
|
||||
return password;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'confirmPassword',
|
||||
message: 'Confirm Password',
|
||||
type: 'password',
|
||||
},
|
||||
]);
|
||||
|
||||
if (answers.password !== answers.confirmPassword) {
|
||||
console.error('Passwords do not match');
|
||||
} else {
|
||||
password = answers.password;
|
||||
}
|
||||
}
|
||||
|
||||
const ctx = Context.forSystem();
|
||||
let { user: newUser } = await SetupService.setup(ctx, {
|
||||
settings: settings.toObject(),
|
||||
user: {
|
||||
email: user.email,
|
||||
username: user.username,
|
||||
password: user.password,
|
||||
email,
|
||||
username,
|
||||
password,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
/* global __webpack_public_path__ */ // eslint-disable-line no-unused-vars
|
||||
|
||||
import queryString from 'querystringify';
|
||||
import pym from 'pym.js';
|
||||
import EventEmitter from 'eventemitter2';
|
||||
import { buildUrl } from 'coral-framework/utils/url';
|
||||
import Snackbar from './Snackbar';
|
||||
import onIntersect from './onIntersect';
|
||||
import {
|
||||
createStorage,
|
||||
connectStorageToPym,
|
||||
@@ -10,13 +13,14 @@ import {
|
||||
|
||||
const NOTIFICATION_OFFSET = 200;
|
||||
|
||||
// Ensure there is a trailing slash.
|
||||
function ensureEndSlash(p) {
|
||||
return p.match(/\/$/) ? p : `${p}/`;
|
||||
}
|
||||
|
||||
// Build the URL to load in the pym iframe.
|
||||
function buildStreamIframeUrl(talkBaseUrl, query) {
|
||||
let url = [
|
||||
talkBaseUrl,
|
||||
talkBaseUrl.match(/\/$/) ? '' : '/', // make sure no double-'/' if opts.talk already ends with '/'
|
||||
'embed/stream?',
|
||||
].join('');
|
||||
let url = talkBaseUrl + 'embed/stream?';
|
||||
|
||||
url += queryString.stringify(query);
|
||||
|
||||
@@ -47,18 +51,76 @@ export default class Stream {
|
||||
events = null,
|
||||
snackBarStyles = null,
|
||||
onAuthChanged = null,
|
||||
talkStaticUrl = talkBaseUrl,
|
||||
...opts
|
||||
} = config;
|
||||
|
||||
this.onAuthChanged = onAuthChanged;
|
||||
this.el = el;
|
||||
this.talkBaseUrl = ensureEndSlash(talkBaseUrl);
|
||||
this.talkStaticUrl = ensureEndSlash(talkStaticUrl);
|
||||
this.opts = opts;
|
||||
|
||||
this.emitter = new EventEmitter({ wildcard: true });
|
||||
this.pym = new pym.Parent(el.id, buildStreamIframeUrl(talkBaseUrl, query), {
|
||||
title: opts.title,
|
||||
id: `${el.id}_iframe`,
|
||||
name: `${el.id}_iframe`,
|
||||
});
|
||||
this.snackBar = new Snackbar(snackBarStyles || {});
|
||||
this.emitter = new EventEmitter({ wildcard: true });
|
||||
|
||||
// Because we're loading chunks dynamically below, we need to point to the
|
||||
// static URL.
|
||||
//
|
||||
// The __webpack_public_path__ can be referenced:
|
||||
// https://webpack.js.org/configuration/output/#output-publicpath
|
||||
//
|
||||
__webpack_public_path__ = this.talkStaticUrl + 'static/';
|
||||
|
||||
// Attach to the events emitted by the pym parent.
|
||||
if (events) {
|
||||
events(this.emitter);
|
||||
}
|
||||
if (config.lazy || process.env.TALK_DEFAULT_LAZY_RENDER === 'TRUE') {
|
||||
const renderOnIntersect = () => onIntersect(this.el, () => this.render());
|
||||
if (!window.IntersectionObserver) {
|
||||
// Include a polyfill for the intersection observer.
|
||||
import(/* webpackChunkName: "intersection-observer" */ 'intersection-observer')
|
||||
.then(() => {
|
||||
// Polyfill applied.
|
||||
renderOnIntersect();
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e);
|
||||
// Loading polyfill failed, just render it directly.
|
||||
this.render();
|
||||
});
|
||||
} else {
|
||||
// No need for polyfill.
|
||||
renderOnIntersect();
|
||||
}
|
||||
} else {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
assertRendered() {
|
||||
if (!this.pym) {
|
||||
throw new Error('Stream Embed must be rendered first');
|
||||
}
|
||||
}
|
||||
|
||||
isRendered() {
|
||||
return !!this.pym;
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.pym) {
|
||||
throw new Error('Stream Embed already rendered');
|
||||
}
|
||||
this.pym = new pym.Parent(
|
||||
this.el.id,
|
||||
buildStreamIframeUrl(this.talkBaseUrl, this.query),
|
||||
{
|
||||
title: this.opts.title,
|
||||
id: `${this.el.id}_iframe`,
|
||||
name: `${this.el.id}_iframe`,
|
||||
}
|
||||
);
|
||||
|
||||
// Workaround: IOS Safari ignores `width` but respects `min-width` value.
|
||||
this.pym.el.firstChild.style.width = '1px';
|
||||
@@ -73,19 +135,14 @@ export default class Stream {
|
||||
}
|
||||
});
|
||||
|
||||
// Attach to the events emitted by the pym parent.
|
||||
if (events) {
|
||||
events(this.emitter);
|
||||
}
|
||||
|
||||
this.pym.onMessage('getConfig', () => {
|
||||
this.pym.sendMessage('config', JSON.stringify(opts));
|
||||
this.pym.sendMessage('config', JSON.stringify(this.opts));
|
||||
});
|
||||
|
||||
// If the auth changes, and someone is listening for it, then re-emit it.
|
||||
if (onAuthChanged) {
|
||||
if (this.onAuthChanged) {
|
||||
this.pym.onMessage('coral-auth-changed', message => {
|
||||
onAuthChanged(message ? JSON.parse(message) : null);
|
||||
this.onAuthChanged(message ? JSON.parse(message) : null);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -163,22 +220,27 @@ export default class Stream {
|
||||
}
|
||||
|
||||
enablePluginsDebug() {
|
||||
this.assertRendered();
|
||||
this.pym.sendMessage('enablePluginsDebug');
|
||||
}
|
||||
|
||||
disablePluginsDebug() {
|
||||
this.assertRendered();
|
||||
this.pym.sendMessage('disablePluginsDebug');
|
||||
}
|
||||
|
||||
login(token) {
|
||||
this.assertRendered();
|
||||
this.pym.sendMessage('login', token);
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.assertRendered();
|
||||
this.pym.sendMessage('logout');
|
||||
}
|
||||
|
||||
remove() {
|
||||
this.assertRendered();
|
||||
// Remove the event listeners.
|
||||
document.removeEventListener('click', this.handleClick.bind(this));
|
||||
this.emitter.removeAllListeners();
|
||||
@@ -191,6 +253,7 @@ export default class Stream {
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
this.assertRendered();
|
||||
this.pym.sendMessage('click');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ export class Talk {
|
||||
* @param {String} [config.asset_url] - Asset URL
|
||||
* @param {String} [config.asset_id] - Asset ID
|
||||
* @param {String} [config.auth_token] - (optional) A jwt representing the session
|
||||
* @param {String} [config.lazy] - (optional) If set the stream will only render lazily.
|
||||
* @return {Object}
|
||||
*
|
||||
* Example:
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
export default function onIntersect(el, callback) {
|
||||
if (!IntersectionObserver) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.warn('IntersectionObserver not available');
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
const options = {
|
||||
rootMargin: '100px',
|
||||
threshold: 1.0,
|
||||
};
|
||||
|
||||
const observer = new IntersectionObserver(entries => {
|
||||
if (entries[0].isIntersecting) {
|
||||
observer.disconnect();
|
||||
callback();
|
||||
}
|
||||
}, options);
|
||||
observer.observe(el);
|
||||
}
|
||||
@@ -76,6 +76,10 @@ const CONFIG = {
|
||||
// on the scraper when it makes requests.
|
||||
SCRAPER_HEADERS: process.env.TALK_SCRAPER_HEADERS || '{}',
|
||||
|
||||
// HTTP_X_REQUEST_ID is a string which represents the request header where we
|
||||
// should source the request ID from, otherwise, a new one will be generated.
|
||||
HTTP_X_REQUEST_ID: process.env.TALK_HTTP_X_REQUEST_ID || null,
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// JWT based configuration
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -187,8 +187,10 @@ and walk through the initial setup steps.
|
||||
|
||||
* First, enter your **Organization Name** and **Organization Contact Email**. This will appear in emails when inviting new team members.
|
||||
* Next, create your Admin user. You can specify an **Email Address**, **Username**, and **Password**
|
||||
|
||||
* Finally, enter your list of **Permitted Domains**, read [here](/talk/configuring-talk/#permitted-domains) about whitelisting domains
|
||||
|
||||
|
||||
_During development, ensure you whitelist 127.0.0.1:3000 otherwise the
|
||||
[http://127.0.0.1:3000/](http://127.0.0.1:3000/) page will not
|
||||
load._
|
||||
@@ -220,6 +222,17 @@ Once you have added the domain of these docs, you can click the button below.
|
||||
<div class="mount"></div>
|
||||
</div>
|
||||
|
||||
### Developer Endpoints
|
||||
|
||||
With your local instance of Talk running in development mode (env variable `NODE_ENV=development`) you should now also be able to access the following developer routes:
|
||||
|
||||
* [http://127.0.0.1:3000/dev](http://127.0.0.1:3000/dev]) provides a sample comment stream
|
||||
|
||||
* [http://127.0.0.1:3000/dev/assets](http://127.0.0.1:3000/dev/assets]) provides a list of all stories in Talk and can generate new sample assets
|
||||
|
||||
### Conclusion
|
||||
At this point you've successfully installed, configured, and ran your very own
|
||||
instance of Talk! Continue through this documentation on this site to learn more
|
||||
on how to configure, develop with, and contribute to Talk!
|
||||
|
||||
|
||||
|
||||
@@ -70,5 +70,7 @@ You can now start the application by running:
|
||||
yarn watch:server
|
||||
```
|
||||
|
||||
If you are developing a custom plugin you can use `yarn watch:client` or `yarn watch` to run both client and server.
|
||||
|
||||
At this stage, you should refer to the [configuration](/talk/configuration/) for
|
||||
configuration variables that are specific to your installation.
|
||||
|
||||
@@ -23,11 +23,13 @@ permalink: /pre-launch-checklist/
|
||||
|
||||
- [ ] Do you need to migrate comments from a legacy system? We currently support Disqus, Livefyre, and Civil Comments.
|
||||
- Use the [Talk Import](https://github.com/coralproject/talk-importer) framework
|
||||
|
||||
|
||||
|
||||
- [ ] Do you want to provide single sign-on (SSO) by integrating with an external auth system?
|
||||
- See [Authenticating with Talk](/talk/integrating/authentication/)
|
||||
|
||||
- [ ] Do you want to integrate Talk with your CMS to automate embedding Talk Comment Stream into your site?
|
||||
- See [Asset Management](/talk/integrating/asset-management/)
|
||||
|
||||
- [ ] Do you want to use Social sign-on?
|
||||
- Facebook
|
||||
|
||||
@@ -5,13 +5,15 @@ permalink: /commenter-features/
|
||||
|
||||
## Signing up for Talk
|
||||
|
||||
There are 2 ways that newsrooms can support signup/login functionality with Talk:
|
||||
There are 3 ways that newsrooms can support signup/login functionality with Talk:
|
||||
|
||||
* Use Talk’s auth plugin out of the box (supports account registration with username and password, as well as features like forgot password)
|
||||
|
||||
* Use 3rd party authentication provider such as FaceBook or Google. We provide plugins that support logging in with either [Facebook](/talk/plugin/talk-plugin-facebook-auth/)
|
||||
or [Google](/talk/plugin/talk-plugin-google-auth/). (Note: you must provide your own Facebook App ID and Secret, which you can read more about here: [https://developers.facebook.com](https://developers.facebook.com))
|
||||
|
||||
* Create their own auth plugin to integrate with your own auth systems
|
||||
|
||||
We also provide a Facebook auth plugin that supports logging in with Facebook (you must provide your own Facebook App ID and Secret, which you can read more about here: [https://developers.facebook.com](https://developers.facebook.com))
|
||||
|
||||
## Comments and Replies
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ By default, Talk will use "Lazy Asset Creation" to dynamically generate Assets
|
||||
in Talk in order to make it easier for lighter installations. In order to have
|
||||
more strict control over this flow, we will create a plugin that will:
|
||||
|
||||
|
||||
1. Disable "Lazy Asset Creation" by [Overriding a Resolver](#overriding-a-resolver).
|
||||
2. Create Assets from our CMS by [Creating a New Asset Route](#creating-a-new-asset-route).
|
||||
3. Facilitate updates from our CMS to keep Talk in sync by [Creating an Asset Update Route](#creating-an-asset-update-route).
|
||||
@@ -248,7 +249,7 @@ module.exports = router => {
|
||||
};
|
||||
```
|
||||
|
||||
As you can see from the previous step of [Creating a New Asset Route](#creating-a-New-Asset-Route)
|
||||
As you can see from the previous step of [Creating a New Asset Route](#Creating%20a%20New%20Asset%20Route)
|
||||
, we have added the new `PUT` route to the router. This is a simple addition
|
||||
that allows your CMS to call into Talk when the asset has updated it's title,
|
||||
it's url (or really anything in the [AssetSchema](https://github.com/coralproject/talk/blob/master/models/asset.js)) to keep the Talk Admin and links up to date.
|
||||
|
||||
@@ -3,8 +3,10 @@ title: Authenticating with Talk
|
||||
permalink: /integrating/authentication/
|
||||
---
|
||||
|
||||
You can integrate Talk with any external authentication service that will enable
|
||||
seamless single sign-on for users within your organization. There are a few
|
||||
Out of the box Talk supports account registration with username and password, as well as features like forgot password.
|
||||
|
||||
You can also integrate Talk with any external authentication service that will enable
|
||||
seamless single sign-on (SSO) for users within your organization. There are a few
|
||||
methods of doing so:
|
||||
|
||||
1. Passport Middleware
|
||||
@@ -17,8 +19,12 @@ choice.
|
||||
|
||||
You would choose the **Passport Middleware** route when you are OK using an auth
|
||||
that is triggered from inside Talk that is not connected to an external auth
|
||||
state (you don't use the auth anywhere else now). A great example of this is our
|
||||
[talk-plugin-facebook-auth](/talk/plugin/talk-plugin-facebook-auth/) plugin.
|
||||
state (you don't use the auth anywhere else now).
|
||||
|
||||
Plugins are available for the following 3rd party authentication providers:
|
||||
|
||||
* [Facebook](/talk/plugin/talk-plugin-facebook-auth/)
|
||||
* [Google](/talk/plugin/talk-plugin-google-auth/)
|
||||
|
||||
## Custom Token Integration
|
||||
|
||||
|
||||
@@ -105,6 +105,8 @@ Then the application can be started as is.
|
||||
|
||||
### Docker
|
||||
|
||||
To deploy customized plugins to a production instance of Talk, we recommend using the Docker onbuild strategy outlined below.
|
||||
|
||||
If you deploy using Docker, you can extend from the `*-onbuild` image, an
|
||||
example `Dockerfile` for your project could be:
|
||||
|
||||
@@ -112,14 +114,22 @@ example `Dockerfile` for your project could be:
|
||||
FROM coralproject/talk:4.5-onbuild
|
||||
```
|
||||
|
||||
Where the directory for your instance would contain a `plugins.json` file
|
||||
describing the plugin requirements and a `plugins` directory containing any
|
||||
Establish a private repository for your instance that includes the following:
|
||||
|
||||
* a `plugins.json` file
|
||||
listing the plugin requirements
|
||||
* a `plugins` directory containing any
|
||||
other local plugins that should be included.
|
||||
* a Dockerfile as outlined above
|
||||
|
||||
Git submodules can also be used to point to plugin directories that might be outside your primary repository.
|
||||
|
||||
Onbuild triggers will execute when the image is building with your custom
|
||||
configuration and will ensure that the image is ready to use by building all
|
||||
assets inside the image as well.
|
||||
|
||||
Once built, you can deploy the docker image to your architecture by tagging the image and including it in your docker-compose.yml.
|
||||
|
||||
For more information on the onbuild image, refer to the
|
||||
[Installation from Docker](/talk/installation-from-docker/) documentation.
|
||||
|
||||
|
||||
+16
-6
@@ -1,11 +1,21 @@
|
||||
const { HTTP_X_REQUEST_ID } = require('../config');
|
||||
const uuid = require('uuid/v1');
|
||||
|
||||
// Trace middleware attaches a request id to each incoming request.
|
||||
module.exports = (req, res, next) => {
|
||||
req.id = uuid();
|
||||
module.exports = HTTP_X_REQUEST_ID
|
||||
? (req, res, next) => {
|
||||
req.id = req.get(HTTP_X_REQUEST_ID) || uuid();
|
||||
|
||||
// Add the context ID to the request as an HTTP header.
|
||||
res.set('X-Talk-Trace-ID', req.id);
|
||||
// Add the context ID to the request as an HTTP header.
|
||||
res.set('X-Talk-Trace-ID', req.id);
|
||||
|
||||
next();
|
||||
};
|
||||
next();
|
||||
}
|
||||
: (req, res, next) => {
|
||||
req.id = uuid();
|
||||
|
||||
// Add the context ID to the request as an HTTP header.
|
||||
res.set('X-Talk-Trace-ID', req.id);
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "talk",
|
||||
"version": "4.6.7",
|
||||
"version": "4.6.9",
|
||||
"description": "A better commenting experience from Mozilla, The New York Times, and the Washington Post. https://coralproject.net",
|
||||
"main": "app.js",
|
||||
"private": true,
|
||||
@@ -105,7 +105,7 @@
|
||||
"eventemitter2": "^4.1.2",
|
||||
"exports-loader": "^0.6.4",
|
||||
"express": "4.16.0",
|
||||
"express-static-gzip": "^0.3.1",
|
||||
"express-static-gzip": "^1.1.1",
|
||||
"extract-text-webpack-plugin": "^3.0.2",
|
||||
"file-loader": "^0.11.2",
|
||||
"final-form": "^4.8.1",
|
||||
@@ -130,6 +130,7 @@
|
||||
"imports-loader": "^0.7.1",
|
||||
"inquirer": "^3.2.2",
|
||||
"inquirer-autocomplete-prompt": "^0.12.1",
|
||||
"intersection-observer": "^0.5.1",
|
||||
"ioredis": "3.1.4",
|
||||
"ip": "^1.1.5",
|
||||
"jest": "^23.0.0",
|
||||
|
||||
+22
-1
@@ -42,6 +42,26 @@ if (!DISABLE_STATIC_SERVER) {
|
||||
res.redirect(301, newEmbed);
|
||||
});
|
||||
|
||||
/**
|
||||
* setHeaders adds new headers related to caching to the static files that are
|
||||
* served.
|
||||
*
|
||||
* @param res the response that can be used to set headers on
|
||||
* @param path the path on the filesystem where the files are being served from
|
||||
*/
|
||||
const setHeaders = (res, path) => {
|
||||
if (path.endsWith('embed.js')) {
|
||||
// The embed.js file itself should not be cached for a long duration of
|
||||
// time, as it may change based on the deploy.
|
||||
res.setHeader('Cache-Control', 'public, max-age=3600');
|
||||
} else {
|
||||
// All static files besides the embed.js file contain hashes, we should
|
||||
// ensure that any other file is cached for a long duration of time. This
|
||||
// is cached for 1 week.
|
||||
res.setHeader('Cache-Control', 'public, max-age=604800, immutable');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Serve the directories under dist.
|
||||
*/
|
||||
@@ -58,10 +78,11 @@ if (!DISABLE_STATIC_SERVER) {
|
||||
fileExtension: 'zz',
|
||||
},
|
||||
],
|
||||
setHeaders,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
router.use('/static', express.static(dist));
|
||||
router.use('/static', express.static(dist, { setHeaders }));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -159,6 +159,9 @@ const config = {
|
||||
'process.env': {
|
||||
VERSION: `"${require('./package.json').version}"`,
|
||||
NODE_ENV: `${JSON.stringify(process.env.NODE_ENV)}`,
|
||||
TALK_DEFAULT_LAZY_RENDER: `${JSON.stringify(
|
||||
process.env.TALK_DEFAULT_LAZY_RENDER
|
||||
)}`,
|
||||
},
|
||||
}),
|
||||
new webpack.EnvironmentPlugin({
|
||||
|
||||
@@ -4277,10 +4277,10 @@ exports-loader@^0.6.4:
|
||||
loader-utils "^1.0.2"
|
||||
source-map "0.5.x"
|
||||
|
||||
express-static-gzip@^0.3.1:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/express-static-gzip/-/express-static-gzip-0.3.2.tgz#89ede84547a5717de3146315f62dc996c071a88d"
|
||||
integrity sha512-xFOW5Lxrh4xLey5i6gGWHOFznJayGCxazUau0kq7ElUh1t7q2B6IlvWv4d3UJwJej+aXEu9os/VpzPvRchdNiA==
|
||||
express-static-gzip@^1.1.1:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/express-static-gzip/-/express-static-gzip-1.1.3.tgz#345ea02637d9d5865777d6fb57ccc0884abcda65"
|
||||
integrity sha512-k8Q4Dx4PDpzEb8kth4uiPWrBeJWJYSgnWMzNdjQUOsEyXfYKbsyZDkU/uXYKcorRwOie5Vzp4RMEVrJLMfB6rA==
|
||||
dependencies:
|
||||
serve-static "^1.12.3"
|
||||
|
||||
@@ -6042,6 +6042,11 @@ interpret@^1.0.0, interpret@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614"
|
||||
integrity sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=
|
||||
|
||||
intersection-observer@^0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/intersection-observer/-/intersection-observer-0.5.1.tgz#e340fc56ce74290fe2b2394d1ce88c4353ac6dfa"
|
||||
integrity sha512-Zd7Plneq82kiXFixs7bX62YnuZ0BMRci9br7io88LwDyF3V43cQMI+G5IiTlTNTt+LsDUppl19J/M2Fp9UkH6g==
|
||||
|
||||
invariant@^2.0.0, invariant@^2.2.0, invariant@^2.2.1, invariant@^2.2.2:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
|
||||
|
||||
Reference in New Issue
Block a user