Secure API endpoints with requireBookingManager authorization check Update confirmation page to prompt for login if unauthorized Add safe redirect handling to login and guest middleware
87 lines
2.6 KiB
TypeScript
87 lines
2.6 KiB
TypeScript
import type { UpdateBookingDetailsResponse } from '~~/shared/booking'
|
|
|
|
import { getHeader, readMultipartFormData } from 'h3'
|
|
|
|
import { requireBookingManager } from '../../../../utils/auth'
|
|
import {
|
|
getBookingByConfirmationToken,
|
|
replaceBookingTransactionDocumentByConfirmationToken
|
|
} from '../../../../utils/booking-repository'
|
|
import { getRequiredRouteParam, httpError } from '../../../../utils/http'
|
|
import {
|
|
deleteTransactionDocument,
|
|
saveTransactionDocument,
|
|
TRANSACTION_DOCUMENT_MAX_SIZE,
|
|
validateTransactionDocumentUpload
|
|
} from '../../../../utils/transaction-documents'
|
|
|
|
export default defineEventHandler(async (event): Promise<UpdateBookingDetailsResponse> => {
|
|
const token = getRequiredRouteParam(event, 'token', 'Confirmation token')
|
|
const booking = await getBookingByConfirmationToken(token, { includeTransactionDocument: true })
|
|
|
|
if (!booking) {
|
|
httpError(404, 'Booking not found')
|
|
}
|
|
|
|
await requireBookingManager(event, booking)
|
|
|
|
if (booking.status !== 'pending') {
|
|
httpError(409, 'Transaction document can only be changed before confirmation')
|
|
}
|
|
|
|
if (booking.paymentMethod !== 'bank') {
|
|
httpError(400, 'Transaction document can only be uploaded for Bank payments')
|
|
}
|
|
|
|
const contentType = String(getHeader(event, 'content-type') || '').toLowerCase()
|
|
|
|
if (!contentType.startsWith('multipart/form-data;')) {
|
|
httpError(400, 'Transaction document upload must use multipart form data')
|
|
}
|
|
|
|
const contentLength = Number(getHeader(event, 'content-length') || 0)
|
|
|
|
if (contentLength > TRANSACTION_DOCUMENT_MAX_SIZE + 1024 * 1024) {
|
|
httpError(413, 'Transaction document must be 10MB or smaller')
|
|
}
|
|
|
|
const formData = await readMultipartFormData(event)
|
|
const filePart = formData?.find((part) => part.name === 'document' && part.filename)
|
|
|
|
if (!filePart) {
|
|
httpError(400, 'Transaction document is required')
|
|
}
|
|
|
|
const upload = validateTransactionDocumentUpload({
|
|
data: filePart.data,
|
|
filename: filePart.filename,
|
|
contentType: filePart.type
|
|
})
|
|
|
|
const storageName = await saveTransactionDocument(filePart.data, upload.fileType)
|
|
|
|
try {
|
|
const result = await replaceBookingTransactionDocumentByConfirmationToken({
|
|
confirmationToken: token,
|
|
originalName: upload.originalName,
|
|
storageName,
|
|
mimeType: upload.fileType.mimeType,
|
|
size: filePart.data.length
|
|
})
|
|
|
|
if (!result) {
|
|
await deleteTransactionDocument(storageName)
|
|
httpError(404, 'Booking not found')
|
|
}
|
|
|
|
await deleteTransactionDocument(result.previousStorageName)
|
|
|
|
return {
|
|
booking: result.booking
|
|
}
|
|
} catch (error) {
|
|
await deleteTransactionDocument(storageName)
|
|
throw error
|
|
}
|
|
})
|