Update to support optional email register and signin
This commit is contained in:
parent
52772829ce
commit
a73d9ce39e
14 changed files with 180 additions and 16 deletions
|
@ -131,6 +131,7 @@ Environment variables (will overwrite other server configs)
|
||||||
| HMD_GOOGLE_CLIENTID | no example | Google API client id |
|
| HMD_GOOGLE_CLIENTID | no example | Google API client id |
|
||||||
| HMD_GOOGLE_CLIENTSECRET | no example | Google API client secret |
|
| HMD_GOOGLE_CLIENTSECRET | no example | Google API client secret |
|
||||||
| HMD_IMGUR_CLIENTID | no example | Imgur API client id |
|
| HMD_IMGUR_CLIENTID | no example | Imgur API client id |
|
||||||
|
| HMD_EMAIL | `true` or `false` | set to allow email register and signin |
|
||||||
| HMD_IMAGE_UPLOAD_TYPE | `imgur`, `s3` or `filesystem` | Where to upload image. For S3, see our [S3 Image Upload Guide](docs/guides/s3-image-upload.md) |
|
| HMD_IMAGE_UPLOAD_TYPE | `imgur`, `s3` or `filesystem` | Where to upload image. For S3, see our [S3 Image Upload Guide](docs/guides/s3-image-upload.md) |
|
||||||
| HMD_S3_ACCESS_KEY_ID | no example | AWS access key id |
|
| HMD_S3_ACCESS_KEY_ID | no example | AWS access key id |
|
||||||
| HMD_S3_SECRET_ACCESS_KEY | no example | AWS secret key |
|
| HMD_S3_SECRET_ACCESS_KEY | no example | AWS secret key |
|
||||||
|
@ -171,6 +172,7 @@ Server settings `config.json`
|
||||||
| heartbeatinterval | `5000` | socket.io heartbeat interval |
|
| heartbeatinterval | `5000` | socket.io heartbeat interval |
|
||||||
| heartbeattimeout | `10000` | socket.io heartbeat timeout |
|
| heartbeattimeout | `10000` | socket.io heartbeat timeout |
|
||||||
| documentmaxlength | `100000` | note max length |
|
| documentmaxlength | `100000` | note max length |
|
||||||
|
| email | `true` or `false` | set to allow email register and signin |
|
||||||
| imageUploadType | `imgur`(default), `s3` or `filesystem` | Where to upload image
|
| imageUploadType | `imgur`(default), `s3` or `filesystem` | Where to upload image
|
||||||
| s3 | `{ "accessKeyId": "YOUR_S3_ACCESS_KEY_ID", "secretAccessKey": "YOUR_S3_ACCESS_KEY", "region": "YOUR_S3_REGION", "bucket": "YOUR_S3_BUCKET_NAME" }` | When `imageUploadType` be setted to `s3`, you would also need to setup this key, check our [S3 Image Upload Guide](docs/guides/s3-image-upload.md) |
|
| s3 | `{ "accessKeyId": "YOUR_S3_ACCESS_KEY_ID", "secretAccessKey": "YOUR_S3_ACCESS_KEY", "region": "YOUR_S3_REGION", "bucket": "YOUR_S3_BUCKET_NAME" }` | When `imageUploadType` be setted to `s3`, you would also need to setup this key, check our [S3 Image Upload Guide](docs/guides/s3-image-upload.md) |
|
||||||
|
|
||||||
|
|
47
app.js
47
app.js
|
@ -17,6 +17,8 @@ var morgan = require('morgan');
|
||||||
var passportSocketIo = require("passport.socketio");
|
var passportSocketIo = require("passport.socketio");
|
||||||
var helmet = require('helmet');
|
var helmet = require('helmet');
|
||||||
var i18n = require('i18n');
|
var i18n = require('i18n');
|
||||||
|
var flash = require('connect-flash');
|
||||||
|
var validator = require('validator');
|
||||||
|
|
||||||
//core
|
//core
|
||||||
var config = require("./lib/config.js");
|
var config = require("./lib/config.js");
|
||||||
|
@ -145,6 +147,8 @@ app.use(function (req, res, next) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.use(flash());
|
||||||
|
|
||||||
//passport
|
//passport
|
||||||
app.use(passport.initialize());
|
app.use(passport.initialize());
|
||||||
app.use(passport.session());
|
app.use(passport.session());
|
||||||
|
@ -362,6 +366,47 @@ if (config.google) {
|
||||||
failureRedirect: config.serverurl + '/'
|
failureRedirect: config.serverurl + '/'
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
// email auth
|
||||||
|
if (config.email) {
|
||||||
|
app.post('/register', urlencodedParser, function (req, res, next) {
|
||||||
|
if (!req.body.email || !req.body.password) return response.errorBadRequest(res);
|
||||||
|
if (!validator.isEmail(req.body.email)) return response.errorBadRequest(res);
|
||||||
|
models.User.findOrCreate({
|
||||||
|
where: {
|
||||||
|
email: req.body.email
|
||||||
|
},
|
||||||
|
defaults: {
|
||||||
|
password: req.body.password
|
||||||
|
}
|
||||||
|
}).spread(function (user, created) {
|
||||||
|
if (user) {
|
||||||
|
if (created) {
|
||||||
|
if (config.debug) logger.info('user registered: ' + user.id);
|
||||||
|
req.flash('info', "You've successfully registered, please signin.");
|
||||||
|
} else {
|
||||||
|
if (config.debug) logger.info('user found: ' + user.id);
|
||||||
|
req.flash('error', "This email has been used, please try another one.");
|
||||||
|
}
|
||||||
|
return res.redirect(config.serverurl + '/');
|
||||||
|
}
|
||||||
|
req.flash('error', "Failed to register your account, please try again.");
|
||||||
|
return res.redirect(config.serverurl + '/');
|
||||||
|
}).catch(function (err) {
|
||||||
|
logger.error('auth callback failed: ' + err);
|
||||||
|
return response.errorInternalError(res);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
app.post('/login', urlencodedParser, function (req, res, next) {
|
||||||
|
if (!req.body.email || !req.body.password) return response.errorBadRequest(res);
|
||||||
|
if (!validator.isEmail(req.body.email)) return response.errorBadRequest(res);
|
||||||
|
setReturnToFromReferer(req);
|
||||||
|
passport.authenticate('local', {
|
||||||
|
successReturnToOrRedirect: config.serverurl + '/',
|
||||||
|
failureRedirect: config.serverurl + '/',
|
||||||
|
failureFlash: 'Invalid email or password.'
|
||||||
|
})(req, res, next);
|
||||||
|
});
|
||||||
|
}
|
||||||
//logout
|
//logout
|
||||||
app.get('/logout', function (req, res) {
|
app.get('/logout', function (req, res) {
|
||||||
if (config.debug && req.isAuthenticated())
|
if (config.debug && req.isAuthenticated())
|
||||||
|
@ -389,7 +434,7 @@ app.get('/me', function (req, res) {
|
||||||
}).then(function (user) {
|
}).then(function (user) {
|
||||||
if (!user)
|
if (!user)
|
||||||
return response.errorNotFound(res);
|
return response.errorNotFound(res);
|
||||||
var profile = models.User.parseProfile(user.profile);
|
var profile = models.User.getProfile(user);
|
||||||
res.send({
|
res.send({
|
||||||
status: 'ok',
|
status: 'ok',
|
||||||
id: req.user.id,
|
id: req.user.id,
|
||||||
|
|
27
lib/auth.js
27
lib/auth.js
|
@ -7,6 +7,8 @@ var GithubStrategy = require('passport-github').Strategy;
|
||||||
var GitlabStrategy = require('passport-gitlab2').Strategy;
|
var GitlabStrategy = require('passport-gitlab2').Strategy;
|
||||||
var DropboxStrategy = require('passport-dropbox-oauth2').Strategy;
|
var DropboxStrategy = require('passport-dropbox-oauth2').Strategy;
|
||||||
var GoogleStrategy = require('passport-google-oauth20').Strategy;
|
var GoogleStrategy = require('passport-google-oauth20').Strategy;
|
||||||
|
var LocalStrategy = require('passport-local').Strategy;
|
||||||
|
var validator = require('validator');
|
||||||
|
|
||||||
//core
|
//core
|
||||||
var config = require('./config.js');
|
var config = require('./config.js');
|
||||||
|
@ -35,12 +37,10 @@ function callback(accessToken, refreshToken, profile, done) {
|
||||||
if (user.accessToken != accessToken) {
|
if (user.accessToken != accessToken) {
|
||||||
user.accessToken = accessToken;
|
user.accessToken = accessToken;
|
||||||
needSave = true;
|
needSave = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
if (user.refreshToken != refreshToken) {
|
if (user.refreshToken != refreshToken) {
|
||||||
user.refreshToken = refreshToken;
|
user.refreshToken = refreshToken;
|
||||||
needSave = true;
|
needSave = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
if (needSave) {
|
if (needSave) {
|
||||||
user.save().then(function () {
|
user.save().then(function () {
|
||||||
|
@ -57,7 +57,7 @@ function callback(accessToken, refreshToken, profile, done) {
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
logger.error('auth callback failed: ' + err);
|
logger.error('auth callback failed: ' + err);
|
||||||
return done(err, null);
|
return done(err, null);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//facebook
|
//facebook
|
||||||
|
@ -109,4 +109,25 @@ if (config.google) {
|
||||||
clientSecret: config.google.clientSecret,
|
clientSecret: config.google.clientSecret,
|
||||||
callbackURL: config.serverurl + '/auth/google/callback'
|
callbackURL: config.serverurl + '/auth/google/callback'
|
||||||
}, callback));
|
}, callback));
|
||||||
|
}
|
||||||
|
// email
|
||||||
|
if (config.email) {
|
||||||
|
passport.use(new LocalStrategy({
|
||||||
|
usernameField: 'email'
|
||||||
|
},
|
||||||
|
function(email, password, done) {
|
||||||
|
if (!validator.isEmail(email)) return done(null, false);
|
||||||
|
models.User.findOne({
|
||||||
|
where: {
|
||||||
|
email: email
|
||||||
|
}
|
||||||
|
}).then(function (user) {
|
||||||
|
if (!user) return done(null, false);
|
||||||
|
if (!user.verifyPassword(password)) return done(null, false);
|
||||||
|
return done(null, user);
|
||||||
|
}).catch(function (err) {
|
||||||
|
logger.error(err);
|
||||||
|
return done(err);
|
||||||
|
});
|
||||||
|
}));
|
||||||
}
|
}
|
|
@ -94,6 +94,7 @@ var google = (process.env.HMD_GOOGLE_CLIENTID && process.env.HMD_GOOGLE_CLIENTSE
|
||||||
clientSecret: process.env.HMD_GOOGLE_CLIENTSECRET
|
clientSecret: process.env.HMD_GOOGLE_CLIENTSECRET
|
||||||
} : config.google || false;
|
} : config.google || false;
|
||||||
var imgur = process.env.HMD_IMGUR_CLIENTID || config.imgur || false;
|
var imgur = process.env.HMD_IMGUR_CLIENTID || config.imgur || false;
|
||||||
|
var email = process.env.HMD_EMAIL || config.email || false;
|
||||||
|
|
||||||
function getserverurl() {
|
function getserverurl() {
|
||||||
var url = '';
|
var url = '';
|
||||||
|
@ -151,6 +152,7 @@ module.exports = {
|
||||||
dropbox: dropbox,
|
dropbox: dropbox,
|
||||||
google: google,
|
google: google,
|
||||||
imgur: imgur,
|
imgur: imgur,
|
||||||
|
email: email,
|
||||||
imageUploadType: imageUploadType,
|
imageUploadType: imageUploadType,
|
||||||
s3: s3,
|
s3: s3,
|
||||||
s3bucket: s3bucket
|
s3bucket: s3bucket
|
||||||
|
|
13
lib/migrations/20161201050312-support-email-signin.js
Normal file
13
lib/migrations/20161201050312-support-email-signin.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
up: function (queryInterface, Sequelize) {
|
||||||
|
queryInterface.addColumn('Users', 'email', Sequelize.TEXT);
|
||||||
|
queryInterface.addColumn('Users', 'password', Sequelize.TEXT);
|
||||||
|
},
|
||||||
|
|
||||||
|
down: function (queryInterface, Sequelize) {
|
||||||
|
queryInterface.removeColumn('Users', 'email', Sequelize.TEXT);
|
||||||
|
queryInterface.removeColumn('Users', 'password', Sequelize.TEXT);
|
||||||
|
}
|
||||||
|
};
|
|
@ -3,6 +3,7 @@
|
||||||
// external modules
|
// external modules
|
||||||
var md5 = require("blueimp-md5");
|
var md5 = require("blueimp-md5");
|
||||||
var Sequelize = require("sequelize");
|
var Sequelize = require("sequelize");
|
||||||
|
var scrypt = require('scrypt');
|
||||||
|
|
||||||
// core
|
// core
|
||||||
var logger = require("../logger.js");
|
var logger = require("../logger.js");
|
||||||
|
@ -29,8 +30,30 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
},
|
},
|
||||||
refreshToken: {
|
refreshToken: {
|
||||||
type: DataTypes.STRING
|
type: DataTypes.STRING
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
type: Sequelize.TEXT,
|
||||||
|
validate: {
|
||||||
|
isEmail: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
type: Sequelize.TEXT,
|
||||||
|
set: function(value) {
|
||||||
|
var hash = scrypt.kdfSync(value, scrypt.paramsSync(0.1)).toString("hex");
|
||||||
|
this.setDataValue('password', hash);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
|
instanceMethods: {
|
||||||
|
verifyPassword: function(attempt) {
|
||||||
|
if (scrypt.verifyKdfSync(new Buffer(this.password, "hex"), attempt)) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
classMethods: {
|
classMethods: {
|
||||||
associate: function (models) {
|
associate: function (models) {
|
||||||
User.hasMany(models.Note, {
|
User.hasMany(models.Note, {
|
||||||
|
@ -42,6 +65,9 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
constraints: false
|
constraints: false
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
getProfile: function (user) {
|
||||||
|
return user.profile ? User.parseProfile(user.profile) : (user.email ? User.parseProfileByEmail(user.email) : null);
|
||||||
|
},
|
||||||
parseProfile: function (profile) {
|
parseProfile: function (profile) {
|
||||||
try {
|
try {
|
||||||
var profile = JSON.parse(profile);
|
var profile = JSON.parse(profile);
|
||||||
|
@ -81,6 +107,13 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return photo;
|
return photo;
|
||||||
|
},
|
||||||
|
parseProfileByEmail: function (email) {
|
||||||
|
var photoUrl = 'https://www.gravatar.com/avatar/' + md5(email);
|
||||||
|
return {
|
||||||
|
name: email.substring(0, email.lastIndexOf("@")),
|
||||||
|
photo: photoUrl += '?s=96'
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -131,7 +131,7 @@ function updateNote(note, callback) {
|
||||||
}
|
}
|
||||||
}).then(function (user) {
|
}).then(function (user) {
|
||||||
if (!user) return callback(null, null);
|
if (!user) return callback(null, null);
|
||||||
note.lastchangeuserprofile = models.User.parseProfile(user.profile);
|
note.lastchangeuserprofile = models.User.getProfile(user);
|
||||||
return finishUpdateNote(note, _note, callback);
|
return finishUpdateNote(note, _note, callback);
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
|
@ -455,10 +455,10 @@ function startConnection(socket) {
|
||||||
return failConnection(404, 'note not found', socket);
|
return failConnection(404, 'note not found', socket);
|
||||||
}
|
}
|
||||||
var owner = note.ownerId;
|
var owner = note.ownerId;
|
||||||
var ownerprofile = note.owner ? models.User.parseProfile(note.owner.profile) : null;
|
var ownerprofile = note.owner ? models.User.getProfile(note.owner) : null;
|
||||||
|
|
||||||
var lastchangeuser = note.lastchangeuserId;
|
var lastchangeuser = note.lastchangeuserId;
|
||||||
var lastchangeuserprofile = note.lastchangeuser ? models.User.parseProfile(note.lastchangeuser.profile) : null;
|
var lastchangeuserprofile = note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null;
|
||||||
|
|
||||||
var body = LZString.decompressFromBase64(note.content);
|
var body = LZString.decompressFromBase64(note.content);
|
||||||
var createtime = note.createdAt;
|
var createtime = note.createdAt;
|
||||||
|
@ -468,7 +468,7 @@ function startConnection(socket) {
|
||||||
var authors = {};
|
var authors = {};
|
||||||
for (var i = 0; i < note.authors.length; i++) {
|
for (var i = 0; i < note.authors.length; i++) {
|
||||||
var author = note.authors[i];
|
var author = note.authors[i];
|
||||||
var profile = models.User.parseProfile(author.user.profile);
|
var profile = models.User.getProfile(author.user);
|
||||||
authors[author.userId] = {
|
authors[author.userId] = {
|
||||||
userid: author.userId,
|
userid: author.userId,
|
||||||
color: author.color,
|
color: author.color,
|
||||||
|
@ -598,7 +598,7 @@ function buildUserOutData(user) {
|
||||||
function updateUserData(socket, user) {
|
function updateUserData(socket, user) {
|
||||||
//retrieve user data from passport
|
//retrieve user data from passport
|
||||||
if (socket.request.user && socket.request.user.logged_in) {
|
if (socket.request.user && socket.request.user.logged_in) {
|
||||||
var profile = models.User.parseProfile(socket.request.user.profile);
|
var profile = models.User.getProfile(socket.request.user);
|
||||||
user.photo = profile.photo;
|
user.photo = profile.photo;
|
||||||
user.name = profile.name;
|
user.name = profile.name;
|
||||||
user.userid = socket.request.user.id;
|
user.userid = socket.request.user.id;
|
||||||
|
|
|
@ -66,7 +66,10 @@ function showIndex(req, res, next) {
|
||||||
gitlab: config.gitlab,
|
gitlab: config.gitlab,
|
||||||
dropbox: config.dropbox,
|
dropbox: config.dropbox,
|
||||||
google: config.google,
|
google: config.google,
|
||||||
signin: req.isAuthenticated()
|
email: config.email,
|
||||||
|
signin: req.isAuthenticated(),
|
||||||
|
infoMessage: req.flash('info'),
|
||||||
|
errorMessage: req.flash('error')
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +97,8 @@ function responseHackMD(res, note) {
|
||||||
github: config.github,
|
github: config.github,
|
||||||
gitlab: config.gitlab,
|
gitlab: config.gitlab,
|
||||||
dropbox: config.dropbox,
|
dropbox: config.dropbox,
|
||||||
google: config.google
|
google: config.google,
|
||||||
|
email: config.email
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,9 +206,9 @@ function showPublishNote(req, res, next) {
|
||||||
body: body,
|
body: body,
|
||||||
useCDN: config.usecdn,
|
useCDN: config.usecdn,
|
||||||
owner: note.owner ? note.owner.id : null,
|
owner: note.owner ? note.owner.id : null,
|
||||||
ownerprofile: note.owner ? models.User.parseProfile(note.owner.profile) : null,
|
ownerprofile: note.owner ? models.User.getProfile(note.owner) : null,
|
||||||
lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
|
lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
|
||||||
lastchangeuserprofile: note.lastchangeuser ? models.User.parseProfile(note.lastchangeuser.profile) : null,
|
lastchangeuserprofile: note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null,
|
||||||
robots: meta.robots || false, //default allow robots
|
robots: meta.robots || false, //default allow robots
|
||||||
GA: meta.GA,
|
GA: meta.GA,
|
||||||
disqus: meta.disqus
|
disqus: meta.disqus
|
||||||
|
@ -591,9 +595,9 @@ function showPublishSlide(req, res, next) {
|
||||||
meta: JSON.stringify(obj.meta || {}),
|
meta: JSON.stringify(obj.meta || {}),
|
||||||
useCDN: config.usecdn,
|
useCDN: config.usecdn,
|
||||||
owner: note.owner ? note.owner.id : null,
|
owner: note.owner ? note.owner.id : null,
|
||||||
ownerprofile: note.owner ? models.User.parseProfile(note.owner.profile) : null,
|
ownerprofile: note.owner ? models.User.getProfile(note.owner) : null,
|
||||||
lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
|
lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
|
||||||
lastchangeuserprofile: note.lastchangeuser ? models.User.parseProfile(note.lastchangeuser.profile) : null,
|
lastchangeuserprofile: note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null,
|
||||||
robots: meta.robots || false, //default allow robots
|
robots: meta.robots || false, //default allow robots
|
||||||
GA: meta.GA,
|
GA: meta.GA,
|
||||||
disqus: meta.disqus
|
disqus: meta.disqus
|
||||||
|
|
|
@ -18,10 +18,12 @@
|
||||||
"blueimp-md5": "^2.4.0",
|
"blueimp-md5": "^2.4.0",
|
||||||
"body-parser": "^1.15.2",
|
"body-parser": "^1.15.2",
|
||||||
"bootstrap": "^3.3.7",
|
"bootstrap": "^3.3.7",
|
||||||
|
"bootstrap-validator": "^0.11.5",
|
||||||
"chance": "^1.0.4",
|
"chance": "^1.0.4",
|
||||||
"cheerio": "^0.22.0",
|
"cheerio": "^0.22.0",
|
||||||
"codemirror": "git+https://github.com/hackmdio/CodeMirror.git",
|
"codemirror": "git+https://github.com/hackmdio/CodeMirror.git",
|
||||||
"compression": "^1.6.2",
|
"compression": "^1.6.2",
|
||||||
|
"connect-flash": "^0.1.1",
|
||||||
"connect-session-sequelize": "^3.2.0",
|
"connect-session-sequelize": "^3.2.0",
|
||||||
"cookie": "0.3.1",
|
"cookie": "0.3.1",
|
||||||
"cookie-parser": "1.4.3",
|
"cookie-parser": "1.4.3",
|
||||||
|
@ -84,6 +86,7 @@
|
||||||
"passport-github": "^1.1.0",
|
"passport-github": "^1.1.0",
|
||||||
"passport-gitlab2": "^2.2.0",
|
"passport-gitlab2": "^2.2.0",
|
||||||
"passport-google-oauth20": "^1.0.0",
|
"passport-google-oauth20": "^1.0.0",
|
||||||
|
"passport-local": "^1.0.0",
|
||||||
"passport-twitter": "^1.0.4",
|
"passport-twitter": "^1.0.4",
|
||||||
"passport.socketio": "^3.6.2",
|
"passport.socketio": "^3.6.2",
|
||||||
"pdfobject": "^2.0.201604172",
|
"pdfobject": "^2.0.201604172",
|
||||||
|
@ -95,6 +98,7 @@
|
||||||
"request": "^2.75.0",
|
"request": "^2.75.0",
|
||||||
"reveal.js": "^3.3.0",
|
"reveal.js": "^3.3.0",
|
||||||
"sequelize": "^3.24.3",
|
"sequelize": "^3.24.3",
|
||||||
|
"scrypt": "^6.0.3",
|
||||||
"select2": "^3.5.2-browserify",
|
"select2": "^3.5.2-browserify",
|
||||||
"sequelize-cli": "^2.4.0",
|
"sequelize-cli": "^2.4.0",
|
||||||
"sharp": "^0.16.2",
|
"sharp": "^0.16.2",
|
||||||
|
@ -109,6 +113,7 @@
|
||||||
"to-markdown": "^3.0.1",
|
"to-markdown": "^3.0.1",
|
||||||
"toobusy-js": "^0.5.1",
|
"toobusy-js": "^0.5.1",
|
||||||
"uws": "^0.11.0",
|
"uws": "^0.11.0",
|
||||||
|
"validator": "^6.2.0",
|
||||||
"velocity-animate": "^1.3.1",
|
"velocity-animate": "^1.3.1",
|
||||||
"visibilityjs": "^1.2.4",
|
"visibilityjs": "^1.2.4",
|
||||||
"viz.js": "^1.3.0",
|
"viz.js": "^1.3.0",
|
||||||
|
|
|
@ -305,6 +305,9 @@ input {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
.modal-body {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-file {
|
.btn-file {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/highlight.min.js" integrity="sha256-s63qpgPYoQk+wv3U6WZqioVJrwFNBTgD4dkeegLuwvo=" crossorigin="anonymous" defer></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/highlight.min.js" integrity="sha256-s63qpgPYoQk+wv3U6WZqioVJrwFNBTgD4dkeegLuwvo=" crossorigin="anonymous" defer></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/gist-embed/2.6.0/gist-embed.min.js" integrity="sha256-KyF2D6xPIJUW5sUDSs93vWyZm+1RzIpKCexxElmxl8g=" crossorigin="anonymous" defer></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/gist-embed/2.6.0/gist-embed.min.js" integrity="sha256-KyF2D6xPIJUW5sUDSs93vWyZm+1RzIpKCexxElmxl8g=" crossorigin="anonymous" defer></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/viz.js/1.3.0/viz.js" integrity="sha256-FGmk+pMdOeRk4xTJ168rnGms1KjYi6jYMYaNEmSsvuQ=" crossorigin="anonymous" defer></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/viz.js/1.3.0/viz.js" integrity="sha256-FGmk+pMdOeRk4xTJ168rnGms1KjYi6jYMYaNEmSsvuQ=" crossorigin="anonymous" defer></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/1000hz-bootstrap-validator/0.11.5/validator.min.js" integrity="sha256-IxYUmOOk74FUrcx5FEMOHVmTJDb7ZAwnC/ivo/OQGxg=" crossorigin="anonymous" defer></script>
|
||||||
<%- include build/index-scripts %>
|
<%- include build/index-scripts %>
|
||||||
<% } else { %>
|
<% } else { %>
|
||||||
<script src="<%- url %>/build/MathJax/MathJax.js" defer></script>
|
<script src="<%- url %>/build/MathJax/MathJax.js" defer></script>
|
||||||
|
|
|
@ -51,6 +51,12 @@
|
||||||
<p class="lead">
|
<p class="lead">
|
||||||
<%= __('Best way to write and share your knowledge in markdown.') %>
|
<%= __('Best way to write and share your knowledge in markdown.') %>
|
||||||
</p>
|
</p>
|
||||||
|
<% if (infoMessage && infoMessage.length > 0) { %>
|
||||||
|
<div class="alert alert-info" style="max-width: 400px; margin: 0 auto;"><%= infoMessage %></div>
|
||||||
|
<% } %>
|
||||||
|
<% if (errorMessage && errorMessage.length > 0) { %>
|
||||||
|
<div class="alert alert-danger" style="max-width: 400px; margin: 0 auto;"><%= errorMessage %></div>
|
||||||
|
<% } %>
|
||||||
<% if(facebook || twitter || github || gitlab || dropbox || google) { %>
|
<% if(facebook || twitter || github || gitlab || dropbox || google) { %>
|
||||||
<span class="ui-signin">
|
<span class="ui-signin">
|
||||||
<br>
|
<br>
|
||||||
|
@ -195,6 +201,7 @@
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/3.5.2/select2.min.js" integrity="sha256-HzzZFiY4t0PIv02Tm8/R3CVvLpcjHhO1z/YAUCp4oQ4=" crossorigin="anonymous" defer></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/3.5.2/select2.min.js" integrity="sha256-HzzZFiY4t0PIv02Tm8/R3CVvLpcjHhO1z/YAUCp4oQ4=" crossorigin="anonymous" defer></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.15.1/moment-with-locales.min.js" integrity="sha256-J5wam9fTysK5BqYlUUBjbomFslRxkLgwB9AhnVWsj1Q=" crossorigin="anonymous" defer></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.15.1/moment-with-locales.min.js" integrity="sha256-J5wam9fTysK5BqYlUUBjbomFslRxkLgwB9AhnVWsj1Q=" crossorigin="anonymous" defer></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-url/2.3.0/url.min.js" integrity="sha256-HOZJz4x+1mn1Si84WT5XKXPtOlTytmZLnMb6n1v4+5Q=" crossorigin="anonymous" defer></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-url/2.3.0/url.min.js" integrity="sha256-HOZJz4x+1mn1Si84WT5XKXPtOlTytmZLnMb6n1v4+5Q=" crossorigin="anonymous" defer></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/1000hz-bootstrap-validator/0.11.5/validator.min.js" integrity="sha256-IxYUmOOk74FUrcx5FEMOHVmTJDb7ZAwnC/ivo/OQGxg=" crossorigin="anonymous" defer></script>
|
||||||
<%- include build/cover-scripts %>
|
<%- include build/cover-scripts %>
|
||||||
<% } else { %>
|
<% } else { %>
|
||||||
<%- include build/cover-pack-scripts %>
|
<%- include build/cover-pack-scripts %>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
</button>
|
</button>
|
||||||
<h4 class="modal-title" id="mySmallModalLabel"><%= __('Choose method') %></h4>
|
<h4 class="modal-title" id="mySmallModalLabel"><%= __('Choose method') %></h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body" style="text-align: center;">
|
||||||
<% if(facebook) { %>
|
<% if(facebook) { %>
|
||||||
<a href="<%- url %>/auth/facebook" class="btn btn-lg btn-block btn-social btn-facebook">
|
<a href="<%- url %>/auth/facebook" class="btn btn-lg btn-block btn-social btn-facebook">
|
||||||
<i class="fa fa-facebook"></i> <%= __('Sign in via %s', 'Facebook') %>
|
<i class="fa fa-facebook"></i> <%= __('Sign in via %s', 'Facebook') %>
|
||||||
|
@ -38,6 +38,32 @@
|
||||||
<i class="fa fa-google"></i> <%= __('Sign in via %s', 'Google') %>
|
<i class="fa fa-google"></i> <%= __('Sign in via %s', 'Google') %>
|
||||||
</a>
|
</a>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
<% if((facebook || twitter || github || gitlab || dropbox || google) && email) { %>
|
||||||
|
<hr>
|
||||||
|
<% }%>
|
||||||
|
<% if(email) { %>
|
||||||
|
<h4>Via Email</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="email" class="form-control" name="email" placeholder="Email" required>
|
||||||
|
<span class="help-block control-label with-errors" style="display: inline;"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<input type="password" class="form-control" name="password" placeholder="Password" 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-default" formaction="<%- url %>/register">Register</button>
|
||||||
|
<button type="submit" class="btn btn-primary" formaction="<%- url %>/login">Sign in</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<% }%>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -168,6 +168,7 @@ module.exports = {
|
||||||
path.join(__dirname, 'node_modules/select2/select2-bootstrap.css'),
|
path.join(__dirname, 'node_modules/select2/select2-bootstrap.css'),
|
||||||
],
|
],
|
||||||
"cover-pack": [
|
"cover-pack": [
|
||||||
|
"validator",
|
||||||
"script!listPagnation",
|
"script!listPagnation",
|
||||||
"expose?select2!select2",
|
"expose?select2!select2",
|
||||||
"expose?moment!moment",
|
"expose?moment!moment",
|
||||||
|
@ -222,6 +223,7 @@ module.exports = {
|
||||||
"index-pack": [
|
"index-pack": [
|
||||||
"expose?Spinner!spin.js",
|
"expose?Spinner!spin.js",
|
||||||
"script!jquery-ui-resizable",
|
"script!jquery-ui-resizable",
|
||||||
|
"validator",
|
||||||
"expose?jsyaml!js-yaml",
|
"expose?jsyaml!js-yaml",
|
||||||
"script!mermaid",
|
"script!mermaid",
|
||||||
"expose?moment!moment",
|
"expose?moment!moment",
|
||||||
|
|
Loading…
Reference in a new issue