const mongoose = require('mongoose'); const { Schema } = mongoose; const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); const userSchema = new Schema({ username: { type: String, required: true, unique: true }, password: { type: String, required: true }, roles: { type: [String], default: ['user'] }, }, { timestamps: true }); const User = mongoose.model('User', userSchema); const add = async ({ username, password } = {}) => { return User.findOne({ username }) .then(existing => { if (existing) { return Promise.reject(new Error('USER_EXISTS')); } return bcrypt.hash(password, 10); }) .then(hashedPassword => { const user = new User({ username, password: hashedPassword }); return user.save(); }); } const login = async ({ username, password } = {}) => { return User.findOne({ username }) .then(user => { if (!user) { return Promise.reject(new Error('USER_NOT_FOUND')); } return Promise.all([ bcrypt.compare(password, user.password), Promise.resolve(user), ]); }) .then(([isMatch, user]) => { if (!isMatch) { return Promise.reject(new Error('PASSWORD_ERROR')); } return user; }) .then(user => { // 签发 Access Token 和 Refresh Token const accessToken = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: '30m' }); const refreshToken = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: '7d' }); const userInfo = { uid: user._id.toString(), username: user.username, roles: user.roles ?? ['user'], } return { info: userInfo, accessToken, refreshToken }; }); } const refresh = async (refreshToken) => { const decoded = jwt.verify(refreshToken, process.env.JWT_SECRET); return User.findById(decoded.userId) .then(user => { if (!user) { return Promise.reject(new Error('USER_NOT_FOUND')); } // 签发新的 Access Token const newAccessToken = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: '30m' }); return newAccessToken; }); } const info = async (userId) => { return User.findById(userId).select('-password') .then(user => { if (!user) { return Promise.reject(new Error('USER_NOT_FOUND')); } return { uid: user._id.toString(), username: user.username, roles: user.roles ?? ['user'], }; }); } const init = async ({ username, password } = {}) => { return User.find() .then(users => { if (users.length) { return Promise.reject('USER_EXISTS'); } return bcrypt.hash(password, 10); }) .then(hashedPassword => { const user = new User({ username, password: hashedPassword, roles: ['admin'], }); return user.save(); }); } module.exports = { add, login, refresh, info, init };