Unverified Commit 16b9409e authored by Yukai Huang's avatar Yukai Huang Committed by GitHub
Browse files

Merge pull request #1439 from hackmdio/release/2.0.0

Release 2.0.0
parents a5bb0d69 fc662661
......@@ -7,7 +7,7 @@ node_js:
- "12"
dist: xenial
cache: yarn
cache: npm
fast_finish: true
......@@ -19,8 +19,8 @@ matrix:
- node_js: "12"
- yarn test:ci
- yarn build
- npm run test:ci
- npm run build
web: ./bin/heroku_start.sh
......@@ -44,7 +44,7 @@ HackMD team is committed to keep CodiMD open source. All contributions are welco
You would find all documentation here: [CodiMD Documentation](https://hackmd.io/c/codimd-documentation)
### Deployment
If you want to spin up an instance and start using immediately, see [Docker deployment](https://hackmd.io/c/codimd-documentation/%2Fs%2Fcodimd-documentation#Deployment).
If you want to spin up an instance and start using immediately, see [Docker deployment](https://hackmd.io/c/codimd-documentation/%2Fs%2Fcodimd-docker-deployment).
If you want to contribute to the project, start with [manual deployment](https://hackmd.io/c/codimd-documentation/%2Fs%2Fcodimd-manual-deployment).
### Configuration
......@@ -24,6 +24,9 @@ var logger = require('./lib/logger')
var response = require('./lib/response')
var models = require('./lib/models')
var csp = require('./lib/csp')
const { Environment } = require('./lib/config/enum')
const { versionCheckMiddleware, checkVersion } = require('./lib/web/middleware/checkVersion')
function createHttpServer () {
if (config.useSSL) {
......@@ -66,7 +69,7 @@ io.engine.ws = new (require('ws').Server)({
// others
var realtime = require('./lib/realtime.js')
var realtime = require('./lib/realtime/realtime.js')
// assign socket io to realtime
realtime.io = io
......@@ -153,7 +156,7 @@ server.on('resumeSession', function (id, cb) {
// middleware which blocks requests when we're too busy
......@@ -162,10 +165,15 @@ app.use(passport.initialize())
// check uri is valid before going further
// redirect url without trailing slashes
if (config.autoVersionCheck && process.env.NODE_ENV === Environment.production) {
// routes need sessions
// template files
......@@ -186,6 +194,7 @@ app.locals.authProviders = {
facebook: config.isFacebookEnable,
twitter: config.isTwitterEnable,
github: config.isGitHubEnable,
bitbucket: config.isBitbucketEnable,
gitlab: config.isGitLabEnable,
mattermost: config.isMattermostEnable,
dropbox: config.isDropboxEnable,
......@@ -199,23 +208,21 @@ app.locals.authProviders = {
email: config.isEmailEnable,
allowEmailRegister: config.allowEmailRegister
app.locals.versionInfo = {
latest: true,
versionItem: null
// Export/Import menu items
app.locals.enableDropBoxSave = config.isDropboxEnable
app.locals.enableGitHubGist = config.isGitHubEnable
app.locals.enableGitlabSnippets = config.isGitlabSnippetsEnable
// response not found if no any route matxches
app.get('*', function (req, res) {
response.errorNotFound(req, res)
// socket.io secure
......@@ -15,124 +15,132 @@
"description": "Let npm also install development build tool",
"value": "false"
"description": "Secret used to secure session cookies.",
"required": false
"description": "whether to also use HSTS if HTTPS is enabled",
"required": false
"description": "max duration, in seconds, to tell clients to keep HSTS status",
"required": false
"description": "whether to tell clients to also regard subdomains as HSTS hosts",
"required": false
"description": "whether to allow at all adding of the site to HSTS preloads (e.g. in browsers)",
"required": false
"description": "domain name",
"required": false
"description": "sub url path, like `www.example.com/<URL_PATH>`",
"required": false
"description": "domain name whitelist (use comma to separate)",
"required": false,
"value": "localhost"
"description": "set to use ssl protocol for resources path (only applied when domain is set)",
"required": false
"description": "set to add port on callback url (port 80 or 443 won't applied) (only applied when domain is set)",
"required": false
"description": "Facebook API client id",
"required": false
"description": "Facebook API client secret",
"required": false
"description": "Twitter API consumer key",
"required": false
"description": "Twitter API consumer secret",
"required": false
"description": "GitHub API client id",
"required": false
"description": "GitHub API client secret",
"required": false
"description": "Bitbucket API client id",
"required": false
"description": "Bitbucket API client secret",
"required": false
"description": "GitLab authentication endpoint, set to use other endpoint than GitLab.com (optional)",
"required": false
"description": "GitLab API client id",
"required": false
"description": "GitLab API client secret",
"required": false
"description": "GitLab API client scope (optional)",
"required": false
"description": "Mattermost authentication endpoint",
"required": false
"description": "Mattermost API client id",
"required": false
"description": "Mattermost API client secret",
"required": false
"description": "Dropbox API client id",
"required": false
"description": "Dropbox API client secret",
"required": false
"description": "Dropbox app key (for import/export)",
"required": false
"description": "Google API client id",
"required": false
"description": "Google API client secret",
"required": false
"description": "Imgur API client id",
"required": false
"description": "Enable or disable PDF exports",
"required": false
......@@ -4,17 +4,7 @@ set -e
if [ ! -z "$DYNO" ]; then
# setup config files
cat << EOF > .sequelizerc
var path = require('path');
module.exports = {
'config': path.resolve('config.json'),
'migrations-path': path.resolve('lib', 'migrations'),
'models-path': path.resolve('lib', 'models'),
'url': process.env.DATABASE_URL
cp .sequelizerc.example .sequelizerc
cat << EOF > config.json
set -euo pipefail
......@@ -8,12 +8,11 @@ if [ -d .git ]; then
cd "$(git rev-parse --show-toplevel)"
if ! type yarn > /dev/null
if ! type npm > /dev/null
cat << EOF
yarn is not installed, please install Node.js, npm and yarn.
npm is not installed, please install Node.js and npm.
Read more on Node.js official website: https://nodejs.org
And for yarn package manager at: https://yarnpkg.com/en/
Setup will not be run
exit 0
......@@ -29,14 +28,13 @@ if [ ! -f .sequelizerc ]; then
echo "install packages"
yarn install --pure-lockfile
yarn install --production=false --pure-lockfile
npm install
cat << EOF
Edit the following config file to setup CodiMD server and client.
Read more info at https://github.com/hackmdio/codimd#configuration-files
Read more info at https://hackmd.io/c/codimd-documentation/%2Fs%2Fcodimd-configuration
* config.json -- CodiMD config
* .sequelizerc -- db config
......@@ -5,14 +5,13 @@ COPY --chown=hackmd:hackmd . .
RUN set -xe && \
git reset --hard && \
git clean -fx && \
yarn install && \
yarn build && \
yarn install --production=true && \
npm install && \
npm run build && \
cp ./deployments/docker-entrypoint.sh ./ && \
cp .sequelizerc.example .sequelizerc && \
rm -rf .git .gitignore .travis.yml .dockerignore .editorconfig .babelrc .mailmap .sequelizerc.example \
test docs contribute \
yarn.lock webpack.prod.js webpack.htmlexport.js webpack.dev.js webpack.common.js \
package-lock.json webpack.prod.js webpack.htmlexport.js webpack.dev.js webpack.common.js \
config.json.example README.md CONTRIBUTING.md AUTHORS
FROM hackmdio/runtime:1.0.6
version: "3"
image: postgres:11.5
image: postgres:11.6-alpine
- POSTGRES_PASSWORD=change_password
......@@ -11,10 +11,10 @@ services:
restart: always
# you can use image or custom build below
# image: nabo.codimd.dev/hackmdio/hackmd:1.4.0
context: ..
dockerfile: ./deployments/Dockerfile
image: nabo.codimd.dev/hackmdio/hackmd:2.0.0
# build:
# context: ..
# dockerfile: ./deployments/Dockerfile
- CMD_DB_URL=postgres://codimd:change_password@database/codimd
- CMD_USECDN=false
# Webpack Docs
## `webpack.common.js`
This file contains all common definition for chunks and plugins, that are needed by the whole app.
**TODO:** Document which entry points are used for what.
## `webpack.htmlexport.js`
Separate config for the "save as html" feature.
Packs all CSS from `public/js/htmlExport.js` to `build/html.min.css`.
This file is then downloaded by client-side JS and used to create the HTML.
See `exportToHTML()` in `public/js/extra.js`.
## `webpack.dev.js`
The development config uses both common configs, enables development mode and enables "cheap" source maps (lines only).
If you need more detailed source maps while developing, you might want to use the `source-maps` option.
See https://webpack.js.org/configuration/devtool/ for details.
## `webpack.prod.js`
The production config uses both common configs and enables production mode.
This automatically enables various optimizations (e.g. UglifyJS). See https://webpack.js.org/concepts/mode/ for details.
For the global app config, the name of the emitted chunks is changed to include the content hash.
See https://webpack.js.org/guides/caching/ on why this is a good idea.
For the HTML export config, CSS minification is enabled.
Authentication guide - GitHub
***Note:** This guide was written before the renaming. Just replace `HackMD` with `CodiMD` in your mind :smile: thanks!*
1. Sign-in or sign-up for a GitHub account
2. Navigate to developer settings in your GitHub account [here](https://github.com/settings/developers) and select the "OAuth Apps" tab
3. Click on the **New OAuth App** button, to create a new OAuth App:
4. Fill out the new OAuth application registration form, and click **Register Application**
*Note: The callback URL is <your-hackmd-url>/auth/github/callback*
5. After successfully registering the application, you'll receive the Client ID and Client Secret for the application
6. Add the Client ID and Client Secret to your config.json file or pass them as environment variables
* config.json:
"production": {
"github": {
"clientID": "3747d30eaccXXXXXXXXX",
"clientSecret": "2a8e682948eee0c580XXXXXXXXXXXXXXXXXXXXXX"
* environment variables:
# GitLab (self-hosted)
***Note:** This guide was written before the renaming. Just replace `HackMD` with `CodiMD` in your mind :smile: thanks!*
1. Sign in to your GitLab
2. Navigate to the application management page at `https://your.gitlab.domain/admin/applications` (admin permissions required)
3. Click **New application** to create a new application and fill out the registration form:
![New GitLab application](../images/auth/gitlab-new-application.png)
4. Click **Submit**
5. In the list of applications select **HackMD**. Leave that site open to copy the application ID and secret in the next step.
![Application: HackMD](../images/auth/gitlab-application-details.png)
6. In the `docker-compose.yml` add the following environment variables to `app:` `environment:`
- HMD_DOMAIN=your.hackmd.domain
- HMD_GITLAB_BASEURL=https://your.gitlab.domain
7. Run `docker-compose up -d` to apply your settings.
8. Sign in to your HackMD using your GitLab ID:
![Sign in via GitLab](../images/auth/gitlab-sign-in.png)
AD LDAP auth
To setup your CodiMD instance with Active Directory you need the following configs:
CMD_LDAP_BINDCREDENTIALS=<super secret password>
`CMD_LDAP_BINDDN` is either the `distinguishedName` or the `userPrincipalName`. *This can cause "username/password is invalid" when either this value or the password from `CMD_LDAP_BINDCREDENTIALS` are incorrect.*
`CMD_LDAP_SEARCHFILTER` matches on all users and uses either the email address or the `sAMAccountName` (usually the login name you also use to login to Windows).
*Only using `sAMAccountName` looks like this:* `(&(objectcategory=person)(objectclass=user)(sAMAccountName={{username}}))`
`CMD_LDAP_USERIDFIELD` says we want to use `sAMAccountName` as unique identifier for the account itself.
`CMD_LDAP_PROVIDERNAME` just the name written above the username and password field on the login page.
Same in json:
"ldap": {
"url": "ldap://internal.example.com",
"bindDn": "cn=binduser,cn=Users,dc=internal,dc=example,dc=com",
"bindCredentials": "<super secret password>",
"searchBase": "dc=internal,dc=example,dc=com",
"searchFilter": "(&(objectcategory=person)(objectclass=user)(|(sAMAccountName={{username}})(mail={{username}})))",
"useridField": "sAMAccountName",
More details and example: https://www.npmjs.com/package/passport-ldapauth
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`
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
5. Fill out the form and click **Save**
*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
7. Add the Client ID and Client Secret to your config.json file or pass them as environment variables
* config.json:
"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:
Authentication guide - Nextcloud (self-hosted)
*This has been constructed using the [Nextcloud OAuth2 Documentation](https://docs.nextcloud.com/server/14/admin_manual/configuration_server/oauth2.html?highlight=oauth2) combined with [this issue comment on the nextcloud bugtracker](https://github.com/nextcloud/server/issues/5694#issuecomment-314761326).*
This guide uses the generic OAuth2 module for compatibility with Nextcloud 13 and above (this guide has been tested successfully with Nextcloud 14).
1. Sign-in with an administrator account to your Nextcloud server
2. Navigate to the OAuth integration settings: Profile Icon (top right) --> Settings
Then choose Security Settings from the *Administration* part of the list - Don't confuse this with Personal Security Settings, where you would change your personal password!
At the top there's OAuth 2.0-Clients.
![Where to find OAuth2 in Nextcloud](../images/auth/nextcloud-oauth2-1-settings.png)
3. Add your CodiMD instance by giving it a *name* (perhaps CodiMD, but could be anything) and a *Redirection-URI*. The Redirection-URI will be `\<your-codimd-url\>/auth/oauth2/callback`. Click <kbd>Add</kbd>.
![Adding a client to Nextcloud](../images/auth/nextcloud-oauth2-2-client-add.png)
4. You'll now see a line containing a *client identifier* and a *Secret*.
![Successfully added OAuth2-client](../images/auth/nextcloud-oauth2-3-clientid-secret.png)
5. That's it for Nextcloud, the rest is configured in your CodiMD `config.json` or via the `CMD_` environment variables!
6. Add the Client ID and Client Secret to your `config.json` file or pass them as environment variables. Make sure you also replace `<your-nextcloud-domain>` with the right domain name.
* `config.json`:
"production": {
"oauth2": {
"clientID": "ii4p1u3jz7dXXXXXXXXXXXXXXX",
"clientSecret": "mqzzx6fydbXXXXXXXXXXXXXXXX",
"authorizationURL": "https://<your-nextcloud-domain>/apps/oauth2/authorize",
"tokenURL": "https://<your-nextcloud-domain>/apps/oauth2/api/v1/token",
"userProfileURL": "https://<your-nextcloud-domain>/ocs/v2.php/cloud/user?format=json",
"userProfileUsernameAttr": "ocs.data.id",