Skip to content

DanielMachluf/Joyi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Joyi

Save social recipe videos as a personal cookbook, then ask an AI assistant for recipe-specific help.

Joyi is a full-stack recipe collection app built around a simple idea: paste a recipe link from TikTok, Instagram, or Facebook, let automation extract the useful cooking data, and keep the result in a clean private dashboard.

The project is split into a React/Vite frontend, an Express/TypeScript backend, a MySQL database layer, and n8n workflows that handle recipe scraping and AI responses.

Live demo: joyi-project.web.app


Screenshots

Landing Page

Landing Page

The public landing page opens with a bold hero — "Save Every Recipe You Love" — and three floating feature callouts overlaid on a food photography backdrop: Save from any platform, AI does the magic, and Your recipes, your way. The top navigation bar keeps things minimal with Home, Login, and a prominent Get Started CTA. Social proof sits below the hero copy with a 4.9-star rating and a row of avatar icons, establishing trust before the user signs up.


Register Page

Register Page

New users land on a clean, centered registration layout. The left column sets the tone — "Create your recipe collection" with a short tagline — while the form card in the center collects first name, last name, email, and password under the Joyi logo. A decorative cookbook illustration on the right reinforces the personal-cookbook concept. Existing users can jump straight to login via the Already saving recipes? Login link at the bottom.


Dashboard

Dashboard

The main workspace for every user. A persistent sidebar holds navigation links (Dashboard, Favorites, Collections, Profile, What is Joyi?), a Upgrade to Premium prompt, and the signed-in account badge. The content area opens with a personalised greeting and a recipe count at a glance. A single input bar accepts TikTok, Instagram, and Facebook links — paste a URL and hit Analyze Recipe to kick off the scraping workflow. Saved recipes appear below as cards, each showing the source platform badge, a polaroid-style thumbnail, recipe name, and a full macro summary (calories, protein, carbs, fats, servings). Platform filter chips and a search bar sit above the grid for quick browsing.


Recipe Page

Recipe Page

Opening any recipe card expands into a full detail view. The page leads with the recipe title in a large serif heading, a polaroid thumbnail, and at-a-glance metadata — platform badge, serving count, and total calories. A short summary paragraph sits below. Three side-by-side panels fill the lower half: a Nutrition card with a Total Recipe / Per Serving toggle and animated macro progress bars, an Ingredients list, and numbered Instructions steps. A floating Joy launcher in the bottom-right corner shows a welcome tip and opens the AI assistant with a single click.


Joy — AI Recipe Assistant

Joy Assistant

Joy is Joyi's built-in recipe assistant, accessible from every recipe page. The chat panel opens with Joy's avatar and a context-aware greeting tied to the current recipe. Users can type any question or tap a suggested quick-reply chip — How can I make this healthier?, What can I substitute? — and Joy responds with recipe-specific advice powered by the n8n AI workflow. The input field at the bottom accepts freeform questions about swaps, prep techniques, macros, and more.


n8n Automation Workflow

n8n Workflow

Both automation pipelines live entirely in n8n. The Recipe Scraping workflow (top) starts at the ScrapeRecipe Webhook, scrapes the social page, routes the raw HTML through a platform Switch node into separate TikTok, Instagram, and Facebook caption extractors, validates the output with an If gate, then runs two sequential AI steps — one for the recipe summary and one for nutrition extraction — before reformatting everything into a single JSON object and returning it via Respond to Webhook. The AI Assistant workflow (bottom) is leaner: the Askai Webhook feeds the user's question and recipe context into an AI Agent node backed by an OpenAI Chat Model with Simple Memory, then responds directly.


What It Does

  • Saves recipes from social links.
  • Supports TikTok, Instagram, and Facebook recipe sources.
  • Extracts recipe title, ingredients, instructions, servings, thumbnail, calories, and macro nutrition.
  • Stores every recipe per authenticated user.
  • Provides login/register flows with JWT authentication.
  • Shows a searchable, filterable recipe dashboard.
  • Opens a detailed recipe page with nutrition tabs, ingredients, instructions, and the original video link.
  • Includes Joy, an AI recipe assistant that answers questions using the saved recipe context.
  • Uses n8n webhooks as the automation layer between the app and external scraping / AI workflows.

Project Structure

Joyi/
├── Backend/
│   ├── src/
│   │   ├── 2-utils/          # config, database access, crypto, n8n parsing
│   │   ├── 3-models/         # Joi-backed models and typed domain objects
│   │   ├── 4-services/       # business logic for users, recipes, and AI
│   │   ├── 5-controllers/    # Express route controllers
│   │   ├── 6-middleware/     # auth, XSS stripping, error handling
│   │   └── app.ts            # Express app bootstrap
│   ├── package.json
│   └── tsconfig.json
│
├── Frontend/
│   ├── src/
│   │   ├── Components/       # pages, cards, layout, assistant UI
│   │   ├── Models/           # frontend TypeScript models
│   │   ├── Services/         # API clients
│   │   ├── Utils/            # app config, auth headers, notifications
│   │   ├── store/            # lightweight auth store
│   │   └── main.tsx
│   ├── package.json
│   └── vite.config.ts
│
└── Database/                 # reserved for database scripts / exports

