diff --git a/backend/backendServer.js b/backend/backendServer.js index c562dbb..919025b 100644 --- a/backend/backendServer.js +++ b/backend/backendServer.js @@ -10,7 +10,7 @@ app.use(express.json()); // ####### ROUTES ####### app.get("/backend/test", async (req, res) => { console.log("test"); - res.json({ test: "test" }); + res.json({ env: process.env.NODE_ENV }); }); // ### GET ALL RECIPES ### app.get("/backend/recipes", async (req, res) => { @@ -81,6 +81,7 @@ app.get("/backend/recipe/:id", async (req, res) => { // ### ADD RECIPE ### app.post("/backend/add-recipe", async (req, res) => { + if (process.env.NODE_ENV === 'demo') { return; }; const { name, author, cuisine, stars, ingredients, steps, prep_minutes, cook_minutes } = req.body; try { const [id] = await db("recipes").insert( @@ -118,6 +119,7 @@ app.post("/backend/add-recipe", async (req, res) => { // ### SET STARS ### app.post("/backend/set-stars", async (req, res) => { + if (process.env.NODE_ENV === 'demo') { return; }; const { id, stars } = req.body; try { await db("recipes").where({ id: id }).update({ stars: stars }); @@ -130,6 +132,7 @@ app.post("/backend/set-stars", async (req, res) => { // ### DELETE RECIPE ### app.delete("/backend/delete-recipe", async (req, res) => { + if (process.env.NODE_ENV === 'demo') { return; }; const { id } = req.body; try { await db("recipe_steps").where({ recipe_id: id }).del(); diff --git a/backend/knexfile.js b/backend/knexfile.js index 0ffe60e..78f22d8 100644 --- a/backend/knexfile.js +++ b/backend/knexfile.js @@ -20,6 +20,25 @@ module.exports = { }, }, + demo: { + client: "postgresql", + connection: { + host: "db", + port: process.env.DB_PORT, + database: process.env.DB_NAME, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + }, + pool: { + min: 2, + max: 10, + }, + migrations: { + tableName: "knex_migrations", + directory: "./migrations", + }, + }, + production: { client: "postgresql", connection: { diff --git a/backend/package.json b/backend/package.json index 9eac5ca..bc51cbe 100644 --- a/backend/package.json +++ b/backend/package.json @@ -3,8 +3,8 @@ "version": "1.0.0", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", "dev": "node backendServer.js", + "demo": "node backendServer.js", "production": "node backendServer.js" }, "keywords": [], diff --git a/docker-compose.yaml b/docker-compose.yaml index 370fef2..26e5f54 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,6 +1,6 @@ services: db: - container_name: recipes_postgres + container_name: recipes_postgres_${ID} image: docker.io/library/postgres:17 # restart: always env_file: @@ -15,13 +15,13 @@ services: - ./postgres/db:/var/lib/postgresql/data backend: image: recipes_backend - container_name: recipes_backend + container_name: recipes_backend_${ID} build: context: ./backend args: NODE_ENV: ${NODE_ENV} ports: - - "3000:3000" + - "${BACKEND_PORT}:3000" volumes: - ./backend:/usr/src/app environment: @@ -31,13 +31,13 @@ services: - DB_NAME=${DB_NAME} frontend: image: recipes_frontend - container_name: recipes_frontend + container_name: recipes_frontend_${ID} build: context: ./backend args: NODE_ENV: ${NODE_ENV} ports: - - "8081:80" + - "${FRONTEND_PORT}:80" volumes: - ./frontend:/usr/src/app environment: diff --git a/frontend/package.json b/frontend/package.json index 3be63aa..bdad101 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,6 +6,7 @@ "scripts": { "dev": "vite --host 0.0.0.0 --port 80", "production": "vite --host 0.0.0.0 --port 80", + "demo": "vite --host 0.0.0.0 --port 80", "build": "tsc -b && vite build", "lint": "eslint .", "preview": "vite preview" diff --git a/frontend/src/components/CookbookRecipeTile.tsx b/frontend/src/components/CookbookRecipeTile.tsx index 9fcc668..4be30bd 100644 --- a/frontend/src/components/CookbookRecipeTile.tsx +++ b/frontend/src/components/CookbookRecipeTile.tsx @@ -1,6 +1,7 @@ import React, { useState } from 'react'; import { type RecipeSmall } from "../types/Recipe" import Modal from '../components/Modal.tsx' +import DemoModal from '../components/DemoModal.tsx' import { Link } from 'react-router-dom'; interface CookbookRecipeTileProps { @@ -9,13 +10,19 @@ interface CookbookRecipeTileProps { } function CookbookRecipeTile({ recipe, handleDelete }: CookbookRecipeTileProps) { - const [isModalOpen, setIsModalOpen] = useState(false); + const [showConfirmModal, setShowConfirmModal] = useState(false); + const [showDemoModal, setShowDemoModal] = useState(false); - const openModal = () => { setIsModalOpen(true) }; - const closeModal = () => { setIsModalOpen(false) }; + const openModal = () => { setShowConfirmModal(true) }; + const closeModal = () => { setShowConfirmModal(false) }; const confirmDelete = () => { - handleDelete(recipe.id); - closeModal(); + if (process.env.NODE_ENV === 'demo') { + closeModal(); + setShowDemoModal(true); + } else { + handleDelete(recipe.id); + closeModal(); + } }; @@ -30,12 +37,19 @@ function CookbookRecipeTile({ recipe, handleDelete }: CookbookRecipeTileProps) { + {showDemoModal && ( + setShowDemoModal(false)} + closeModal={() => setShowDemoModal(false)} + /> + )} ); }; diff --git a/frontend/src/components/DemoModal.tsx b/frontend/src/components/DemoModal.tsx new file mode 100644 index 0000000..c3b1d0f --- /dev/null +++ b/frontend/src/components/DemoModal.tsx @@ -0,0 +1,28 @@ +import { Link } from 'react-router-dom'; + +interface DemoModalProps { + isOpen: boolean; + onClose: () => void; + closeModal: () => void; +} + +const DemoModal = ({ isOpen, onClose, closeModal }: DemoModalProps) => { + if (!isOpen) return null; + + return ( +
+
e.stopPropagation()}> +
+

