From 1a2241c40e10193c5ff7008a7b7b36cc1d855d96 Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Tue, 25 Mar 2025 15:55:45 +0900 Subject: initial commit --- pages/api/po/createContractFile.ts | 57 +++++++ pages/api/po/sendDocuSign.ts | 49 ++++++ pages/api/po/webhook.ts | 337 +++++++++++++++++++++++++++++++++++++ 3 files changed, 443 insertions(+) create mode 100644 pages/api/po/createContractFile.ts create mode 100644 pages/api/po/sendDocuSign.ts create mode 100644 pages/api/po/webhook.ts (limited to 'pages/api') diff --git a/pages/api/po/createContractFile.ts b/pages/api/po/createContractFile.ts new file mode 100644 index 00000000..b74c95f2 --- /dev/null +++ b/pages/api/po/createContractFile.ts @@ -0,0 +1,57 @@ +export const config = { + api: { + bodyParser: true, // ✅ 이게 false면 안 됨! + }, +}; + +import type { NextApiRequest, NextApiResponse } from "next"; +import { downloadContractFile } from "@/lib/docuSign/docuSignFns"; +import path from "path"; +import fs from "fs"; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + if (req.method !== "POST") { + return res.status(405).end(); + } + + try { + const { envelopeId, fileName, filePath } = req.body; + + if (!envelopeId) { + return res + .status(500) + .json({ success: false, message: "Not use envelopeId" }); + } + + const docuSignStart = await downloadContractFile(envelopeId); + + const { result, buffer, error } = docuSignStart; + + if (result && buffer) { + const cleanedPath = filePath.replace(/^\/+/, ""); + const dirPath = path.resolve(process.cwd(), "public", cleanedPath); // 예: 'contracts/185/signatures' + const fullFilePath = path.join(dirPath, fileName); // 예: 'contracts/185/signatures/xxx.pdf' + + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); + } + + fs.writeFileSync(fullFilePath, buffer); + return res.status(200).json({ + success: result, + }); + } + + return res.status(200).json({ + success: result, + message: error?.message, + }); + } catch (error: any) { + res + .status(500) + .json({ success: false, message: error?.message || "Unknown error" }); + } +} diff --git a/pages/api/po/sendDocuSign.ts b/pages/api/po/sendDocuSign.ts new file mode 100644 index 00000000..ccb83733 --- /dev/null +++ b/pages/api/po/sendDocuSign.ts @@ -0,0 +1,49 @@ +export const config = { + api: { + bodyParser: true, // ✅ 이게 false면 안 됨! + }, +}; + +import type { NextApiRequest, NextApiResponse } from "next"; +import { requestContractSign } from "@/lib/docuSign/docuSignFns"; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + if (req.method !== "POST") { + return res.status(405).end(); + } + + try { + const { + docuSignTempId, + contractInfo, + subcontractorinfo, + contractorInfo, + ccInfo = [], + brandId, + } = req.body; + + const docuSignStart = await requestContractSign( + docuSignTempId, + contractInfo, + subcontractorinfo, + contractorInfo, + ccInfo, + brandId + ); + + const { result, envelopeId, error } = docuSignStart; + + res.status(200).json({ + success: result, + envelopeId, + message: error?.message, + }); + } catch (error: any) { + res + .status(500) + .json({ success: false, message: error?.message || "Unknown error" }); + } +} diff --git a/pages/api/po/webhook.ts b/pages/api/po/webhook.ts new file mode 100644 index 00000000..50b3c1f4 --- /dev/null +++ b/pages/api/po/webhook.ts @@ -0,0 +1,337 @@ +export const config = { + api: { + bodyParser: true, // ✅ 이게 false면 안 됨! + }, +}; + +import type { NextApiRequest, NextApiResponse } from "next"; +import path from "path"; +import fs from "fs"; +import * as z from "zod"; +import db from "@/db/db"; +import { GetPOSchema } from "@/lib/po/validations"; +import { unstable_cache } from "@/lib/unstable-cache"; +import { filterColumns } from "@/lib/filter-columns"; +import { + asc, + desc, + ilike, + inArray, + and, + gte, + lte, + not, + or, + eq, + count, +} from "drizzle-orm"; +import { countPos, selectPos } from "@/lib/po/repository"; +import { + contractEnvelopes, + contractsDetailView, + contractSigners, + contracts, +} from "@/db/schema/contract"; +import { vendors, vendorContacts } from "@/db/schema/vendors"; +import dayjs from "dayjs"; + +import { POContent } from "@/lib/docuSign/types"; +import { downloadContractFile, getRecipients } from "@/lib/docuSign/docuSignFns"; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + if (req.method !== "POST") { + return res.status(405).end(); + } + + try { + const { event, data = {} } = req.body; + + //recipientId === "1" 첫번째 서명자 서명 완료 + //recipientId === "2" 두번쨰 서명자 서명 완료 + const { envelopeId, recipientId } = data; + + console.log(req.body) + + const contractList = [ + { + dbName: "contract_envelopes", + dbSchma: contractEnvelopes, + recipients: [ + { + recipientId: "1", + role: "VENDOR", + }, + { + recipientId: "2", + role: "REQUESTER", + }, + ], + }, + ]; + + //서명 요청 발송 + if (event === "recipient-sent") { + await db.transaction(async (tx) => { + for (const targetDB of contractList) { + const { dbSchma, dbName, recipients = [] } = targetDB; + const [targetContract] = await tx + .select() + .from(dbSchma) + .where(eq(dbSchma.envelopeId, envelopeId)) + .limit(1); + + if (!targetContract) { + continue; + } + + const { contractId, id } = targetContract; + + if (contractId === null || contractId === undefined) { + continue; + } + + const { result: sendResult, message } = await getRecipients( + envelopeId, + recipientId + ); + + if (!sendResult) { + const targetRole = recipients.find( + (c) => c.recipientId === recipientId + ); + + if (targetRole) { + const { role } = targetRole; + + const safeRole = role as "REQUESTER" | "VENDOR"; + + await tx + .update(contracts) + .set({ status: `$FAILED_${safeRole}_SEND_MAIL(${message})` }) + .where(eq(contracts.id, contractId)); + + await tx + .update(dbSchma) + .set({ envelopeStatus: `${safeRole}_${message}` }) + .where(eq(dbSchma.id, id)); + } + + continue; + } + + // const createContractFile = await downloadContractFile(envelopeId); + + // const { result, buffer, error } = createContractFile; + + // if (!result || !buffer) { + // console.error( + // `${dbName}: ${envelopeId} can not download contract file, ${error}` + // ); + // continue; + // } + + // const fullFilePath = createFolderTree(fileName, filePath); + + // fs.writeFileSync(fullFilePath, buffer); + } + }); + } + + //서명 요청 수신 + if (event === "recipient-delivered") { + } + + //서명 부분 거절 + if (event === "recipient-declined") { + await db.transaction(async (tx) => { + for (const targetDB of contractList) { + const { dbSchma, recipients = [] } = targetDB; + + const [targetContract] = await tx + .select() + .from(dbSchma) + .where(eq(dbSchma.envelopeId, envelopeId)) + .limit(1); + + const { id, contractId } = targetContract; + + if (contractId === null || contractId === undefined) { + continue; + } + + const targetRole = recipients.find( + (c) => c.recipientId === recipientId + ); + + if (targetRole) { + const { role } = targetRole; + + const safeRole = role as "REQUESTER" | "VENDOR"; + + await tx + .update(contracts) + .set({ status: `${safeRole}_DECLINED_SIGNATURE` }) + .where(eq(contracts.id, contractId)); + + if (["REQUESTER", "VENDOR"].includes(role)) { + await tx + .update(contractEnvelopes) + .set({ envelopeStatus: `${role}_DECLINED` }) + .where(and(eq(contractEnvelopes.envelopeId, envelopeId))); + + await tx + .update(contractSigners) + .set({ + signerStatus: "DECLINED", + signedAt: new Date(), + }) + .where( + and( + eq(contractSigners.envelopeId, id), + eq(contractSigners.signerType, safeRole) + ) + ); + } + } + } + }); + } + + //서명 부분 완료 + if (event === "recipient-completed") { + await db.transaction(async (tx) => { + for (const targetDB of contractList) { + const { dbSchma, dbName, recipients = [] } = targetDB; + + const [targetContract] = await tx + .select() + .from(dbSchma) + .where(eq(dbSchma.envelopeId, envelopeId)) + .limit(1); + + const { id, contractId, fileName, filePath } = targetContract; + + if (contractId === null || contractId === undefined) { + continue; + } + + const createContractFile = await downloadContractFile(envelopeId); + + const { result, buffer, error } = createContractFile; + + if (!result || !buffer) { + console.error( + `${dbName}: ${envelopeId} can not download contract file, ${error}` + ); + continue; + } + + const fullFilePath = createFolderTree(fileName, filePath); + + fs.writeFileSync(fullFilePath, buffer); + + const targetRole = recipients.find( + (c) => c.recipientId === recipientId + ); + + if (targetRole) { + const { role } = targetRole; + + const safeRole = role as "REQUESTER" | "VENDOR"; + + await tx + .update(contracts) + .set({ status: `${safeRole}_FINISHED_SIGNATURE` }) + .where(eq(contracts.id, contractId)); + + if (["REQUESTER", "VENDOR"].includes(role)) { + await tx + .update(contractEnvelopes) + .set({ envelopeStatus: `${role}_FINISHED` }) + .where(and(eq(contractEnvelopes.envelopeId, envelopeId))); + + await tx + .update(contractSigners) + .set({ signerStatus: "FINISHED", signedAt: new Date() }) + .where( + and( + eq(contractSigners.envelopeId, id), + eq(contractSigners.signerType, safeRole) + ) + ); + } + } + } + }); + } + + if (event === "envelope-completed") { + await db.transaction(async (tx) => { + for (const targetDB of contractList) { + const { dbSchma, dbName } = targetDB; + + const [targetContract] = await tx + .select() + .from(dbSchma) + .where(eq(dbSchma.envelopeId, envelopeId)) + .limit(1); + + const { contractId, fileName, filePath } = targetContract; + + if (contractId === null || contractId === undefined) { + continue; + } + + const createContractFile = await downloadContractFile(envelopeId); + + const { result, buffer, error } = createContractFile; + + if (!result || !buffer) { + console.error( + `${dbName}: ${envelopeId} can not download contract file, ${error}` + ); + continue; + } + + const fullFilePath = createFolderTree(fileName, filePath); + + fs.writeFileSync(fullFilePath, buffer); + + await tx + .update(contracts) + .set({ status: "COMPLETED_SIGNATURE" }) + .where(eq(contracts.id, contractId)); + + await tx + .update(contractEnvelopes) + .set({ envelopeStatus: "COMPLETED_SIGNATURE" }) + .where(and(eq(contractEnvelopes.envelopeId, envelopeId))); + } + }); + } + + if (event === "envelope-declined") { + } + + return res.status(200).json("OK"); + } catch (error: any) { + return res + .status(500) + .json({ success: false, message: error?.message || "Unknown error" }); + } +} + +const createFolderTree = (fileName: string, filePath: string): string => { + const cleanedPath = filePath.replace(/^\/+/, ""); + const dirPath = path.resolve(process.cwd(), "public", cleanedPath); // 예: 'contracts/185/signatures' + const fullFilePath = path.join(dirPath, fileName); // 예: 'contracts/185/signatures/xxx.pdf' + + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); + } + + return fullFilePath; +}; -- cgit v1.2.3