migrate from knex to prisma on backend

Co-authored-by: fred <>
Reviewed-on: #1
This commit is contained in:
fred 2025-08-15 20:52:38 +00:00
parent 24281a6322
commit 0a41568a2e
31 changed files with 1577 additions and 472 deletions

View file

@ -0,0 +1,89 @@
const RecipeModel = require("../models/recipeModel");
const model = new RecipeModel();
exports.test = async (req, res) => {
console.log("test");
res.json({ env: process.env.NODE_ENV });
};
exports.getAllRecipes = async (req, res) => {
try {
const recipes = await model.getAllRecipes();
res.json(recipes);
} catch (error) {
res.status(500).json({
msg: "Failed to fetch all recipes",
source: "recipeController",
error: error.message,
});
}
};
exports.getRecipeById = async (req, res) => {
const id = parseInt(req.params.id, 10);
try {
const recipe = await model.findById(id);
if (recipe) {
res.json(recipe);
} else {
res.status(404).json({ msg: "Recipe not found" });
}
} catch (error) {
res.status(500).json({
msg: "Failed to fetch recipe",
source: "recipeController",
error: error.message,
});
}
};
exports.addRecipe = async (req, res) => {
if (process.env.NODE_ENV === "demo") {
return;
}
try {
const createdRecipe = await model.addRecipe(req.body);
res.status(201).json(createdRecipe);
} catch (error) {
res.status(500).json({
msg: "Failed to add recipe",
source: "recipeController",
error: error.message,
});
}
};
exports.setStars = async (req, res) => {
if (process.env.NODE_ENV === "demo") {
return;
}
const id = parseInt(req.body.id, 10);
const stars = parseInt(req.body.stars, 10);
try {
const createdRecipe = await model.setStars(id, stars);
res.status(202).json(createdRecipe);
} catch (error) {
res.status(500).json({
msg: "Failed to set stars",
source: "recipeController",
error: error.message,
});
}
};
exports.deleteRecipe = async (req, res) => {
if (process.env.NODE_ENV === "demo") {
return;
}
const id = parseInt(req.body.id, 10);
try {
await model.deleteRecipe(id);
res.json({ success: "true" });
} catch (error) {
res.status(500).json({
msg: "Failed to delete recipe",
source: "recipeController",
error: error.message,
});
}
};

41
backend/src/index.js Normal file
View file

@ -0,0 +1,41 @@
const express = require("express");
const cors = require("cors");
const { PrismaClient } = require("@prisma/client");
const appRoutes = require("./routes/appRoutes");
const app = express();
const port = process.env.PORT || 3000;
const prisma = new PrismaClient();
function setupMiddleware(app) {
app.use(cors());
app.use(express.json());
app.use("/api", appRoutes);
}
setupMiddleware(app);
// Start server
async function startServer() {
try {
app.listen(port);
console.log(`Server is running on http://localhost:${port}`);
} catch (error) {
console.error("Error starting the server:", error);
}
}
process.on("SIGINT", async () => {
try {
await prisma.$disconnect();
console.log("Prisma client disconnected");
process.exit(0);
} catch (error) {
console.error("Error disconnecting Prisma client:", error);
process.exit(1);
}
});
startServer();
module.exports = { app, prisma };

View file

@ -0,0 +1,134 @@
const { PrismaClient } = require("@prisma/client");
const Logger = require("../utils/logger.js");
const logger = new Logger();
class recipeModel {
constructor() {
this.prisma = new PrismaClient();
}
async getAllRecipes() {
try {
return await this.prisma.recipes.findMany();
} catch (err) {
console.error("Error fetching all recipies:", err);
throw new Error(err.message);
}
}
async findById(id) {
try {
const recipe = await this.prisma.recipes.findUnique({
where: { id },
include: { recipeSteps: true, recipeIngredients: true },
});
if (!recipe) {
logger.warn(`recipe with id ${id} cannot be found`);
return null;
}
const data = {
details: {
id: recipe.id,
name: recipe.name,
author: recipe.author,
cuisine: recipe.cuisine,
stars: recipe.stars,
prep_minutes: recipe.prep_minutes,
cook_minutes: recipe.cook_minutes,
},
ingredients: recipe.recipeIngredients.map((ing) => ing.raw),
steps: recipe.recipeSteps.map((step, idx) => ({
step_number: step.step_number,
instruction: step.instruction,
})),
};
return data;
} catch (err) {
console.log("Error finding recipe:", err);
logger.error("error finding recipe", err);
throw new Error(err.message);
}
}
async addRecipe(recipeData) {
const {
name,
author,
cuisine,
stars,
ingredients,
steps,
prep_minutes,
cook_minutes,
} = recipeData;
try {
const createdRecipe = await this.prisma.recipes.create({
data: {
name,
author,
cuisine,
prep_minutes,
cook_minutes,
stars,
recipeIngredients: {
create: ingredients.map((ing) => ({ raw: ing })),
},
recipeSteps: {
create: Object.keys(steps).map((stepNumber) => ({
step_number: parseInt(stepNumber),
instruction: steps[stepNumber],
})),
},
},
});
logger.info("new recipe created", {
id: createdRecipe.id,
name: createdRecipe.name,
});
return createdRecipe;
} catch (error) {
console.log(error);
logger.error("error creating recipe", err);
throw new Error("Failed to add recipe");
}
}
async setStars(id, stars) {
try {
await this.prisma.recipes.update({
where: { id },
data: { stars },
});
return { message: "stars updated" };
} catch (err) {
console.error("Error updating stars:", err);
logger.error("error setting stars", err);
throw new Error(err.message);
}
}
async deleteRecipe(id) {
try {
await this.prisma.recipe_ingredients.deleteMany({
where: { recipe_id: id },
});
await this.prisma.recipe_steps.deleteMany({
where: { recipe_id: id },
});
const deletedRecipe = await this.prisma.recipes.delete({
where: { id },
});
logger.info("recipe deleted", {
id: deletedRecipe.id,
name: deletedRecipe.name,
});
return { message: "Recipe deleted successfully" };
} catch (err) {
console.error("Error deleting recipe:", err);
logger.error("error deleting recipe", err);
throw new Error(err.message);
}
}
}
module.exports = recipeModel;

View file

@ -0,0 +1,18 @@
const express = require("express");
const recipeController = require("../controllers/recipeController");
const router = express.Router();
router.get("/test", recipeController.test);
router.get("/recipes", recipeController.getAllRecipes);
router.get("/recipe/:id", recipeController.getRecipeById);
router.post("/add-recipe", recipeController.addRecipe);
router.post("/set-stars", recipeController.setStars);
router.delete("/delete-recipe", recipeController.deleteRecipe);
module.exports = router;

View file

@ -0,0 +1,33 @@
const fs = require("fs");
class Logger {
constructor(filePath) {
this.filePath = "/logs/app.log";
}
log(level, message, params) {
const logEntry = {
timestamp: new Date().toISOString(),
level: level,
message: message,
params: params,
};
fs.appendFile(this.filePath, JSON.stringify(logEntry) + "\n", (err) => {
if (err) throw err;
});
}
info(message, params = {}) {
this.log("info", message, params);
}
warn(message, params = {}) {
this.log("warn", message, params);
}
error(message, params = {}) {
this.log("error", message, params);
}
}
module.exports = Logger;