summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md37
-rw-r--r--app.js5
-rw-r--r--docs/dev/webpack.md26
-rw-r--r--docs/guides/auth/nextcloud.md52
-rw-r--r--docs/guides/images/auth/nextcloud-oauth2-1-settings.pngbin0 -> 47047 bytes
-rw-r--r--docs/guides/images/auth/nextcloud-oauth2-2-client-add.pngbin0 -> 11684 bytes
-rw-r--r--docs/guides/images/auth/nextcloud-oauth2-3-clientid-secret.pngbin0 -> 13428 bytes
-rw-r--r--lib/config/default.js11
-rw-r--r--lib/config/environment.js4
-rw-r--r--lib/config/index.js28
-rw-r--r--lib/models/revision.js3
-rw-r--r--lib/response.js18
-rw-r--r--lib/web/auth/index.js1
-rw-r--r--lib/web/auth/openid/index.js61
-rw-r--r--lib/web/imageRouter/filesystem.js3
-rw-r--r--lib/web/statusRouter.js2
-rw-r--r--package.json15
-rw-r--r--public/css/github-extract.css37
-rw-r--r--public/js/extra.js6
-rw-r--r--public/js/index.js2
-rw-r--r--public/views/codimd/body.ejs2
-rw-r--r--public/views/codimd/head.ejs3
-rw-r--r--public/views/codimd/header.ejs16
-rw-r--r--public/views/html.hbs3
-rw-r--r--public/views/index/body.ejs4
-rw-r--r--public/views/index/head.ejs2
-rw-r--r--public/views/pretty.ejs3
-rw-r--r--public/views/shared/refresh-modal.ejs2
-rw-r--r--public/views/shared/signin-modal.ejs21
-rw-r--r--public/views/slide.ejs3
-rw-r--r--webpack.common.js (renamed from webpackBaseConfig.js)18
-rw-r--r--webpack.config.js41
-rw-r--r--webpack.dev.js14
-rw-r--r--webpack.htmlexport.js25
-rw-r--r--webpack.prod.js23
-rw-r--r--webpack.production.js75
-rw-r--r--yarn.lock115
37 files changed, 447 insertions, 234 deletions
diff --git a/README.md b/README.md
index ecdc8c77..7aab06fe 100644
--- a/README.md
+++ b/README.md
@@ -68,7 +68,7 @@ Just to more confusion: We are still friends with HackMD :heart:
### Prerequisite
-- Node.js 6.x or up (test up to 7.5.0)
+- Node.js 6.x or up (test up to 7.5.0) and <10.x
- Database (PostgreSQL, MySQL, MariaDB, SQLite, MSSQL) use charset `utf8`
- npm (and its dependencies, especially [uWebSockets](https://github.com/uWebSockets/uWebSockets#nodejs-developers), [node-gyp](https://github.com/nodejs/node-gyp#installation))
- For **building** CodiMD we recommend to use a machine with at least **2GB** RAM
@@ -174,6 +174,7 @@ There are some config settings you need to change in the files below.
| --------- | ------ | ----------- |
| `NODE_ENV` | `production` or `development` | set current environment (will apply corresponding settings in the `config.json`) |
| `DEBUG` | `true` or `false` | set debug mode; show more logs |
+| `CMD_CONFIG_FILE` | `/path/to/config.json` | optional override for the path to CodiMD's config file |
| `CMD_DOMAIN` | `codimd.org` | domain name |
| `CMD_URL_PATH` | `codimd` | sub URL path, like `www.example.com/<URL_PATH>` |
| `CMD_HOST` | `localhost` | host to listen on |
@@ -228,6 +229,15 @@ There are some config settings you need to change in the files below.
| `CMD_SAML_ATTRIBUTE_ID` | `sAMAccountName` | attribute map for `id` (optional, default: NameID of SAML response) |
| `CMD_SAML_ATTRIBUTE_USERNAME` | `mailNickname` | attribute map for `username` (optional, default: NameID of SAML response) |
| `CMD_SAML_ATTRIBUTE_EMAIL` | `mail` | attribute map for `email` (optional, default: NameID of SAML response if `CMD_SAML_IDENTIFIERFORMAT` is default) |
+| `CMD_OAUTH2_USER_PROFILE_URL` | `https://example.com` | where retrieve information about a user after succesful login. Needs to output JSON. (no default value) Refer to the [Mattermost](docs/guides/auth/mattermost-self-hosted.md) or [Nextcloud](docs/guides/auth/nextcloud.md) examples for more details on all of the `CMD_OAUTH2...` options. |
+| `CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR` | `name` | where to find the username in the JSON from the user profile URL. (no default value)|
+| `CMD_OAUTH2_USER_PROFILE_DISPLAY_NAME_ATTR` | `display-name` | where to find the display-name in the JSON from the user profile URL. (no default value) |
+| `CMD_OAUTH2_USER_PROFILE_EMAIL_ATTR` | `email` | where to find the email address in the JSON from the user profile URL. (no default value) |
+| `CMD_OAUTH2_TOKEN_URL` | `https://example.com` | sometimes called token endpoint, please refer to the documentation of your OAuth2 provider (no default value) |
+| `CMD_OAUTH2_AUTHORIZATION_URL` | `https://example.com` | authorization URL of your provider, please refer to the documentation of your OAuth2 provider (no default value) |
+| `CMD_OAUTH2_CLIENT_ID` | `afae02fckafd...` | you will get this from your OAuth2 provider when you register CodiMD as OAuth2-client, (no default value) |
+| `CMD_OAUTH2_CLIENT_SECRET` | `afae02fckafd...` | you will get this from your OAuth2 provider when you register CodiMD as OAuth2-client, (no default value) |
+| `CMD_OAUTH2_PROVIDERNAME` | `My institution` | Optional name to be displayed at login form indicating the oAuth2 provider |
| `CMD_IMGUR_CLIENTID` | no example | Imgur API client id |
| `CMD_EMAIL` | `true` or `false` | set to allow email signin |
| `CMD_ALLOW_PDF_EXPORT` | `true` or `false` | Enable or disable PDF exports |
@@ -277,19 +287,15 @@ There are some config settings you need to change in the files below.
| `defaultPermission` | `freely`, `editable`, `limited`, `locked`, `protected` or `private` | set notes default permission (only applied on signed users) |
| `dbURL` | `mysql://localhost:3306/database` | set the db URL; if set, then db config (below) won't be applied |
| `db` | `{ "dialect": "sqlite", "storage": "./db.codimd.sqlite" }` | set the db configs, [see more here](http://sequelize.readthedocs.org/en/latest/api/sequelize/) |
-| `sslKeyPath` | `./cert/client.key` | SSL key path (only need when you set `useSSL`) |
-| `sslCertPath` | `./cert/codimd_io.crt` | SSL cert path (only need when you set `useSSL`) |
-| `sslCAPath` | `['./cert/COMODORSAAddTrustCA.crt']` | SSL ca chain (only need when you set `useSSL`) |
-| `dhParamPath` | `./cert/dhparam.pem` | SSL dhparam path (only need when you set `useSSL`) |
-| `tmpPath` | `./tmp/` | temp directory path |
-| `defaultNotePath` | `./public/default.md` | default note file path |
-| `docsPath` | `./public/docs` | docs directory path |
-| `indexPath` | `./public/views/index.ejs` | index template file path |
-| `hackmdPath` | `./public/views/hackmd.ejs` | hackmd template file path |
-| `errorPath` | `./public/views/error.ejs` | error template file path |
-| `prettyPath` | `./public/views/pretty.ejs` | pretty template file path |
-| `slidePath` | `./public/views/slide.hbs` | slide template file path |
-| `uploadsPath` | `./public/uploads` | uploads directory - needs to be persistent when you use imageUploadType `filesystem` |
+| `sslKeyPath` | `./cert/client.key` | SSL key path<sup>1</sup> (only need when you set `useSSL`) |
+| `sslCertPath` | `./cert/codimd_io.crt` | SSL cert path<sup>1</sup> (only need when you set `useSSL`) |
+| `sslCAPath` | `['./cert/COMODORSAAddTrustCA.crt']` | SSL ca chain<sup>1</sup> (only need when you set `useSSL`) |
+| `dhParamPath` | `./cert/dhparam.pem` | SSL dhparam path<sup>1</sup> (only need when you set `useSSL`) |
+| `tmpPath` | `./tmp/` | temp directory path<sup>1</sup> |
+| `defaultNotePath` | `./public/default.md` | default note file path<sup>1</sup> |
+| `docsPath` | `./public/docs` | docs directory path<sup>1</sup> |
+| `viewPath` | `./public/views` | template directory path<sup>1</sup> |
+| `uploadsPath` | `./public/uploads` | uploads directory<sup>1</sup> - needs to be persistent when you use imageUploadType `filesystem` |
| `sessionName` | `connect.sid` | cookie session name |
| `sessionSecret` | `secret` | cookie session secret |
| `sessionLife` | `14 * 24 * 60 * 60 * 1000` | cookie session life |
@@ -298,6 +304,7 @@ There are some config settings you need to change in the files below.
| `heartbeatTimeout` | `10000` | socket.io heartbeat timeout |
| `documentMaxLength` | `100000` | note max length |
| `email` | `true` or `false` | set to allow email signin |
+| `oauth2` | `{baseURL: ..., userProfileURL: ..., userProfileUsernameAttr: ..., userProfileDisplayNameAttr: ..., userProfileEmailAttr: ..., tokenURL: ..., authorizationURL: ..., clientID: ..., clientSecret: ...}` | An object detailing your OAuth2 provider. Refer to the [Mattermost](docs/guides/auth/mattermost-self-hosted.md) or [Nextcloud](docs/guides/auth/nextcloud.md) examples for more details!|
| `allowEmailRegister` | `true` or `false` | set to allow email register (only applied when email is set, default is `true`. Note `bin/manage_users` might help you if registration is `false`.) |
| `allowGravatar` | `true` or `false` | set to `false` to disable gravatar as profile picture source on your instance |
| `imageUploadType` | `imgur`, `s3`, `minio`, `azure` or `filesystem`(default) | Where to upload images. For S3, see our Image Upload Guides for [S3](docs/guides/s3-image-upload.md) or [Minio](docs/guides/minio-image-upload.md)|
@@ -305,6 +312,8 @@ There are some config settings you need to change in the files below.
| `s3` | `{ "accessKeyId": "YOUR_S3_ACCESS_KEY_ID", "secretAccessKey": "YOUR_S3_ACCESS_KEY", "region": "YOUR_S3_REGION" }` | When `imageuploadtype` be set to `s3`, you would also need to setup this key, check our [S3 Image Upload Guide](docs/guides/s3-image-upload.md) |
| `s3bucket` | `YOUR_S3_BUCKET_NAME` | bucket name when `imageUploadType` is set to `s3` or `minio` |
+<sup>1</sup>: relative paths are based on CodiMD's base directory
+
## Third-party integration API key settings
| service | settings location | description |
diff --git a/app.js b/app.js
index 34890bb1..46fd041c 100644
--- a/app.js
+++ b/app.js
@@ -126,6 +126,9 @@ app.use(i18n.init)
// routes without sessions
// static files
app.use('/', express.static(path.join(__dirname, '/public'), { maxAge: config.staticCacheTime }))
+app.use('/docs', express.static(path.resolve(__dirname, config.docsPath), { maxAge: config.staticCacheTime }))
+app.use('/uploads', express.static(path.resolve(__dirname, config.uploadsPath), { maxAge: config.staticCacheTime }))
+app.use('/default.md', express.static(path.resolve(__dirname, config.defaultNotePath), { maxAge: config.staticCacheTime }))
// session
app.use(session({
@@ -167,7 +170,7 @@ app.use(require('./lib/web/middleware/codiMDVersion'))
// routes need sessions
// template files
-app.set('views', path.join(__dirname, '/public/views'))
+app.set('views', config.viewPath)
// set render engine
app.engine('ejs', ejs.renderFile)
// set view engine
diff --git a/docs/dev/webpack.md b/docs/dev/webpack.md
new file mode 100644
index 00000000..7b391f3a
--- /dev/null
+++ b/docs/dev/webpack.md
@@ -0,0 +1,26 @@
+# 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.
diff --git a/docs/guides/auth/nextcloud.md b/docs/guides/auth/nextcloud.md
new file mode 100644
index 00000000..108772dd
--- /dev/null
+++ b/docs/guides/auth/nextcloud.md
@@ -0,0 +1,52 @@
+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`:
+ ```javascript
+ {
+ "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",
+ "userProfileDisplayNameAttr": "ocs.data.display-name",
+ "userProfileEmailAttr": "ocs.data.email"
+ }
+ }
+ }
+ ```
+ * environment variables:
+ ```sh
+ CMD_OAUTH2_CLIENT_ID=ii4p1u3jz7dXXXXXXXXXXXXXXX
+ CMD_OAUTH2_CLIENT_SECRET=mqzzx6fydbXXXXXXXXXXXXXXXX
+ CMD_OAUTH2_AUTHORIZATION_URL=https://<your-nextcloud-domain>/apps/oauth2/authorize
+ CMD_OAUTH2_TOKEN_URL=https://<your-nextcloud-domain>/apps/oauth2/api/v1/token
+ CMD_OAUTH2_USER_PROFILE_URL=https://<your-nextcloud-domain>/ocs/v2.php/cloud/user?format=json
+ CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR=ocs.data.id
+ CMD_OAUTH2_USER_PROFILE_DISPLAY_NAME_ATTR=ocs.data.display-name
+ CMD_OAUTH2_USER_PROFILE_EMAIL_ATTR=ocs.data.email
+ ```
diff --git a/docs/guides/images/auth/nextcloud-oauth2-1-settings.png b/docs/guides/images/auth/nextcloud-oauth2-1-settings.png
new file mode 100644
index 00000000..82652a54
--- /dev/null
+++ b/docs/guides/images/auth/nextcloud-oauth2-1-settings.png
Binary files differ
diff --git a/docs/guides/images/auth/nextcloud-oauth2-2-client-add.png b/docs/guides/images/auth/nextcloud-oauth2-2-client-add.png
new file mode 100644
index 00000000..7909fa62
--- /dev/null
+++ b/docs/guides/images/auth/nextcloud-oauth2-2-client-add.png
Binary files differ
diff --git a/docs/guides/images/auth/nextcloud-oauth2-3-clientid-secret.png b/docs/guides/images/auth/nextcloud-oauth2-3-clientid-secret.png
new file mode 100644
index 00000000..b05513d1
--- /dev/null
+++ b/docs/guides/images/auth/nextcloud-oauth2-3-clientid-secret.png
Binary files differ
diff --git a/lib/config/default.js b/lib/config/default.js
index c34279bd..c3ada982 100644
--- a/lib/config/default.js
+++ b/lib/config/default.js
@@ -38,15 +38,10 @@ module.exports = {
sslCAPath: '',
dhParamPath: '',
// other path
+ viewPath: './public/views',
tmpPath: './tmp',
defaultNotePath: './public/default.md',
docsPath: './public/docs',
- indexPath: './public/views/index.ejs',
- codimdPath: './public/views/codimd.ejs',
- errorPath: './public/views/error.ejs',
- prettyPath: './public/views/pretty.ejs',
- slidePath: './public/views/slide.ejs',
- constantsPath: './public/js/lib/common/constant.ejs',
uploadsPath: './public/uploads',
// session
sessionName: 'connect.sid',
@@ -83,6 +78,7 @@ module.exports = {
},
// authentication
oauth2: {
+ providerName: undefined,
authorizationURL: undefined,
tokenURL: undefined,
clientID: undefined,
@@ -150,5 +146,6 @@ module.exports = {
email: true,
allowEmailRegister: true,
allowGravatar: true,
- allowPDFExport: true
+ allowPDFExport: true,
+ openID: true
}
diff --git a/lib/config/environment.js b/lib/config/environment.js
index 6c4ce92f..6737637c 100644
--- a/lib/config/environment.js
+++ b/lib/config/environment.js
@@ -75,6 +75,7 @@ module.exports = {
clientSecret: process.env.CMD_MATTERMOST_CLIENTSECRET
},
oauth2: {
+ providerName: process.env.CMD_OAUTH2_PROVIDERNAME,
baseURL: process.env.CMD_OAUTH2_BASEURL,
userProfileURL: process.env.CMD_OAUTH2_USER_PROFILE_URL,
userProfileUsernameAttr: process.env.CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR,
@@ -123,5 +124,6 @@ module.exports = {
email: toBooleanConfig(process.env.CMD_EMAIL),
allowEmailRegister: toBooleanConfig(process.env.CMD_ALLOW_EMAIL_REGISTER),
allowGravatar: toBooleanConfig(process.env.CMD_ALLOW_GRAVATAR),
- allowPDFExport: toBooleanConfig(process.env.CMD_ALLOW_PDF_EXPORT)
+ allowPDFExport: toBooleanConfig(process.env.CMD_ALLOW_PDF_EXPORT),
+ openID: toBooleanConfig(process.env.CMD_OPENID)
}
diff --git a/lib/config/index.js b/lib/config/index.js
index 26f0ae96..f8b68e30 100644
--- a/lib/config/index.js
+++ b/lib/config/index.js
@@ -9,7 +9,7 @@ const deepFreeze = require('deep-freeze')
const {Environment, Permission} = require('./enum')
const logger = require('../logger')
-const appRootPath = path.join(__dirname, '../../')
+const appRootPath = path.resolve(__dirname, '../../')
const env = process.env.NODE_ENV || Environment.development
const debugConfig = {
debug: (env === Environment.development)
@@ -23,7 +23,8 @@ const packageConfig = {
minimumCompatibleVersion: '0.5.0'
}
-const configFilePath = path.join(appRootPath, 'config.json')
+const configFilePath = path.resolve(appRootPath, process.env.CMD_CONFIG_FILE ||
+'config.json')
const fileConfig = fs.existsSync(configFilePath) ? require(configFilePath)[env] : undefined
let config = require('./default')
@@ -95,6 +96,7 @@ config.isGoogleEnable = config.google.clientID && config.google.clientSecret
config.isDropboxEnable = config.dropbox.clientID && config.dropbox.clientSecret
config.isTwitterEnable = config.twitter.consumerKey && config.twitter.consumerSecret
config.isEmailEnable = config.email
+config.isOpenIDEnable = config.openID
config.isGitHubEnable = config.github.clientID && config.github.clientSecret
config.isGitLabEnable = config.gitlab.clientID && config.gitlab.clientSecret
config.isMattermostEnable = config.mattermost.clientID && config.mattermost.clientSecret
@@ -173,20 +175,14 @@ config.sslCAPath.forEach(function (capath, i, array) {
array[i] = path.resolve(appRootPath, capath)
})
-config.sslCertPath = path.join(appRootPath, config.sslCertPath)
-config.sslKeyPath = path.join(appRootPath, config.sslKeyPath)
-config.dhParamPath = path.join(appRootPath, config.dhParamPath)
-
-config.tmpPath = path.join(appRootPath, config.tmpPath)
-config.defaultNotePath = path.join(appRootPath, config.defaultNotePath)
-config.docsPath = path.join(appRootPath, config.docsPath)
-config.indexPath = path.join(appRootPath, config.indexPath)
-config.codimdPath = path.join(appRootPath, config.codimdPath)
-config.errorPath = path.join(appRootPath, config.errorPath)
-config.prettyPath = path.join(appRootPath, config.prettyPath)
-config.slidePath = path.join(appRootPath, config.slidePath)
-config.constantsPath = path.join(appRootPath, config.constantsPath)
-config.uploadsPath = path.join(appRootPath, config.uploadsPath)
+config.sslCertPath = path.resolve(appRootPath, config.sslCertPath)
+config.sslKeyPath = path.resolve(appRootPath, config.sslKeyPath)
+config.dhParamPath = path.resolve(appRootPath, config.dhParamPath)
+config.viewPath = path.resolve(appRootPath, config.viewPath)
+config.tmpPath = path.resolve(appRootPath, config.tmpPath)
+config.defaultNotePath = path.resolve(appRootPath, config.defaultNotePath)
+config.docsPath = path.resolve(appRootPath, config.docsPath)
+config.uploadsPath = path.resolve(appRootPath, config.uploadsPath)
// make config readonly
config = deepFreeze(config)
diff --git a/lib/models/revision.js b/lib/models/revision.js
index 8bc95cb1..4ee080da 100644
--- a/lib/models/revision.js
+++ b/lib/models/revision.js
@@ -5,6 +5,7 @@ var async = require('async')
var moment = require('moment')
var childProcess = require('child_process')
var shortId = require('shortid')
+var path = require('path')
// core
var config = require('../config')
@@ -14,7 +15,7 @@ var dmpWorker = createDmpWorker()
var dmpCallbackCache = {}
function createDmpWorker () {
- var worker = childProcess.fork('./lib/workers/dmpWorker.js', {
+ var worker = childProcess.fork(path.resolve(__dirname, '../workers/dmpWorker.js'), {
stdio: 'ignore'
})
if (config.debug) logger.info('dmp worker process started')
diff --git a/lib/response.js b/lib/response.js
index 4df036b7..4f572e47 100644
--- a/lib/response.js
+++ b/lib/response.js
@@ -54,7 +54,7 @@ var response = {
}
function responseError (res, code, detail, msg) {
- res.status(code).render(config.errorPath, {
+ res.status(code).render('error.ejs', {
url: config.serverURL,
title: code + ' ' + detail + ' ' + msg,
code: code,
@@ -88,6 +88,7 @@ function showIndex (req, res, next) {
email: config.isEmailEnable,
allowEmailRegister: config.allowEmailRegister,
allowPDFExport: config.allowPDFExport,
+ openID: config.isOpenIDEnable,
signin: authStatus,
infoMessage: req.flash('info'),
errorMessage: req.flash('error'),
@@ -104,11 +105,11 @@ function showIndex (req, res, next) {
}).then(function (user) {
if (user) {
data.deleteToken = user.deleteToken
- res.render(config.indexPath, data)
+ res.render('index.ejs', data)
}
})
} else {
- res.render(config.indexPath, data)
+ res.render('index.ejs', data)
}
}
@@ -122,7 +123,7 @@ function responseCodiMD (res, note) {
'Cache-Control': 'private', // only cache by client
'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
})
- res.render(config.codimdPath, {
+ res.render('codimd.ejs', {
url: config.serverURL,
title: title,
useCDN: config.useCDN,
@@ -142,7 +143,8 @@ function responseCodiMD (res, note) {
oauth2: config.isOAuth2Enable,
email: config.isEmailEnable,
allowEmailRegister: config.allowEmailRegister,
- allowPDFExport: config.allowPDFExport
+ allowPDFExport: config.allowPDFExport,
+ openID: config.isOpenIDEnable
})
}
@@ -283,7 +285,7 @@ function renderPublish (data, res) {
res.set({
'Cache-Control': 'private' // only cache by client
})
- res.render(config.prettyPath, data)
+ res.render('pretty.ejs', data)
}
function actionPublish (req, res, note) {
@@ -589,7 +591,7 @@ function gitlabActionProjects (req, res, note) {
ret.accesstoken = user.accessToken
ret.profileid = user.profileid
request(
- config.gitlab.baseURL + '/api/' + config.gitlab.version + '/projects?access_token=' + user.accessToken,
+ config.gitlab.baseURL + '/api/' + config.gitlab.version + '/projects?membership=yes&per_page=100&access_token=' + user.accessToken,
function (error, httpResponse, body) {
if (!error && httpResponse.statusCode === 200) {
ret.projects = JSON.parse(body)
@@ -665,7 +667,7 @@ function renderPublishSlide (data, res) {
res.set({
'Cache-Control': 'private' // only cache by client
})
- res.render(config.slidePath, data)
+ res.render('slide.ejs', data)
}
module.exports = response
diff --git a/lib/web/auth/index.js b/lib/web/auth/index.js
index 61e7c3f9..86ab4b28 100644
--- a/lib/web/auth/index.js
+++ b/lib/web/auth/index.js
@@ -45,6 +45,7 @@ if (config.isLDAPEnable) authRouter.use(require('./ldap'))
if (config.isSAMLEnable) authRouter.use(require('./saml'))
if (config.isOAuth2Enable) authRouter.use(require('./oauth2'))
if (config.isEmailEnable) authRouter.use(require('./email'))
+if (config.isOpenIDEnable) authRouter.use(require('./openid'))
// logout
authRouter.get('/logout', function (req, res) {
diff --git a/lib/web/auth/openid/index.js b/lib/web/auth/openid/index.js
new file mode 100644
index 00000000..96f61807
--- /dev/null
+++ b/lib/web/auth/openid/index.js
@@ -0,0 +1,61 @@
+'use strict'
+
+const Router = require('express').Router
+const passport = require('passport')
+const OpenIDStrategy = require('@passport-next/passport-openid').Strategy
+const config = require('../../../config')
+const models = require('../../../models')
+const logger = require('../../../logger')
+const {urlencodedParser} = require('../../utils')
+const {setReturnToFromReferer} = require('../utils')
+
+let openIDAuth = module.exports = Router()
+
+passport.use(new OpenIDStrategy({
+ returnURL: config.serverURL + '/auth/openid/callback',
+ realm: config.serverURL,
+ profile: true
+}, function (openid, profile, done) {
+ var stringifiedProfile = JSON.stringify(profile)
+ models.User.findOrCreate({
+ where: {
+ profileid: openid
+ },
+ defaults: {
+ profile: stringifiedProfile
+ }
+ }).spread(function (user, created) {
+ if (user) {
+ var needSave = false
+ if (user.profile !== stringifiedProfile) {
+ user.profile = stringifiedProfile
+ needSave = true
+ }
+ if (needSave) {
+ user.save().then(function () {
+ if (config.debug) { logger.info('user login: ' + user.id) }
+ return done(null, user)
+ })
+ } else {
+ if (config.debug) { logger.info('user login: ' + user.id) }
+ return done(null, user)
+ }
+ }
+ }).catch(function (err) {
+ logger.error('auth callback failed: ' + err)
+ return done(err, null)
+ })
+}))
+
+openIDAuth.post('/auth/openid', urlencodedParser, function (req, res, next) {
+ setReturnToFromReferer(req)
+ passport.authenticate('openid')(req, res, next)
+})
+
+// openID auth callback
+openIDAuth.get('/auth/openid/callback',
+ passport.authenticate('openid', {
+ successReturnToOrRedirect: config.serverurl + '/',
+ failureRedirect: config.serverurl + '/'
+ })
+)
diff --git a/lib/web/imageRouter/filesystem.js b/lib/web/imageRouter/filesystem.js
index 4bf82b31..8c432b0c 100644
--- a/lib/web/imageRouter/filesystem.js
+++ b/lib/web/imageRouter/filesystem.js
@@ -1,5 +1,6 @@
'use strict'
const url = require('url')
+const path = require('path')
const config = require('../../config')
const logger = require('../../logger')
@@ -15,5 +16,5 @@ exports.uploadImage = function (imagePath, callback) {
return
}
- callback(null, url.resolve(config.serverURL + '/', imagePath.match(/public\/(.+)$/)[1]))
+ callback(null, url.resolve(config.serverURL + '/uploads/', path.basename(imagePath)))
}
diff --git a/lib/web/statusRouter.js b/lib/web/statusRouter.js
index 7ecf3839..fb2609ea 100644
--- a/lib/web/statusRouter.js
+++ b/lib/web/statusRouter.js
@@ -105,5 +105,5 @@ statusRouter.get('/config', function (req, res) {
'X-Robots-Tag': 'noindex, nofollow', // prevent crawling
'Content-Type': 'application/javascript'
})
- res.render(config.constantsPath, data)
+ res.render('../js/lib/common/constant.ejs', data)
})
diff --git a/package.json b/package.json
index 6a48e903..a887ada1 100644
--- a/package.json
+++ b/package.json
@@ -8,13 +8,14 @@
"test": "npm run-script standard && npm run-script jsonlint",
"jsonlint": "find . -not -path './node_modules/*' -type f -name '*.json' -o -type f -name '*.json.example' | while read json; do echo $json ; jq . $json; done",
"standard": "node ./node_modules/standard/bin/cmd.js",
- "dev": "webpack --config webpack.config.js --progress --colors --watch",
- "build": "webpack --config webpack.production.js --progress --colors --bail",
+ "dev": "webpack --config webpack.dev.js --progress --colors --watch",
+ "build": "webpack --config webpack.prod.js --progress --colors --bail",
"postinstall": "bin/heroku",
"start": "node app.js",
"doctoc": "doctoc --title='# Table of Contents' README.md"
},
"dependencies": {
+ "@passport-next/passport-openid": "^1.0.0",
"Idle.Js": "git+https://github.com/shawnmclean/Idle.js",
"archiver": "^2.1.1",
"async": "^2.1.4",
@@ -41,7 +42,7 @@
"express-session": "^1.14.2",
"file-saver": "^1.3.3",
"flowchart.js": "^1.6.4",
- "font-awesome": "^4.7.0",
+ "fork-awesome": "^1.1.3",
"formidable": "^1.0.17",
"gist-embed": "~2.6.0",
"graceful-fs": "^4.1.11",
@@ -88,7 +89,6 @@
"morgan": "^1.7.0",
"mysql": "^2.12.0",
"node-uuid": "^1.4.7",
- "octicons": "~4.4.0",
"passport": "^0.4.0",
"passport-dropbox-oauth2": "^1.1.0",
"passport-facebook": "^2.1.1",
@@ -142,11 +142,15 @@
"Markdown",
"Notes"
],
- "homepage": "https://hackmd.io",
+ "homepage": "https://codimd.org",
"maintainers": [
{
"name": "Max Wu",
"email": "jackymaxj@gmail.com"
+ },
+ {
+ "name": "Christoph (Sheogorath) Kern",
+ "email": "codimd@sheogorath.shivering-isles.com"
}
],
"repository": {
@@ -183,6 +187,7 @@
"url-loader": "^1.0.1",
"webpack": "^4.14.0",
"webpack-cli": "^3.1.0",
+ "webpack-merge": "^4.1.4",
"webpack-parallel-uglify-plugin": "^1.1.0"
},
"standard": {
diff --git a/public/css/github-extract.css b/public/css/github-extract.css
index 7f7058a0..f7b15445 100644
--- a/public/css/github-extract.css
+++ b/public/css/github-extract.css
@@ -119,23 +119,24 @@
color: #ddd;
}
-.markdown-body h1 .octicon-link,
-.markdown-body h2 .octicon-link,
-.markdown-body h3 .octicon-link,
-.markdown-body h4 .octicon-link,
-.markdown-body h5 .octicon-link,
-.markdown-body h6 .octicon-link {
+.markdown-body h1 .fa-link,
+.markdown-body h2 .fa-link,
+.markdown-body h3 .fa-link,
+.markdown-body h4 .fa-link,
+.markdown-body h5 .fa-link,
+.markdown-body h6 .fa-link {
color: #000;
vertical-align: middle;
visibility: hidden;
+ font-size: 16px;
}
-.night .markdown-body h1 .octicon-link,
-.night .markdown-body h2 .octicon-link,
-.night .markdown-body h3 .octicon-link,
-.night .markdown-body h4 .octicon-link,
-.night .markdown-body h5 .octicon-link,
-.night .markdown-body h6 .octicon-link {
+.night .markdown-body h1 .fa-link,
+.night .markdown-body h2 .fa-link,
+.night .markdown-body h3 .fa-link,
+.night .markdown-body h4 .fa-link,
+.night .markdown-body h5 .fa-link,
+.night .markdown-body h6 .fa-link {
color: #fff;
}
@@ -148,12 +149,12 @@
text-decoration: none;
}
-.markdown-body h1:hover .anchor .octicon-link,
-.markdown-body h2:hover .anchor .octicon-link,
-.markdown-body h3:hover .anchor .octicon-link,
-.markdown-body h4:hover .anchor .octicon-link,
-.markdown-body h5:hover .anchor .octicon-link,
-.markdown-body h6:hover .anchor .octicon-link {
+.markdown-body h1:hover .anchor .fa-link,
+.markdown-body h2:hover .anchor .fa-link,
+.markdown-body h3:hover .anchor .fa-link,
+.markdown-body h4:hover .anchor .fa-link,
+.markdown-body h5:hover .anchor .fa-link,
+.markdown-body h6:hover .anchor .fa-link {
visibility: visible;
}
diff --git a/public/js/extra.js b/public/js/extra.js
index d6bbb0c6..dd264047 100644
--- a/public/js/extra.js
+++ b/public/js/extra.js
@@ -570,7 +570,9 @@ export function postProcess (code) {
$(value).html(html)
})
// link should open in new window or tab
- result.find('a:not([href^="#"]):not([target])').attr('target', '_blank')
+ // also add noopener to prevent clickjacking
+ // See details: https://mathiasbynens.github.io/rel-noopener/
+ result.find('a:not([href^="#"]):not([target])').attr('target', '_blank').attr('rel', 'noopener')
// update continue line numbers
const linenumberdivs = result.find('.gutter.linenumber').toArray()
for (let i = 0; i < linenumberdivs.length; i++) {
@@ -832,7 +834,7 @@ const anchorForId = id => {
const anchor = document.createElement('a')
anchor.className = 'anchor hidden-xs'
anchor.href = `#${id}`
- anchor.innerHTML = '<span class="octicon octicon-link"></span>'
+ anchor.innerHTML = '<i class="fa fa-link"></i>'
anchor.title = id
return anchor
}
diff --git a/public/js/index.js b/public/js/index.js
index 1330deac..3dd25fa6 100644
--- a/public/js/index.js
+++ b/public/js/index.js
@@ -1479,7 +1479,7 @@ $('#snippetExportModalConfirm').click(function () {
file_name: $('#snippetExportModalFileName').val(),
code: editor.getValue(),
visibility_level: $('#snippetExportModalVisibility').val(),
- visibility: $('#snippetExportModalVisibility').val() === 0 ? 'private' : ($('#snippetExportModalVisibility').val() === 10 ? 'internal' : '')
+ visibility: $('#snippetExportModalVisibility').val() === '0' ? 'private' : ($('#snippetExportModalVisibility').val() === '10' ? 'internal' : 'private')
}
if (!data.title || !data.file_name || !data.code || !data.visibility_level || !$('#snippetExportModalProjects').val()) return
diff --git a/public/views/codimd/body.ejs b/public/views/codimd/body.ejs
index d4f27a93..dc111909 100644
--- a/public/views/codimd/body.ejs
+++ b/public/views/codimd/body.ejs
@@ -113,7 +113,7 @@
</div>
<div class="modal-body" style="color:black;">
<h5></h5>
- <a target="_blank" style="word-break: break-all;"></a>
+ <a target="_blank" rel="noopener" style="word-break: break-all;"></a>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><%= __('OK') %></button>
diff --git a/public/views/codimd/head.ejs b/public/views/codimd/head.ejs
index 1f446253..356ded6d 100644
--- a/public/views/codimd/head.ejs
+++ b/public/views/codimd/head.ejs
@@ -9,10 +9,9 @@
<link rel="apple-touch-icon" href="<%- url %>/apple-touch-icon.png">
<% if(useCDN) { %>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w=" crossorigin="anonymous" />
-<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" />
+<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fork-awesome/1.1.3/css/fork-awesome.min.css" integrity="sha256-ZhApazu+kejqTYhMF+1DzNKjIzP7KXu6AzyXcC1gMus=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-social/4.9.0/bootstrap-social.min.css" integrity="sha256-02JtFTurpwBjQJ6q13iJe82/NF0RbZlJroDegK5g87Y=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css" integrity="sha256-3iu9jgsy9TpTwXKb7bNQzqWekRX7pPK+2OLj3R922fo=" crossorigin="anonymous" />
-<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/octicons/4.4.0/octicons.min.css" integrity="sha256-4eCms8tUm+iL29tUvmRhNRnz/7lNI4oTtQzqa/uM2dI=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/emojify.js/1.1.0/css/basic/emojify.min.css" integrity="sha256-UOrvMOsSDSrW6szVLe8ZDZezBxh5IoIfgTwdNDgTjiU=" crossorigin="anonymous" />
<%- include ../build/index-header %>
<%- include ../shared/polyfill %>
diff --git a/public/views/codimd/header.ejs b/public/views/codimd/header.ejs
index 8fc050b7..1b5e4222 100644
--- a/public/views/codimd/header.ejs
+++ b/public/views/codimd/header.ejs
@@ -22,15 +22,15 @@
<i class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu list" role="menu" aria-labelledby="menu">
- <li role="presentation"><a role="menuitem" class="ui-new" tabindex="-1" href="<%- url %>/new" target="_blank"><i class="fa fa-plus fa-fw"></i> <%= __('New') %></a>
+ <li role="presentation"><a role="menuitem" class="ui-new" tabindex="-1" href="<%- url %>/new" target="_blank" rel="noopener"><i class="fa fa-plus fa-fw"></i> <%= __('New') %></a>
</li>
- <li role="presentation"><a role="menuitem" class="ui-publish" tabindex="-1" href="#" target="_blank"><i class="fa fa-share-square-o fa-fw"></i> <%= __('Publish') %></a>
+ <li role="presentation"><a role="menuitem" class="ui-publish" tabindex="-1" href="#" target="_blank" rel="noopener"><i class="fa fa-share-square-o fa-fw"></i> <%= __('Publish') %></a>
</li>
<li class="divider"></li>
<li class="dropdown-header"><%= __('Extra') %></li>
<li role="presentation"><a role="menuitem" class="ui-extra-revision" tabindex="-1" data-toggle="modal" data-target="#revisionModal"><i class="fa fa-history fa-fw"></i> <%= __('Revision') %></a>
</li>
- <li role="presentation"><a role="menuitem" class="ui-extra-slide" tabindex="-1" href="#" target="_blank"><i class="fa fa-tv fa-fw"></i> <%= __('Slide Mode') %></a>
+ <li role="presentation"><a role="menuitem" class="ui-extra-slide" tabindex="-1" href="#" target="_blank" rel="noopener"><i class="fa fa-tv fa-fw"></i> <%= __('Slide Mode') %></a>
</li>
<% if((typeof github !== 'undefined' && github) || (typeof dropbox !== 'undefined' && dropbox) || (typeof gitlab !== 'undefined' && gitlab && (!gitlab.scope || gitlab.scope === 'api'))) { %>
<li class="divider"></li>
@@ -38,7 +38,7 @@
<li role="presentation"><a role="menuitem" class="ui-save-dropbox" tabindex="-1" href="#" target="_self"><i class="fa fa-dropbox fa-fw"></i> Dropbox</a>
</li>
<% if(typeof github !== 'undefined' && github) { %>
- <li role="presentation"><a role="menuitem" class="ui-save-gist" tabindex="-1" href="#" target="_blank"><i class="fa fa-github fa-fw"></i> Gist</a>
+ <li role="presentation"><a role="menuitem" class="ui-save-gist" tabindex="-1" href="#" target="_blank" rel="noopener"><i class="fa fa-github fa-fw"></i> Gist</a>
</li>
<% } %>
<% if(typeof gitlab !== 'undefined' && gitlab && (!gitlab.scope || gitlab.scope === 'api')) { %>
@@ -115,12 +115,12 @@
</ul>
<ul class="nav navbar-nav navbar-right" style="padding:0;">
<li>
- <a href="<%- url %>/new" target="_blank" class="ui-new">
+ <a href="<%- url %>/new" target="_blank" rel="noopener" class="ui-new">
<i class="fa fa-plus"></i> <%= __('New') %>
</a>
</li>
<li>
- <a href="#" target="_blank" class="ui-publish">
+ <a href="#" target="_blank" rel="noopener" class="ui-publish">
<i class="fa fa-share-square-o"></i> <%= __('Publish') %>
</a>
</li>
@@ -132,7 +132,7 @@
<li class="dropdown-header"><%= __('Extra') %></li>
<li role="presentation"><a role="menuitem" class="ui-extra-revision" tabindex="-1" data-toggle="modal" data-target="#revisionModal"><i class="fa fa-history fa-fw"></i> <%= __('Revision') %></a>
</li>
- <li role="presentation"><a role="menuitem" class="ui-extra-slide" tabindex="-1" href="#" target="_blank"><i class="fa fa-tv fa-fw"></i> <%= __('Slide Mode') %></a>
+ <li role="presentation"><a role="menuitem" class="ui-extra-slide" tabindex="-1" href="#" target="_blank" rel="noopener"><i class="fa fa-tv fa-fw"></i> <%= __('Slide Mode') %></a>
</li>
<% if((typeof github !== 'undefined' && github) || (typeof dropbox !== 'undefined' && dropbox) || (typeof gitlab !== 'undefined' && gitlab && (!gitlab.scope || gitlab.scope === 'api'))) { %>
<li class="divider"></li>
@@ -140,7 +140,7 @@
<li role="presentation"><a role="menuitem" class="ui-save-dropbox" tabindex="-1" href="#" target="_self"><i class="fa fa-dropbox fa-fw"></i> Dropbox</a>
</li>
<% if(typeof github !== 'undefined' && github) { %>
- <li role="presentation"><a role="menuitem" class="ui-save-gist" tabindex="-1" href="#" target="_blank"><i class="fa fa-github fa-fw"></i> Gist</a>
+ <li role="presentation"><a role="menuitem" class="ui-save-gist" tabindex="-1" href="#" target="_blank" rel="noopener"><i class="fa fa-github fa-fw"></i> Gist</a>
</li>
<% } %>
<% if(typeof gitlab !== 'undefined' && gitlab && (!gitlab.scope || gitlab.scope === 'api')) { %>
diff --git a/public/views/html.hbs b/public/views/html.hbs
index a87bf81a..490d31a4 100644
--- a/public/views/html.hbs
+++ b/public/views/html.hbs
@@ -16,9 +16,8 @@
<link rel="apple-touch-icon" href="{{{url}}}/apple-touch-icon.png">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w=" crossorigin="anonymous" />
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" />
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fork-awesome/1.1.3/css/fork-awesome.min.css" integrity="sha256-ZhApazu+kejqTYhMF+1DzNKjIzP7KXu6AzyXcC1gMus=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css" integrity="sha256-3iu9jgsy9TpTwXKb7bNQzqWekRX7pPK+2OLj3R922fo=" crossorigin="anonymous" />
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/octicons/4.4.0/octicons.min.css" integrity="sha256-4eCms8tUm+iL29tUvmRhNRnz/7lNI4oTtQzqa/uM2dI=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/themes/prism.min.css" integrity="sha256-vtR0hSWRc3Tb26iuN2oZHt3KRUomwTufNIf5/4oeCyg=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github-gist.min.css" integrity="sha256-tAflq+ymku3Khs+I/WcAneIlafYgDiOQ9stIHH985Wo=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/emojify.js/1.1.0/css/basic/emojify.min.css" integrity="sha256-UOrvMOsSDSrW6szVLe8ZDZezBxh5IoIfgTwdNDgTjiU=" crossorigin="anonymous" />
diff --git a/public/views/index/body.ejs b/public/views/index/body.ejs
index 0f2813b9..18bffddb 100644
--- a/public/views/index/body.ejs
+++ b/public/views/index/body.ejs
@@ -150,10 +150,10 @@
<option value="id">Bahasa Indonesia</option>
</select>
<p>
- Powered by <a href="https://codimd.org">CodiMD</a> | <a href="<%- url %>/s/release-notes" target="_blank"><%= __('Releases') %></a><% if(privacyStatement) { %> | <a href="<%- url %>/s/privacy" target="_blank"><%= __('Privacy') %></a><% } %><% if(termsOfUse) { %> | <a href="<%- url %>/s/terms-of-use" target="_blank"><%= __('Terms of Use') %></a><% } %>
+ Powered by <a href="https://codimd.org">CodiMD</a> | <a href="<%- url %>/s/release-notes" target="_blank" rel="noopener"><%= __('Releases') %></a><% if(privacyStatement) { %> | <a href="<%- url %>/s/privacy" target="_blank" rel="noopener"><%= __('Privacy') %></a><% } %><% if(termsOfUse) { %> | <a href="<%- url %>/s/terms-of-use" target="_blank" rel="noopener"><%= __('Terms of Use') %></a><% } %>
</p>
<h6 class="social-foot">
- <%- __('Follow us on %s and %s.', '<a href="https://github.com/hackmdio/CodiMD" target="_blank"><i class="fa fa-github"></i> GitHub</a>, <a href="https://riot.im/app/#/room/#codimd:matrix.org" target="_blank"><i class="fa fa-comments"></i> Riot</a>', '<a href="https://translate.codimd.org" target="_blank"><i class="fa fa-globe"></i> POEditor</a>') %>
+ <%- __('Follow us on %s and %s.', '<a href="https://github.com/hackmdio/CodiMD" target="_blank" rel="noopener"><i class="fa fa-github"></i> GitHub</a>, <a href="https://riot.im/app/#/room/#codimd:matrix.org" target="_blank" rel="noopener"><i class="fa fa-comments"></i> Riot</a>', '<a href="https://translate.codimd.org" target="_blank" rel="noopener"><i class="fa fa-globe"></i> POEditor</a>') %>
</h6>
</div>
</div>
diff --git a/public/views/index/head.ejs b/public/views/index/head.ejs
index f826d9ee..fc10dfa0 100644
--- a/public/views/index/head.ejs
+++ b/public/views/index/head.ejs
@@ -11,7 +11,7 @@
<link rel="apple-touch-icon" href="<%- url %>/apple-touch-icon.png">
<% if(useCDN) { %>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w=" crossorigin="anonymous" />
-<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" />
+<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fork-awesome/1.1.3/css/fork-awesome.min.css" integrity="sha256-ZhApazu+kejqTYhMF+1DzNKjIzP7KXu6AzyXcC1gMus=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-social/4.9.0/bootstrap-social.min.css" integrity="sha256-02JtFTurpwBjQJ6q13iJe82/NF0RbZlJroDegK5g87Y=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/3.5.4/select2.min.css" integrity="sha256-ijlUKKj3hJCiiT2HWo1kqkI79NTEYpzOsw5Rs3k42dI=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/3.5.4/select2-bootstrap.min.css" integrity="sha256-NAWFcNIZdH+TS1xpWujF/EB/Y8gwBbEOCoaK/eqaer8=" crossorigin="anonymous" />
diff --git a/public/views/pretty.ejs b/public/views/pretty.ejs
index 4c2fc0ab..a087be7f 100644
--- a/public/views/pretty.ejs
+++ b/public/views/pretty.ejs
@@ -19,9 +19,8 @@
<link rel="apple-touch-icon" href="<%- url %>/apple-touch-icon.png">
<% if(useCDN) { %>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w=" crossorigin="anonymous" />
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" />
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fork-awesome/1.1.3/css/fork-awesome.min.css" integrity="sha256-ZhApazu+kejqTYhMF+1DzNKjIzP7KXu6AzyXcC1gMus=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css" integrity="sha256-3iu9jgsy9TpTwXKb7bNQzqWekRX7pPK+2OLj3R922fo=" crossorigin="anonymous" />
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/octicons/4.4.0/octicons.min.css" integrity="sha256-4eCms8tUm+iL29tUvmRhNRnz/7lNI4oTtQzqa/uM2dI=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/emojify.js/1.1.0/css/basic/emojify.min.css" integrity="sha256-UOrvMOsSDSrW6szVLe8ZDZezBxh5IoIfgTwdNDgTjiU=" crossorigin="anonymous" />
<%- include build/pretty-header %>
<%- include shared/polyfill %>
diff --git a/public/views/shared/refresh-modal.ejs b/public/views/shared/refresh-modal.ejs
index 5be41b2a..64580545 100644
--- a/public/views/shared/refresh-modal.ejs
+++ b/public/views/shared/refresh-modal.ejs
@@ -14,7 +14,7 @@
</div>
<div class="new-version" style="display:none;">
<h5><%= __('New version available!') %></h5>
- <a href="<%- url %>/s/release-notes" target="_blank"><%= __('See releases notes here') %></a>
+ <a href="<%- url %>/s/release-notes" target="_blank" rel="noopener"><%= __('See releases notes here') %></a>
<br>
<strong><%= __('Refresh to enjoy new features.') %></strong>
</div>
diff --git a/public/views/shared/signin-modal.ejs b/public/views/shared/signin-modal.ejs
index f0cffad9..b413511c 100644
--- a/public/views/shared/signin-modal.ejs
+++ b/public/views/shared/signin-modal.ejs
@@ -78,7 +78,26 @@
</div>
</form>
<% } %>
- <% if((facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap || oauth2) && email) { %>
+ <% if((facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap || oauth2) && openID) { %>
+ <hr>
+ <% }%>
+ <% if(openID) { %>
+ <h4>OpenID</h4>
+ <form data-toggle="validator" role="form" class="form-horizontal" method="post" enctype="application/x-www-form-urlencoded">
+ <div class="form-group">
+ <div class="col-sm-12">
+ <input type="text" class="form-control" name="openid_identifier" placeholder="OpenID" required>
+ <span class="help-block control-label with-errors" style="display: inline;"></span>
+ </div>
+ </div>
+ <div class="form-group">
+ <div class="col-sm-12">
+ <button type="submit" class="btn btn-primary" formaction="<%- url %>/auth/openid">Sign in</button>
+ </div>
+ </div>
+ </form>
+ <% } %>
+ <% if((facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap || oauth2 || openID) && email) { %>
<hr>
<% }%>
<% if(email) { %>
diff --git a/public/views/slide.ejs b/public/views/slide.ejs
index c11d2984..42fb5199 100644
--- a/public/views/slide.ejs
+++ b/public/views/slide.ejs
@@ -16,9 +16,8 @@
<link rel="apple-touch-icon" href="<%- url %>/apple-touch-icon.png">
<% if(useCDN) { %>
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" />
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fork-awesome/1.1.3/css/fork-awesome.min.css" integrity="sha256-ZhApazu+kejqTYhMF+1DzNKjIzP7KXu6AzyXcC1gMus=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css" integrity="sha256-3iu9jgsy9TpTwXKb7bNQzqWekRX7pPK+2OLj3R922fo=" crossorigin="anonymous" />
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/octicons/4.4.0/octicons.min.css" integrity="sha256-4eCms8tUm+iL29tUvmRhNRnz/7lNI4oTtQzqa/uM2dI=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.6.0/css/reveal.min.css" integrity="sha256-ol2N5Xr80jdDqxK0lte3orKGb9Ja3sOnpAUV7TTADmg=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/emojify.js/1.1.0/css/basic/emojify.min.css" integrity="sha256-UOrvMOsSDSrW6szVLe8ZDZezBxh5IoIfgTwdNDgTjiU=" crossorigin="anonymous" />
<%- include build/slide-header %>
diff --git a/webpackBaseConfig.js b/webpack.common.js
index 19d021d9..efd2b3e2 100644
--- a/webpackBaseConfig.js
+++ b/webpack.common.js
@@ -10,6 +10,7 @@ var gracefulFs = require('graceful-fs')
gracefulFs.gracefulify(fs)
module.exports = {
+ name: 'app',
plugins: [
new webpack.ProvidePlugin({
Visibility: 'visibilityjs',
@@ -184,7 +185,7 @@ module.exports = {
],
'cover-styles-pack': [
path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'),
- path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'),
+ path.join(__dirname, 'node_modules/fork-awesome/css/fork-awesome.min.css'),
path.join(__dirname, 'public/css/bootstrap-social.css'),
path.join(__dirname, 'node_modules/select2/select2.css'),
path.join(__dirname, 'node_modules/select2/select2-bootstrap.css')
@@ -236,10 +237,9 @@ module.exports = {
],
'index-styles-pack': [
path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'),
- path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'),
+ path.join(__dirname, 'node_modules/fork-awesome/css/fork-awesome.min.css'),
path.join(__dirname, 'public/css/bootstrap-social.css'),
- path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'),
- path.join(__dirname, 'node_modules/octicons/build/octicons.css')
+ path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css')
],
'index-pack': [
'babel-polyfill',
@@ -287,9 +287,8 @@ module.exports = {
],
'pretty-styles-pack': [
path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'),
- path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'),
- path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'),
- path.join(__dirname, 'node_modules/octicons/build/octicons.css')
+ path.join(__dirname, 'node_modules/fork-awesome/css/fork-awesome.min.css'),
+ path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css')
],
'pretty-pack': [
'babel-polyfill',
@@ -324,9 +323,8 @@ module.exports = {
path.join(__dirname, 'public/css/markdown.css')
],
'slide-styles-pack': [
- path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'),
- path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'),
- path.join(__dirname, 'node_modules/octicons/build/octicons.css')
+ path.join(__dirname, 'node_modules/fork-awesome/css/fork-awesome.min.css'),
+ path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css')
],
'slide-pack': [
'babel-polyfill',
diff --git a/webpack.config.js b/webpack.config.js
deleted file mode 100644
index 3c7c727e..00000000
--- a/webpack.config.js
+++ /dev/null
@@ -1,41 +0,0 @@
-var baseConfig = require('./webpackBaseConfig')
-const MiniCssExtractPlugin = require('mini-css-extract-plugin')
-var path = require('path')
-
-module.exports = [Object.assign({}, baseConfig, {
- plugins: baseConfig.plugins.concat([
- new MiniCssExtractPlugin({
- filename: '[name].css',
- chunkFilename: '[id].css'
- })
-
- ]),
- devtool: 'source-map'
-}), {
- devtool: 'source-map',
- entry: {
- htmlExport: path.join(__dirname, 'public/js/htmlExport.js')
- },
- module: {
- rules: [{
- test: /\.css$/,
- use: ['style-loader', 'css-loader']
- }, {
- test: /\.scss$/,
- use: ['style-loader', 'sass-loader']
- }, {
- test: /\.less$/,
- use: ['style-loader', 'less-loader']
- }]
- },
- output: {
- path: path.join(__dirname, 'public/build'),
- publicPath: '/build/',
- filename: '[name].js'
- },
- plugins: [
- new MiniCssExtractPlugin({
- filename: 'html.min.css'
- })
- ]
-}]
diff --git a/webpack.dev.js b/webpack.dev.js
new file mode 100644
index 00000000..b1ed3612
--- /dev/null
+++ b/webpack.dev.js
@@ -0,0 +1,14 @@
+const common = require('./webpack.common.js')
+const htmlexport = require('./webpack.htmlexport')
+const merge = require('webpack-merge')
+
+module.exports = [
+ // merge common config
+ merge(common, {
+ mode: 'development',
+ devtool: 'cheap-module-eval-source-map'
+ }),
+ merge(htmlexport, {
+ mode: 'development',
+ devtool: 'cheap-module-eval-source-map'
+ })]
diff --git a/webpack.htmlexport.js b/webpack.htmlexport.js
new file mode 100644
index 00000000..dd6f4c01
--- /dev/null
+++ b/webpack.htmlexport.js
@@ -0,0 +1,25 @@
+const MiniCssExtractPlugin = require('mini-css-extract-plugin')
+const path = require('path')
+
+module.exports = {
+ name: 'save-as-html',
+ entry: {
+ htmlExport: path.join(__dirname, 'public/js/htmlExport.js')
+ },
+ module: {
+ rules: [{
+ test: /\.css$/,
+ use: [MiniCssExtractPlugin.loader, 'css-loader']
+ }]
+ },
+ output: {
+ path: path.join(__dirname, 'public/build'),
+ publicPath: '/build/',
+ filename: '[name].js'
+ },
+ plugins: [
+ new MiniCssExtractPlugin({
+ filename: 'html.min.css'
+ })
+ ]
+}
diff --git a/webpack.prod.js b/webpack.prod.js
new file mode 100644
index 00000000..188d9881
--- /dev/null
+++ b/webpack.prod.js
@@ -0,0 +1,23 @@
+const common = require('./webpack.common.js')
+const htmlexport = require('./webpack.htmlexport')
+const merge = require('webpack-merge')
+const path = require('path')
+const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
+
+module.exports = [
+ merge(common, {
+ mode: 'production',
+ output: {
+ path: path.join(__dirname, 'public/build'),
+ publicPath: '/build/',
+ filename: '[name].[contenthash].js'
+ }
+ }),
+ merge(htmlexport, {
+ mode: 'production',
+ optimization: {
+ minimizer: [
+ new OptimizeCSSAssetsPlugin({})
+ ]
+ }
+ })]
diff --git a/webpack.production.js b/webpack.production.js
deleted file mode 100644
index 67387583..00000000
--- a/webpack.production.js
+++ /dev/null
@@ -1,75 +0,0 @@
-var baseConfig = require('./webpackBaseConfig')
-var webpack = require('webpack')
-var path = require('path')
-const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
-const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
-const MiniCssExtractPlugin = require('mini-css-extract-plugin')
-
-module.exports = [Object.assign({}, baseConfig, {
- plugins: baseConfig.plugins.concat([
- new webpack.DefinePlugin({
- 'process.env': {
- 'NODE_ENV': JSON.stringify('production')
- }
- })
- ]),
-
- optimization: {
- minimizer: [
- new UglifyJsPlugin({
- parallel: true,
- cache: true
- })
- ],
- splitChunks: {
- chunks: 'async',
- minChunks: Infinity
- }
- },
-
- output: {
- path: path.join(__dirname, 'public/build'),
- publicPath: '/build/',
- filename: '[id].[name].[hash].js'
- // baseUrl: '<%- url %>'
- }
-}), {
- // This Chunk is used in the 'save as html' feature.
- // It is embedded in the html file and contains CSS for styling.
-
- entry: {
- htmlExport: path.join(__dirname, 'public/js/htmlExport.js')
- },
-
- output: {
- path: path.join(__dirname, 'public/build'),
- publicPath: '/build/',
- filename: '[name].js'
- },
- plugins: [
- new webpack.DefinePlugin({
- 'process.env': {
- 'NODE_ENV': JSON.stringify('production')
- }
- }),
- new MiniCssExtractPlugin({
- filename: 'html.min.css'
- })
- ],
-
- optimization: {
- minimizer: [
- new OptimizeCSSAssetsPlugin({})
- ]
- },
-
- module: {
- rules: [{
- test: /\.css$/,
- use: [
- MiniCssExtractPlugin.loader,
- 'css-loader'
- ]
- }]
- }
-}]
diff --git a/yarn.lock b/yarn.lock
index 7c50e5e5..838f53fb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,6 +2,19 @@
# yarn lockfile v1
+"@passport-next/passport-openid@^1.0.0":
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/@passport-next/passport-openid/-/passport-openid-1.0.0.tgz#d3b5e067a9aa1388ed172ab7cc02c39b8634283d"
+ integrity sha512-W9uj4Ui/ZK/iBUNzSNxPWDQ8wCD1tUddGEVSGm0FN0B7ewo3yBQLGMoW3i3UqcwEzxdyGbAj06ohAhNQIXC4VA==
+ dependencies:
+ "@passport-next/passport-strategy" "1.x.x"
+ openid "2.x.x"
+
+"@passport-next/passport-strategy@1.x.x":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@passport-next/passport-strategy/-/passport-strategy-1.1.0.tgz#4c0df069e2ec9262791b9ef1e23320c1d73bdb74"
+ integrity sha512-2KhFjtPueJG6xVj2HnqXt9BlANOfYCVLyu+pXYjPGBDT8yk+vQwc/6tsceIj+mayKcoxMau2JimggXRPHgoc8w==
+
"@types/body-parser@*":
version "1.17.0"
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.0.tgz#9f5c9d9bd04bb54be32d5eb9fc0d8c974e6cf58c"
@@ -350,7 +363,7 @@ ajv@^4.7.0, ajv@^4.9.1:
co "^4.6.0"
json-stable-stringify "^1.0.1"
-ajv@^5.1.0:
+ajv@^5.1.0, ajv@^5.3.0:
version "5.5.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=
@@ -738,6 +751,11 @@ aws4@^1.2.1, aws4@^1.6.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289"
integrity sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==
+aws4@^1.8.0:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
+ integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
+
azure-storage@^2.7.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/azure-storage/-/azure-storage-2.10.0.tgz#020ac343262c5552ef86516cbb7679241e95e4de"
@@ -2225,6 +2243,13 @@ combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5:
dependencies:
delayed-stream "~1.0.0"
+combined-stream@~1.0.6:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828"
+ integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==
+ dependencies:
+ delayed-stream "~1.0.0"
+
commander@2.15.x, commander@~2.15.0:
version "2.15.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
@@ -3818,6 +3843,11 @@ extend@~1.2.1:
resolved "https://registry.yarnpkg.com/extend/-/extend-1.2.1.tgz#a0f5fd6cfc83a5fe49ef698d60ec8a624dd4576c"
integrity sha1-oPX9bPyDpf5J72mNYOyKYk3UV2w=
+extend@~3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
+ integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
+
extendr@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/extendr/-/extendr-2.1.0.tgz#301aa0bbea565f4d2dc8f570f2a22611a8527b56"
@@ -4120,11 +4150,6 @@ flush-write-stream@^1.0.0:
inherits "^2.0.1"
readable-stream "^2.0.4"
-font-awesome@^4.7.0:
- version "4.7.0"
- resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133"
- integrity sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=
-
for-in@^1.0.1, for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
@@ -4149,6 +4174,11 @@ forever-agent@~0.6.1:
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
+fork-awesome@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/fork-awesome/-/fork-awesome-1.1.3.tgz#820403b860a8f8bb90c3e0d8509c81b42ae2dcf8"
+ integrity sha512-pjeV0OWPbl/EiZJ64nsxB0ATGyG+e0/mnOI/BFlwT+171P2/2eI96KaRHy8ySZTdXG/lCCdffs7dODGm0hDBXg==
+
form-data@1.0.0-rc3:
version "1.0.0-rc3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-1.0.0-rc3.tgz#d35bc62e7fbc2937ae78f948aaa0d38d90607577"
@@ -4167,7 +4197,7 @@ form-data@~2.1.1:
combined-stream "^1.0.5"
mime-types "^2.1.12"
-form-data@~2.3.1:
+form-data@~2.3.1, form-data@~2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099"
integrity sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=
@@ -4733,6 +4763,14 @@ har-validator@~5.0.3:
ajv "^5.1.0"
har-schema "^2.0.0"
+har-validator@~5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.0.tgz#44657f5688a22cfd4b72486e81b3a3fb11742c29"
+ integrity sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==
+ dependencies:
+ ajv "^5.3.0"
+ har-schema "^2.0.0"
+
has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
@@ -6830,6 +6868,11 @@ mime-db@~1.33.0:
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db"
integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==
+mime-db@~1.36.0:
+ version "1.36.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.36.0.tgz#5020478db3c7fe93aad7bbcc4dcf869c43363397"
+ integrity sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==
+
mime-types@^2.1.12, mime-types@^2.1.14, mime-types@^2.1.3, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.7:
version "2.1.18"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8"
@@ -6837,6 +6880,13 @@ mime-types@^2.1.12, mime-types@^2.1.14, mime-types@^2.1.3, mime-types@~2.1.17, m
dependencies:
mime-db "~1.33.0"
+mime-types@~2.1.19:
+ version "2.1.20"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.20.tgz#930cb719d571e903738520f8470911548ca2cc19"
+ integrity sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==
+ dependencies:
+ mime-db "~1.36.0"
+
mime@1.3.4:
version "1.3.4"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53"
@@ -7338,6 +7388,11 @@ oauth-sign@~0.8.1, oauth-sign@~0.8.2:
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
integrity sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=
+oauth-sign@~0.9.0:
+ version "0.9.0"
+ resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
+ integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
+
oauth@0.9.x:
version "0.9.15"
resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1"
@@ -7478,6 +7533,13 @@ onetime@^2.0.0:
dependencies:
mimic-fn "^1.0.0"
+openid@2.x.x:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/openid/-/openid-2.0.6.tgz#707375e59ab9f73025899727679b20328171c9aa"
+ integrity sha1-cHN15Zq59zAliZcnZ5sgMoFxyao=
+ dependencies:
+ request "^2.61.0"
+
optimist@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
@@ -8605,7 +8667,7 @@ qs@6.5.1:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
integrity sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==
-qs@6.5.2, qs@~6.5.1:
+qs@6.5.2, qs@~6.5.1, qs@~6.5.2:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
@@ -9112,6 +9174,32 @@ request@2.x, request@^2.40.0, request@^2.79.0, request@^2.81.0, request@^2.86.0:
tunnel-agent "^0.6.0"
uuid "^3.1.0"
+request@^2.61.0:
+ version "2.88.0"
+ resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
+ integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
+ dependencies:
+ aws-sign2 "~0.7.0"
+ aws4 "^1.8.0"
+ caseless "~0.12.0"
+ combined-stream "~1.0.6"
+ extend "~3.0.2"
+ forever-agent "~0.6.1"
+ form-data "~2.3.2"
+ har-validator "~5.1.0"
+ http-signature "~1.2.0"
+ is-typedarray "~1.0.0"
+ isstream "~0.1.2"
+ json-stringify-safe "~5.0.1"
+ mime-types "~2.1.19"
+ oauth-sign "~0.9.0"
+ performance-now "^2.1.0"
+ qs "~6.5.2"
+ safe-buffer "^5.1.2"
+ tough-cookie "~2.4.3"
+ tunnel-agent "^0.6.0"
+ uuid "^3.3.2"
+
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
@@ -10420,7 +10508,7 @@ toposort-class@^1.0.1:
resolved "https://registry.yarnpkg.com/toposort-class/-/toposort-class-1.0.1.tgz#7ffd1f78c8be28c3ba45cd4e1a3f5ee193bd9988"
integrity sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg=
-tough-cookie@^2.3.2:
+tough-cookie@^2.3.2, tough-cookie@~2.4.3:
version "2.4.3"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==
@@ -10879,7 +10967,7 @@ uuid@3.1.0:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04"
integrity sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==
-uuid@^3.0.0, uuid@^3.1.0:
+uuid@^3.0.0, uuid@^3.1.0, uuid@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
@@ -11080,6 +11168,13 @@ webpack-cli@^3.1.0:
v8-compile-cache "^2.0.0"
yargs "^12.0.1"
+webpack-merge@^4.1.4:
+ version "4.1.4"
+ resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.1.4.tgz#0fde38eabf2d5fd85251c24a5a8c48f8a3f4eb7b"
+ integrity sha512-TmSe1HZKeOPey3oy1Ov2iS3guIZjWvMT2BBJDzzT5jScHTjVC3mpjJofgueEzaEd6ibhxRDD6MIblDr8tzh8iQ==
+ dependencies:
+ lodash "^4.17.5"
+
webpack-parallel-uglify-plugin@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/webpack-parallel-uglify-plugin/-/webpack-parallel-uglify-plugin-1.1.0.tgz#252a6c796bf79a8047b00de2cf08c23aa9861441"