Thanks for checking out my app! Database write operations are disabled in demo mode.

+

access@fredzernia.com to request access to the production build

+

Find out more about this app here

+
+
+ +
+
+
+ ); +}; + +export default DemoModal; diff --git a/frontend/src/pages/AddRecipe.tsx b/frontend/src/pages/AddRecipe.tsx index b73f054..35172ca 100644 --- a/frontend/src/pages/AddRecipe.tsx +++ b/frontend/src/pages/AddRecipe.tsx @@ -4,6 +4,7 @@ import { useNavigate } from "react-router-dom"; import AddBulkIngredients from "../components/AddBulkIngredients.tsx" import AddBulkSteps from "../components/AddBulkSteps.tsx" import StarRating from "../components/StarRating.tsx" +import DemoModal from '../components/DemoModal.tsx' // import { type Step } from "../types/Recipe"; interface Step { @@ -23,9 +24,14 @@ function AddRecipe() { const [stars, setStars] = useState(0); const [prepMinutes, setPrepMinutes] = useState(5); const [cookMinutes, setCookMinutes] = useState(5); + const [showDemoModal, setShowDemoModal] = useState(false); const addRecipeForm = async (event: React.FormEvent) => { event.preventDefault(); + if (process.env.NODE_ENV === 'demo') { + setShowDemoModal(true); + return; + } const stepsHash = Object.fromEntries( steps.map(step => [step.step_number, step.instruction]) ); @@ -139,6 +145,13 @@ function AddRecipe() { submit + {showDemoModal && ( + setShowDemoModal(false)} + closeModal={() => setShowDemoModal(false)} + /> + )} ) } diff --git a/frontend/src/services/frontendApi.js b/frontend/src/services/frontendApi.js index 3a83ce7..4158c4a 100644 --- a/frontend/src/services/frontendApi.js +++ b/frontend/src/services/frontendApi.js @@ -1,4 +1,4 @@ -const baseUrl = process.env.NODE_ENV === 'production' +const baseUrl = process.env.NODE_ENV !== 'dev' ? '/' : 'http://localhost:3000/'; diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 7b700dd..d07a6bf 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -5,7 +5,6 @@ import react from "@vitejs/plugin-react"; export default defineConfig({ plugins: [react()], server: { - host: "ec683cee72d30c5030.fredzernia.com", - allowedHosts: ["ec683cee72d30c5030.fredzernia.com"], + allowedHosts: ["recipe-prod.fredzernia.com", "recipe-demo.fredzernia.com"], }, });