import { Entity, PrimaryGeneratedColumn, Column, BaseEntity, OneToMany, OneToOne, JoinColumn, CreateDateColumn, MoreThanOrEqual } from "typeorm"; import bcrypt from "bcrypt"; import { MovieTicket } from "./MovieTicket"; import { MovieQuota } from "./MovieQuota"; import { IApiMovie } from "@common/api_schema"; import { DiscordAccount } from "./DiscordAccount"; @Entity() export class User extends BaseEntity { @PrimaryGeneratedColumn() id!: number; @Column() isAdmin!: boolean; @Column({ length: 50 }) name!: string; @Column({ length: 255 }) email!: string; @Column({ type: "char", length: 60 }) password!: string; @CreateDateColumn() createdAt!: Date; @OneToOne(() => MovieQuota, { nullable: true }) @JoinColumn() quota!: MovieQuota | null; @OneToMany(() => User, user => user.movieTickets) movieTickets!: MovieTicket[]; @OneToMany(() => DiscordAccount, account => account.user) discordAccounts!: DiscordAccount[]; /** * Authenticate a user and return an auth token upon success */ public static async authenticate(email: string, password: string) { let user = await User.findOne({ email }); if (user === undefined || !(await bcrypt.compare(password, user.password))) { return null; } return user; } /** * Create a new user */ public static async createUser(name: string, email: string, password: string, quota: number|null = 5) { let user = new User(); user.isAdmin = false; user.name = name; user.email = email; user.password = await bcrypt.hash(password, 8); // Create a quota if necessary if (quota !== null) { user.quota = new MovieQuota; user.quota.moviesPerWeek = quota; await user.quota.save(); } return await user.save(); } /** * Determine the user's available quota */ public async availableQuota() { let quota = await this.fetchQuota(); if (quota === null) { return null; } let oneWeekAgo = new Date(Date.now() - 1000*60*60*24*7); let numTicketsThisWeek = await MovieTicket.count({ user: this, createdAt: MoreThanOrEqual(oneWeekAgo), isCanceled: false }); return quota.moviesPerWeek - numTicketsThisWeek; } /** * Get the user's quota, fetching it if undefined */ public async fetchQuota() { if (this.quota !== undefined) { return this.quota; } let user = await User.findOne(this.id, { relations: ["quota"] }); return user.quota; } /** * Fetch active movie tickets for this user */ public async activeMovieTickets() { let tickets = await MovieTicket.find({ where: { user: this, isCanceled: false, isFulfilled: false }, relations: ["info"] }); return tickets.map(ticket => ({ isOnPlex : false, posterPath : ticket.info?.posterPath, releaseDate: ticket.info?.releaseDate, ticketId : ticket.id, title : ticket.title, tmdbId : ticket.tmdbId })); } }