2022-02-04 03:04:56 +00:00
|
|
|
import * as admin from 'firebase-admin'
|
2022-07-08 22:00:03 +00:00
|
|
|
import { z } from 'zod'
|
2022-02-04 03:04:56 +00:00
|
|
|
|
2022-02-17 23:00:19 +00:00
|
|
|
import { getUser } from './utils'
|
2022-09-14 08:33:59 +00:00
|
|
|
import { Bet } from '../../common/bet'
|
2022-05-15 17:39:42 +00:00
|
|
|
import { Contract } from '../../common/contract'
|
|
|
|
import { Comment } from '../../common/comment'
|
|
|
|
import { User } from '../../common/user'
|
2022-06-18 21:31:39 +00:00
|
|
|
import {
|
|
|
|
cleanUsername,
|
|
|
|
cleanDisplayName,
|
|
|
|
} from '../../common/util/clean-username'
|
2022-05-15 17:39:42 +00:00
|
|
|
import { removeUndefinedProps } from '../../common/util/object'
|
|
|
|
import { Answer } from '../../common/answer'
|
2022-07-08 22:00:03 +00:00
|
|
|
import { APIError, newEndpoint, validate } from './api'
|
2022-02-04 03:04:56 +00:00
|
|
|
|
2022-07-08 22:00:03 +00:00
|
|
|
const bodySchema = z.object({
|
|
|
|
username: z.string().optional(),
|
|
|
|
name: z.string().optional(),
|
|
|
|
avatarUrl: z.string().optional(),
|
|
|
|
})
|
|
|
|
|
|
|
|
export const changeuserinfo = newEndpoint({}, async (req, auth) => {
|
|
|
|
const { username, name, avatarUrl } = validate(bodySchema, req.body)
|
|
|
|
|
|
|
|
const user = await getUser(auth.uid)
|
|
|
|
if (!user) throw new APIError(400, 'User not found')
|
|
|
|
|
|
|
|
await changeUser(user, { username, name, avatarUrl })
|
|
|
|
return { message: 'Successfully changed user info.' }
|
|
|
|
})
|
2022-02-04 03:04:56 +00:00
|
|
|
|
|
|
|
export const changeUser = async (
|
|
|
|
user: User,
|
|
|
|
update: {
|
|
|
|
username?: string
|
|
|
|
name?: string
|
|
|
|
avatarUrl?: string
|
|
|
|
}
|
|
|
|
) => {
|
2022-09-09 03:21:29 +00:00
|
|
|
// Update contracts, comments, and answers outside of a transaction to avoid contention.
|
|
|
|
// Using bulkWriter to supports >500 writes at a time
|
|
|
|
const contractsRef = firestore
|
|
|
|
.collection('contracts')
|
|
|
|
.where('creatorId', '==', user.id)
|
|
|
|
|
|
|
|
const contracts = await contractsRef.get()
|
|
|
|
|
|
|
|
const contractUpdate: Partial<Contract> = removeUndefinedProps({
|
|
|
|
creatorName: update.name,
|
|
|
|
creatorUsername: update.username,
|
|
|
|
creatorAvatarUrl: update.avatarUrl,
|
|
|
|
})
|
|
|
|
|
|
|
|
const commentSnap = await firestore
|
|
|
|
.collectionGroup('comments')
|
|
|
|
.where('userUsername', '==', user.username)
|
|
|
|
.get()
|
|
|
|
|
|
|
|
const commentUpdate: Partial<Comment> = removeUndefinedProps({
|
|
|
|
userName: update.name,
|
|
|
|
userUsername: update.username,
|
|
|
|
userAvatarUrl: update.avatarUrl,
|
|
|
|
})
|
|
|
|
|
|
|
|
const answerSnap = await firestore
|
|
|
|
.collectionGroup('answers')
|
|
|
|
.where('username', '==', user.username)
|
|
|
|
.get()
|
|
|
|
const answerUpdate: Partial<Answer> = removeUndefinedProps(update)
|
|
|
|
|
2022-09-14 08:33:59 +00:00
|
|
|
const betsSnap = await firestore
|
|
|
|
.collectionGroup('bets')
|
|
|
|
.where('userId', '==', user.id)
|
|
|
|
.get()
|
|
|
|
const betsUpdate: Partial<Bet> = removeUndefinedProps({
|
|
|
|
userName: update.name,
|
|
|
|
userUsername: update.username,
|
|
|
|
userAvatarUrl: update.avatarUrl,
|
|
|
|
})
|
|
|
|
|
2022-09-09 03:21:29 +00:00
|
|
|
const bulkWriter = firestore.bulkWriter()
|
|
|
|
commentSnap.docs.forEach((d) => bulkWriter.update(d.ref, commentUpdate))
|
|
|
|
answerSnap.docs.forEach((d) => bulkWriter.update(d.ref, answerUpdate))
|
|
|
|
contracts.docs.forEach((d) => bulkWriter.update(d.ref, contractUpdate))
|
2022-09-14 08:33:59 +00:00
|
|
|
betsSnap.docs.forEach((d) => bulkWriter.update(d.ref, betsUpdate))
|
2022-09-09 03:21:29 +00:00
|
|
|
await bulkWriter.flush()
|
|
|
|
console.log('Done writing!')
|
|
|
|
|
|
|
|
// Update the username inside a transaction
|
2022-02-04 03:04:56 +00:00
|
|
|
return await firestore.runTransaction(async (transaction) => {
|
|
|
|
if (update.username) {
|
|
|
|
update.username = cleanUsername(update.username)
|
|
|
|
if (!update.username) {
|
2022-07-08 22:00:03 +00:00
|
|
|
throw new APIError(400, 'Invalid username')
|
2022-02-04 03:04:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const sameNameUser = await transaction.get(
|
|
|
|
firestore.collection('users').where('username', '==', update.username)
|
|
|
|
)
|
|
|
|
if (!sameNameUser.empty) {
|
2022-07-08 22:00:03 +00:00
|
|
|
throw new APIError(400, 'Username already exists')
|
2022-02-04 03:04:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-18 21:31:39 +00:00
|
|
|
if (update.name) {
|
|
|
|
update.name = cleanDisplayName(update.name)
|
|
|
|
}
|
|
|
|
|
2022-02-04 03:04:56 +00:00
|
|
|
const userRef = firestore.collection('users').doc(user.id)
|
|
|
|
const userUpdate: Partial<User> = removeUndefinedProps(update)
|
2022-07-08 22:00:03 +00:00
|
|
|
transaction.update(userRef, userUpdate)
|
2022-02-04 03:04:56 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
const firestore = admin.firestore()
|