Create skeleton of buyLeaderboardSlot cloud function.
This commit is contained in:
		
							parent
							
								
									17b9ccae83
								
							
						
					
					
						commit
						de562799f4
					
				
							
								
								
									
										84
									
								
								functions/src/buy-leaderboard-slot.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								functions/src/buy-leaderboard-slot.tsx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,84 @@
 | 
				
			||||||
 | 
					import * as functions from 'firebase-functions'
 | 
				
			||||||
 | 
					import * as admin from 'firebase-admin'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { User } from '../../common/user'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const buyLeaderboardSlot = functions
 | 
				
			||||||
 | 
					  .runWith({ minInstances: 1 })
 | 
				
			||||||
 | 
					  .https.onCall(
 | 
				
			||||||
 | 
					    async (
 | 
				
			||||||
 | 
					      data: {
 | 
				
			||||||
 | 
					        slotId: string
 | 
				
			||||||
 | 
					        reassessValue: number
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      context
 | 
				
			||||||
 | 
					    ) => {
 | 
				
			||||||
 | 
					      const userId = context?.auth?.uid
 | 
				
			||||||
 | 
					      if (!userId) return { status: 'error', message: 'Not authorized' }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Run as transaction to prevent race conditions.
 | 
				
			||||||
 | 
					      return await firestore.runTransaction(async (transaction) => {
 | 
				
			||||||
 | 
					        const userDoc = firestore.doc(`users/${userId}`)
 | 
				
			||||||
 | 
					        const userSnap = await transaction.get(userDoc)
 | 
				
			||||||
 | 
					        if (!userSnap.exists)
 | 
				
			||||||
 | 
					          return { status: 'error', message: 'User not found' }
 | 
				
			||||||
 | 
					        const user = userSnap.data() as User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const { slotId, reassessValue } = data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // TODO: find most recent purchase of slotId.
 | 
				
			||||||
 | 
					        // Fake data below:
 | 
				
			||||||
 | 
					        const prevSlotPurchase = {
 | 
				
			||||||
 | 
					          id: slotId,
 | 
				
			||||||
 | 
					          reassessValue: 100,
 | 
				
			||||||
 | 
					          userId: '',
 | 
				
			||||||
 | 
					          timestamp: 0,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (prevSlotPurchase) {
 | 
				
			||||||
 | 
					          const prevSlotUserDoc = firestore.doc(
 | 
				
			||||||
 | 
					            `users/${prevSlotPurchase.userId}`
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					          const prevSlotUserSnap = await transaction.get(prevSlotUserDoc)
 | 
				
			||||||
 | 
					          if (!prevSlotUserSnap.exists)
 | 
				
			||||||
 | 
					            return { status: 'error', message: 'Previous slot owner not found' }
 | 
				
			||||||
 | 
					          const prevSlotUser = prevSlotUserSnap.data() as User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          const timeSinceLastPurchase = Date.now() - prevSlotPurchase.timestamp
 | 
				
			||||||
 | 
					          const hoursSinceLastPurchase =
 | 
				
			||||||
 | 
					            timeSinceLastPurchase / (1000 * 60 * 60)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          const harbergerTax =
 | 
				
			||||||
 | 
					            prevSlotPurchase.reassessValue * 0.1 * hoursSinceLastPurchase
 | 
				
			||||||
 | 
					          const prevSlotUserBalance = prevSlotUser.balance - harbergerTax
 | 
				
			||||||
 | 
					          if (!isFinite(prevSlotUserBalance)) {
 | 
				
			||||||
 | 
					            throw new Error(
 | 
				
			||||||
 | 
					              'Invalid user balance for previous slot owner ' +
 | 
				
			||||||
 | 
					                prevSlotUser.username
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          transaction.update(prevSlotUserDoc, { balance: prevSlotUserBalance })
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // TODO: If no prevSlotPurchase, use a default purchase price?
 | 
				
			||||||
 | 
					        const newBalance = user.balance - prevSlotPurchase.reassessValue
 | 
				
			||||||
 | 
					        if (!isFinite(newBalance)) {
 | 
				
			||||||
 | 
					          throw new Error('Invalid user balance for ' + user.username)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        transaction.update(userDoc, { balance: newBalance })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const newSlotPurchase = {
 | 
				
			||||||
 | 
					          id: slotId,
 | 
				
			||||||
 | 
					          reassessValue,
 | 
				
			||||||
 | 
					          userId,
 | 
				
			||||||
 | 
					          timestamp: Date.now(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // TODO: save doc newSlotPurchase in some collection.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return { status: 'success', slotPurchase: newSlotPurchase }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const firestore = admin.firestore()
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user