adapt backend on database migration of UserContacts

This commit is contained in:
Claus-Peter Hübner 2022-08-26 02:28:04 +02:00
parent fde236aa43
commit 228451574d
16 changed files with 245 additions and 97 deletions

View File

@ -32,6 +32,7 @@ export class EventRegister extends EventBasicUserId {}
export class EventRedeemRegister extends EventBasicRedeem {}
export class EventInactiveAccount extends EventBasicUserId {}
export class EventSendConfirmationEmail extends EventBasicUserId {}
export class EventSendAccountMultiRegistrationEmail extends EventBasicUserId {}
export class EventConfirmationEmail extends EventBasicUserId {}
export class EventRegisterEmailKlicktipp extends EventBasicUserId {}
export class EventLogin extends EventBasicUserId {}
@ -113,6 +114,15 @@ export class Event {
return this
}
public setEventSendAccountMultiRegistrationEmail(
ev: EventSendAccountMultiRegistrationEmail,
): Event {
this.setByBasicUser(ev.userId)
this.type = EventProtocolType.SEND_ACCOUNT_MULTI_REGISTRATION_EMAIL
return this
}
public setEventConfirmationEmail(ev: EventConfirmationEmail): Event {
this.setByBasicUser(ev.userId)
this.type = EventProtocolType.CONFIRM_EMAIL

View File

@ -5,6 +5,7 @@ export enum EventProtocolType {
REDEEM_REGISTER = 'REDEEM_REGISTER',
INACTIVE_ACCOUNT = 'INACTIVE_ACCOUNT',
SEND_CONFIRMATION_EMAIL = 'SEND_CONFIRMATION_EMAIL',
SEND_ACCOUNT_MULTI_REGISTRATION_EMAIL = 'SEND_ACCOUNT_MULTI_REGISTRATION_EMAIL',
CONFIRM_EMAIL = 'CONFIRM_EMAIL',
REGISTER_EMAIL_KLICKTIPP = 'REGISTER_EMAIL_KLICKTIPP',
LOGIN = 'LOGIN',

View File

@ -13,7 +13,7 @@ export class UnconfirmedContribution {
this.date = contribution.contributionDate
this.firstName = user ? user.firstName : ''
this.lastName = user ? user.lastName : ''
this.email = user ? user.email : ''
this.email = user ? user.emailContact.email : ''
this.creation = creations
}

View File

@ -3,6 +3,7 @@ import { KlickTipp } from './KlickTipp'
import { User as dbUser } from '@entity/User'
import Decimal from 'decimal.js-light'
import { FULL_CREATION_AVAILABLE } from '../resolver/const/const'
import { UserContact } from './UserContact'
@ObjectType()
export class User {
@ -10,8 +11,9 @@ export class User {
this.id = user.id
this.gradidoID = user.gradidoID
this.alias = user.alias
// this.email = user.email
this.emailId = user.emailId
this.email = user.emailContact.email
this.emailContact = user.emailContact
this.firstName = user.firstName
this.lastName = user.lastName
this.deletedAt = user.deletedAt
@ -35,12 +37,18 @@ export class User {
gradidoID: string
@Field(() => String, { nullable: true })
alias: string
alias?: string
@Field(() => Number, { nullable: true })
emailId: number | null
// TODO privacy issue here
@Field(() => String)
email: string
@Field(() => UserContact)
emailContact: UserContact
@Field(() => String, { nullable: true })
firstName: string | null

View File

@ -6,11 +6,11 @@ import { User } from '@entity/User'
export class UserAdmin {
constructor(user: User, creation: Decimal[], hasElopage: boolean, emailConfirmationSend: string) {
this.userId = user.id
this.email = user.email
this.email = user.emailContact.email
this.firstName = user.firstName
this.lastName = user.lastName
this.creation = creation
this.emailChecked = user.emailChecked
this.emailChecked = user.emailContact.emailChecked
this.hasElopage = hasElopage
this.deletedAt = user.deletedAt
this.emailConfirmationSend = emailConfirmationSend

View File

@ -0,0 +1,56 @@
import { ObjectType, Field } from 'type-graphql'
import { UserContact as dbUserCOntact} from '@entity/UserContact'
@ObjectType()
export class UserContact {
constructor(userContact: dbUserCOntact) {
this.id = userContact.id
this.type = userContact.type
this.userId = userContact.userId
this.email = userContact.email
// this.emailVerificationCode = userContact.emailVerificationCode
this.emailOptInTypeId = userContact.emailOptInTypeId
this.emailResendCount = userContact.emailResendCount
this.emailChecked = userContact.emailChecked
this.phone = userContact.phone
this.createdAt = userContact.createdAt
this.updatedAt = userContact.updatedAt
this.deletedAt = userContact.deletedAt
}
@Field(() => Number)
id: number
@Field(() => String)
type: string
@Field(() => Number)
userId: number
@Field(() => String)
email: string
// @Field(() => BigInt, { nullable: true })
// emailVerificationCode: BigInt | null
@Field(() => Number, { nullable: true })
emailOptInTypeId: number | null
@Field(() => Number, { nullable: true })
emailResendCount: number | null
@Field(() => Boolean)
emailChecked: boolean
@Field(() => String, { nullable: true })
phone: string | null
@Field(() => Date)
createdAt: Date
@Field(() => Date, { nullable: true })
updatedAt: Date | null
@Field(() => Date, { nullable: true })
deletedAt: Date | null
}

View File

@ -44,7 +44,7 @@ import Paginated from '@arg/Paginated'
import TransactionLinkFilters from '@arg/TransactionLinkFilters'
import { Order } from '@enum/Order'
import { communityUser } from '@/util/communityUser'
import { checkOptInCode, activationLink, printTimeDuration } from './UserResolver'
import { checkEmailVerificationCode, activationLink, printTimeDuration } from './UserResolver'
import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail'
import { transactionLinkCode as contributionLinkCode } from './TransactionLinkResolver'
import CONFIG from '@/config'
@ -62,6 +62,7 @@ import {
MEMO_MAX_CHARS,
MEMO_MIN_CHARS,
} from './const/const'
import { UserContact } from '@entity/UserContact'
// const EMAIL_OPT_IN_REGISTER = 1
// const EMAIL_OPT_UNKNOWN = 3 // elopage?
@ -118,7 +119,8 @@ export class AdminResolver {
const adminUsers = await Promise.all(
users.map(async (user) => {
let emailConfirmationSend = ''
if (!user.emailChecked) {
if (!user.emailContact.emailChecked) {
/*
const emailOptIn = await LoginEmailOptIn.findOne(
{
userId: user.id,
@ -138,12 +140,18 @@ export class AdminResolver {
emailConfirmationSend = emailOptIn.createdAt.toISOString()
}
}
*/
if (user.emailContact.updatedAt) {
emailConfirmationSend = user.emailContact.updatedAt.toISOString()
} else {
emailConfirmationSend = user.emailContact.createdAt.toISOString()
}
}
const userCreations = creations.find((c) => c.id === user.id)
const adminUser = new UserAdmin(
user,
userCreations ? userCreations.creations : FULL_CREATION_AVAILABLE,
await hasElopageBuys(user.email),
await hasElopageBuys(user.emailContact.email),
emailConfirmationSend,
)
return adminUser
@ -239,24 +247,27 @@ export class AdminResolver {
@Args() { email, amount, memo, creationDate }: AdminCreateContributionArgs,
@Ctx() context: Context,
): Promise<Decimal[]> {
const user = await dbUser.findOne({ email }, { withDeleted: true })
if (!user) {
const emailContact = await UserContact.findOne({ email }, { withDeleted: true })
if (!emailContact) {
logger.error(`Could not find user with email: ${email}`)
throw new Error(`Could not find user with email: ${email}`)
}
if (user.deletedAt) {
throw new Error('This user was deleted. Cannot create a contribution.')
if (emailContact.deletedAt) {
logger.error('This emailContact was deleted. Cannot create a contribution.')
throw new Error('This emailContact was deleted. Cannot create a contribution.')
}
if (!user.emailChecked) {
if (!emailContact.emailChecked) {
logger.error('Contribution could not be saved, Email is not activated')
throw new Error('Contribution could not be saved, Email is not activated')
}
const moderator = getUser(context)
logger.trace('moderator: ', moderator.id)
const creations = await getUserCreation(user.id)
const creations = await getUserCreation(emailContact.userId)
logger.trace('creations', creations)
const creationDateObj = new Date(creationDate)
validateContribution(creations, amount, creationDateObj)
const contribution = Contribution.create()
contribution.userId = user.id
contribution.userId = emailContact.userId
contribution.amount = amount
contribution.createdAt = new Date()
contribution.contributionDate = creationDateObj
@ -267,7 +278,7 @@ export class AdminResolver {
logger.trace('contribution to save', contribution)
await Contribution.save(contribution)
return getUserCreation(user.id)
return getUserCreation(emailContact.userId)
}
@Authorized([RIGHTS.ADMIN_CREATE_CONTRIBUTIONS])
@ -303,11 +314,18 @@ export class AdminResolver {
@Args() { id, email, amount, memo, creationDate }: AdminUpdateContributionArgs,
@Ctx() context: Context,
): Promise<AdminUpdateContribution> {
const user = await dbUser.findOne({ email }, { withDeleted: true })
const emailContact = await UserContact.findOne({ email }, { withDeleted: true })
if (!emailContact) {
logger.error(`Could not find UserContact with email: ${email}`)
throw new Error(`Could not find UserContact with email: ${email}`)
}
const user = await dbUser.findOne({ id: emailContact.userId }, { withDeleted: true })
if (!user) {
throw new Error(`Could not find user with email: ${email}`)
logger.error(`Could not find User to emailContact: ${email}`)
throw new Error(`Could not find User to emailContact: ${email}`)
}
if (user.deletedAt) {
logger.error(`User was deleted (${email})`)
throw new Error(`User was deleted (${email})`)
}
@ -318,14 +336,17 @@ export class AdminResolver {
})
if (!contributionToUpdate) {
logger.error('No contribution found to given id.')
throw new Error('No contribution found to given id.')
}
if (contributionToUpdate.userId !== user.id) {
logger.error('user of the pending contribution and send user does not correspond')
throw new Error('user of the pending contribution and send user does not correspond')
}
if (contributionToUpdate.moderatorId === null) {
logger.error('An admin is not allowed to update a user contribution.')
throw new Error('An admin is not allowed to update a user contribution.')
}
@ -379,7 +400,7 @@ export class AdminResolver {
moderator: contribution.moderatorId,
firstName: user ? user.firstName : '',
lastName: user ? user.lastName : '',
email: user ? user.email : '',
email: user ? user.emailContact.email : '',
creation: creation ? creation.creations : FULL_CREATION_AVAILABLE,
}
})
@ -390,10 +411,10 @@ export class AdminResolver {
async adminDeleteContribution(@Arg('id', () => Int) id: number): Promise<boolean> {
const contribution = await Contribution.findOne(id)
if (!contribution) {
logger.error(`Contribution not found for given id: ${id}`)
throw new Error('Contribution not found for given id.')
}
contribution.contributionStatus = ContributionStatus.DELETED
await contribution.save()
const res = await contribution.softRemove()
return !!res
}
@ -406,15 +427,19 @@ export class AdminResolver {
): Promise<boolean> {
const contribution = await Contribution.findOne(id)
if (!contribution) {
logger.error(`Contribution not found for given id: ${id}`)
throw new Error('Contribution not found to given id.')
}
const moderatorUser = getUser(context)
if (moderatorUser.id === contribution.userId)
if (moderatorUser.id === contribution.userId) {
logger.error('Moderator can not confirm own contribution')
throw new Error('Moderator can not confirm own contribution')
}
const user = await dbUser.findOneOrFail({ id: contribution.userId }, { withDeleted: true })
if (user.deletedAt) throw new Error('This user was deleted. Cannot confirm a contribution.')
if (user.deletedAt) {
logger.error('This user was deleted. Cannot confirm a contribution.')
throw new Error('This user was deleted. Cannot confirm a contribution.')
}
const creations = await getUserCreation(contribution.userId, false)
validateContribution(creations, contribution.amount, contribution.contributionDate)
@ -501,6 +526,18 @@ export class AdminResolver {
@Mutation(() => Boolean)
async sendActivationEmail(@Arg('email') email: string): Promise<boolean> {
email = email.trim().toLowerCase()
const emailContact = await UserContact.findOne({ email: email })
if (!emailContact) {
logger.error(`Could not find UserContact with email: ${email}`)
throw new Error(`Could not find UserContact with email: ${email}`)
}
const user = await dbUser.findOne({ id: emailContact.userId })
if (!user) {
logger.error(`Could not find User to emailContact: ${email}`)
throw new Error(`Could not find User to emailContact: ${email}`)
}
/*
const user = await dbUser.findOneOrFail({ email: email })
// can be both types: REGISTER and RESET_PASSWORD
@ -510,23 +547,21 @@ export class AdminResolver {
})
optInCode = await checkOptInCode(optInCode, user)
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const emailSent = await sendAccountActivationEmail({
link: activationLink(optInCode),
link: activationLink(emailContact.emailVerificationCode),
firstName: user.firstName,
lastName: user.lastName,
email,
duration: printTimeDuration(CONFIG.EMAIL_CODE_VALID_TIME),
})
/* uncomment this, when you need the activation link on the console
// In case EMails are disabled log the activation link for the user
if (!emailSent) {
// eslint-disable-next-line no-console
console.log(`Account confirmation link: ${activationLink}`)
logger.info(`Account confirmation link: ${activationLink}`)
}
*/
return true
}

View File

@ -20,7 +20,7 @@ export class GdtResolver {
try {
const resultGDT = await apiGet(
`${CONFIG.GDT_API_URL}/GdtEntries/listPerEmailApi/${userEntity.email}/${currentPage}/${pageSize}/${order}`,
`${CONFIG.GDT_API_URL}/GdtEntries/listPerEmailApi/${userEntity.emailContact.email}/${currentPage}/${pageSize}/${order}`,
)
if (!resultGDT.success) {
throw new Error(resultGDT.data)
@ -37,7 +37,7 @@ export class GdtResolver {
const user = getUser(context)
try {
const resultGDTSum = await apiPost(`${CONFIG.GDT_API_URL}/GdtEntries/sumPerEmailApi`, {
email: user.email,
email: user.emailContact.email,
})
if (!resultGDTSum.success) {
throw new Error('Call not successful')

View File

@ -35,6 +35,7 @@ import Decimal from 'decimal.js-light'
import { BalanceResolver } from './BalanceResolver'
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const'
import { UserContact } from '@entity/UserContact'
export const executeTransaction = async (
amount: Decimal,
@ -148,8 +149,8 @@ export const executeTransaction = async (
senderLastName: sender.lastName,
recipientFirstName: recipient.firstName,
recipientLastName: recipient.lastName,
email: recipient.email,
senderEmail: sender.email,
email: recipient.emailContact.email,
senderEmail: sender.emailContact.email,
amount,
memo,
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW,
@ -171,7 +172,7 @@ export class TransactionResolver {
const user = getUser(context)
logger.addContext('user', user.id)
logger.info(`transactionList(user=${user.firstName}.${user.lastName}, ${user.email})`)
logger.info(`transactionList(user=${user.firstName}.${user.lastName}, ${user.emailId})`)
// find current balance
const lastTransaction = await dbTransaction.findOne(
@ -293,16 +294,22 @@ export class TransactionResolver {
}
// validate recipient user
const recipientUser = await dbUser.findOne({ email: email }, { withDeleted: true })
const emailContact = await UserContact.findOne({ email }, { withDeleted: true })
if (!emailContact) {
logger.error(`Could not find UserContact with email: ${email}`)
throw new Error(`Could not find UserContact with email: ${email}`)
}
const recipientUser = await dbUser.findOne({ id: emailContact.userId })
if (!recipientUser) {
logger.error(`recipient not known: email=${email}`)
throw new Error('recipient not known')
logger.error(`unknown recipient to UserContact: email=${email}`)
throw new Error('unknown recipient')
}
if (recipientUser.deletedAt) {
logger.error(`The recipient account was deleted: recipientUser=${recipientUser}`)
throw new Error('The recipient account was deleted')
}
if (!recipientUser.emailChecked) {
if (!emailContact.emailChecked) {
logger.error(`The recipient account is not activated: recipientUser=${recipientUser}`)
throw new Error('The recipient account is not activated')
}

View File

@ -29,6 +29,7 @@ import {
EventLogin,
EventRedeemRegister,
EventRegister,
EventSendAccountMultiRegistrationEmail,
EventSendConfirmationEmail,
} from '@/event/Event'
import { getUserCreation } from './util/creations'
@ -417,50 +418,55 @@ export class UserResolver {
)
// TODO: wrong default value (should be null), how does graphql work here? Is it an required field?
// default int publisher_id = 0;
const event = new Event()
// Validate Language (no throw)
if (!language || !isLanguage(language)) {
language = DEFAULT_LANGUAGE
}
// Validate email unique
// check if user with email still exists?
email = email.trim().toLowerCase()
const foundUser = await findUserByEmail(email)
if (await checkEmailExists(email)) {
const foundUser = await findUserByEmail(email)
logger.info(`DbUser.findOne(email=${email}) = ${foundUser}`)
// TODO we cannot use repository.count(), since it does not allow to specify if you want to include the soft deletes
// const userFound = await DbUser.findOne({ email }, { withDeleted: true })
logger.info(`DbUser.findOne(email=${email}) = ${foundUser}`)
if (foundUser) {
// ATTENTION: this logger-message will be exactly expected during tests
logger.info(`User already exists with this email=${email}`)
// TODO: this is unsecure, but the current implementation of the login server. This way it can be queried if the user with given EMail is existent.
if (foundUser) {
// ATTENTION: this logger-message will be exactly expected during tests
logger.info(`User already exists with this email=${email}`)
// TODO: this is unsecure, but the current implementation of the login server. This way it can be queried if the user with given EMail is existent.
const user = new User(communityDbUser)
user.id = sodium.randombytes_random() % (2048 * 16) // TODO: for a better faking derive id from email so that it will be always the same id when the same email comes in?
user.gradidoID = uuidv4()
user.email = email
user.firstName = firstName
user.lastName = lastName
user.language = language
user.publisherId = publisherId
logger.debug('partly faked user=' + user)
const user = new User(communityDbUser)
user.id = sodium.randombytes_random() % (2048 * 16) // TODO: for a better faking derive id from email so that it will be always the same id when the same email comes in?
user.gradidoID = uuidv4()
user.email = email
user.firstName = firstName
user.lastName = lastName
user.language = language
user.publisherId = publisherId
logger.debug('partly faked user=' + user)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const emailSent = await sendAccountMultiRegistrationEmail({
firstName,
lastName,
email,
})
const eventSendAccountMultiRegistrationEmail = new EventSendAccountMultiRegistrationEmail()
eventSendAccountMultiRegistrationEmail.userId = foundUser.id
eventProtocol.writeEvent(
event.setEventSendConfirmationEmail(eventSendAccountMultiRegistrationEmail),
)
logger.info(`sendAccountMultiRegistrationEmail of ${firstName}.${lastName} to ${email}`)
/* uncomment this, when you need the activation link on the console */
// In case EMails are disabled log the activation link for the user
if (!emailSent) {
logger.debug(`Email not send!`)
}
logger.info('createUser() faked and send multi registration mail...')
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const emailSent = await sendAccountMultiRegistrationEmail({
firstName,
lastName,
email,
})
logger.info(`sendAccountMultiRegistrationEmail of ${firstName}.${lastName} to ${email}`)
/* uncomment this, when you need the activation link on the console */
// In case EMails are disabled log the activation link for the user
if (!emailSent) {
logger.debug(`Email not send!`)
return user
}
logger.info('createUser() faked and send multi registration mail...')
return user
}
const passphrase = PassphraseGenerate()
@ -473,16 +479,11 @@ export class UserResolver {
const eventRegister = new EventRegister()
const eventRedeemRegister = new EventRedeemRegister()
const eventSendConfirmEmail = new EventSendConfirmationEmail()
// const dbEmailContact = new DbUserContact()
// dbEmailContact.email = email
const dbUser = new DbUser()
// dbUser.emailContact = dbEmailContact
let dbUser = new DbUser()
dbUser.gradidoID = gradidoID
// dbUser.email = email
dbUser.firstName = firstName
dbUser.lastName = lastName
// dbUser.emailHash = emailHash
dbUser.language = language
dbUser.publisherId = publisherId
dbUser.passphrase = passphrase.join(' ')
@ -513,22 +514,22 @@ export class UserResolver {
// loginUser.pubKey = keyPair[0]
// loginUser.privKey = encryptedPrivkey
const event = new Event()
const queryRunner = getConnection().createQueryRunner()
await queryRunner.connect()
await queryRunner.startTransaction('READ UNCOMMITTED')
try {
await queryRunner.manager.save(dbUser).catch((error) => {
dbUser = await queryRunner.manager.save(dbUser).catch((error) => {
logger.error('Error while saving dbUser', error)
throw new Error('error saving user')
})
const emailContact = newEmailContact(email, dbUser.id)
await queryRunner.manager.save(emailContact).catch((error) => {
let emailContact = newEmailContact(email, dbUser.id)
emailContact = await queryRunner.manager.save(emailContact).catch((error) => {
logger.error('Error while saving emailContact', error)
throw new Error('error saving email user contact')
})
dbUser.emailContact = emailContact
dbUser.emailId = emailContact.id
await queryRunner.manager.save(dbUser).catch((error) => {
logger.error('Error while updating dbUser', error)
throw new Error('error updating user')
@ -559,8 +560,6 @@ export class UserResolver {
eventSendConfirmEmail.userId = dbUser.id
eventProtocol.writeEvent(event.setEventSendConfirmationEmail(eventSendConfirmEmail))
/* uncomment this, when you need the activation link on the console */
// In case EMails are disabled log the activation link for the user
if (!emailSent) {
logger.debug(`Account confirmation link: ${activationLink}`)
}
@ -893,20 +892,30 @@ export class UserResolver {
}
async function findUserByEmail(email: string): Promise<DbUser> {
const dbUserContact = await DbUserContact.findOneOrFail(email, { withDeleted: true }).catch(
() => {
logger.error(`UserContact with email=${email} does not exists`)
throw new Error('No user with this credentials')
},
)
const userId = dbUserContact.userId
const dbUser = await DbUser.findOneOrFail(userId).catch(() => {
logger.error(`User with emeilContact=${email} connected per userId=${userId} does not exist`)
const dbUserContact = await DbUserContact.findOneOrFail(
{ email: email },
{ withDeleted: true },
).catch(() => {
logger.error(`UserContact with email=${email} does not exists`)
throw new Error('No user with this credentials')
})
const userId = dbUserContact.userId
const dbUser = await DbUser.findOneOrFail(userId).catch(() => {
logger.error(`User with emailContact=${email} connected per userId=${userId} does not exist`)
throw new Error('No user with this credentials')
})
dbUser.emailContact = dbUserContact
return dbUser
}
async function checkEmailExists(email: string): Promise<boolean> {
const userContact = await DbUserContact.findOne({ email: email }, { withDeleted: true })
if (userContact) {
return true
}
return false
}
/*
const isTimeExpired = (optIn: LoginEmailOptIn, duration: number): boolean => {
const timeElapsed = Date.now() - new Date(optIn.updatedAt).getTime()

View File

@ -4,9 +4,15 @@ import { User } from '@entity/User'
@EntityRepository(User)
export class UserRepository extends Repository<User> {
async findByPubkeyHex(pubkeyHex: string): Promise<User> {
return this.createQueryBuilder('user')
const user = await this.createQueryBuilder('user')
.where('hex(user.pubKey) = :pubkeyHex', { pubkeyHex })
.getOneOrFail()
/*
user.emailContact = await this.createQueryBuilder('userContact')
.where('userContact.id = :user.emailId', { user.emailId })
.getOneOrFail()
*/
return user
}
async findBySearchCriteriaPagedFiltered(

View File

@ -12,6 +12,7 @@ const communityDbUser: dbUser = {
alias: '',
// email: 'support@gradido.net',
emailContact: new UserContact(),
emailId: -1,
firstName: 'Gradido',
lastName: 'Akademie',
pubKey: Buffer.from(''),

View File

@ -30,6 +30,7 @@
import { LoginElopageBuys } from '@entity/LoginElopageBuys'
import { UserResolver } from '@/graphql/resolver/UserResolver'
import { User as dbUser } from '@entity/User'
import { UserContact as dbUserContact } from '@entity/UserContact'
export const elopageWebhook = async (req: any, res: any): Promise<void> => {
// eslint-disable-next-line no-console
@ -127,7 +128,8 @@ export const elopageWebhook = async (req: any, res: any): Promise<void> => {
}
// Do we already have such a user?
if ((await dbUser.count({ email })) !== 0) {
// if ((await dbUser.count({ email })) !== 0) {
if ((await dbUserContact.count({ email })) !== 0) {
// eslint-disable-next-line no-console
console.log(`Did not create User - already exists with email: ${email}`)
return

View File

@ -43,12 +43,12 @@ export class User extends BaseEntity {
@Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' })
email: string
*/
@OneToOne(() => UserContact, { primary: true, cascade: true })
@OneToOne(() => UserContact)
@JoinColumn({ name: 'email_id' })
emailContact: UserContact
@Column({ name: 'email_id', type: 'int', unsigned: true, nullable: true, default: null })
emailId?: number | null
emailId: number | null
@Column({
name: 'first_name',

View File

@ -1,4 +1,13 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, DeleteDateColumn } from 'typeorm'
import {
BaseEntity,
Entity,
PrimaryGeneratedColumn,
Column,
DeleteDateColumn,
OneToOne,
JoinColumn,
} from 'typeorm'
import { User } from './User'
@Entity('user_contacts', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
export class UserContact extends BaseEntity {
@ -14,6 +23,10 @@ export class UserContact extends BaseEntity {
})
type: string
@OneToOne(() => User)
@JoinColumn({ name: 'user_id' })
user: User
@Column({ name: 'user_id', type: 'int', unsigned: true, nullable: false })
userId: number

View File

@ -22,7 +22,7 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis
\`email_checked\` tinyint(4) NOT NULL DEFAULT 0,
\`phone\` varchar(255) COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
\`created_at\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
\`updated_at\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
\`updated_at\` datetime NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
\`deleted_at\` datetime NULL DEFAULT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`)