From cc4c5c5ad4b96133042e019b9d210c0a5cc71cb3 Mon Sep 17 00:00:00 2001 From: Astrian Zheng Date: Sun, 12 Jan 2025 12:59:32 +1100 Subject: [PATCH] feat: add endpoint to update payer with bearer token validation and input validation --- backend/src/app.ts | 42 +++++++++++++++++++++++++++++++++ backend/src/func/index.ts | 2 ++ backend/src/func/updatePayer.ts | 37 +++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 backend/src/func/updatePayer.ts diff --git a/backend/src/app.ts b/backend/src/app.ts index f1d49d8..45a8dad 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -101,6 +101,48 @@ app.use(route.get('/payer', async (ctx) => { ctx.body = payers })) +app.use(route.put('/payer/:id', async (ctx, id) => { + // 请求头验证 bearer token + const bearerToken = ctx.request.headers['authorization']?.split(' ') + if (!bearerToken) throw new HttpError(ErrorDescEnum.unauthorized, 401) + if (bearerToken[0] !== 'Bearer') throw new HttpError(ErrorDescEnum.unauthorized, 401) + if (!bearerToken[1]) throw new HttpError(ErrorDescEnum.unauthorized, 401) + await func.verifyBearerToken(bearerToken[1]) + + // 验证 id 必须是数字 + if (isNaN(parseInt(id))) throw new HttpError(ErrorDescEnum.invalid_field_format, 400, ['id']) + + // 验证必填字段 + // 字段缺失时 + if (!ctx.request.body) throw new HttpError(ErrorDescEnum.required_fields_missing, 400, ['name', 'address', 'email']) + + // 有字段,但为空时 + const { name, address, email, abn } = ctx.request.body + let emptyFields = [] + if (!name) emptyFields.push('name') + if (!address) emptyFields.push('address') + if (!email) emptyFields.push('email') + if (emptyFields.length > 0) throw new HttpError(ErrorDescEnum.required_fields_missing, 400, emptyFields) + + // 验证地址格式,格式应为 string[],最多两行 + if (!Array.isArray(address) || address.length > 2 || address.length <= 0) throw new HttpError(ErrorDescEnum.invalid_field_format, 400, ['address']) + + // 名字应为字符串 + if (typeof name !== 'string') throw new HttpError(ErrorDescEnum.invalid_field_format, 400, ['name']) + + // 验证邮箱格式 + const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ + if (!emailRegex.test(email)) throw new HttpError(ErrorDescEnum.invalid_field_format, 400, ['email']) + + // 验证 ABN 格式 + const abnRegex = /^\d{11}$/ + if (abn && !abnRegex.test(abn)) throw new HttpError(ErrorDescEnum.invalid_field_format, 400, ['abn']) + + // 更新付款人 + await func.updatePayer(ctx.prisma, id, name, address, email, abn) + ctx.status = 204 +})) + app.use(route.post('/invoice', async (ctx) => { // 请求头验证 bearer token const bearerToken = ctx.request.headers['authorization']?.split(' ') diff --git a/backend/src/func/index.ts b/backend/src/func/index.ts index 16d78ef..acf1226 100644 --- a/backend/src/func/index.ts +++ b/backend/src/func/index.ts @@ -4,6 +4,7 @@ import verifyBearerToken from "./verifyBearerToken" import getSpecificInvoice from "./getSpecificInvoice" import getInvoices from "./getInvoices" import getPayers from "./getPayers" +import updatePayer from "./updatePayer" export default { createPayer, @@ -12,4 +13,5 @@ export default { getSpecificInvoice, getInvoices, getPayers, + updatePayer } \ No newline at end of file diff --git a/backend/src/func/updatePayer.ts b/backend/src/func/updatePayer.ts new file mode 100644 index 0000000..f5c2c83 --- /dev/null +++ b/backend/src/func/updatePayer.ts @@ -0,0 +1,37 @@ +import { PrismaClient } from '@prisma/client' +import { ErrorDescEnum, HttpError } from '../classes/HttpError' + +export default async function updatePayer(prisma: PrismaClient, id: number, name: string, address: string[], email: string, abn?: string) { + // 验证 ABN 是否已存在 + const existingPayer = await prisma.payer.findFirst({ + where: { + payer_abn: abn + } + }) + if (existingPayer && existingPayer.payer_id !== id) { + // 验证 abn 是否已经是当前付款人的 abn + if (existingPayer.payer_id !== id) throw new HttpError(ErrorDescEnum.item_exists, 400, ['abn']) + } + + // 验证付款人是否存在 + const payerExists = await prisma.payer.findFirst({ + where: { + payer_id: id + } + }) + if (!payerExists) throw new HttpError(ErrorDescEnum.item_not_found, 404, ['payer']) + + // 更新付款人 + const payer = await prisma.payer.update({ + where: { + payer_id: id + }, + data: { + payer_name: name, + payer_address: address.join('\n'), + payer_email: email, + payer_abn: abn + } + }) + return payer +} \ No newline at end of file