Handle uploads in territory descriptions (#2379)
* Remove unused parameter * Mark uploads as paid on territory create and update * Refactor upload expiry check * Check upload expiry on territory create * Include upload fees in territory create/update cost * Also check for expired uploads on edits * Find deleted uploads with one query
This commit is contained in:
parent
45acbaa4fa
commit
6d244a5de6
@ -3,6 +3,7 @@ import { notifyItemMention, notifyItemParents, notifyMention, notifyTerritorySub
|
||||
import { getItemMentions, getMentions, performBotBehavior } from './lib/item'
|
||||
import { msatsToSats, satsToMsats } from '@/lib/format'
|
||||
import { GqlInputError } from '@/lib/error'
|
||||
import { throwOnExpiredUploads } from '@/api/resolvers/upload'
|
||||
|
||||
export const anonable = true
|
||||
|
||||
@ -61,15 +62,7 @@ export async function perform (args, context) {
|
||||
const { tx, me, cost } = context
|
||||
const boostMsats = satsToMsats(boost)
|
||||
|
||||
const deletedUploads = []
|
||||
for (const uploadId of uploadIds) {
|
||||
if (!await tx.upload.findUnique({ where: { id: uploadId } })) {
|
||||
deletedUploads.push(uploadId)
|
||||
}
|
||||
}
|
||||
if (deletedUploads.length > 0) {
|
||||
throw new Error(`upload(s) ${deletedUploads.join(', ')} are expired, consider reuploading.`)
|
||||
}
|
||||
await throwOnExpiredUploads(uploadIds, { tx })
|
||||
|
||||
let invoiceData = {}
|
||||
if (invoiceId) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { PAID_ACTION_PAYMENT_METHODS, USER_ID } from '@/lib/constants'
|
||||
import { uploadFees } from '../resolvers/upload'
|
||||
import { throwOnExpiredUploads, uploadFees } from '@/api/resolvers/upload'
|
||||
import { getItemMentions, getMentions, performBotBehavior } from './lib/item'
|
||||
import { notifyItemMention, notifyMention } from '@/lib/webPush'
|
||||
import { satsToMsats } from '@/lib/format'
|
||||
@ -60,6 +60,7 @@ export async function perform (args, context) {
|
||||
const itemMentions = await getItemMentions(args, context)
|
||||
const itemUploads = uploadIds.map(id => ({ uploadId: id }))
|
||||
|
||||
await throwOnExpiredUploads(uploadIds, { tx })
|
||||
await tx.upload.updateMany({
|
||||
where: { id: { in: uploadIds } },
|
||||
data: { paid: true }
|
||||
|
@ -2,6 +2,7 @@ import { PAID_ACTION_PAYMENT_METHODS, TERRITORY_PERIOD_COST } from '@/lib/consta
|
||||
import { satsToMsats } from '@/lib/format'
|
||||
import { nextBilling } from '@/lib/territory'
|
||||
import { initialTrust } from './lib/territory'
|
||||
import { throwOnExpiredUploads, uploadFees } from '@/api/resolvers/upload'
|
||||
|
||||
export const anonable = false
|
||||
|
||||
@ -11,8 +12,9 @@ export const paymentMethods = [
|
||||
PAID_ACTION_PAYMENT_METHODS.PESSIMISTIC
|
||||
]
|
||||
|
||||
export async function getCost ({ billingType }) {
|
||||
return satsToMsats(TERRITORY_PERIOD_COST(billingType))
|
||||
export async function getCost ({ billingType, uploadIds }, { models, me }) {
|
||||
const { totalFees } = await uploadFees(uploadIds, { models, me })
|
||||
return satsToMsats(TERRITORY_PERIOD_COST(billingType) + totalFees)
|
||||
}
|
||||
|
||||
export async function perform ({ invoiceId, ...data }, { me, cost, tx }) {
|
||||
@ -21,6 +23,19 @@ export async function perform ({ invoiceId, ...data }, { me, cost, tx }) {
|
||||
const billedLastAt = new Date()
|
||||
const billPaidUntil = nextBilling(billedLastAt, billingType)
|
||||
|
||||
await throwOnExpiredUploads(data.uploadIds, { tx })
|
||||
if (data.uploadIds.length > 0) {
|
||||
await tx.upload.updateMany({
|
||||
where: {
|
||||
id: { in: data.uploadIds }
|
||||
},
|
||||
data: {
|
||||
paid: true
|
||||
}
|
||||
})
|
||||
}
|
||||
delete data.uploadIds
|
||||
|
||||
const sub = await tx.sub.create({
|
||||
data: {
|
||||
...data,
|
||||
|
@ -2,6 +2,7 @@ import { PAID_ACTION_PAYMENT_METHODS, TERRITORY_PERIOD_COST } from '@/lib/consta
|
||||
import { satsToMsats } from '@/lib/format'
|
||||
import { proratedBillingCost } from '@/lib/territory'
|
||||
import { datePivot } from '@/lib/time'
|
||||
import { throwOnExpiredUploads, uploadFees } from '@/api/resolvers/upload'
|
||||
|
||||
export const anonable = false
|
||||
|
||||
@ -11,18 +12,16 @@ export const paymentMethods = [
|
||||
PAID_ACTION_PAYMENT_METHODS.PESSIMISTIC
|
||||
]
|
||||
|
||||
export async function getCost ({ oldName, billingType }, { models }) {
|
||||
export async function getCost ({ oldName, billingType, uploadIds }, { models, me }) {
|
||||
const oldSub = await models.sub.findUnique({
|
||||
where: {
|
||||
name: oldName
|
||||
}
|
||||
})
|
||||
|
||||
const cost = proratedBillingCost(oldSub, billingType)
|
||||
if (!cost) {
|
||||
return 0n
|
||||
}
|
||||
const { totalFees } = await uploadFees(uploadIds, { models, me })
|
||||
|
||||
const cost = proratedBillingCost(oldSub, billingType) + totalFees
|
||||
return satsToMsats(cost)
|
||||
}
|
||||
|
||||
@ -63,6 +62,19 @@ export async function perform ({ oldName, invoiceId, ...data }, { me, cost, tx }
|
||||
})
|
||||
}
|
||||
|
||||
await throwOnExpiredUploads(data.uploadIds, { tx })
|
||||
if (data.uploadIds.length > 0) {
|
||||
await tx.upload.updateMany({
|
||||
where: {
|
||||
id: { in: data.uploadIds }
|
||||
},
|
||||
data: {
|
||||
paid: true
|
||||
}
|
||||
})
|
||||
}
|
||||
delete data.uploadIds
|
||||
|
||||
return await tx.sub.update({
|
||||
data,
|
||||
where: {
|
||||
|
@ -1506,7 +1506,7 @@ export const updateItem = async (parent, { sub: subName, forward, hash, hmac, ..
|
||||
item = { subName, ...item }
|
||||
item.forwardUsers = await getForwardUsers(models, forward)
|
||||
}
|
||||
item.uploadIds = uploadIdsFromText(item.text, { models })
|
||||
item.uploadIds = uploadIdsFromText(item.text)
|
||||
|
||||
// never change author of item
|
||||
item.userId = old.userId
|
||||
@ -1525,7 +1525,7 @@ export const createItem = async (parent, { forward, ...item }, { me, models, lnd
|
||||
item.userId = me ? Number(me.id) : USER_ID.anon
|
||||
|
||||
item.forwardUsers = await getForwardUsers(models, forward)
|
||||
item.uploadIds = uploadIdsFromText(item.text, { models })
|
||||
item.uploadIds = uploadIdsFromText(item.text)
|
||||
|
||||
if (item.url && !isJob(item)) {
|
||||
item.url = ensureProtocol(item.url)
|
||||
|
@ -5,6 +5,7 @@ import { viewGroup } from './growth'
|
||||
import { notifyTerritoryTransfer } from '@/lib/webPush'
|
||||
import performPaidAction from '../paidAction'
|
||||
import { GqlAuthenticationError, GqlInputError } from '@/lib/error'
|
||||
import { uploadIdsFromText } from './upload'
|
||||
|
||||
export async function getSub (parent, { name }, { models, me }) {
|
||||
if (!name) return null
|
||||
@ -210,6 +211,8 @@ export default {
|
||||
|
||||
await validateSchema(territorySchema, data, { models, me, sub: { name: data.oldName } })
|
||||
|
||||
data.uploadIds = uploadIdsFromText(data.desc)
|
||||
|
||||
if (data.oldName) {
|
||||
return await updateSub(parent, data, { me, models, lnd })
|
||||
} else {
|
||||
|
@ -54,7 +54,7 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
export function uploadIdsFromText (text, { models }) {
|
||||
export function uploadIdsFromText (text) {
|
||||
if (!text) return []
|
||||
return [...new Set([...text.matchAll(AWS_S3_URL_REGEXP)].map(m => Number(m[1])))]
|
||||
}
|
||||
@ -68,3 +68,19 @@ export async function uploadFees (s3Keys, { models, me }) {
|
||||
const totalFees = msatsToSats(totalFeesMsats)
|
||||
return { ...info, uploadFees, totalFees, totalFeesMsats }
|
||||
}
|
||||
|
||||
export async function throwOnExpiredUploads (uploadIds, { tx }) {
|
||||
if (uploadIds.length === 0) return
|
||||
|
||||
const existingUploads = await tx.upload.findMany({
|
||||
where: { id: { in: uploadIds } },
|
||||
select: { id: true }
|
||||
})
|
||||
|
||||
const existingIds = new Set(existingUploads.map(upload => upload.id))
|
||||
const deletedIds = uploadIds.filter(id => !existingIds.has(id))
|
||||
|
||||
if (deletedIds.length > 0) {
|
||||
throw new Error(`upload(s) ${deletedIds.join(', ')} are expired, consider reuploading.`)
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ export function purchasedType (sub) {
|
||||
export function proratedBillingCost (sub, newBillingType) {
|
||||
if (!sub ||
|
||||
sub.billingType === 'ONCE' ||
|
||||
sub.billingType === newBillingType.toUpperCase()) return null
|
||||
sub.billingType === newBillingType.toUpperCase()) return 0
|
||||
|
||||
return TERRITORY_PERIOD_COST(newBillingType) - TERRITORY_PERIOD_COST(purchasedType(sub))
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user