Merge branch 'master' into DepauMD

This commit is contained in:
Davide Depau 2018-09-10 00:11:17 +02:00
commit 550f6ebb1f
19 changed files with 92 additions and 23 deletions

View file

@ -16,7 +16,7 @@ jobs:
- export PATH="$HOME/.yarn/bin:$PATH" - export PATH="$HOME/.yarn/bin:$PATH"
- env: task=npm-test - env: task=npm-test
node_js: node_js:
- 7 - 8
before_install: before_install:
- curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version "$YARN_VERSION" - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version "$YARN_VERSION"
- export PATH="$HOME/.yarn/bin:$PATH" - export PATH="$HOME/.yarn/bin:$PATH"

View file

@ -29,6 +29,7 @@ Thanks for using! :smile:
- [Heroku Deployment](#heroku-deployment) - [Heroku Deployment](#heroku-deployment)
- [Kubernetes](#kubernetes) - [Kubernetes](#kubernetes)
- [CodiMD by docker container](#codimd-by-docker-container) - [CodiMD by docker container](#codimd-by-docker-container)
- [Cloudron](#cloudron)
- [Upgrade](#upgrade) - [Upgrade](#upgrade)
- [Native setup](#native-setup) - [Native setup](#native-setup)
- [Configuration](#configuration) - [Configuration](#configuration)
@ -100,7 +101,7 @@ To install use `helm install stable/hackmd`.
For all further details, please check out the offical CodiMD [K8s helm chart](https://github.com/kubernetes/charts/tree/master/stable/hackmd). For all further details, please check out the offical CodiMD [K8s helm chart](https://github.com/kubernetes/charts/tree/master/stable/hackmd).
## CodiMD by docker container ## CodiMD by docker container
[![Try in PWD](https://cdn.rawgit.com/play-with-docker/stacks/cff22438/assets/images/button.png)](http://play-with-docker.com?stack=https://github.com/hackmdio/docker-hackmd/raw/master/docker-compose.yml&stack_name=codimd) [![Try in PWD](https://cdn.rawgit.com/play-with-docker/stacks/cff22438/assets/images/button.png)](http://play-with-docker.com?stack=https://github.com/hackmdio/codimd-container/raw/master/docker-compose.yml&stack_name=codimd)
**Debian-based version:** **Debian-based version:**
@ -115,11 +116,17 @@ For all further details, please check out the offical CodiMD [K8s helm chart](h
The easiest way to setup CodiMD using docker are using the following three commands: The easiest way to setup CodiMD using docker are using the following three commands:
```console ```console
git clone https://github.com/hackmdio/docker-hackmd.git git clone https://github.com/hackmdio/codimd-container.git
cd docker-codimd cd codimd-container
docker-compose up docker-compose up
``` ```
Read more about it in the [docker repository…](https://github.com/hackmdio/docker-hackmd) Read more about it in the [container repository…](https://github.com/hackmdio/codimd-container)
## Cloudron
Install CodiMD on [Cloudron](https://cloudron.io):
[![Install](https://cloudron.io/img/button.svg)](https://cloudron.io/button.html?app=io.hackmd.cloudronapp)
# Upgrade # Upgrade
@ -195,7 +202,7 @@ There are some config settings you need to change in the files below.
| `CMD_GITLAB_CLIENTID` | no example | GitLab API client id | | `CMD_GITLAB_CLIENTID` | no example | GitLab API client id |
| `CMD_GITLAB_CLIENTSECRET` | no example | GitLab API client secret | | `CMD_GITLAB_CLIENTSECRET` | no example | GitLab API client secret |
| `CMD_GITLAB_VERSION` | no example | GitLab API version (v3 or v4) | | `CMD_GITLAB_VERSION` | no example | GitLab API version (v3 or v4) |
| `CMD_MATTERMOST_BASEURL` | no example | Mattermost authentication endpoint | | `CMD_MATTERMOST_BASEURL` | no example | Mattermost authentication endpoint for versions below 5.0. For Mattermost version 5.0 and above, see [guide](docs/guides/auth/mattermost-self-hosted.md). |
| `CMD_MATTERMOST_CLIENTID` | no example | Mattermost API client id | | `CMD_MATTERMOST_CLIENTID` | no example | Mattermost API client id |
| `CMD_MATTERMOST_CLIENTSECRET` | no example | Mattermost API client secret | | `CMD_MATTERMOST_CLIENTSECRET` | no example | Mattermost API client secret |
| `CMD_DROPBOX_CLIENTID` | no example | Dropbox API client id | | `CMD_DROPBOX_CLIENTID` | no example | Dropbox API client id |

View file

@ -0,0 +1,58 @@
Authentication guide - Mattermost (self-hosted)
===
*Note: The Mattermost setup portion of this document is just a quick guide. See the [official documentation](https://docs.mattermost.com/developer/oauth-2-0-applications.html) for more details.*
This guide uses the generic OAuth2 module for compatibility with Mattermost version 5.0 and above.
1. Sign-in with an administrator account to your Mattermost instance
2. Make sure **OAuth 2.0 Service Provider** is enabled in the Main Menu (menu button next to your username in the top left corner) --> System Console --> Custom Integrations menu, which you can find at `https://your.mattermost.domain/admin_console/integrations/custom`
![mattermost-enable-oauth2](../images/auth/mattermost-enable-oauth2.png)
3. Navigate to the OAuth integration settings through Main Menu --> Integrations --> OAuth 2.0 Applications, at `https://your.mattermost.domain/yourteam/integrations/oauth2-apps`
4. Click on the **Add OAuth 2.0 Application** button to add a new OAuth application
![mattermost-oauth-app-add](../images/auth/mattermost-oauth-app-add.png)
5. Fill out the form and click **Save**
![mattermost-oauth-app-form](../images/auth/mattermost-oauth-app-form.png)
*Note: The callback URL is \<your-codimd-url\>/auth/oauth2/callback*
6. After saving the application, you'll receive the Client ID and Client Secret
![mattermost-oauth-app-done](../images/auth/mattermost-oauth-app-done.png)
7. Add the Client ID and Client Secret to your config.json file or pass them as environment variables
* config.json:
````javascript
{
"production": {
"oauth2": {
"baseURL": "https://your.mattermost.domain",
"userProfileURL": "https://your.mattermost.domain/api/v4/users/me",
"userProfileUsernameAttr": "id",
"userProfileDisplayNameAttr": "username",
"userProfileEmailAttr": "email",
"tokenURL": "https://your.mattermost.domain/oauth/access_token",
"authorizationURL": "https://your.mattermost.domain/oauth/authorize",
"clientID": "ii4p1u3jz7dXXXXXXXXXXXXXXX",
"clientSecret": "mqzzx6fydbXXXXXXXXXXXXXXXX"
}
}
}
````
* environment variables:
````
CMD_OAUTH2_BASEURL=https://your.mattermost.domain
CMD_OAUTH2_USER_PROFILE_URL=https://your.mattermost.domain/api/v4/users/me
CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR=id
CMD_OAUTH2_USER_PROFILE_DISPLAY_NAME_ATTR=username
CMD_OAUTH2_USER_PROFILE_EMAIL_ATTR=email
CMD_OAUTH2_TOKEN_URL=https://your.mattermost.domain/oauth/access_token
CMD_OAUTH2_AUTHORIZATION_URL=https://your.mattermost.domain/oauth/authorize
CMD_OAUTH2_CLIENT_ID=ii4p1u3jz7dXXXXXXXXXXXXXXX
CMD_OAUTH2_CLIENT_SECRET=mqzzx6fydbXXXXXXXXXXXXXXXX
````

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View file

@ -104,8 +104,8 @@ config.isOAuth2Enable = config.oauth2.clientID && config.oauth2.clientSecret
config.isPDFExportEnable = config.allowPDFExport config.isPDFExportEnable = config.allowPDFExport
// Check gitlab api version // Check gitlab api version
if (config.gitlab.version !== 'v4' || config.gitlab.version !== 'v3') { if (config.gitlab.version !== 'v4' && config.gitlab.version !== 'v3') {
logger.warn('config.js contains wrong version (' + config.gitlab.version + ') for gitlab api; it should be \'v3\' or \'v4\'. Defaulting to v3') logger.warn('config.js contains wrong version (' + config.gitlab.version + ') for gitlab api; it should be \'v3\' or \'v4\'. Defaulting to v4')
config.gitlab.version = 'v4' config.gitlab.version = 'v4'
} }

View file

@ -27,6 +27,10 @@ exports.generateAvatar = function (name) {
exports.generateAvatarURL = function (name, email = '', big = true) { exports.generateAvatarURL = function (name, email = '', big = true) {
let photo let photo
if (typeof email !== 'string') {
email = '' + name + '@example.com'
}
if (email !== '' && config.allowGravatar) { if (email !== '' && config.allowGravatar) {
photo = 'https://www.gravatar.com/avatar/' + md5(email.toLowerCase()) photo = 'https://www.gravatar.com/avatar/' + md5(email.toLowerCase())
if (big) { if (big) {

View file

@ -21,7 +21,7 @@ module.exports = {
defaultValue: 0 defaultValue: 0
}) })
}).catch(function (error) { }).catch(function (error) {
if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'shortid'") { if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'shortid'" || error.message === 'column "shortid" of relation "Notes" already exists') {
console.log('Migration has already run… ignoring.') console.log('Migration has already run… ignoring.')
} else { } else {
throw error throw error

View file

@ -8,7 +8,7 @@ module.exports = {
type: Sequelize.DATE type: Sequelize.DATE
}) })
}).catch(function (error) { }).catch(function (error) {
if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'lastchangeuserId'") { if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'lastchangeuserId'" || error.message === 'column "lastchangeuserId" of relation "Notes" already exists') {
console.log('Migration has already run… ignoring.') console.log('Migration has already run… ignoring.')
} else { } else {
throw error throw error

View file

@ -8,7 +8,7 @@ module.exports = {
indicesType: 'UNIQUE' indicesType: 'UNIQUE'
}) })
}).catch(function (error) { }).catch(function (error) {
if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'alias'") { if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'alias'" || error.message === 'column "alias" of relation "Notes" already exists') {
console.log('Migration has already run… ignoring.') console.log('Migration has already run… ignoring.')
} else { } else {
throw error throw error

View file

@ -4,7 +4,7 @@ module.exports = {
return queryInterface.addColumn('Users', 'accessToken', Sequelize.STRING).then(function () { return queryInterface.addColumn('Users', 'accessToken', Sequelize.STRING).then(function () {
return queryInterface.addColumn('Users', 'refreshToken', Sequelize.STRING) return queryInterface.addColumn('Users', 'refreshToken', Sequelize.STRING)
}).catch(function (error) { }).catch(function (error) {
if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'accessToken'") { if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'accessToken'" || error.message === 'column "accessToken" of relation "Users" already exists') {
console.log('Migration has already run… ignoring.') console.log('Migration has already run… ignoring.')
} else { } else {
throw error throw error

View file

@ -16,7 +16,7 @@ module.exports = {
updatedAt: Sequelize.DATE updatedAt: Sequelize.DATE
}) })
}).catch(function (error) { }).catch(function (error) {
if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'savedAt'") { if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'savedAt'" || error.message === 'column "savedAt" of relation "Notes" already exists') {
console.log('Migration has already run… ignoring.') console.log('Migration has already run… ignoring.')
} else { } else {
throw error throw error

View file

@ -17,7 +17,7 @@ module.exports = {
updatedAt: Sequelize.DATE updatedAt: Sequelize.DATE
}) })
}).catch(function (error) { }).catch(function (error) {
if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'authorship'") { if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'authorship'" || error.message === 'column "authorship" of relation "Notes" already exists') {
console.log('Migration has already run… ignoring.') console.log('Migration has already run… ignoring.')
} else { } else {
throw error throw error

View file

@ -2,7 +2,7 @@
module.exports = { module.exports = {
up: function (queryInterface, Sequelize) { up: function (queryInterface, Sequelize) {
return queryInterface.addColumn('Notes', 'deletedAt', Sequelize.DATE).catch(function (error) { return queryInterface.addColumn('Notes', 'deletedAt', Sequelize.DATE).catch(function (error) {
if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'deletedAt'") { if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'deletedAt'" || error.message === 'column "deletedAt" of relation "Notes" already exists') {
console.log('Migration has already run… ignoring.') console.log('Migration has already run… ignoring.')
} else { } else {
throw error throw error

View file

@ -3,14 +3,14 @@ module.exports = {
up: function (queryInterface, Sequelize) { up: function (queryInterface, Sequelize) {
return queryInterface.addColumn('Users', 'email', Sequelize.TEXT).then(function () { return queryInterface.addColumn('Users', 'email', Sequelize.TEXT).then(function () {
return queryInterface.addColumn('Users', 'password', Sequelize.TEXT).catch(function (error) { return queryInterface.addColumn('Users', 'password', Sequelize.TEXT).catch(function (error) {
if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'password'") { if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'password'" || error.message === 'column "password" of relation "Users" already exists') {
console.log('Migration has already run… ignoring.') console.log('Migration has already run… ignoring.')
} else { } else {
throw error throw error
} }
}) })
}).catch(function (error) { }).catch(function (error) {
if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'email'") { if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'email'" || error.message === 'column "email" of relation "Users" already exists') {
console.log('Migration has already run… ignoring.') console.log('Migration has already run… ignoring.')
} else { } else {
throw error throw error

View file

@ -19,8 +19,8 @@
"archiver": "^2.1.1", "archiver": "^2.1.1",
"async": "^2.1.4", "async": "^2.1.4",
"aws-sdk": "^2.7.20", "aws-sdk": "^2.7.20",
"base64url": "^3.0.0",
"azure-storage": "^2.7.0", "azure-storage": "^2.7.0",
"base64url": "^3.0.0",
"blueimp-md5": "^2.6.0", "blueimp-md5": "^2.6.0",
"body-parser": "^1.15.2", "body-parser": "^1.15.2",
"bootstrap": "^3.3.7", "bootstrap": "^3.3.7",
@ -62,7 +62,7 @@
"keymaster": "^1.6.2", "keymaster": "^1.6.2",
"list.js": "^1.5.0", "list.js": "^1.5.0",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"lz-string": "1.4.4", "lz-string": "git+https://github.com/hackmdio/lz-string.git",
"markdown-it": "^8.2.2", "markdown-it": "^8.2.2",
"markdown-it-abbr": "^1.0.4", "markdown-it-abbr": "^1.0.4",
"markdown-it-container": "^2.0.0", "markdown-it-container": "^2.0.0",
@ -78,8 +78,8 @@
"markdown-it-sup": "^1.0.0", "markdown-it-sup": "^1.0.0",
"markdown-pdf": "^9.0.0", "markdown-pdf": "^9.0.0",
"mathjax": "~2.7.0", "mathjax": "~2.7.0",
"mermaid": "~7.1.0",
"mattermost": "^3.4.0", "mattermost": "^3.4.0",
"mermaid": "~7.1.0",
"meta-marked": "^0.4.2", "meta-marked": "^0.4.2",
"method-override": "^2.3.7", "method-override": "^2.3.7",
"minimist": "^1.2.0", "minimist": "^1.2.0",
@ -98,8 +98,8 @@
"passport-ldapauth": "^2.0.0", "passport-ldapauth": "^2.0.0",
"passport-local": "^1.0.0", "passport-local": "^1.0.0",
"passport-oauth2": "^1.4.0", "passport-oauth2": "^1.4.0",
"passport-twitter": "^1.0.4",
"passport-saml": "^0.31.0", "passport-saml": "^0.31.0",
"passport-twitter": "^1.0.4",
"passport.socketio": "^3.7.0", "passport.socketio": "^3.7.0",
"pdfobject": "^2.0.201604172", "pdfobject": "^2.0.201604172",
"pg": "^6.1.2", "pg": "^6.1.2",

View file

@ -4612,9 +4612,9 @@ lru-queue@0.1:
dependencies: dependencies:
es5-ext "~0.10.2" es5-ext "~0.10.2"
lz-string@1.4.4: "lz-string@git+https://github.com/hackmdio/lz-string.git":
version "1.4.4" version "1.4.4"
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" resolved "git+https://github.com/hackmdio/lz-string.git#6edfccb79cd8c210f03fd3bf18e41ca144fbeefb"
macaddress@^0.2.8: macaddress@^0.2.8:
version "0.2.8" version "0.2.8"