mirror of
https://github.com/wassname/talk.git
synced 2026-07-29 11:28:24 +08:00
Merge branch 'master' into setup-fixes
This commit is contained in:
@@ -3,11 +3,13 @@
|
||||
color: white;
|
||||
background: grey;
|
||||
box-sizing: border-box;
|
||||
padding: 2px 8px;
|
||||
padding: 0px 5px;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
height: 28px;
|
||||
|
||||
height: 24px;
|
||||
letter-spacing: 0.4px;
|
||||
margin-bottom: 1px;
|
||||
|
||||
> i {
|
||||
font-size: 14px;
|
||||
vertical-align: text-top;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.flagBox {
|
||||
border-top: 1px solid rgba(66, 66, 66, 0.12);
|
||||
|
||||
margin-top: 10px;
|
||||
.container {
|
||||
padding: 0 14px;
|
||||
}
|
||||
|
||||
@@ -186,7 +186,6 @@
|
||||
.actionButton {
|
||||
transform: scale(.8);
|
||||
margin: 0;
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
.minimal {
|
||||
|
||||
@@ -10,16 +10,18 @@
|
||||
.logo span {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
font-size: 18px;
|
||||
font-size: 26px;
|
||||
vertical-align: middle;
|
||||
font-weight: 500;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.logo {
|
||||
background: #E5E5E5;
|
||||
background: #696969;
|
||||
height: 100%;
|
||||
width: 128px;
|
||||
z-index: 10;
|
||||
border-right: 1px #757575 solid;
|
||||
}
|
||||
|
||||
.base {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
.count {
|
||||
display: inline-block;
|
||||
background: #989797;
|
||||
background: #616161;
|
||||
margin: 2px;
|
||||
vertical-align: middle;
|
||||
padding: 1px 7px;
|
||||
padding: 1px 5px;
|
||||
border-radius: 2px;
|
||||
margin-left: 2px;
|
||||
line-height: 20px;
|
||||
line-height: 18px;
|
||||
box-sizing: border-box;
|
||||
height: 21px;
|
||||
height: 18px;
|
||||
right: 0;
|
||||
margin-top: -2px;
|
||||
margin-top: 0px;
|
||||
font-size: 12px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
.tab {
|
||||
flex: 1;
|
||||
color: #C0C0C0;
|
||||
color: #BDBDBD;
|
||||
text-transform: capitalize;
|
||||
font-weight: 100;
|
||||
font-size: 14px;
|
||||
@@ -29,7 +29,7 @@
|
||||
margin-right: 20px;
|
||||
&:hover {
|
||||
color: white;
|
||||
border-bottom: solid 2px #F36451;
|
||||
/*border-bottom: solid 2px #F36451;*/
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
@@ -111,7 +111,7 @@ span {
|
||||
color: white;
|
||||
text-transform: capitalize;
|
||||
font-weight: 400;
|
||||
font-size: 15px;
|
||||
font-size: 20px;
|
||||
letter-spacing: 1px;
|
||||
transition: background-color 200ms;
|
||||
opacity: 1;
|
||||
@@ -173,7 +173,7 @@ span {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
font-size: 18px;
|
||||
width: 100%;
|
||||
max-width: 700px;
|
||||
max-width: 650px;
|
||||
min-width: 400px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
@@ -470,7 +470,7 @@ span {
|
||||
|
||||
.searchTrigger {
|
||||
position: relative;
|
||||
top: .3em;
|
||||
top: .2em;
|
||||
}
|
||||
|
||||
.adminCommentInfoBar {
|
||||
|
||||
@@ -27,9 +27,10 @@
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-right: 13px;
|
||||
margin-right: 5px;
|
||||
font-size: 18px;
|
||||
vertical-align: middle;
|
||||
margin-top: -3px;
|
||||
}
|
||||
|
||||
.type--black {
|
||||
@@ -143,7 +144,7 @@
|
||||
border-radius: 3px;
|
||||
text-transform: capitalize;
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.03), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.09);
|
||||
width: 128px;
|
||||
width: 129px;
|
||||
|
||||
&:hover {
|
||||
box-shadow: none;
|
||||
@@ -166,7 +167,7 @@
|
||||
border-radius: 3px;
|
||||
text-transform: capitalize;
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.03), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.09);
|
||||
width: 128px;
|
||||
width: 129px;
|
||||
|
||||
&:hover {
|
||||
color: white;
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
// entrypoint for the entire applications configuration.
|
||||
require('env-rewrite').rewrite();
|
||||
|
||||
const uniq = require('lodash/uniq');
|
||||
|
||||
//==============================================================================
|
||||
// CONFIG INITIALIZATION
|
||||
//==============================================================================
|
||||
@@ -31,6 +33,13 @@ const CONFIG = {
|
||||
// token.
|
||||
JWT_COOKIE_NAME: process.env.TALK_JWT_COOKIE_NAME || 'authorization',
|
||||
|
||||
// JWT_SIGNING_COOKIE_NAME will be the cookie set when cookies are issued.
|
||||
// This defaults to the TALK_JWT_COOKIE_NAME value.
|
||||
JWT_SIGNING_COOKIE_NAME: process.env.TALK_JWT_SIGNING_COOKIE_NAME || process.env.TALK_JWT_COOKIE_NAME || 'authorization',
|
||||
|
||||
// JWT_COOKIE_NAMES declares the many cookie names used for verification.
|
||||
JWT_COOKIE_NAMES: process.env.TALK_JWT_COOKIE_NAMES || null,
|
||||
|
||||
// JWT_CLEAR_COOKIE_LOGOUT specifies whether the named cookie should be
|
||||
// cleared when the user is logged out.
|
||||
JWT_CLEAR_COOKIE_LOGOUT: process.env.TALK_JWT_CLEAR_COOKIE_LOGOUT ? process.env.TALK_JWT_CLEAR_COOKIE_LOGOUT !== 'FALSE' : true,
|
||||
@@ -165,6 +174,16 @@ if (CONFIG.JWT_DISABLE_ISSUER) {
|
||||
CONFIG.JWT_ISSUER = undefined;
|
||||
}
|
||||
|
||||
// Parse and handle cookie names.
|
||||
if (CONFIG.JWT_COOKIE_NAMES) {
|
||||
CONFIG.JWT_COOKIE_NAMES = CONFIG.JWT_COOKIE_NAMES.split(',');
|
||||
} else {
|
||||
CONFIG.JWT_COOKIE_NAMES = [];
|
||||
}
|
||||
|
||||
// Add in the default cookie names and strip duplicates.
|
||||
CONFIG.JWT_COOKIE_NAMES = uniq(CONFIG.JWT_COOKIE_NAMES.concat([CONFIG.JWT_COOKIE_NAME, CONFIG.JWT_SIGNING_COOKIE_NAME]));
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// External database url's
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -87,8 +87,16 @@ on the contents of those variables.**
|
||||
These are advanced settings for fine tuning the auth integration, and
|
||||
is not needed in most situations.
|
||||
|
||||
- `TALK_JWT_COOKIE_NAME` (_optional_) - the name of the cookie to extract the
|
||||
JWT from (Default `authorization`)
|
||||
- `TALK_JWT_COOKIE_NAME` (_optional_) - the default cookie name to check for a
|
||||
valid JWT token to use for verifying a user. (Default `authorization`)
|
||||
- `TALK_JWT_SIGNING_COOKIE_NAME` (_optional_) - the default cookie name that is
|
||||
use to set a cookie containing a JWT that was issued by Talk.
|
||||
(Default `process.env.TALK_JWT_COOKIE_NAME`)
|
||||
- `TALK_JWT_COOKIE_NAMES` (_optional_) - the different cookie names to check for
|
||||
a JWT token in, seperated by `,`. By default, we always use the
|
||||
`process.env.TALK_JWT_COOKIE_NAME` and `process.env.TALK_JWT_SIGNING_COOKIE_NAME`
|
||||
for this value. Any additional cookie names specified here will be appended to
|
||||
the list of cookie names to inspect.
|
||||
- `TALK_JWT_CLEAR_COOKIE_LOGOUT` (_optional_) - when `FALSE`, Talk will not
|
||||
clear the cookie with name `TALK_JWT_COOKIE_NAME` when logging out (Default
|
||||
`TRUE`)
|
||||
@@ -115,6 +123,14 @@ will be used:
|
||||
}
|
||||
```
|
||||
|
||||
When our passport middleware checks for JWT tokens, it searches in the following
|
||||
order:
|
||||
|
||||
1. Custom cookies named from the list in `TALK_JWT_COOKIE_NAMES`.
|
||||
2. Default cookies named `TALK_JWT_COOKIE_NAME` then `TALK_JWT_SIGNING_COOKIE_NAME`.
|
||||
3. Query parameter `?access_token={TOKEN}`.
|
||||
4. Header: `Authorization: Bearer {TOKEN}`.
|
||||
|
||||
### Email
|
||||
|
||||
- `TALK_SMTP_EMAIL` (*required for email*) - the address to send emails from
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "talk",
|
||||
"version": "3.0.0",
|
||||
"version": "3.1.0",
|
||||
"description": "A better commenting experience from Mozilla, The New York Times, and the Washington Post. https://coralproject.net",
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
color: #696969;
|
||||
background-color: white;
|
||||
box-sizing: border-box;
|
||||
padding: 2px 8px;
|
||||
padding: 0px 5px;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
height: 28px;
|
||||
height: 24px;
|
||||
transition: background-color .2s cubic-bezier(.4,0,.2,1), color .2s cubic-bezier(.4,0,.2,1), border-color .2s cubic-bezier(.4,0,.2,1);
|
||||
margin: 2px 0px;
|
||||
letter-spacing: 0.4px;
|
||||
|
||||
}
|
||||
|
||||
.tag:hover {
|
||||
@@ -39,4 +40,3 @@
|
||||
font-size: 15px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
+15
-8
@@ -23,7 +23,8 @@ const {
|
||||
JWT_ALG,
|
||||
RECAPTCHA_SECRET,
|
||||
RECAPTCHA_ENABLED,
|
||||
JWT_COOKIE_NAME,
|
||||
JWT_SIGNING_COOKIE_NAME,
|
||||
JWT_COOKIE_NAMES,
|
||||
JWT_CLEAR_COOKIE_LOGOUT,
|
||||
JWT_USER_ID_CLAIM,
|
||||
} = require('../config');
|
||||
@@ -53,7 +54,7 @@ const GenerateToken = (user) => {
|
||||
const SetTokenForSafari = (req, res, token) => {
|
||||
const browser = bowser._detect(req.headers['user-agent']);
|
||||
if (browser.ios || browser.safari) {
|
||||
res.cookie(JWT_COOKIE_NAME, token, {
|
||||
res.cookie(JWT_SIGNING_COOKIE_NAME, token, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
expires: new Date(Date.now() + ms(JWT_EXPIRY))
|
||||
@@ -169,7 +170,7 @@ const HandleLogout = (req, res, next) => {
|
||||
|
||||
// Only clear the cookie on logout if enabled.
|
||||
if (JWT_CLEAR_COOKIE_LOGOUT) {
|
||||
res.clearCookie(JWT_COOKIE_NAME);
|
||||
res.clearCookie(JWT_SIGNING_COOKIE_NAME);
|
||||
}
|
||||
|
||||
res.status(204).end();
|
||||
@@ -209,14 +210,20 @@ const CheckBlacklisted = async (jwt) => {
|
||||
const JwtStrategy = require('passport-jwt').Strategy;
|
||||
const ExtractJwt = require('passport-jwt').ExtractJwt;
|
||||
|
||||
let cookieExtractor = function(req) {
|
||||
let token = null;
|
||||
|
||||
let cookieExtractor = (req) => {
|
||||
if (req && req.cookies) {
|
||||
token = req.cookies[JWT_COOKIE_NAME];
|
||||
|
||||
// Walk over all the cookie names in JWT_COOKIE_NAMES.
|
||||
for (const cookieName of JWT_COOKIE_NAMES) {
|
||||
|
||||
// Check to see if that cookie is set.
|
||||
if (cookieName in req.cookies && req.cookies[cookieName] !== null && req.cookies[cookieName].length > 0) {
|
||||
return req.cookies[cookieName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return token;
|
||||
return null;
|
||||
};
|
||||
|
||||
// Override the JwtVerifier method on the JwtStrategy so we can pack the
|
||||
|
||||
+2
-2
@@ -205,8 +205,8 @@ class TagsService {
|
||||
return updateModel(item_type, query, {
|
||||
$pull: {
|
||||
tags: {
|
||||
name: link.tag.name
|
||||
}
|
||||
'tag.name': link.tag.name,
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ describe('services.TagsService', () => {
|
||||
const id = comment.id;
|
||||
const name = 'BEST';
|
||||
const assigned_by = user.id;
|
||||
|
||||
|
||||
await TagsService.add(id, 'COMMENTS', {
|
||||
tag: {
|
||||
name
|
||||
@@ -45,7 +45,7 @@ describe('services.TagsService', () => {
|
||||
const id = comment.id;
|
||||
const name = 'BEST';
|
||||
const assigned_by = user.id;
|
||||
|
||||
|
||||
await TagsService.add(id, 'COMMENTS', {
|
||||
tag: {
|
||||
name
|
||||
@@ -103,5 +103,43 @@ describe('services.TagsService', () => {
|
||||
expect(tags.length).to.equal(0);
|
||||
}
|
||||
});
|
||||
it('removes a tag out of 2', async () => {
|
||||
const id = comment.id;
|
||||
const name = 'BEST';
|
||||
const assigned_by = user.id;
|
||||
|
||||
await TagsService.add(id, 'COMMENTS', {
|
||||
tag: {
|
||||
name: 'ANOTHER'
|
||||
},
|
||||
assigned_by
|
||||
});
|
||||
|
||||
await TagsService.add(id, 'COMMENTS', {
|
||||
tag: {
|
||||
name
|
||||
},
|
||||
assigned_by
|
||||
});
|
||||
|
||||
{
|
||||
const {tags} = await CommentsService.findById(id);
|
||||
expect(tags.length).to.equal(2);
|
||||
}
|
||||
|
||||
// ok now to remove it
|
||||
await TagsService.remove(id, 'COMMENTS', {
|
||||
tag: {
|
||||
name
|
||||
},
|
||||
assigned_by
|
||||
});
|
||||
|
||||
{
|
||||
const {tags} = await CommentsService.findById(id);
|
||||
expect(tags.length).to.equal(1);
|
||||
expect(tags[0].tag.name).to.equal('ANOTHER');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user