Skip to content
Snippets Groups Projects
User.js 4 KiB
Newer Older
Louis's avatar
Louis committed
const timestamps = require('./properties/timestamps')

module.exports = (sequelize, DataTypes) => {
	const Model = sequelize.define('User', Object.assign(
		{
			id: {
				type: DataTypes.UUID,
				primaryKey: true,
				defaultValue: DataTypes.UUIDV4,
			},
			name: {
				type: DataTypes.TEXT,
			},
			email: {
				type: DataTypes.TEXT,
			},
			password: {
				type: DataTypes.TEXT,
			},
			reset_token: {
				type: DataTypes.TEXT,
			},
			meta: {
				type: DataTypes.JSONB,
			}
		},
		timestamps(DataTypes),
	), {
		paranoid: true,
		tableName: 'users',
	})

	Model.getPolyIdentifier = () => 'user'
	Model.getRelationIdentifier = () => 'users'

	Model.prototype.toJSON = function userToJSON() {
		const age = this.hasOwnProperty('age') ? { age: this.age } : { }

		const meta = { ...this.meta }
		if (meta.hasOwnProperty('dob')) {
			delete meta.dob
		}

Louis's avatar
Louis committed
		return {
			id: this.id,
			name: this.name,
			email: this.email,
Louis's avatar
Louis committed
			created_at: this.created_at,
			updated_at: this.updated_at,
		}
	}

	Model.prototype.setPlaintextPassword = async function(password) {
		const crypto = require('core/utils/crypto')
		this.setDataValue('password', await crypto.hash(password))
Louis's avatar
Louis committed
	}
	Model.prototype.asToken = async function() {
		const crypto = require('core/utils/crypto')
		return await crypto.encrypt(JSON.stringify({ session: this.id }))
	}
	Model.fromToken = async function(token) {
		const crypto = require('core/utils/crypto')
		const { session } = JSON.parse(await crypto.decrypt(token))
		return Model.findOne({ where: { id: session } })
	}
Louis's avatar
Louis committed
	Model.prototype.checkPassword = async function(password) {
		const crypto = require('core/utils/crypto')
		if (this.password == null) {
			return false
		}
		return await crypto.verify(this.password, password)
	}

	Model.prototype.generateResetToken = async function() {
		const crypto = require('core/utils/crypto')
		const moment = require('moment')

		const id = this.id
		const expires = moment.utc()
			.add(1, 'hour')
			.toISOString()

		const token = await crypto.encrypt(JSON.stringify({ id, expires }))

		this.reset_token = token

		await this.save()

		return token
	}

Louis's avatar
Louis committed
	Model.createSystemUser = async function() {
		const crypto = require('core/utils/crypto')
		const user = Model.build({
			id: '00000000-0000-0000-0000-000000000000',
			name: 'System',
			email: 'louis@microhacks.co.uk',
		})
		await user.setPlaintextPassword(await crypto.secureHexString(32))
		await user.save()
		return user
	}

	Model.register = async function(args) {
		const user = Model.build(args)
		await user.setPlaintextPassword(args.password)
		await user.save()
		return user
	}

Louis's avatar
Louis committed
	Model.prototype.handleIncludes = async function(includes, loaders = null) {
		const inc = new Set(includes)
		if (inc.has('user_age')) {
			this.age = await this.getAge()
		}
	}
	
	Model.prototype.getAge = async function getUserAge() {
		const [ userAge ] = await Model.sequelize.query(`
			SELECT
				CASE
					WHEN meta -> 'dob' IS NOT NULL THEN
						age(concat_ws('/', meta -> 'dob' ->> 'day', meta -> 'dob' ->> 'month', meta -> 'dob' ->> 'year')::timestamptz)
				END AS age
			FROM users
			WHERE id = :userId
			LIMIT 1
		`, {
			replacements: { userId: this.id },
			type: sequelize.QueryTypes.SELECT,
Louis's avatar
Louis committed
		}).catch(e => console.error(e) || [null])
		return (userAge && userAge.age) ? (userAge.age.years < 18 ? 'youth' : 'adult') : 'youth'
Louis's avatar
Louis committed
}
Louis's avatar
Louis committed

	Model.associate = function defineModelAssociations(models) {
		Model.hasMany(models.AuthorizationCode, { foreignKey: 'user_id' })
		Model.hasMany(models.OAuthClient, { foreignKey: 'owner_id' })
		Model.hasMany(models.AccessToken, { foreignKey: 'user_id' })
		Model.hasMany(models.RefreshToken, { foreignKey: 'user_id' })
		Model.hasMany(models.File, { foreignKey: 'user_id' })
		Model.belongsToMany(models.File, { as: 'likes', through: 'user_file_likes', foreignKey: 'user_id', otherKey: 'file_id' })
		Model.belongsToMany(models.BundleCode, { through: 'user_bundle_codes', foreignKey: 'user_id', otherKey: 'bundle_code_id', timestamps: false })
Louis's avatar
Louis committed
	}

	Model.relations = [

	]

	return Model
}