cubash-archive/models/user.js

482 lines
11 KiB
JavaScript

let bcrypt = require('bcryptjs')
let randomColor = require('randomcolor')
var passportLocalSequelize = require('passport-local-sequelize');
let pagination = require('../lib/pagination.js')
const Errors = require('../lib/errors.js')
var crypto = require("crypto");
var cryptoRandomString = require("crypto-random-string");
module.exports = (sequelize, DataTypes) => {
let User = sequelize.define('User', {
username: {
type: DataTypes.STRING(191),
unique: {
msg: 'Username is already taken, try another!',
fields: ['username']
},
validate: {
is: {
args: [/^[a-zA-Z0-9_]*$/],
msg: 'Username can only contain numbers and letters'
},
len: {
args: [3, 16],
msg: 'username must be between 3 and 16 characters'
},
isString (val) {
if(typeof val !== 'string') {
throw new sequelize.ValidationError('username must be a string')
}
},
containsNoBlankCharacters (val) {
if(/\s/g.test(val)) {
throw new sequelize.ValidationError('username can\'t contain blank characters')
}
}
}
},
email: {
type: DataTypes.TEXT,
unique: {
msg: 'This email is in use by another Kaverti account',
fields: ['email']
},
validate: {
isEmail: {
args: true,
msg: 'Email is not formatted correctly'
},
isString (val) {
if(typeof val !== 'string') {
throw new sequelize.ValidationError('email must be a string')
}
},
len: {
args: [5, 100],
msg: 'email must be between 5 and 100 characters'
}
}
},
description: {
type: DataTypes.TEXT,
validate: {
isString (val) {
if(typeof val !== 'string') {
throw new sequelize.ValidationError('description must be a string')
}
},
len: {
args: [0, 256],
msg: 'description must be less than 256 characters'
}
}
},
color: {
type: DataTypes.STRING,
defaultValue () {
return randomColor()
}
},
theme: {
type: DataTypes.STRING,
defaultValue: 'light',
values: ['light', 'dark'],
isIn: {
args: [['light', 'dark']],
msg: "Theme can only be one of the pre-defined options"
},
},
lastRewardDate: {
type: DataTypes.DATE
},
contributor: {
type: DataTypes.BOOLEAN,
defaultValue: false,
isBoolean: {
msg: "Can only be a true or false value"
}
},
userWallOptOut: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
cookieOptOut: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
hash: {
type: DataTypes.STRING,
allowNull: false,
validate: {
len: {
args: [6, 50],
msg: 'password must be between 6 and 50 characters'
},
isString (val) {
if(typeof val !== 'string') {
throw new sequelize.ValidationError('Please enter your password')
}
}
}
},
admin: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
executive: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
developerMode: {
type: DataTypes.BOOLEAN,
defaultValue: false,
validate: {
isBoolean: {
msg: 'Developer mode can only be true or false.'
}
}
},
experimentMode: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
bot: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
booster: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
koins: {
type: DataTypes.BIGINT,
defaultValue: "250"
},
currency2: {
type: DataTypes.BIGINT,
defaultValue: "0"
},
system: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
hidden: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
bodyColor: {
type: DataTypes.STRING(191),
defaultValue: false
},
headColor: {
type: DataTypes.STRING(191),
defaultValue: false
},
leftLegColor: {
type: DataTypes.STRING(191),
defaultValue: false
},
rightLegColor: {
type: DataTypes.STRING(191),
defaultValue: false
},
leftArmColor: {
type: DataTypes.STRING(191),
defaultValue: false
},
rightArmColor: {
type: DataTypes.STRING(191),
defaultValue: false
},
emailVerified: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
emailToken: {
type: DataTypes.STRING,
defaultValue: false
},
passwordResetToken: {
type: DataTypes.STRING,
required: false
},
level: {
type: DataTypes.BIGINT,
required: true,
default: 1
},
levelProgress: {
type: DataTypes.BIGINT,
required: true,
default: 25
},
passwordResetExpiry: {
type: DataTypes.DATE,
required: false
},
passwordResetOptOut: {
type: DataTypes.BOOLEAN,
default: false,
required: false
},
passwordResetEnabled: {
type: DataTypes.BOOLEAN,
required: false,
},
deleteCode: {
type: DataTypes.TEXT,
required: false,
default: false,
defaultValue: false
},
deleteEnabled: {
type: DataTypes.BOOLEAN,
default: false,
defaultValue: false
},
picture: {
type: DataTypes.TEXT('long'),
validate: {
isString (val) {
if(typeof val !== 'string') {
throw new sequelize.ValidationError('avatar must be a string')
}
}
}
}
}, {
instanceMethods: {
async rand() {
await this.update({ emailToken: cryptoRandomString({length: 250})})
},
async randPasswordReset() {
if(User) {
await this.update({ passwordResetToken: cryptoRandomString({length: 250}), deleteEnabled: true })
} else {
throw Errors.accountDoesNotExist
}
},
async randAccountDelete() {
if(User) {
await this.update({ deleteCode: cryptoRandomString({length: 1024}), deleteEnabled: true})
} else {
throw Errors.accountDoesNotExist
}
},
async emailVerify() {
await this.update({ emailVerified: true })
},
async updatePassword (currentPassword, newPassword) {
if(currentPassword === newPassword) {
throw Errors.passwordSame
} else if(typeof currentPassword !== 'string' || typeof newPassword !== 'string') {
throw new sequelize.ValidationError('Please enter your password')
}
let correctPassword = await bcrypt.compare(currentPassword, this.hash)
if(correctPassword) {
await this.update({ hash: newPassword })
} else {
throw Errors.invalidLoginCredentials
}
},
async reward () {
let ms = Date.now() - this.lastRewardDate
let dayMs = 1000*60*60*24
//Has less than 1 day passed
//since generating token?
return ms / dayMs < 1
},
async doReward () {
if(User.lastRewardDate) {
if(User.lastRewardDate.reward()) {
throw Errors.invalidToken
} else {
throw Errors.invalidPassKey
}
} else {
console.log("idk")
}
},
async recoveryUpdatePassword (password) {
if(typeof password !== 'string') {
throw new sequelize.ValidationError('Please enter your password')
}
await this.update({ hash: password, passwordResetEnabled: false })
},
/* async GenerateJWT() {
const today = new Date();
const expirationDate = new Date(today);
expirationDate.setDate(today.getDate() + 60);
let payload = {
id: this.id,
email: this.email,
username: this.username,
};
return jwt.sign(payload, "AUSDHIASDHAHDAiyrgy3476rty734we6yrgwesyufeyhurfehyrurgty7346ertg645e37t6rgyu", {
expiresIn: parseInt(expirationDate.getTime() / 1000, 10)
});
}, */
async updateEmail (emailCurrentPassword, newEmail) {
if(typeof emailCurrentPassword !== 'string' || typeof newEmail !== 'string') {
throw new sequelize.ValidationError('input must be a string')
}
let correctPassword = await bcrypt.compare(emailCurrentPassword, this.hash)
if(correctPassword) {
await this.update({ email: newEmail, emailVerified: false, emailToken: cryptoRandomString({length: 16})})
} else {
throw Errors.invalidLoginCredentials
}
},
async updateUsername (username, password) {
if(typeof username !== 'string') {
throw new sequelize.ValidationError('Please enter your new username')
}
if(typeof password !== 'string') {
throw new sequelize.ValidationError('Please enter your password')
}
let correctPassword = await bcrypt.compare(password, this.hash)
if(correctPassword) {
await this.update({username: username, koins: this.koins - 200})
} else {
throw Errors.invalidLoginCredentials
}
},
async comparePassword (password) {
return await bcrypt.compare(password, this.hash)
},
async destroyVerifyPassword (password) {
if(typeof password !== 'string') {
throw Errors.invalidLoginCredentials
}
let correctPassword = await bcrypt.compare(password, this.hash)
if(correctPassword) {
await this.destroy()
} else {
throw Errors.invalidLoginCredentials
}
},
async getMeta (limit) {
let Post = sequelize.models.Post
let meta = {}
let nextId = await pagination.getNextIdDesc(Post, { userId: this.id }, this.Posts)
if(nextId === null) {
meta.nextURL = null
meta.nextPostsCount = 0
} else {
meta.nextURL =
`/api/v1/user/${this.username}?posts=true&limit=${limit}&from=${nextId - 1}`
meta.nextPostsCount = await pagination.getNextCount(
Post, this.Posts, limit,
{ UserId: this.id },
true
)
}
return meta
},
async getWallMeta (limit) {
let Post = sequelize.models.userWall
let meta = {}
let nextId = await pagination.getNextIdDesc(Post, { userId: this.id }, this.Posts)
if(nextId === null) {
meta.nextURL = null
meta.nextPostsCount = 0
} else {
meta.nextURL =
``
meta.nextPostsCount = await pagination.getNextCount(
Post, this.Post, limit,
{ Post: this.id },
true
)
}
return meta
}
},
classMethods: {
associate (models) {
User.hasMany(models.Post)
User.hasMany(models.Thread)
User.hasMany(models.userWall)
User.belongsToMany(models.Conversation, { through: models.UserConversation })
User.belongsToMany(models.Ip, { through: 'UserIp' })
},
includeOptions (from, limit) {
let models = sequelize.models
let options = models.Post.includeOptions()
return [{
model: models.Post,
include: options,
limit,
where: { postNumber: { $gte: from } },
order: [['id', 'ASC']]
}]
},
includeWallOptions (from, limit) {
let models = sequelize.models
return [{
model: models.userWall,
limit,
where: { postNumber: { $gte: from } },
order: [['id', 'ASC']]
}]
},
async canBeUser (passkey) {
let { User, PassKey } = sequelize.models
if(User) {
if(PassKey) {
let passkey = await PassKey.findOne({ where: { passkey } })
if(passkey && PassKey.isValid()) {
await passkey.destroy()
return true
} else {
throw Errors.invalidPassKey
}
} else {
throw Errors.sequelizeValidation(sequelize, {
error: 'Invalid PassKey',
path: 'passkey'
})
}
} else {
return true
}
}
},
hooks: {
async afterValidate(user, options) {
if(user.changed('hash') && user.hash.length <= 50) {
user.hash = await bcrypt.hash(user.hash, 12)
}
options.hooks = false
return options
}
}
})
return User
}