From c7b9d6de9d173c69681a1ab357bf59bb3f80432a Mon Sep 17 00:00:00 2001 From: Jun-te kim Date: Tue, 12 Aug 2025 19:28:40 +0000 Subject: [PATCH] upload works --- .env.development | 108 ++++++++++++++++++ src/app/api/upload/csv/route.ts | 2 +- .../route.ts | 19 +-- .../[propertyId]/documents/UploadModal.tsx | 16 ++- src/app/utils/s3.ts | 39 ++++++- 5 files changed, 165 insertions(+), 19 deletions(-) create mode 100644 .env.development rename src/app/api/upload/{retrofit-energy-assessments-dev => retrofit-energy-assessments}/route.ts (79%) diff --git a/.env.development b/.env.development new file mode 100644 index 00000000..fb46e3c9 --- /dev/null +++ b/.env.development @@ -0,0 +1,108 @@ +NEXTAUTH_SECRET=df425f28-06ab-47c2-bb78-7e604387d463 +NEXTAUTH_URL=http://localhost:3000 + + +GOOGLE_CLIENT_ID=232063354367-ustovlgtk3cmtvohvd6tdlejnj1qjjj0.apps.googleusercontent.com + +GOOGLE_CLIENT_SECRET=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 + +EPC_AUTH_TOKEN=a2Nvbm5rb3dsZXNzYXJAZ21haWwuY29tOjY5MGJiMWM0NmIyOGI5ZDUxYzAxMzQzYzNiZGNlZGJjZDNmODQwMzA= + + + +AZURE_AD_B2C_TENANT_NAME=DomnaApp + +AZURE_AD_B2C_CLIENT_ID=f0a1f977-ddc4-4037-b129-a310008ee934 + +AZURE_AD_B2C_CLIENT_SECRET=6uh8Q~dmZNqQy3ZxM_Ce33fVSeW24K27R~pYYduD + +AZURE_AD_B2C_PRIMARY_USER_FLOW=B2C_1_signupsignin + + + +AZURE_AD_CLIENT_ID=069e75ee-ba54-45ff-ba77-a06f29c0e21c + +AZURE_AD_CLIENT_SECRET=x6D8Q~f2roqrnoP1YuomSGN5CvU0HPtIWqqPPaYW + +AZURE_AD_TENANT_ID=4a85e8bb-8b7f-4bbd-adc2-1448bb6a9810 + + + +DB_HOST=terraform-20230705170609686900000001.cdgzupxvdyp0.eu-west-2.rds.amazonaws.com + +DB_PORT=5432 + +DB_NAME=DevAssessmentModelDB + +DB_USERNAME=DevAddessmentModelDB + +DB_PASSWORD=!}-A=3D%(2Awy[Qx + + + +URL=http://localhost:3000 + +PRESIGN_AWS_ACCESS_KEY=AKIAU5A36PPNMR2G7ZQO + +PRESIGN_AWS_SECRET_KEY=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 + +RETROFIT_PLAN_INPUT_BUCKET_NAME=retrofit-plan-inputs-dev + +PRESIGN_AWS_REGION=eu-west-2 + + + +DUE_CONSIDERATIONS_BUCKET=retrofit-due-considerations-dev + +DUE_CONSIDERATIONS_AWS_ACCESS_KEY=AKIAU5A36PPNPNFWLJOY + +DUE_CONSIDERATIONS_AWS_SECRET_KEY=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 + +DUE_CONSIDERATIONS_AWS_REGION=eu-west-2 + + + +ECO_SPREADSHEET_BUCKET=retrofit-eco-spreadsheet-dev + +ECO_SPREADSHEET_AWS_ACCESS_KEY=AKIAU5A36PPNPTFDQGOJ + +ECO_SPREADSHEET_AWS_SECRET_KEY=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 + +ECO_SPREADSHEET_AWS_REGION=eu-west-2 + + + +RETROFIT_ENERGY_ASSESSMENTS_BUCKET=retrofit-energy-assessments-dev + +RETROFIT_ENERGY_ASSESSMENTS_AWS_ACCESS_KEY=AKIAU5A36PPNJMZZ3KRW + +RETROFIT_ENERGY_ASSESSMENTS_AWS_SECRET=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 + + + +FASTAPI_API_KEY=4QPwbB6hEdUloDVtbBJCUTfGBdBgWwpeavWQ7t5Z + +FASTAPI_API_URL=https://api.dev.hestia.homes + + + +DUE_CONSIDERATIONS_API_URL=https://api.dev.hestia.homes + +ECO_SPREADSHEET_API_URL=https://api.dev.hestia.homes + + + + + +DOCUMENTS_DATABASE_URL=postgresql://postgres:makingwarmhomes@terraform-20250331175522503500000002.cdgzupxvdyp0.eu-west-2.rds.amazonaws.com:5432/surveyDB + +DOCUMENTS_DB_HOST=terraform-20250331175522503500000002.cdgzupxvdyp0.eu-west-2.rds.amazonaws.com + +DOCUMENTS_DB_PORT=5432 + +DOCUMENTS_DB_NAME=surveyDB + +DOCUMENTS_DB_USERNAME=postgres + +DOCUMENTS_DB_PASSWORD=makingwarmhomes + diff --git a/src/app/api/upload/csv/route.ts b/src/app/api/upload/csv/route.ts index a9965407..f140a90d 100644 --- a/src/app/api/upload/csv/route.ts +++ b/src/app/api/upload/csv/route.ts @@ -35,7 +35,7 @@ export async function POST(request: NextRequest) { // Presigned url is valid for 5 minutes const preSignedUrl = await s3.getSignedUrl("putObject", { - Bucket: process.env.RETOFIT_PLAN_INPUT_BUCKET_NAME, + Bucket: process.env.RETROFIT_PLAN_INPUT_BUCKET_NAME, Key: fileKey, ContentType: "text/csv", Expires: 5 * 60, diff --git a/src/app/api/upload/retrofit-energy-assessments-dev/route.ts b/src/app/api/upload/retrofit-energy-assessments/route.ts similarity index 79% rename from src/app/api/upload/retrofit-energy-assessments-dev/route.ts rename to src/app/api/upload/retrofit-energy-assessments/route.ts index bd5e69d2..a1e4b59f 100644 --- a/src/app/api/upload/retrofit-energy-assessments-dev/route.ts +++ b/src/app/api/upload/retrofit-energy-assessments/route.ts @@ -5,18 +5,18 @@ import { createS3Client, presignGetUrl } from "@/app/utils/s3"; const Schema = z.object({ path: z.string(), expiresInSeconds: z.number().int().positive().default(300), - responseContentType: z.string().optional(), - responseContentDisposition: z.string().optional(), // e.g., 'attachment; filename="file.csv"' + contentType: z.string(), }); export async function POST(request: NextRequest) { try { const body = await request.json(); - const { path, expiresInSeconds, responseContentType, responseContentDisposition } = + const { path, expiresInSeconds, contentType } = Schema.parse(body); // Retrofit s3 bucket connection let bucket = process.env.RETROFIT_ENERGY_ASSESSMENTS_BUCKET + // let bucket = process.env.RETROFIT_PLAN_INPUT_BUCKET_NAME if (!bucket) { return NextResponse.json({ msg: "RETROFIT_ENERGY_ASSESSMENTS_BUCKET is not set" }, { status: 400 }); } @@ -24,15 +24,16 @@ export async function POST(request: NextRequest) { const s3 = createS3Client({ region: process.env.PRESIGN_AWS_REGION, accessKeyId: process.env.RETROFIT_ENERGY_ASSESSMENTS_AWS_ACCESS_KEY, - secretAccessKey: process.env.RETROFIT_ENERGY_ASSESSMENTS_AWS_ACCESS_KEY, + secretAccessKey: process.env.RETROFIT_ENERGY_ASSESSMENTS_AWS_SECRET, }); - + + + const url = await presignGetUrl(s3, { bucket, key: path, expiresInSeconds, - responseContentType, - responseContentDisposition, + ContentType: contentType, }); return NextResponse.json({ url }); @@ -42,4 +43,6 @@ export async function POST(request: NextRequest) { ? NextResponse.json({ msg: "Invalid input", issues: err.issues }, { status: 400 }) : NextResponse.json({ msg: "Internal server error" }, { status: 500 }); } -} \ No newline at end of file +} + + diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/documents/UploadModal.tsx b/src/app/portfolio/[slug]/building-passport/[propertyId]/documents/UploadModal.tsx index ba10c97b..3f791e9f 100644 --- a/src/app/portfolio/[slug]/building-passport/[propertyId]/documents/UploadModal.tsx +++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/documents/UploadModal.tsx @@ -12,6 +12,7 @@ import { Button } from "@/app/shadcn_components/ui/button"; import { ReportType } from "@/app/db/documents_schema/documents"; import { Input } from "@/app/shadcn_components/ui/input"; import { useState } from "react"; +import { uploadFileToS3 } from "@/app/utils/s3"; type UploadModalProps = { open: boolean; @@ -35,11 +36,12 @@ async function generatePresignedUrls({ }) { const body = JSON.stringify({ path: path, - responseContentType: contentType, - expiresInSeconds: expiresInSeconds + expiresInSeconds: expiresInSeconds, + contentType: contentType }); - const presignedResponse = await fetch("/api/upload/retrofit-energy-assessments-dev", { + const presignedResponse = await fetch("/api/upload/retrofit-energy-assessments", { method: "POST", + headers: { "Content-Type": "application/json" }, body, }); @@ -61,13 +63,19 @@ export const UploadModal = ({ async function handleS3Upload() { console.log("Get Presigned url in a specific bucket location") - + console.log("files are", uploadFiles[0]); const { url } = await generatePresignedUrls({ path: "foo/test/trololol", // path in bucket contentType: "application/pdf", expiresInSeconds: 5 * 60, }); console.log("URl is ", url); + console.log("uploading file"); + await uploadFileToS3({ + presignedUrl: url, + file: uploadFiles[0], + contentType: "application/pdf" + }) onClose(); //probably khalim call back to update the front end properl } diff --git a/src/app/utils/s3.ts b/src/app/utils/s3.ts index a70efedc..ebb56308 100644 --- a/src/app/utils/s3.ts +++ b/src/app/utils/s3.ts @@ -26,20 +26,47 @@ export type PresignGetOptions = { bucket: string; key: string; expiresInSeconds?: number; // default 300 - responseContentType?: string; - responseContentDisposition?: string; // e.g. 'attachment; filename="file.csv"' + ContentType?: string; }; /** Presign a GET URL using an existing S3 instance (aws-sdk v2). */ export async function presignGetUrl( s3: S3, - { bucket, key, expiresInSeconds = 300, responseContentType, responseContentDisposition }: PresignGetOptions + { bucket, key, expiresInSeconds = 300, ContentType}: PresignGetOptions ): Promise { - return (s3 as any).getSignedUrlPromise("getObject", { + return (s3 as any).getSignedUrlPromise("putObject", { Bucket: bucket, Key: key, Expires: expiresInSeconds, - ResponseContentType: responseContentType, - ResponseContentDisposition: responseContentDisposition, + ContentType: ContentType, }); +} + +export async function uploadFileToS3({ + presignedUrl, + file, + contentType, +}: { + presignedUrl: string; + file: Blob; + contentType: string; +}) { + try { + const response = await fetch(presignedUrl, { + method: "PUT", + body: file, + headers: { "Content-Type": contentType }, + }); + + if (!response.ok) { + console.error("Upload failed response:", response); + throw new Error("Network response was not ok"); + } + } catch (error) { + console.error("Upload error:", error); + throw new Error("Upload failed."); + } + + console.log("File uploaded successfully"); + return { success: true }; } \ No newline at end of file