注册功能

This commit is contained in:
masnn committed 2020-04-09 20:47:23 +08:00
1 parent f259106aa3
commit 6e28dd868e
8 files changed
+55 -39

No files matched your search

+3
View File
@@ -122,6 +122,9 @@ MIDDLEWARE(async (ctx, next) => {
ctx.response.type = 'application/octet-stream'; ctx.response.type = 'application/octet-stream';
ctx.redirect(ctx.setRedirect); ctx.redirect(ctx.setRedirect);
} }
} else if (ctx.setRedirect) {
ctx.response.type = 'application/octet-stream';
ctx.redirect(ctx.setRedirect);
} else { } else {
throw new NotFoundError(); throw new NotFoundError();
} }
+29 -24
View File
@@ -37,37 +37,43 @@ POST('/logout', requirePerm(PERM_LOGGEDIN), async ctx => {
ctx.session = { uid: 1 }; ctx.session = { uid: 1 };
ctx.body = {}; ctx.body = {};
}); });
GET('/register/:code', requirePerm(PERM_REGISTER_USER), async ctx => { GET('/register', requirePerm(PERM_REGISTER_USER), async ctx => {
let code = ctx.request.body.code; ctx.templateName = 'user_register.html';
let { mail } = await token.get(code, token.TYPE_REGISTRATION);
if (!mail) throw new InvalidTokenError(token.TYPE_REGISTRATION, code);
ctx.body = { mail };
}); });
GET('/register/:code', requirePerm(PERM_REGISTER_USER), async ctx => { GET('/register/:code', requirePerm(PERM_REGISTER_USER), async ctx => {
let { code, password, verify_password, uname } = ctx.request.body; ctx.templateName = 'user_register_with_code.html';
let { mail } = await token.get(code, token.TYPE_REGISTRATION); let code = ctx.params.code;
if (!mail) throw new InvalidTokenError(token.TYPE_REGISTRATION, code); let { email } = await token.get(code, token.TYPE_REGISTRATION);
if (!email) throw new InvalidTokenError(token.TYPE_REGISTRATION, code);
ctx.body = { email };
});
POST('/register/:code', requirePerm(PERM_REGISTER_USER), async ctx => {
let code = ctx.params.code;
let { password, verify_password, uname } = ctx.request.body;
let { email } = await token.get(code, token.TYPE_REGISTRATION);
if (!email) throw new InvalidTokenError(token.TYPE_REGISTRATION, code);
if (password != verify_password) throw new VerifyPasswordError(); if (password != verify_password) throw new VerifyPasswordError();
let uid = await system.incUserCounter(); let uid = await system.incUserCounter();
await user.add(uid, uname, password, mail, ctx.remote_ip); await user.create({ uid, uname, password, email, regip: ctx.request.ip });
await token.delete(code, token.TYPE_REGISTRATION); await token.delete(code, token.TYPE_REGISTRATION);
ctx.session.uid = uid; ctx.session.uid = uid;
ctx.body = {}; ctx.body = {};
ctx.setRedirect = '/';
}); });
if (options.smtp.user) { if (options.smtp.user) {
POST('/register', requirePerm(PERM_REGISTER_USER), limitRate('send_mail', 3600, 30), async ctx => { POST('/register', requirePerm(PERM_REGISTER_USER), limitRate('send_mail', 3600, 30), async ctx => {
let email = ctx.request.body.email; let email = ctx.request.body.mail;
validator.check_mail(email); validator.checkEmail(email);
if (await user.get_by_mail(email)) throw new UserAlreadyExistError(email); if (await user.getByEmail(email, true)) throw new UserAlreadyExistError(email);
let rid = await token.add(token.TYPE_REGISTRATION, options.registration_token_expire_seconds, { mail: email }); let rid = await token.add(token.TYPE_REGISTRATION, options.registration_token_expire_seconds, { email });
let m = await ctx.render('user_register_mail', { url: `/register/${rid}` }, true); let m = await ctx.render('user_register_mail', { url: `/register/${rid}` }, true);
await mail.send_mail(email, 'Sign Up', 'user_register_mail', m); await mail.sendMail(email, 'Sign Up', 'user_register_mail', m);
ctx.body = {}; ctx.body = {};
}); });
POST('/lostpass', limitRate('send_mail', 3600, 30), async ctx => { POST('/lostpass', limitRate('send_mail', 3600, 30), async ctx => {
let email = ctx.request.body.mail; let email = ctx.request.body.mail;
validator.check_mail(email); validator.checkEmail(email);
let udoc = await user.getByEmail(email); let udoc = await user.getByEmail(email);
if (!udoc) throw new UserNotFoundError(email); if (!udoc) throw new UserNotFoundError(email);
let tid = await token.add( let tid = await token.add(
@@ -76,7 +82,7 @@ if (options.smtp.user) {
{ uid: udoc._id } { uid: udoc._id }
); );
let m = await ctx.render('user_lostpass_mail', { url: `/lostpass/${tid}`, uname: udoc.uname }, true); let m = await ctx.render('user_lostpass_mail', { url: `/lostpass/${tid}`, uname: udoc.uname }, true);
await mail.send_mail(email, 'Lost Password', 'user_lostpass_mail', m); await mail.sendMail(email, 'Lost Password', 'user_lostpass_mail', m);
ctx.body = {}; ctx.body = {};
}); });
GET('/lostpass/:code', async ctx => { GET('/lostpass/:code', async ctx => {
@@ -93,19 +99,18 @@ if (options.smtp.user) {
let tdoc = await token.get(code, token.TYPE_LOSTPASS); let tdoc = await token.get(code, token.TYPE_LOSTPASS);
if (!tdoc) throw new InvalidTokenError(token.TYPE_LOSTPASS, code); if (!tdoc) throw new InvalidTokenError(token.TYPE_LOSTPASS, code);
if (password != verify_password) throw new VerifyPasswordError(); if (password != verify_password) throw new VerifyPasswordError();
await user.set_password(tdoc.uid, password); await user.setPassword(tdoc.uid, password);
await token.delete(code, token.TYPE_LOSTPASS); await token.delete(code, token.TYPE_LOSTPASS);
ctx.redirect('/'); ctx.redirect('/');
}); });
} else } else
POST('/register', requirePerm(PERM_REGISTER_USER), async ctx => { POST('/register', requirePerm(PERM_REGISTER_USER), limitRate('send_mail', 3600, 60), async ctx => {
let email = ctx.request.body.email; let email = ctx.request.body.mail;
validator.check_mail(email); validator.checkEmail(email);
if (await user.get_by_mail(email)) throw new UserAlreadyExistError(email); if (await user.getByEmail(email, true)) throw new UserAlreadyExistError(email);
let token = await token.add(token.TYPE_REGISTRATION, options.registration_token_expire_seconds, { mail: email }); let t = await token.add(token.TYPE_REGISTRATION, options.session.registration_token_expire_seconds, { email });
ctx.body = { token }; ctx.setRedirect = `/register/${t[0]}`;
}); });
/* /*
@app.route('/user/{uid:-?\d+}', 'user_detail') @app.route('/user/{uid:-?\d+}', 'user_detail')
+1 -1
View File
@@ -15,7 +15,7 @@ module.exports = {
let begin_at = new Date(cur_time - cur_time % (period_secs * 1000)); let begin_at = new Date(cur_time - cur_time % (period_secs * 1000));
let expire_at = new Date(begin_at.getTime() + period_secs * 1000); let expire_at = new Date(begin_at.getTime() + period_secs * 1000);
try { try {
await coll.find_one_and_update({ await coll.findOneAndUpdate({
ident, begin_at, expire_at, ident, begin_at, expire_at,
op: { $not: { $gte: max_operations } } op: { $not: { $gte: max_operations } }
}, { $inc: { op: 1 } }, { upsert: true }); }, { $inc: { op: 1 } }, { upsert: true });
+2 -2
View File
@@ -11,7 +11,7 @@ module.exports = {
* @param {number} tokenType type of the token. * @param {number} tokenType type of the token.
* @param {number} expireSeconds expire time, in seconds. * @param {number} expireSeconds expire time, in seconds.
* @param {object} data extra data. * @param {object} data extra data.
* @returns {string} token ID * @returns {Array} token ID, token data
*/ */
async add(tokenType, expireSeconds, data) { async add(tokenType, expireSeconds, data) {
let now = new Date(); let now = new Date();
@@ -62,7 +62,7 @@ module.exports = {
* @returns {boolean} true if deleted, or false. * @returns {boolean} true if deleted, or false.
*/ */
async delete(tokenId, tokenType) { async delete(tokenId, tokenType) {
let result = await coll.delete_one({ _id: tokenId, tokenType }); let result = await coll.deleteOne({ _id: tokenId, tokenType });
return !!result.deletedCount; return !!result.deletedCount;
}, },
+15 -10
View File
@@ -1,8 +1,9 @@
const const
{ UserNotFoundError, UserAlreadyExistError } = require('../error'),
system = require('./system'), system = require('./system'),
{ pwhash, validator } = require('../utils'), { UserNotFoundError, UserAlreadyExistError } = require('../error'),
db = require('../service/db.js'), { pwhash } = require('../utils'),
validator = require('../lib/validator'),
db = require('../service/db'),
coll = db.collection('user'), coll = db.collection('user'),
coll_role = db.collection('role'); coll_role = db.collection('role');
@@ -40,10 +41,13 @@ async function getByUname(uname) {
udoc.perm = role.perm; udoc.perm = role.perm;
return new USER(udoc); return new USER(udoc);
} }
async function getByEmail(email) { async function getByEmail(email, ignoreMissing = false) {
let emailLower = email.trim().toLowerCase(); let emailLower = email.trim().toLowerCase();
let udoc = await coll.findOne({ emailLower }); let udoc = await coll.findOne({ emailLower });
if (!udoc) throw new UserNotFoundError(email); if (!udoc) {
if (ignoreMissing) return null;
else throw new UserNotFoundError(email);
}
let role = await coll_role.findOne({ _id: udoc.role || 'default' }); let role = await coll_role.findOne({ _id: udoc.role || 'default' });
udoc.perm = role.perm; udoc.perm = role.perm;
return new USER(udoc); return new USER(udoc);
@@ -73,7 +77,7 @@ async function changePassword(uid, currentPassword, newPassword) {
$set: { salt, hash: pwhash.hash(newPassword, salt) } $set: { salt, hash: pwhash.hash(newPassword, salt) }
}); });
} }
async function create({ uid, email, uname, password, regip, perm = this.perm.PERM_DEFAULT || 0 }) { async function create({ uid, email, uname, password, regip = '127.0.0.1', role = 'default' }) {
validator.checkUname(uname); validator.checkUname(uname);
validator.checkPassword(password); validator.checkPassword(password);
validator.checkEmail(email); validator.checkEmail(email);
@@ -81,21 +85,22 @@ async function create({ uid, email, uname, password, regip, perm = this.perm.PER
if (!uid) uid = system.incUserCounter(); if (!uid) uid = system.incUserCounter();
try { try {
await coll.insertOne({ await coll.insertOne({
_id: uid,
email, email,
emailLower: email.strip().toLowerCase(), emailLower: email.trim().toLowerCase(),
uname, uname,
unameLower: uname.strip().toLowerCase(), unameLower: uname.trim().toLowerCase(),
password: pwhash.hash(password, salt), password: pwhash.hash(password, salt),
salt, salt,
regat: new Date(), regat: new Date(),
regip, regip,
loginat: new Date(), loginat: new Date(),
loginip: regip, loginip: regip,
perm, role,
gravatar: email gravatar: email
}); });
} catch (e) { } catch (e) {
throw new UserAlreadyExistError(uid, uname, email); throw new UserAlreadyExistError([uid, uname, email]);
} }
} }
+2 -1
View File
@@ -34,7 +34,8 @@ let options = {
secure: false, secure: false,
domain: '127.0.0.1', domain: '127.0.0.1',
saved_expire_seconds: 3600 * 24, saved_expire_seconds: 3600 * 24,
unsaved_expire_seconds: 600 unsaved_expire_seconds: 600,
registration_token_expire_seconds: 600
}, },
constants: { constants: {
PROBLEM_PER_PAGE: 100, PROBLEM_PER_PAGE: 100,
+1
View File
@@ -1,3 +1,4 @@
{% set page_name = "user_register" %}
{% extends "layout/immersive.html" %} {% extends "layout/immersive.html" %}
{% block content %} {% block content %}
<div class="row"><div class="columns"> <div class="row"><div class="columns">
+2 -1
View File
@@ -1,3 +1,4 @@
{% set page_name = "user_register_with_code" %}
{% extends "layout/immersive.html" %} {% extends "layout/immersive.html" %}
{% block content %} {% block content %}
<div class="immersive--content immersive--center"> <div class="immersive--content immersive--center">
@@ -6,7 +7,7 @@
<div> <div>
<label class="inverse material textbox"> <label class="inverse material textbox">
{{ _('Email') }} {{ _('Email') }}
<input type="text" value="{{ mail }}" disabled> <input type="text" value="{{ email }}" disabled>
</label> </label>
</div> </div>
<div> <div>