Tech Stack

Frontend

  • React 19
  • TypeScript
  • Vite
  • React Router
  • Axios
  • React Hook Form
  • Notyf notifications
  • Lucide React icons

Backend

  • Node.js
  • Express 5
  • TypeScript
  • MySQL2
  • Joi validation
  • JWT authentication
  • HMAC SHA-512 password hashing
  • n8n webhook integration

Automation

  • n8n recipe scraping webhook
  • n8n AI assistant webhook
  • Backend response normalization for inconsistent n8n webhook output

Main User Flow

  1. A user registers or logs in.
  2. The frontend stores the JWT in localStorage.
  3. The dashboard sends authenticated requests with Authorization: Bearer <token>.
  4. The user pastes a TikTok, Instagram, or Facebook recipe link.
  5. The backend sends that link to the configured n8n scraping webhook.
  6. n8n returns normalized recipe data.
  7. The backend validates and saves the recipe in MySQL.
  8. The frontend displays the recipe in the dashboard.
  9. The user can open the recipe page and ask Joy questions.
  10. Joy sends the question plus recipe context to a second n8n AI webhook.

Backend

The backend runs from Backend/src/app.ts and listens on the port configured in .env.

Backend Commands

cd Backend
npm install
npm start

npm start runs:

nodemon --exec ts-node src/app.ts --quiet

Backend Environment Variables

Create Backend/.env:

ENVIRONMENT=development
PORT=4000

MYSQL_HOST=localhost
MYSQL_USER=root
MYSQL_PASSWORD=your_mysql_password
MYSQL_DATABASE=joyi

HASH_SALT=replace_with_a_long_random_salt
JWT_SECRET=replace_with_a_long_random_secret

N8N_WEBHOOK_URL=https://your-n8n-domain/webhook/recipe-scraper
N8N_ASK_AI_WEBHOOK_URL=https://your-n8n-domain/webhook/ask-recipe-ai

Do not commit real secrets.

API Routes

Method Route Auth Purpose
POST /api/register No Creates a user and returns a JWT
POST /api/login No Logs in and returns a JWT
POST /api/recipes/scrape Yes Sends a social recipe URL to n8n, saves the returned recipe
GET /api/recipes Yes Returns the logged-in user's saved recipes
GET /api/recipes/:id Yes Returns one recipe owned by the logged-in user
DELETE /api/recipes/:id Yes Deletes one recipe owned by the logged-in user
POST /api/recipes/ask Yes Sends a recipe question to the n8n AI workflow

Security Notes

  • Passwords are hashed with HMAC SHA-512 using HASH_SALT.
  • JWTs expire after 3 hours.
  • Protected recipe routes verify the JWT.
  • Recipe reads and deletes are scoped by userId.
  • Incoming string fields are passed through striptags to reduce XSS risk.
  • Joi models validate auth, recipe, and AI assistant payloads.

Frontend

The frontend is a Vite React app configured to talk to:

http://localhost:4000/api

This is defined in Frontend/src/Utils/AppConfig.ts.

Frontend Commands

cd Frontend
npm install
npm run dev

For production build:

npm run build

Frontend Pages

Route Purpose
/ and /landing Public landing page
/login Login form
/register Registration form
/dashboard Main saved-recipe dashboard
/recipe/:id Detailed recipe view with Joy assistant
/favorites Placeholder / coming feature area
/collections Placeholder / coming feature area
/history Placeholder / coming feature area
/profile Placeholder / coming feature area
/settings Placeholder / coming feature area

Frontend Features

  • Protected routes redirect logged-out users to /login.
  • Public auth routes redirect logged-in users to /dashboard.
  • Dashboard supports paste-from-clipboard, platform filtering, search, save, and delete.
  • Recipe pages show image, platform badge, servings, nutrition totals/per-serving, ingredients, steps, and original video CTA.
  • Joy assistant opens as a recipe-specific chat panel with suggested questions and animated answers.

Database

The backend expects a MySQL database. The Database/ folder contains JoyiDB.sql with the full schema. To set up locally, import it into MySQL:

mysql -u root -p < Database/JoyiDB.sql

The schema includes at least:

create database if not exists joyi;
use joyi;

create table users (
    userId int primary key auto_increment,
    firstName varchar(50) not null,
    lastName varchar(50) not null,
    email varchar(255) not null unique,
    password varchar(255) not null,
    role varchar(20) not null
);

