Skip to content

Commit

Permalink
Fixed Recipe Image Storage & "Update Recipe" Page Improvements (#434)
Browse files Browse the repository at this point in the history
* added-image-to-database-and-fixed-update-recipe

* removed-logs

* delete-fix

* removed-github-env
  • Loading branch information
IkkiOcean authored Nov 9, 2024
1 parent 7dde644 commit c499745
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 240 deletions.
11 changes: 1 addition & 10 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,7 @@ PORT=8080
# Add your JWT secret token here, it can be anything eg: pineapple
SECRET=""
JWT_TIMEOUT='12h'
# Add your github username
OWNER=""
# Create a public repository and add the name of that repo below
REPO=""
# Add the name of your branch below eg:main
BRANCH=""
# Add your github email id
GITHUB_EMAIL=""
#Add your github access token
TOKEN=""

#Add your email id for communication medium for nodemailer
SMTP_EMAIL=
# Add your SMTP passkey (you need two factor authentication for it and this passkey you need to generate first by following steps :
Expand Down
170 changes: 6 additions & 164 deletions backend/Controllers/RecipeController.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,6 @@ import Comment from "../models/Comment.js";
import axios from "axios";
import User from "../models/User.js";
import mongoose from "mongoose";
import { Octokit } from "@octokit/rest";
import { json } from "express";

const g_owner = process.env.OWNER;
const g_repo = process.env.REPO;
const g_branch = process.env.BRANCH;
const g_email = process.env.GITHUB_EMAIL;

/**
* @route {POST} /api/recipe/add
Expand All @@ -37,24 +30,15 @@ const addRecipe = async (req, res) => {
? lastDocument._id.toString().slice(-4)
: "0000";

// Upload the image to GitHub
const imageUrl = await imageToGithub(image, imagename, unique);

// If image upload fails, return an error
if (!imageUrl) {
return res
.status(422)
.json({ success: false, message: "Image upload failed" });
}

// Prepare the recipe data

const data = {
user,
name,
description,
ingredients,
steps,
image: imageUrl, // Save the URL of the uploaded image
image,
imagename,
author,
type,
};
Expand Down Expand Up @@ -126,87 +110,8 @@ const allRecipe = async (req, res) => {
}
};

/**
* @function
* @description Uploads Image to a github repo and returns the downloadable link
* @access private
*/

const imageToGithub = async (fileImage, name, unique) => {
const owner = process.env.OWNER;
const repo = process.env.REPO;
const branch = process.env.BRANCH;

// Validate environment variables
if (!owner || !repo || !branch || !process.env.TOKEN) {
console.error("Missing required environment variables");
return null;
}

console.log("Config:", { owner, repo, branch }); // Debug log
const base64Content = fileImage.split(";base64,").pop();
const fileContent = Buffer.from(base64Content, "base64").toString("base64");

// Use the correct repository structure
const path = `images/${unique}${name}`; // Make sure this directory exists in your repo
const message = `Add ${unique} ${name} via API`;
const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path}`;

console.log("Request URL:", url); // Debug log

try {
// Correct GitHub token format
const headers = {
Authorization: `token ${process.env.TOKEN}`, // Changed from Bearer to token
"X-GitHub-Api-Version": "2022-11-28",
"Content-Type": "application/json",
};

// Check if file exists
let fileExists = false;
try {
const response = await axios.get(url, { headers });
fileExists = response.status === 200;
console.log("File exists check:", fileExists); // Debug log
} catch (error) {
if (error.response && error.response.status !== 404) {
console.error("Error checking file existence:", error.response.data);
throw error;
}
}

// Prepare request payload
const requestPayload = {
message,
content: fileContent,
branch,
};

if (fileExists) {
const existingFile = await axios.get(url, { headers });
requestPayload.sha = existingFile.data.sha;
}

// Upload or update file
console.log("Sending PUT request..."); // Debug log
const response = await axios.put(url, requestPayload, { headers });

if (response.status === 201 || response.status === 200) {
console.log("Upload successful:", response.data.content.download_url);
return response.data.content.download_url;
} else {
console.error("Upload failed:", response.status, response.statusText);
return null;
}
} catch (error) {
console.error("Upload error details:", {
status: error.response?.status,
data: error.response?.data,
message: error.message,
});
return null;
}
};
/**
* @POST /api/recipes/readall
* @description Returns recipe created by an User
Expand All @@ -229,7 +134,7 @@ const getOneUserRecipes = async (req, res) => {
*/
const updateRecipe = async (req, res) => {
try {
const { name, description, ingredients, steps, type, user, author, id } =
const { name, description, ingredients, steps, type, user, author, id, imagename, image } =
req.body;
const data = {
user,
Expand All @@ -239,6 +144,8 @@ const updateRecipe = async (req, res) => {
steps,
author,
type,
image,
imagename
};
const update = await Recipe.updateOne(
{ _id: id },
Expand Down Expand Up @@ -269,72 +176,8 @@ const deleteRecipe = async (req, res) => {
.json({ success: false, message: "Recipe not found" });
}

const imageName = recipe.image;
const owner = g_owner;
const repo = g_repo;
const branch = g_branch;
const result = trimUrl(imageName, owner, repo);

const octokit = new Octokit({
auth: process.env.TOKEN,
});

function trimUrl(url, owner, repo) {
// To remove the unncessary part from the URL
const parsedUrl = new URL(url);
const trimmedPath = decodeURIComponent(parsedUrl.pathname.slice(1)); // Decode the URL and remove leading '/'
const partToRemove = `${owner}/${repo}/${branch}/`;
const finalPath = trimmedPath.replace(partToRemove, "");

return finalPath;
}

// The function to fetch the file content
const fetchFileContent = async () => {
try {
// Make the request to the GitHub API
const response = await octokit.request(
"GET /repos/{owner}/{repo}/contents/{path}",
{
owner: owner,
repo: repo,
path: result,
headers: {
"X-GitHub-Api-Version": "2022-11-28",
},
}
);
// Getting the SHA key so that it can assist in deletion
const sha = response.data.sha;
await octokit.request("DELETE /repos/{owner}/{repo}/contents/{path}", {
owner: owner,
repo: repo,
path: result,
message: `deleted the image ${result.replace(
"TastyTrails/Recipe/",
" "
)}`,
committer: {
name: recipe.author,
email: g_email,
},
sha: sha,
headers: {
"X-GitHub-Api-Version": "2022-11-28",
},
});
} catch (error) {
// Handle and log any errors
console.error(
"Error fetching file content:",
error.response ? error.response.data : error.message
);
throw new Error("Failed to delete image from GitHub");
}
};

// Try to delete the file from GitHub
await fetchFileContent();

// If successful, delete the recipe from the database
await Recipe.deleteOne({ _id: req.body.id });
Expand Down Expand Up @@ -402,7 +245,6 @@ const addComment = async (req, res) => {
const getComments = async (req, res) => {
try {
const { recipeId } = req.params;
console.log("Fetching comments for recipe:", recipeId);

// Ensure the recipe exists
const recipe = await Recipe.findById(recipeId);
Expand Down
6 changes: 5 additions & 1 deletion backend/models/Recipe.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ const recipeSchema = new mongoose.Schema({
default: Date.now,
},
image: {
type: String,
type: String,
required: true,
},
imagename : {
type: String,
required: true,
},
likes: {
Expand Down
Loading

0 comments on commit c499745

Please sign in to comment.