mirror of
https://github.com/wassname/talk.git
synced 2026-07-06 05:17:19 +08:00
Merge branch 'master' into reject-username
This commit is contained in:
+2
-1
@@ -7,6 +7,7 @@ ONBUILD ARG TALK_REPLY_COMMENTS_LOAD_DEPTH=3
|
||||
ONBUILD ARG TALK_THREADING_LEVEL=3
|
||||
ONBUILD ARG TALK_DEFAULT_STREAM_TAB=all
|
||||
ONBUILD ARG TALK_DEFAULT_LANG=en
|
||||
ONBUILD ARG TALK_WHITELISTED_LANGUAGES
|
||||
ONBUILD ARG TALK_PLUGINS_JSON
|
||||
ONBUILD ARG TALK_WEBPACK_SOURCE_MAP
|
||||
|
||||
@@ -20,4 +21,4 @@ ONBUILD COPY . /usr/src/app
|
||||
ONBUILD RUN cli plugins reconcile && \
|
||||
yarn && \
|
||||
yarn build && \
|
||||
yarn cache clean
|
||||
yarn cache clean
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
@custom-media --big-viewport (min-width: 780px);
|
||||
|
||||
.root {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
font-size: 18px;
|
||||
@@ -13,7 +12,6 @@
|
||||
margin-top: 13px;
|
||||
min-height: 0;
|
||||
outline: 0;
|
||||
|
||||
/*
|
||||
Fix rendering issues in Safari by promoting this
|
||||
into its own layer.
|
||||
@@ -21,7 +19,6 @@
|
||||
https://www.pivotaltracker.com/story/show/151142211
|
||||
*/
|
||||
transform: translateZ(0);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
@@ -39,7 +36,6 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
}
|
||||
|
||||
.author {
|
||||
@@ -74,7 +70,7 @@
|
||||
max-width: 500px;
|
||||
font-weight: 300;
|
||||
font-size: 16px;
|
||||
word-break: break-all;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.created {
|
||||
@@ -95,7 +91,6 @@
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
max-width: 500px;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
color: #063b9a;
|
||||
@@ -103,17 +98,15 @@
|
||||
font-weight: 500;
|
||||
letter-spacing: .5px;
|
||||
margin-left: 10px;
|
||||
|
||||
font-size: 13px;
|
||||
margin-left: 5px;
|
||||
padding-bottom: 0px;
|
||||
border-bottom: solid 1px;
|
||||
line-height: 16px;
|
||||
|
||||
&:hover {
|
||||
opacity: .9;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,12 +131,10 @@
|
||||
cursor: pointer;
|
||||
font-weight: normal;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
opacity: .9;
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
@@ -172,7 +163,6 @@
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
margin-right: 14px;
|
||||
|
||||
i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
@@ -181,7 +171,6 @@
|
||||
@media (--big-viewport) {
|
||||
.root {
|
||||
margin-bottom: 30px;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
@@ -190,7 +179,6 @@
|
||||
|
||||
.commentContent {
|
||||
display: flex;
|
||||
|
||||
blockquote {
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
|
||||
@@ -39,7 +39,20 @@ import pt_BR from '../../../locales/pt_BR.yml';
|
||||
import zh_CN from '../../../locales/zh_CN.yml';
|
||||
import zh_TW from '../../../locales/zh_TW.yml';
|
||||
|
||||
export const defaultLocale = process.env.TALK_DEFAULT_LANG.replace(/-/g, '_');
|
||||
// the list of languages that are whitelisted. If false, all languages that are
|
||||
// supported by Talk will be enabled.
|
||||
const whitelistedLanguages =
|
||||
process.env.TALK_WHITELISTED_LANGUAGES &&
|
||||
process.env.TALK_WHITELISTED_LANGUAGES.split(',').map(l => l.trim());
|
||||
|
||||
// The default language. If the whitelisted languages is specified and the
|
||||
// default language is not in that list, then the first language in the
|
||||
// whitelisted list will be used as the default.
|
||||
export const defaultLocale = whitelistedLanguages
|
||||
? !whitelistedLanguages.includes(process.env.TALK_DEFAULT_LANG)
|
||||
? whitelistedLanguages[0]
|
||||
: process.env.TALK_DEFAULT_LANG
|
||||
: process.env.TALK_DEFAULT_LANG;
|
||||
|
||||
export const translations = {
|
||||
...ar,
|
||||
@@ -64,10 +77,14 @@ let TIMEAGO_INSTANCE;
|
||||
// to the default language.
|
||||
const detectLanguage = () =>
|
||||
first(
|
||||
negotiateLanguages(navigator.languages, supportedLocales, {
|
||||
defaultLocale,
|
||||
strategy: 'lookup',
|
||||
})
|
||||
negotiateLanguages(
|
||||
navigator.languages,
|
||||
whitelistedLanguages || supportedLocales,
|
||||
{
|
||||
defaultLocale,
|
||||
strategy: 'lookup',
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export function setupTranslations() {
|
||||
|
||||
@@ -36,6 +36,13 @@ const CONFIG = {
|
||||
// rendered text.
|
||||
DEFAULT_LANG: process.env.TALK_DEFAULT_LANG || 'en',
|
||||
|
||||
// WHITELISTED_LANGUAGES is a comma separated list of language/locales that
|
||||
// should be supported. If the default language is not included in the
|
||||
// whitelist list, the first entry will be used as the default.
|
||||
WHITELISTED_LANGUAGES:
|
||||
process.env.TALK_WHITELISTED_LANGUAGES &&
|
||||
process.env.TALK_WHITELISTED_LANGUAGES.split(',').map(l => l.trim()),
|
||||
|
||||
// When TRUE, it ensures that database indexes created in core will not add
|
||||
// indexes.
|
||||
CREATE_MONGO_INDEXES: process.env.DISABLE_CREATE_MONGO_INDEXES !== 'TRUE',
|
||||
@@ -317,6 +324,17 @@ CONFIG.JWT_COOKIE_NAMES = uniq(
|
||||
])
|
||||
);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Locale validation
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
if (
|
||||
CONFIG.WHITELISTED_LANGUAGES &&
|
||||
!CONFIG.WHITELISTED_LANGUAGES.includes(CONFIG.DEFAULT_LANG)
|
||||
) {
|
||||
CONFIG.DEFAULT_LANG = CONFIG.WHITELISTED_LANGUAGES[0];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// External database url's
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -81,4 +81,4 @@ TALK_JWT_SECRET=jX9y8G2ApcVLwyL{$6s3
|
||||
|
||||
Be default, we sign our tokens with HMAC using a SHA-256 hash algorithm. If you
|
||||
want to change the signing algorithm, or use multiple signing/verifying keys,
|
||||
refer to our [Advanced Configuration](/talk/advanced-configuration/) documentation.
|
||||
refer to our [Advanced Configuration](/talk/advanced-configuration/#talk-jwt-secret) documentation.
|
||||
|
||||
@@ -31,6 +31,21 @@ image you can specify it with `--build-arg TALK_DEFAULT_LANG=en`.
|
||||
|
||||
Specify the default translation language. (Default `en`)
|
||||
|
||||
## TALK_WHITELISTED_LANGUAGES
|
||||
|
||||
This is a **Build Variable** and must be consumed during build. If using the
|
||||
[Docker-onbuild](/talk/installation-from-docker/#onbuild)
|
||||
image you can specify it with `--build-arg TALK_WHITELISTED_LANGUAGES=en`.
|
||||
|
||||
Specify the comma separated whitelisted languages that you want the Talk
|
||||
application to serve. This will override the available set of languages that
|
||||
Talk will allow to be served.
|
||||
|
||||
If the [TALK_DEFAULT_LANG](#talk-default-lang) is not included in this list of
|
||||
whitelisted languages, then the first whitelisted language will become the
|
||||
default language. If this parameter is empty, then all languages supported by
|
||||
Talk will be whitelisted. (Default '')
|
||||
|
||||
## TALK_DEFAULT_STREAM_TAB
|
||||
|
||||
This is a **Build Variable** and must be consumed during build. If using the
|
||||
|
||||
@@ -25,9 +25,10 @@ state (you don't use the auth anywhere else now). A great example of this is our
|
||||
You can integrate Talk with any authentication service to enable single sign-on
|
||||
for users. The steps to do that are:
|
||||
|
||||
1. Create a service that generates [JWT tokens](https://jwt.io).
|
||||
1. Create a service that generates [JWT tokens](https://jwt.io/introduction/).
|
||||
2. Push the token into the embed.
|
||||
3. Implement the `tokenUserNotFound` hook to process the token.
|
||||
3. Implement the [`tokenUserNotFound`](#implement-tokenusernotfound) hook to
|
||||
process the token.
|
||||
|
||||
### Create JWT Token
|
||||
|
||||
@@ -39,7 +40,20 @@ Using that demo application, you'll see how you can:
|
||||
|
||||
1. Create a node application that can issue JWT's that are compatible with Talk.
|
||||
2. Provide a validation endpoint that can be used by Talk to validate the token
|
||||
and get the user via the `tokenUserNotFound` hook.
|
||||
and get the user via the [`tokenUserNotFound`](#implement-tokenusernotfound)
|
||||
hook.
|
||||
|
||||
It's also important to note a few requirements for proper integration with Talk.
|
||||
The generated JWT must contain the following claims:
|
||||
|
||||
- [`jti`](https://tools.ietf.org/html/rfc7519#section-4.1.7): a unique identifier for the token (like a uuid/v4)
|
||||
- [`exp`](https://tools.ietf.org/html/rfc7519#section-4.1.4): the expiry date of the token as a unix timestamp
|
||||
- [`sub`](https://tools.ietf.org/html/rfc7519#section-4.1.2): the user identifier that can be used to lookup the user in the mongo
|
||||
database
|
||||
- The user may not yet exist in the database, but that's the responsibility
|
||||
of the [`tokenUserNotFound`](#implement-tokenusernotfound) hook.
|
||||
- [`iss`](https://tools.ietf.org/html/rfc7519#section-4.1.1): the issuer for the token must match the value of `TALK_JWT_ISSUER`
|
||||
- [`aud`](https://tools.ietf.org/html/rfc7519#section-4.1.3): the audience for the token must match the value of `TALK_JWT_AUDIENCE`
|
||||
|
||||
### Push token into embed
|
||||
|
||||
@@ -47,7 +61,8 @@ We're assuming that your CMS is capable of authenticating a user account, or
|
||||
at least having the user's details available to send off to the token creation
|
||||
service we created/used in the previous step.
|
||||
|
||||
Using the token that was created for the user, you simply have to ammend the template where Talk is rendering to read as the following:
|
||||
Using the token that was created for the user, you simply have to amend the
|
||||
template where Talk is rendering to read as the following:
|
||||
|
||||
```js
|
||||
Coral.Talk.render(document.getElementById('coralStreamEmbed'), {
|
||||
@@ -72,12 +87,12 @@ example issuer and Talk must match:
|
||||
|
||||
| Talk | Token Issuer Example |
|
||||
|------|----------------------|
|
||||
|`JWT_ISSUER`|`JWT_ISSUER`|
|
||||
|`JWT_AUDIENCE`|`JWT_AUDIENCE`|
|
||||
|`SECRET`|`JWT_SECRET`*|
|
||||
|[`TALK_JWT_ISSUER`](/talk/advanced-configuration/#talk-jwt-issuer)|`JWT_ISSUER`|
|
||||
|[`TALK_JWT_AUDIENCE`](/talk/advanced-configuration/#talk-jwt-audience)|`JWT_AUDIENCE`|
|
||||
|[`TALK_JWT_SECRET`](/talk/advanced-configuration/#talk-jwt-secret)|`JWT_SECRET`*|
|
||||
|
||||
\* Note that secrets is a pretty complex topic, refer to the
|
||||
[TALK-JWT-SECRET](/talk/advanced-configuration/#TALK-JWT-SECRET) configuration
|
||||
[TALK_JWT_SECRET](/talk/advanced-configuration/#talk-jwt-secret) configuration
|
||||
reference, the basic takeaway is that the secret used to sign the tokens issued
|
||||
by the issuer must be able to be verified by Talk.
|
||||
|
||||
|
||||
+2
-3
@@ -291,11 +291,10 @@ pre {
|
||||
|
||||
.content {
|
||||
article {
|
||||
p a:not(.plain-link) {
|
||||
@extend .coral-link;
|
||||
}
|
||||
p a:not(.plain-link),
|
||||
ul:not(.toc__menu) li a,
|
||||
ol li a,
|
||||
td a,
|
||||
dd > a {
|
||||
@extend .coral-link;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
@@ -97,6 +97,7 @@
|
||||
color: #3B4A53;
|
||||
font-size: 1em;
|
||||
margin-bottom: 15px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.block {
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
top: calc(50% - 2px);
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
@@ -33,4 +35,4 @@
|
||||
align-items: center;
|
||||
height: 50px;
|
||||
margin: 0 20px;
|
||||
}
|
||||
}
|
||||
|
||||
+9
-5
@@ -8,7 +8,7 @@ const {
|
||||
const { first, get, has, merge, isUndefined } = require('lodash');
|
||||
const yaml = require('yamljs');
|
||||
const plugins = require('./plugins');
|
||||
const { DEFAULT_LANG } = require('../config');
|
||||
const { DEFAULT_LANG, WHITELISTED_LANGUAGES } = require('../config');
|
||||
|
||||
const resolve = (...paths) =>
|
||||
path.resolve(path.join(__dirname, '..', 'locales', ...paths));
|
||||
@@ -105,10 +105,14 @@ const i18n = {
|
||||
|
||||
// negotiate the language.
|
||||
const lang = first(
|
||||
negotiateLanguages(acceptsLanguages, supportedLocales, {
|
||||
defaultLocale: DEFAULT_LANG,
|
||||
strategy: 'lookup',
|
||||
})
|
||||
negotiateLanguages(
|
||||
acceptsLanguages,
|
||||
WHITELISTED_LANGUAGES || supportedLocales,
|
||||
{
|
||||
defaultLocale: DEFAULT_LANG,
|
||||
strategy: 'lookup',
|
||||
}
|
||||
)
|
||||
);
|
||||
debug(`decided language as '${lang}'`);
|
||||
|
||||
|
||||
@@ -169,6 +169,7 @@ const config = {
|
||||
TALK_REPLY_COMMENTS_LOAD_DEPTH: '3',
|
||||
TALK_DEFAULT_STREAM_TAB: 'all',
|
||||
TALK_DEFAULT_LANG: 'en',
|
||||
TALK_WHITELISTED_LANGUAGES: '',
|
||||
}),
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||
|
||||
|
||||
Reference in New Issue
Block a user