2022-01-18 07:18:38 +00:00
|
|
|
import * as admin from 'firebase-admin'
|
2022-07-10 21:02:32 +00:00
|
|
|
import { z } from 'zod'
|
2022-08-04 18:03:02 +00:00
|
|
|
|
2022-09-14 23:17:32 +00:00
|
|
|
import { PrivateUser, User } from '../../common/user'
|
2022-08-24 17:58:32 +00:00
|
|
|
import { getUser, getUserByUsername, getValues } from './utils'
|
2022-05-15 17:39:42 +00:00
|
|
|
import { randomString } from '../../common/util/random'
|
|
|
|
import {
|
|
|
|
cleanDisplayName,
|
|
|
|
cleanUsername,
|
|
|
|
} from '../../common/util/clean-username'
|
|
|
|
import { isWhitelisted } from '../../common/envs/constants'
|
2022-07-13 21:11:22 +00:00
|
|
|
import {
|
|
|
|
CATEGORIES_GROUP_SLUG_POSTFIX,
|
|
|
|
DEFAULT_CATEGORIES,
|
|
|
|
} from '../../common/categories'
|
2022-06-21 15:01:41 +00:00
|
|
|
|
2022-06-16 15:45:44 +00:00
|
|
|
import { track } from './analytics'
|
2022-07-10 21:02:32 +00:00
|
|
|
import { APIError, newEndpoint, validate } from './api'
|
2022-09-03 00:06:48 +00:00
|
|
|
import { Group } from '../../common/group'
|
2022-08-22 12:31:30 +00:00
|
|
|
import { SUS_STARTING_BALANCE, STARTING_BALANCE } from '../../common/economy'
|
2022-09-14 23:17:32 +00:00
|
|
|
import { getDefaultNotificationPreferences } from '../../common/user-notification-preferences'
|
2022-07-10 21:02:32 +00:00
|
|
|
|
|
|
|
const bodySchema = z.object({
|
|
|
|
deviceToken: z.string().optional(),
|
|
|
|
})
|
|
|
|
|
|
|
|
const opts = { secrets: ['MAILGUN_KEY'] }
|
|
|
|
|
|
|
|
export const createuser = newEndpoint(opts, async (req, auth) => {
|
|
|
|
const { deviceToken } = validate(bodySchema, req.body)
|
|
|
|
const preexistingUser = await getUser(auth.uid)
|
|
|
|
if (preexistingUser)
|
|
|
|
throw new APIError(400, 'User already exists', { user: preexistingUser })
|
|
|
|
|
|
|
|
const fbUser = await admin.auth().getUser(auth.uid)
|
|
|
|
|
|
|
|
const email = fbUser.email
|
|
|
|
if (!isWhitelisted(email)) {
|
|
|
|
throw new APIError(400, `${email} is not whitelisted`)
|
|
|
|
}
|
|
|
|
const emailName = email?.replace(/@.*$/, '')
|
|
|
|
|
|
|
|
const rawName = fbUser.displayName || emailName || 'User' + randomString(4)
|
|
|
|
const name = cleanDisplayName(rawName)
|
|
|
|
let username = cleanUsername(name)
|
|
|
|
|
|
|
|
const sameNameUser = await getUserByUsername(username)
|
|
|
|
if (sameNameUser) {
|
|
|
|
username += randomString(4)
|
|
|
|
}
|
|
|
|
|
|
|
|
const avatarUrl = fbUser.photoURL
|
|
|
|
const deviceUsedBefore =
|
|
|
|
!deviceToken || (await isPrivateUserWithDeviceToken(deviceToken))
|
|
|
|
|
2022-07-26 01:25:23 +00:00
|
|
|
const balance = deviceUsedBefore ? SUS_STARTING_BALANCE : STARTING_BALANCE
|
2022-07-10 21:02:32 +00:00
|
|
|
|
|
|
|
const user: User = {
|
|
|
|
id: auth.uid,
|
|
|
|
name,
|
|
|
|
username,
|
|
|
|
avatarUrl,
|
|
|
|
balance,
|
|
|
|
totalDeposits: balance,
|
|
|
|
createdTime: Date.now(),
|
|
|
|
profitCached: { daily: 0, weekly: 0, monthly: 0, allTime: 0 },
|
|
|
|
creatorVolumeCached: { daily: 0, weekly: 0, monthly: 0, allTime: 0 },
|
2022-08-22 05:22:49 +00:00
|
|
|
nextLoanCached: 0,
|
2022-07-10 21:02:32 +00:00
|
|
|
followerCountCached: 0,
|
|
|
|
followedCategories: DEFAULT_CATEGORIES,
|
2022-07-30 07:50:03 +00:00
|
|
|
shouldShowWelcome: true,
|
2022-10-03 09:49:19 +00:00
|
|
|
fractionResolvedCorrectly: 1,
|
2022-10-10 20:32:29 +00:00
|
|
|
achievements: {},
|
2022-07-10 21:02:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
await firestore.collection('users').doc(auth.uid).create(user)
|
|
|
|
console.log('created user', username, 'firebase id:', auth.uid)
|
|
|
|
|
|
|
|
const privateUser: PrivateUser = {
|
|
|
|
id: auth.uid,
|
|
|
|
username,
|
|
|
|
email,
|
|
|
|
initialIpAddress: req.ip,
|
|
|
|
initialDeviceToken: deviceToken,
|
2022-09-14 23:17:32 +00:00
|
|
|
notificationPreferences: getDefaultNotificationPreferences(auth.uid),
|
2022-07-10 21:02:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
await firestore.collection('private-users').doc(auth.uid).create(privateUser)
|
2022-07-13 21:11:22 +00:00
|
|
|
await addUserToDefaultGroups(user)
|
2022-08-19 22:02:52 +00:00
|
|
|
|
2022-07-10 21:02:32 +00:00
|
|
|
await track(auth.uid, 'create user', { username }, { ip: req.ip })
|
|
|
|
|
2022-08-12 20:41:00 +00:00
|
|
|
return { user, privateUser }
|
2022-07-10 21:02:32 +00:00
|
|
|
})
|
2022-01-18 07:18:38 +00:00
|
|
|
|
|
|
|
const firestore = admin.firestore()
|
2022-01-19 03:36:46 +00:00
|
|
|
|
|
|
|
const isPrivateUserWithDeviceToken = async (deviceToken: string) => {
|
|
|
|
const snap = await firestore
|
|
|
|
.collection('private-users')
|
|
|
|
.where('initialDeviceToken', '==', deviceToken)
|
|
|
|
.get()
|
|
|
|
|
|
|
|
return !snap.empty
|
|
|
|
}
|
|
|
|
|
2022-07-26 01:25:23 +00:00
|
|
|
export const numberUsersWithIp = async (ipAddress: string) => {
|
2022-01-19 03:36:46 +00:00
|
|
|
const snap = await firestore
|
|
|
|
.collection('private-users')
|
|
|
|
.where('initialIpAddress', '==', ipAddress)
|
|
|
|
.get()
|
|
|
|
|
|
|
|
return snap.docs.length
|
|
|
|
}
|
2022-07-13 21:11:22 +00:00
|
|
|
|
|
|
|
const addUserToDefaultGroups = async (user: User) => {
|
|
|
|
for (const category of Object.values(DEFAULT_CATEGORIES)) {
|
|
|
|
const slug = category.toLowerCase() + CATEGORIES_GROUP_SLUG_POSTFIX
|
|
|
|
const groups = await getValues<Group>(
|
|
|
|
firestore.collection('groups').where('slug', '==', slug)
|
|
|
|
)
|
|
|
|
await firestore
|
2022-09-03 00:06:48 +00:00
|
|
|
.collection(`groups/${groups[0].id}/groupMembers`)
|
|
|
|
.doc(user.id)
|
|
|
|
.set({ userId: user.id, createdTime: Date.now() })
|
2022-07-13 21:11:22 +00:00
|
|
|
}
|
|
|
|
}
|