Issue with Google Drive API Integration: Unexpected HTML Response from Backend in Production Environment

38 views Asked by At

I have developed a web application using the MERN stack.I'm encountering a problem with the integration of the Google Drive API into my web application deployed in a production environment. The goal of my code is to allow users to upload a file (such as a PDF) from their local machine, then utilize the Google Drive API to import the file to a Google Drive account. Subsequently, a public link for this file is generated and returned as a response to the backend, which in turn should transmit it to the frontend to be stored in the variable myPDFFile and displayed to the user and other users via Socket.IO.

The issue arises when, after successfully uploading the file and processing it through the Google Drive API,despite receiving a 200 status code for the POST request, the backend returns unexpected HTML content instead of the expected JSON response containing the public link to the file.This unexpected behavior occurs only in the production environment, as everything works as expected locally.

Here's the frontend code:

import React, { useState } from "react";
import axios from 'axios';

function PdfViewer({setMyPDFFile,roomId, socket}) {
 
  const [file, setFile] = useState();

  const upload = async () => {
    const formData = new FormData();
    formData.append('file', file);
    try {
      const response = await axios.post(`${process.env.REACT_APP_API_URL}/upload`, formData);
      const fileDriveLink = response.data.result.webViewLink;
      socket.emit("send pdf", {roomId:roomId, pdf: fileDriveLink});
      setMyPDFFile(fileDriveLink);
    } catch (error) {
      console.error(error);
    }
  }

  return (
    <div>
      <input id="pic" type="file" onChange={(e) => setFile(e.target.files[0])} />
      <button>
        <label htmlFor="pic">Upload</label>
      </button>
      <input type="text" readOnly defaultValue={file?.name} />
      <button  onClick={upload}>GO</button>
    </div>
  );
}

export default PdfViewer;

And here's the backend code handling the file upload and response generation:

const { google } = require('googleapis');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const { promisify } = require('util');

const unlinkAsync = promisify(fs.unlink);

const CLIENT_ID = process.env.CLIENT_ID;
const CLIENT_SECRET = process.env.CLIENT_SECRET;
const REDIRECT_URL = process.env.REDIRECT_URL;
const REFRESH_TOKEN = process.env.REFRESH_TOKEN;

const oauth2Client = new google.auth.OAuth2(
    CLIENT_ID,
    CLIENT_SECRET,
    REDIRECT_URL,
);

oauth2Client.setCredentials({ refresh_token: REFRESH_TOKEN });

const drive = google.drive({
    version: 'v3',
    auth: oauth2Client
});

const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        return cb(null, "./filesFolder");
    },
    filename: function (req, file, cb) {
        return cb(null, `${Date.now()}_${file.originalname}`);
    }
});

const upload = multer({ storage });

app.post('/upload', upload.single('file'), async(req, res) => {

  const filePath = path.join(__dirname, `${req.file.path}`);
  const result = await uploadFile(req.file.originalname,filePath);
  
  return res.json('yyy',{result});
  await unlinkAsync(req.file.path)
});

async function uploadFile(name, filePath){
    
    try {
         const response = await drive.files.create({
    requestBody: {
      name: name,
      mimeType: ''
    },
    media: {
      mimeType: '',
      body: fs.createReadStream(filePath),
    }
         })

    return generatePublicUrl(response.data.id)

    } catch (error) {
        console.log(error.message)
    }
}

async function generatePublicUrl(id){
    try {
        const fileId =id;
        const response = await drive.permissions.create({
            fileId : fileId,
            requestBody : {
                role : 'reader',
                type : 'anyone'
            }
         })
    const result = await drive.files.get({
        fileId : fileId,
        fields : 'webViewLink, webContentLink'
    });

   
    return result.data
    } catch (error) {
        console.log(error.message)
    }
}

Here is the unexpected response:

 <head>
        <meta charset="utf-8"/>
        <link rel="icon" href="/favicon.ico"/>
        <meta name="viewport" content="width=device-width,initial-scale=1"/>
        <meta name="theme-color" content="#000000"/>
        <meta name="description" content="Web site created using create-react-app"/>
        <link rel="preconnect" href="https://fonts.googleapis.com">
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
        <link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;700&display=swap" rel="stylesheet">
        <link rel="apple-touch-icon" href="/logo192.png"/>
        <link rel="manifest" href="/manifest.json"/>
        <title>MERN APP</title>
        <link href="./fontawesome-icons/css/all.css" rel="stylesheet">
        <script defer="defer" src="/static/js/main.29c05fe9.js"></script>
        <link href="/static/css/main.728211f8.css" rel="stylesheet">
    </head>
    <body>
        <noscript>You need to enable JavaScript to run this app.</noscript>
        <div id="root"></div>
    </body>
</html>

Here's a summary of the setup:

Deployment: Docker-compose is utilized to deploy the application on an AWS EC2 instance.

Backend: Node.js with Express.js, using Axios to send POST requests to the backend API.

Frontend: React.js for the client-side, utilizing Axios to interact with the backend.

File Upload: Multer is used to handle file uploads to the backend.

Cloudflare: I'm using Cloudflare for SSL certificate management.

Any insights or suggestions on why I'm receiving HTML content instead of the expected JSON response in the production environment would be greatly appreciated. Thank you!

0

There are 0 answers