Building a File Encryptor with ReactJS, Express, and TailwindCSS: A Journey into Data Security
Tags: webdev, javascript, react, programming
Published: 2024-05-15 07:35:30

## Introduction

Hello again, fellow developers! 👋 After successfully building a web scraper inspired by an amazing list of [53 project ideas](https://dev.to/gokayburuc/53-project-ideas-for-beginner-to-advanced-2j5), I'm excited to share the next stop on this coding adventure: a File Encryptor built using ReactJS, Express, and TailwindCSS. In this post, I'll walk you through the process, challenges, and lessons learned while creating a tool that secures files with ease. Whether you're just starting out or you're a seasoned developer, I hope you enjoy and learn from this journey!

## Why a File Encryptor?

Data security is more important than ever. With sensitive information floating around, it's crucial to protect it from unauthorized access. A file encryptor automates the process of securing data, making it an invaluable tool for anyone concerned with privacy. For this project, I aimed to create an efficient and user-friendly application to encrypt files, ensuring data remains safe.

## Setting Up the Backend

Let's dive into the backend first. We'll use Express to handle file uploads and perform encryption. Here's how we set it up:

### Server Code (`server.js`)

```javascript
const express = require("express");
const multer = require("multer");
const bodyParser = require("body-parser");
const crypto = require("crypto");
const fs = require("fs");
const path = require("path");
const cors = require("cors");

const app = express();
const upload = multer({ dest: "uploads/" });

app.use(cors());
app.use(bodyParser.json());

const encryptFile = (filePath, callback) => {
  const algorithm = "aes-256-ctr";
  const key = crypto.randomBytes(32);
  const iv = crypto.randomBytes(16);

  const cipher = crypto.createCipheriv(algorithm, Buffer.from(key), iv);
  const input = fs.createReadStream(filePath);
  const encryptedPath = filePath + ".enc";
  const output = fs.createWriteStream(encryptedPath);

  input.pipe(cipher).pipe(output);

  output.on("finish", () => {
    callback({
      key: key.toString("hex"),
      iv: iv.toString("hex"),
      path: encryptedPath,
    });
  });
};

app.post("/encrypt", upload.single("file"), (req, res) => {
  const filePath = path.join(__dirname, req.file.path);
  encryptFile(filePath, (encryptionInfo) => {
    res.json(encryptionInfo);
  });
});

const PORT = 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
```

### Explanation

- **Import Modules**: We import necessary modules like `express` for creating the server, `multer` for file uploads, `body-parser` for parsing request bodies, `crypto` for encryption, `fs` for file system operations, `path` for handling file paths, and `cors` for enabling cross-origin requests.
- **Initialize App**: We initialize an Express app and configure it to use CORS and body-parser.
- **File Encryption Function**: The `encryptFile` function takes a file path and a callback function. It uses the AES-256-CTR algorithm to encrypt the file. Random bytes are generated for the encryption key and initialization vector (IV). The file is read, encrypted, and written to a new file with the `.enc` extension. Once encryption is complete, the callback is called with the encryption details.
- **File Upload Endpoint**: The `/encrypt` endpoint handles file uploads using multer. It calls the `encryptFile` function and responds with the encryption details (key, IV, and encrypted file path).
- **Start Server**: The server listens on port 5000.

## Creating the Frontend

Now, let's move on to the frontend. We'll build a simple React interface that allows users to upload a file and display the encryption details.

### Frontend Code (`client/src/App.js`)

```javascript
import React, { useState } from "react";
import axios from "axios";

function App() {
  const [file, setFile] = useState(null);
  const [encryptionData, setEncryptionData] = useState(null);
  const [showModal, setShowModal] = useState(false);

  const onFileChange = (event) => {
    setFile(event.target.files[0]);
  };

  const onSubmit = async (event) => {
    event.preventDefault();
    const formData = new FormData();
    formData.append("file", file);

    try {
      const response = await axios.post(
        "http://localhost:5000/encrypt",
        formData,
        {
          headers: {
            "Content-Type": "multipart/form-data",
          },
        }
      );
      setEncryptionData(response.data);
      setShowModal(true);
    } catch (error) {
      console.error("Error uploading file:", error);
    }
  };

  const closeModal = () => {
    setShowModal(false);
  };

  return (
    <div className="min-h-screen bg-gray-100 flex flex-col items-center justify-center">
      <div className="bg-white shadow-xl rounded-lg p-8">
        <h1 className="text-3xl font-bold text-brand mb-6">File Encryptor</h1>
        <form onSubmit={onSubmit} className="space-y-4">
          <input
            type="file"
            onChange={onFileChange}
            className="file:mr-4 file:py-2 file:px-4 file:rounded file:border-0 file:text-sm file:font-semibold file:bg-brand file:text-white hover:file:bg-brand-dark"
          />
          <button
            type="submit"
            className="bg-brand hover:bg-brand-dark text-white font-bold py-2 px-4 rounded transition-colors duration-150 ease-in-out"
          >
            Encrypt File
          </button>
        </form>
      </div>

      {encryptionData && (
        <button
          onClick={() => setShowModal(true)}
          className="mt-5 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded transition-colors duration-150 ease-in-out"
        >
          Show Key & IV
        </button>
      )}

      {showModal && (
        <div className="fixed inset-0 bg-gray-900 bg-opacity-50 flex justify-center items-center">
          <div className="bg-white p-6 rounded shadow-lg">
            <h2 className="text-xl font-bold mb-2">
              Encryption Details
              <span className="block text-sm font-normal text-gray-500">
                (Check the uploads folder for the encrypted file)
              </span>
            </h2>
            <p>
              <strong>Key:</strong> {encryptionData.key}
            </p>
            <p>
              <strong>IV:</strong> {encryptionData.iv}
            </p>
            <button
              onClick={closeModal}
              className="mt-4 bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded transition-colors duration-150 ease-in-out"
            >
              Close
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

export default App;
```

### Explanation

- **State Management**: We use React's `useState` hook to manage the state for the selected file (`file`), the encryption data (`encryptionData`), and the visibility of the modal (`showModal`).
- **File Selection**: The `onFileChange` function updates the `file` state when the user selects a file.
- **Form Submission**: The `onSubmit` function prevents the default form submission, creates a `FormData` object with the selected file, and sends a POST request to the server using Axios. On successful encryption, it updates the `encryptionData` state and shows the modal.
- **Close Modal**: The `closeModal` function hides the modal.
- **Rendering**: The component renders a file upload form, a button to show the encryption details, and a modal displaying the encryption key and IV.

## Styling with TailwindCSS

To give our app a clean and modern look, we'll use TailwindCSS.

### Tailwind Configuration (`client/tailwind.config.js`)

```javascript
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./src/**/*.{js,jsx,ts,tsx}"],
  theme: {
    extend: {
      colors: {
        brand: "#0f172a",
      },
    },
  },
  plugins: [],
};
```

### Installing TailwindCSS

First, ensure TailwindCSS is installed in your project:

```bash
npm install -D tailwindcss
npx tailwindcss init
```

Next, add Tailwind to your CSS (`client/src/index.css`):

```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

## Testing the File Encryptor

To test the file encryptor:
1. Start the Express server by running `node server.js`.
2. Run the React app using `npm start` in the `client` directory.
3. Upload a file through the React frontend and observe the encryption details.

## Conclusion

And there you have it! We've successfully built a file encryptor using ReactJS, Express, and TailwindCSS. This project not only reinforced my understanding of file handling and encryption but also showcased the power of combining these technologies to create a secure and user-friendly application. I hope you found this journey as rewarding as I did. Stay tuned for more exciting projects as I continue exploring this list of ideas. Happy coding! 🚀