create table recipes (
    recipeId int primary key auto_increment,
    userId int not null,
    title varchar(255) not null,
    linkUrl varchar(1000) not null,
    platform enum('tiktok', 'instagram', 'facebook') not null,
    ingredients json not null,
    instructions text null,
    servings varchar(50) null,
    thumbnail varchar(1000) null,
    totalCalories decimal(10,2) null,
    caloriesPerServing decimal(10,2) null,
    protein decimal(10,2) null,
    carbs decimal(10,2) null,
    fats decimal(10,2) null,
    proteinPerServing decimal(10,2) null,
    carbsPerServing decimal(10,2) null,
    fatsPerServing decimal(10,2) null,
    savedAt timestamp not null default current_timestamp,
    foreign key (userId) references users(userId)
);

The backend stores ingredients as JSON text and safely parses it when reading recipes back from the database.


n8n Integration

n8n is the automation heart of this project. The backend does not scrape social platforms directly and does not call an AI model directly. Instead, it calls two external n8n webhooks.

1. Recipe Scraping Workflow

Configured by:

N8N_WEBHOOK_URL=...

Called from:

Backend/src/4-services/recipe-service.ts

Backend request sent to n8n:

{
  "linkUrl": "https://www.tiktok.com/..."
}

Expected normalized n8n response:

{
  "title": "Roasted Tomato Rigatoni",
  "linkUrl": "https://www.tiktok.com/...",
  "platform": "tiktok",
  "ingredients": ["tomatoes", "rigatoni", "garlic"],
  "instructions": "Step 1...\nStep 2...",
  "servings": "2",
  "thumbnail": "https://...",
  "totalCalories": 800,
  "caloriesPerServing": 400,
  "protein": 30,
  "carbs": 110,
  "fats": 25,
  "proteinPerServing": 15,
  "carbsPerServing": 55,
  "fatsPerServing": 12.5
}

The backend accepts n8n responses as a plain object, an array, or an n8n { json: ... } envelope. It also handles junk prefixes such as null{...} through Backend/src/2-utils/n8n-parser.ts.

The workflow should:

  • Receive linkUrl.
  • Detect or return the source platform.
  • Extract title, thumbnail, ingredients, instructions, servings, calories, and macros.
  • Return one clean recipe object through the n8n Respond to Webhook node.

2. Joy AI Assistant Workflow

Configured by:

N8N_ASK_AI_WEBHOOK_URL=...

Called from:

Backend/src/4-services/ai-service.ts

Backend request sent to n8n:

{
  "question": "Can I meal prep this?",
  "recipe": {
    "recipeId": 12,
    "title": "Protein Pancakes",
    "ingredients": ["eggs", "banana", "protein powder"],
    "instructions": "Mix and cook.",
    "servings": "2",
    "caloriesPerServing": 270,
    "proteinPerServing": 16,
    "carbsPerServing": 30,
    "fatsPerServing": 8
  }
}

Accepted n8n response keys:

{ "text": "Yes, this works well for meal prep..." }

or:

{ "answer": "Yes, this works well for meal prep..." }

or:

{ "output": "Yes, this works well for meal prep..." }

The workflow should:

  • Receive a user question and recipe context.
  • Pass both into an AI model node.
  • Return a concise helpful answer using text, answer, or output.
  • Avoid returning unrelated metadata as the final webhook response.

Running The Full Project Locally

  1. Start MySQL and create the database/tables.
  2. Create Backend/.env with database, JWT, hash salt, and n8n webhook values.
  3. Start the backend:
cd Backend
npm install
npm start
  1. Start the frontend in a second terminal:
cd Frontend
npm install
npm run dev
  1. Open the Vite URL shown in the terminal, usually:
http://localhost:5173

Data Shape

A saved recipe looks like:

interface RecipeModel {
    recipeId: number;
    userId: number;
    title: string;
    linkUrl: string;
    platform: "tiktok" | "instagram" | "facebook";
    ingredients: string[];
    instructions: string;
    servings: string;
    thumbnail: string;
    totalCalories: number;
    caloriesPerServing: number;
    protein: number;
    carbs: number;
    fats: number;
    proteinPerServing: number;
    carbsPerServing: number;
    fatsPerServing: number;
    savedAt: Date;
}

Current Notes

  • Database/JoyiDB.sql contains the full MySQL schema.
  • Backend/.env.example documents the required local environment variables.
  • The n8n workflows are required for the core save-recipe and ask-Joy features to work.
  • The frontend is deployed on Firebase at joyi-project.web.app; the backend must be running separately for data features to function.

Suggested Next Improvements

  • Add automated tests for the n8n parser and service validation.
  • Add request rate limiting around auth and n8n webhook routes.
  • Deploy the backend and configure production environment variables so the live Firebase frontend is fully functional.

About

Full-stack recipe cookbook app — paste a TikTok, Instagram, or Facebook link and an n8n automation extracts ingredients, instructions, and nutrition data. Built with React 19, Express 5, MySQL, and JWT auth. Includes Joy, an AI assistant that answers questions about any saved recipe.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors