Created
January 18, 2019 15:14
-
-
Save birkir/594a8cfbecb7bd5a18479de081f20483 to your computer and use it in GitHub Desktop.
@accounts/typeorm - Typeorm database implementation for @accounts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ConnectionInformations, CreateUser, DatabaseInterface, Session } from '@accounts/types'; | |
import { Repository, getRepository } from 'typeorm'; | |
import { User } from '../entity/User'; | |
import { UserEmail } from '../entity/UserEmail'; | |
import { UserService } from '../entity/UserService'; | |
import { UserSession } from '../entity/UserSession'; | |
type ISession = Session & UserSession; | |
export class Typeorm implements DatabaseInterface { | |
private userRepository: Repository<User>; | |
private serviceRepository: Repository<UserService>; | |
private emailRepository: Repository<UserEmail>; | |
private sessionRepository: Repository<UserSession>; | |
constructor() { | |
this.userRepository = getRepository(User); | |
this.serviceRepository = getRepository(UserService); | |
this.emailRepository = getRepository(UserEmail); | |
this.sessionRepository = getRepository(UserSession); | |
} | |
public async findUserByEmail(email: string): Promise<User | null> { | |
return this.userRepository.findOne({ | |
where: { email }, | |
}); | |
} | |
public async findUserByUsername(username: string): Promise<User | null> { | |
return this.userRepository.findOne({ | |
where: { username }, | |
}); | |
} | |
public async findUserById(userId: string): Promise<User | null> { | |
const user = await this.userRepository.findOne(userId); | |
if (!user) { | |
throw new Error('User not found'); | |
} | |
return user; | |
} | |
public async findUserByResetPasswordToken(token: string): Promise<User | null> { | |
const service = await this.serviceRepository.findOne({ | |
where: { | |
name: 'password.reset', | |
token, | |
}, | |
}); | |
return service.user; | |
} | |
public async findUserByEmailVerificationToken(token: string): Promise<User | null> { | |
const service = await this.serviceRepository.findOne({ | |
where: { | |
name: 'email.verification', | |
token, | |
}, | |
}); | |
return service.user; | |
} | |
public async createUser(createUser: CreateUser): Promise<string> { | |
const { username, email, password } = createUser; | |
const user = new User(); | |
if (email) { | |
const userEmail = new UserEmail(); | |
userEmail.address = email; | |
userEmail.verified = false; | |
this.emailRepository.save(userEmail); | |
user.emails = [userEmail]; | |
} | |
if (password) { | |
const userService = new UserService(); | |
userService.name = 'password'; | |
userService.options = { bcrypt: password }; | |
await this.serviceRepository.save(userService); | |
user.services = [userService]; | |
} | |
if (username) { | |
user.username = username; | |
} | |
await this.userRepository.save(user); | |
return user.id; | |
} | |
public async setUsername(userId: string, newUsername: string): Promise<void> { | |
const user = await this.findUserById(userId); | |
user.username = newUsername; | |
await this.userRepository.save(user); | |
} | |
public async setProfile(userId: string, profile: object): Promise<object> { | |
const user = await this.findUserById(userId); | |
user.profile = profile; | |
await this.userRepository.save(user); | |
return profile; | |
} | |
public async findUserByServiceId(serviceName: string, serviceId: string): Promise<User | null> { | |
const service = await this.serviceRepository.findOne({ | |
name: serviceName, | |
id: serviceId, | |
}); | |
if (service) { | |
return service.user; | |
} | |
} | |
public async setService(userId: string, serviceName: string, data: object): Promise<void> { | |
const user = await this.findUserById(userId); | |
const service = user.services.find((s) => s.name === serviceName); | |
if (service) { | |
service.options = data; | |
await this.serviceRepository.save(service); // @todo is this needed? | |
await this.userRepository.save(user); | |
} | |
} | |
public async unsetService(userId: string, serviceName: string): Promise<void> { | |
const user = await this.findUserById(userId); | |
const service = user.services && user.services.find((s) => s.name === serviceName); | |
if (service) { | |
await this.serviceRepository.remove(service); | |
await this.userRepository.save(user); | |
} | |
} | |
public async findPasswordHash(userId: string): Promise<string | null> { | |
const user = await this.findUserById(userId); | |
const service = user.services && user.services.find((s) => s.name === 'password'); | |
return service.options && service.options.bcrypt; | |
return null; | |
} | |
public async setPassword(userId: string, newPassword: string): Promise<void> { | |
const user = await this.findUserById(userId); | |
const service = user.services && user.services.find((s) => s.name === 'password'); | |
service.options = { bcrypt: newPassword }; | |
await this.serviceRepository.save(service); | |
await this.userRepository.save(user); | |
} | |
public async addResetPasswordToken(userId: string, email: string, token: string, reason: string): Promise<void> { | |
const user = await this.findUserById(userId); | |
const service = new UserService(); | |
service.name = 'password.reset'; | |
service.token = token; | |
service.options = { | |
email: email.toLocaleLowerCase(), | |
when: (new Date()).toJSON(), | |
reason, | |
}; | |
await this.serviceRepository.save(service); | |
user.services.push(service); | |
await this.userRepository.save(user); | |
} | |
public async setResetPassword(userId: string, email: string, newPassword: string, token: string): Promise<void> { | |
await this.setPassword(userId, newPassword); | |
} | |
public async addEmail(userId: string, newEmail: string, verified: boolean): Promise<void> { | |
const user = await this.findUserById(userId); | |
const userEmail = new UserEmail(); | |
userEmail.address = newEmail; | |
userEmail.verified = verified; | |
user.emails.push(userEmail); | |
await this.userRepository.save(user); | |
} | |
public async removeEmail(userId: string, email: string): Promise<void> { | |
const user = await this.findUserById(userId); | |
const userEmail = user.emails.find((s) => s.address === email); | |
if (!userEmail) { | |
throw new Error('Email not found'); | |
} | |
await this.emailRepository.remove(userEmail); | |
} | |
public async verifyEmail(userId: string, email: string): Promise<void> { | |
const user = await this.findUserById(userId); | |
const userEmail = user.emails.find((s) => s.address === email); | |
if (!userEmail) { | |
throw new Error('Email not found'); | |
} | |
userEmail.verified = true; | |
await this.emailRepository.save(userEmail); | |
} | |
public async addEmailVerificationToken(userId: string, email: string, token: string): Promise<void> { | |
const user = await this.findUserById(userId); | |
const service = new UserService(); | |
service.token = token; | |
service.name = 'email.verification'; | |
await this.serviceRepository.save(service); | |
user.services.push(service); | |
await this.userRepository.save(user); | |
} | |
public async setUserDeactivated(userId: string, deactivated: boolean): Promise<void> { | |
const user = await this.findUserById(userId); | |
user.deactivated = deactivated; | |
await this.userRepository.save(user); | |
} | |
public findSessionById(sessionId: string): Promise<Session | null> { | |
return this.sessionRepository.findOne(sessionId) as Promise<ISession>; | |
} | |
public findSessionByToken(token: string): Promise<ISession | null> { | |
return this.sessionRepository.findOne({ token }) as Promise<ISession>; | |
} | |
public async createSession( | |
userId: string, | |
token: string, | |
connection: ConnectionInformations, | |
extra?: object, | |
): Promise<string> { | |
const user = await this.findUserById(userId); | |
const session = new UserSession(); | |
session.token = token; | |
session.userAgent = connection.userAgent; | |
session.ip = connection.ip; | |
session.extra = extra; | |
session.valid = true; | |
await this.sessionRepository.save(session); | |
user.sessions.push(session); | |
await this.userRepository.save(user); | |
return session.id; | |
} | |
public async updateSession(sessionId: string, connection: ConnectionInformations): Promise<void> { | |
const session = await this.findSessionById(sessionId); | |
session.userAgent = connection.userAgent; | |
session.ip = connection.ip; | |
await this.sessionRepository.save(session); | |
} | |
public async invalidateSession(sessionId: string): Promise<void> { | |
const session = await this.findSessionById(sessionId); | |
session.valid = false; | |
await this.sessionRepository.save(session); | |
} | |
public async invalidateAllSessions(userId: string): Promise<void> { | |
const user = await this.findUserById(userId); | |
await Promise.all( | |
user.sessions.map((session) => | |
this.sessionRepository.delete(session), | |
), | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, OneToMany } from 'typeorm'; | |
import { ObjectType, Field } from 'typegql'; | |
import { GraphQLDateTime } from 'graphql-iso-date'; | |
import { UserService } from './UserService'; | |
import { UserEmail } from './UserEmail'; | |
import { UserSession } from './UserSession'; | |
@Entity() | |
@ObjectType() | |
export class User { | |
@PrimaryGeneratedColumn('uuid') | |
@Field() | |
public id: string; | |
@Column() | |
public username: string; | |
@Column('jsonb', { nullable: true }) | |
public profile: any; | |
@OneToMany(() => UserService, (userService) => userService.user, { eager: true }) | |
public services: UserService[]; | |
@OneToMany(() => UserEmail, (userEmail) => userEmail.user, { eager: true }) | |
public emails: UserEmail[]; | |
@OneToMany(() => UserSession, (userSession) => userSession.user, { eager: true }) | |
public sessions: UserSession[]; | |
@Column({ default: false }) | |
public deactivated: boolean; | |
@CreateDateColumn() | |
@Field({ type: GraphQLDateTime }) | |
public createdAt: Date; | |
@UpdateDateColumn() | |
@Field({ type: GraphQLDateTime }) | |
public updatedAt: Date; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Entity, Column, ManyToOne, Unique, PrimaryGeneratedColumn } from 'typeorm'; | |
import { User } from './User'; | |
@Entity() | |
export class UserEmail { | |
@PrimaryGeneratedColumn('uuid') | |
public id: string; | |
@ManyToOne(() => User, (user) => user.emails) | |
public user: Promise<User>; | |
@Unique(['address']) | |
@Column() | |
public address: string; | |
@Column({ default: false }) | |
public verified: boolean; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm'; | |
import { User } from './User'; | |
@Entity() | |
export class UserService { | |
@PrimaryGeneratedColumn('uuid') | |
public id: string; | |
@ManyToOne(() => User, (user) => user.services) | |
public user: Promise<User>; | |
@Column() | |
public name: string; | |
@Column({ nullable: true }) | |
public token: string; | |
@Column('jsonb') | |
public options: { bcrypt: string } | any; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Entity, Column, ManyToOne, Unique, PrimaryGeneratedColumn, UpdateDateColumn, CreateDateColumn } from 'typeorm'; | |
import { User } from './User'; | |
@Entity() | |
export class UserSession { | |
@PrimaryGeneratedColumn('uuid') | |
public id: string; | |
@ManyToOne(() => User, (user) => user.sessions) | |
public user: Promise<User>; | |
public userId: string; | |
@Column() | |
public token: string; | |
@Column() | |
public valid: boolean; | |
@Column({ nullable: true }) | |
public userAgent: string; | |
@Column({ nullable: true }) | |
public ip: string; | |
@Column('jsonb') | |
public extra: object; | |
@CreateDateColumn() | |
public createdAt: Date; | |
@UpdateDateColumn() | |
public updatedAt: Date; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment