feat: full health & fitness app with workout/meal planning
Some checks failed
CI/CD - Build, Push & Deploy / Build & Push Docker Image (push) Has been cancelled
CI/CD - Build, Push & Deploy / Update GitOps Manifest (push) Has been cancelled

- Login/register with JWT auth
- User profile (age, weight, height, goal, country)
- AI trainer agent: 7-day workout programs by goal
- AI dietitian agent: calorie-based meal plans with Turkish cuisine
- Shopping list generator from meal plans
- Modern Turkish UI (SPA)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-02 10:02:46 +00:00
parent d0d40c2195
commit c17f143a40
13 changed files with 3318 additions and 18 deletions

371
src/services/trainer.js Normal file
View File

@@ -0,0 +1,371 @@
"use strict";
const EXERCISES = {
chest: [
{ name: "Bench Press (Göğüs Presi)", sets: 4, reps: "8-10", rest_seconds: 90, notes: "Kontrollü iniş, göğüs kaslarını sıkın" },
{ name: "Incline Dumbbell Press (Üst Göğüs)", sets: 4, reps: "10-12", rest_seconds: 75, notes: "30 derece açı, tam hareket açıklığı" },
{ name: "Cable Flyes (Kablo Çapraz)", sets: 3, reps: "12-15", rest_seconds: 60, notes: "Kollar hafif bükük, göğüs merkezinde sıkın" },
{ name: "Dips (Paralel Bar)", sets: 3, reps: "10-12", rest_seconds: 75, notes: "Öne eğilin, göğüs hedefleyin" },
{ name: "Push-ups (Şınav)", sets: 3, reps: "15-20", rest_seconds: 45, notes: "Geniş tutuş, göğüs yere değsin" },
{ name: "Dumbbell Pullover", sets: 3, reps: "12", rest_seconds: 60, notes: "Kollar hafif bükük, göğüs gerilmesini hissedin" },
],
back: [
{ name: "Deadlift (Toplu Kaldırma)", sets: 4, reps: "6-8", rest_seconds: 120, notes: "Sırt düz, kalçadan kaldırın" },
{ name: "Barbell Row (Sırt Çekişi)", sets: 4, reps: "8-10", rest_seconds: 90, notes: "Gövde yere paralel, dirsekler geri" },
{ name: "Lat Pulldown (Lat Çekme)", sets: 4, reps: "10-12", rest_seconds: 75, notes: "Geniş tutuş, göğüse çekin" },
{ name: "Seated Cable Row (Oturarak Çekiş)", sets: 3, reps: "10-12", rest_seconds: 75, notes: "Sırt düz, kürek kemiklerini sıkın" },
{ name: "T-Bar Row", sets: 3, reps: "10-12", rest_seconds: 75, notes: "Göğüse doğru çekin, sırt kaslarını sıkın" },
{ name: "Face Pull (Yüz Çekişi)", sets: 3, reps: "15", rest_seconds: 45, notes: "Omuz arkası ve üst sırt hedefli" },
],
legs: [
{ name: "Squat (Çömelme)", sets: 4, reps: "8-10", rest_seconds: 120, notes: "Diz açısı 90 derece, sırt düz" },
{ name: "Romanian Deadlift (Romen Kaldırma)", sets: 4, reps: "10-12", rest_seconds: 90, notes: "Bacak arkası gerilmeli, kalça menteşesi" },
{ name: "Leg Press (Bacak Presi)", sets: 4, reps: "10-12", rest_seconds: 90, notes: "Ayaklar omuz genişliğinde" },
{ name: "Walking Lunges (Yürüyüş Hamle)", sets: 3, reps: "12 (her bacak)", rest_seconds: 75, notes: "Diz parmak ucunu geçmesin" },
{ name: "Leg Curl (Bacak Büküm)", sets: 3, reps: "12-15", rest_seconds: 60, notes: "Kontrollü hareket, hamstring sıkın" },
{ name: "Leg Extension (Bacak Açma)", sets: 3, reps: "12-15", rest_seconds: 60, notes: "Tepe noktada sıkın" },
{ name: "Calf Raise (Baldır Kaldırma)", sets: 4, reps: "15-20", rest_seconds: 45, notes: "Tam gerilme ve tam kasılma" },
],
shoulders: [
{ name: "Overhead Press (Omuz Presi)", sets: 4, reps: "8-10", rest_seconds: 90, notes: "Ayakta, core sıkı, başın üstüne itin" },
{ name: "Lateral Raise (Yan Kaldırma)", sets: 4, reps: "12-15", rest_seconds: 60, notes: "Hafif ağırlık, kontrollü hareket" },
{ name: "Front Raise (Ön Kaldırma)", sets: 3, reps: "12", rest_seconds: 60, notes: "Dönüşümlü kollarla" },
{ name: "Reverse Fly (Ters Sinek)", sets: 3, reps: "15", rest_seconds: 60, notes: "Arka omuz hedefli, kürek kemiklerini sıkın" },
{ name: "Arnold Press", sets: 3, reps: "10-12", rest_seconds: 75, notes: "Rotasyonlu hareket, omuzun 3 başını çalıştırır" },
{ name: "Shrugs (Omuz Silkme)", sets: 3, reps: "12-15", rest_seconds: 60, notes: "Trapez hedefli, yukarı çekin" },
],
arms: [
{ name: "Barbell Curl (Halter Biceps)", sets: 4, reps: "10-12", rest_seconds: 60, notes: "Dirsekler sabit, biceps sıkın" },
{ name: "Triceps Pushdown (Triceps İtme)", sets: 4, reps: "10-12", rest_seconds: 60, notes: "Dirsekler vücuda yapışık" },
{ name: "Hammer Curl (Çekiç Curl)", sets: 3, reps: "12", rest_seconds: 60, notes: "Nötr tutuş, ön kol da çalışır" },
{ name: "Overhead Triceps Extension", sets: 3, reps: "12", rest_seconds: 60, notes: "Dirsekler sabit, tam gerilme" },
{ name: "Concentration Curl (Konsantrasyon)", sets: 3, reps: "12 (her kol)", rest_seconds: 45, notes: "Yavaş ve kontrollü" },
{ name: "Skull Crushers", sets: 3, reps: "10-12", rest_seconds: 60, notes: "Alına doğru indirin, triceps sıkın" },
{ name: "Wrist Curl (Bilek Büküm)", sets: 2, reps: "15-20", rest_seconds: 30, notes: "Ön kol güçlendirme" },
],
cardio: [
{ name: "Koşu Bandı (Tempolu Koşu)", sets: 1, reps: "25 dakika", rest_seconds: 0, notes: "Kalp atış hızı %65-75 arası" },
{ name: "Bisiklet", sets: 1, reps: "20 dakika", rest_seconds: 0, notes: "Orta direnç, stabil tempo" },
{ name: "Kürek Çekme Makinesi", sets: 1, reps: "15 dakika", rest_seconds: 0, notes: "Tüm vücut kardiyosu" },
{ name: "Merdiven Çıkma (StairMaster)", sets: 1, reps: "15 dakika", rest_seconds: 0, notes: "Bacak ve kalça hedefli" },
{ name: "Eliptik Bisiklet", sets: 1, reps: "20 dakika", rest_seconds: 0, notes: "Düşük etki, tüm vücut" },
],
hiit: [
{ name: "Burpee", sets: 4, reps: "30 saniye", rest_seconds: 30, notes: "Maksimum efor" },
{ name: "Mountain Climber (Dağcı)", sets: 4, reps: "30 saniye", rest_seconds: 30, notes: "Core sıkı, hızlı hareket" },
{ name: "Jump Squat (Sıçramalı Çömelme)", sets: 4, reps: "30 saniye", rest_seconds: 30, notes: "Yumuşak iniş, diz koruması" },
{ name: "High Knees (Yüksek Diz)", sets: 4, reps: "30 saniye", rest_seconds: 30, notes: "Dizleri göğüse çekin" },
{ name: "Box Jump (Kutu Atlama)", sets: 4, reps: "30 saniye", rest_seconds: 30, notes: "İki ayakla atlayıp yumuşak inin" },
{ name: "Battle Ropes (Savaş İpleri)", sets: 4, reps: "30 saniye", rest_seconds: 30, notes: "Kollar ve core hedefli" },
{ name: "Kettlebell Swing", sets: 4, reps: "30 saniye", rest_seconds: 30, notes: "Kalça menteşesi, explosive hareket" },
],
core: [
{ name: "Plank (Düz Plank)", sets: 3, reps: "45 saniye", rest_seconds: 30, notes: "Vücut düz çizgi, core sıkı" },
{ name: "Russian Twist (Rus Bükülmesi)", sets: 3, reps: "20 (her taraf)", rest_seconds: 30, notes: "Ayaklar yerden, kontrollü rotasyon" },
{ name: "Bicycle Crunch (Bisiklet Karın)", sets: 3, reps: "20 (her taraf)", rest_seconds: 30, notes: "Karşı diz-dirsek buluşsun" },
{ name: "Leg Raise (Bacak Kaldırma)", sets: 3, reps: "15", rest_seconds: 30, notes: "Alt karın hedefli, sırt yere yapışık" },
{ name: "Dead Bug", sets: 3, reps: "12 (her taraf)", rest_seconds: 30, notes: "Sırt yere yapışık, kontrollü hareket" },
{ name: "Side Plank (Yan Plank)", sets: 2, reps: "30 saniye (her taraf)", rest_seconds: 30, notes: "Kalça düşmesin" },
],
flexibility: [
{ name: "Kedi-İnek Germe", sets: 1, reps: "10 tekrar", rest_seconds: 0, notes: "Nefesle senkronize" },
{ name: "Hamstring Germe", sets: 1, reps: "30 saniye (her bacak)", rest_seconds: 0, notes: "Bacak arkasını hissedin" },
{ name: "Quadriceps Germe", sets: 1, reps: "30 saniye (her bacak)", rest_seconds: 0, notes: "Dengeyi koruyun" },
{ name: "Güvercin Pozu", sets: 1, reps: "30 saniye (her taraf)", rest_seconds: 0, notes: "Kalça açıcı" },
{ name: "Çocuk Pozu", sets: 1, reps: "45 saniye", rest_seconds: 0, notes: "Sırt ve kalça rahatlama" },
{ name: "Kelebek Germe", sets: 1, reps: "30 saniye", rest_seconds: 0, notes: "İç bacak ve kasık" },
{ name: "Omuz Çapraz Germe", sets: 1, reps: "30 saniye (her kol)", rest_seconds: 0, notes: "Omuz esnekliği" },
{ name: "Aşağı Bakan Köpek", sets: 1, reps: "45 saniye", rest_seconds: 0, notes: "Tüm arka zincir" },
{ name: "Dünya Germe (World's Greatest Stretch)", sets: 1, reps: "5 tekrar (her taraf)", rest_seconds: 0, notes: "Tüm vücut mobilitesi" },
{ name: "Foam Rolling - Sırt", sets: 1, reps: "60 saniye", rest_seconds: 0, notes: "Yavaş ve kontrollü" },
{ name: "Foam Rolling - Bacaklar", sets: 1, reps: "60 saniye (her bacak)", rest_seconds: 0, notes: "Ağrılı noktada durun" },
],
compound: [
{ name: "Squat (Çömelme)", sets: 5, reps: "5", rest_seconds: 180, notes: "Ağır, compound hareket, güç odaklı" },
{ name: "Bench Press (Göğüs Presi)", sets: 5, reps: "5", rest_seconds: 180, notes: "Ağır yükleme, temel hareket" },
{ name: "Deadlift (Toplu Kaldırma)", sets: 5, reps: "5", rest_seconds: 180, notes: "En güçlü compound hareket" },
{ name: "Overhead Press (Omuz Presi)", sets: 5, reps: "5", rest_seconds: 150, notes: "Ayakta, tüm üst vücut" },
{ name: "Barbell Row (Sırt Çekişi)", sets: 5, reps: "5", rest_seconds: 150, notes: "Ağır, sırt kalınlığı" },
],
circuit: [
{ name: "Kettlebell Swing", sets: 3, reps: "15", rest_seconds: 15, notes: "Hızlı geçiş" },
{ name: "Push-ups (Şınav)", sets: 3, reps: "15", rest_seconds: 15, notes: "Hızlı geçiş" },
{ name: "Goblet Squat", sets: 3, reps: "15", rest_seconds: 15, notes: "Hızlı geçiş" },
{ name: "Dumbbell Row", sets: 3, reps: "12 (her kol)", rest_seconds: 15, notes: "Hızlı geçiş" },
{ name: "Jumping Lunges", sets: 3, reps: "12 (her bacak)", rest_seconds: 15, notes: "Hızlı geçiş" },
{ name: "Plank", sets: 3, reps: "30 saniye", rest_seconds: 60, notes: "Devrenin sonu, 60sn dinlenme sonra tekrar" },
],
};
function pickRandom(arr, count) {
const shuffled = [...arr].sort(() => 0.5 - Math.random());
return shuffled.slice(0, count);
}
function adjustForAge(exercises, age) {
if (age > 55) {
return exercises.map((e) => ({
...e,
sets: Math.max(2, e.sets - 1),
notes: e.notes + " | Yaşa uygun: Ağırlığı azaltın, kontrollü hareket",
}));
}
if (age < 20) {
return exercises.map((e) => ({
...e,
notes: e.notes + " | Genç sporcu: Form ve tekniğe odaklanın",
}));
}
return exercises;
}
function generateLoseWeightProgram(profile) {
const { age, gender } = profile;
const days = [
{
day: "Pazartesi",
type: "cardio",
title: "HIIT Kardiyo",
exercises: adjustForAge([...pickRandom(EXERCISES.hiit, 5), ...pickRandom(EXERCISES.core, 3)], age),
},
{
day: "Salı",
type: "strength",
title: "Üst Vücut Devre",
exercises: adjustForAge([...pickRandom(EXERCISES.chest, 2), ...pickRandom(EXERCISES.back, 2), ...pickRandom(EXERCISES.shoulders, 2), ...pickRandom(EXERCISES.core, 2)], age),
},
{
day: "Çarşamba",
type: "cardio",
title: "Kardiyo & Core",
exercises: adjustForAge([...pickRandom(EXERCISES.cardio, 3), ...pickRandom(EXERCISES.core, 4)], age),
},
{
day: "Perşembe",
type: "strength",
title: "Alt Vücut Devre",
exercises: adjustForAge([...pickRandom(EXERCISES.legs, 4), ...pickRandom(EXERCISES.core, 2)], age),
},
{
day: "Cuma",
type: "cardio",
title: "HIIT & Circuit",
exercises: adjustForAge(EXERCISES.circuit, age),
},
{
day: "Cumartesi",
type: "cardio",
title: "Uzun Kardiyo",
exercises: adjustForAge([
{ name: "Açık Hava Koşusu veya Yürüyüş", sets: 1, reps: "45-60 dakika", rest_seconds: 0, notes: "Orta tempo, yağ yakım bölgesi" },
...pickRandom(EXERCISES.flexibility, 4),
], age),
},
{
day: "Pazar",
type: "rest",
title: "Dinlenme & İyileşme",
exercises: adjustForAge(EXERCISES.flexibility, age),
},
];
return days;
}
function generateBuildMuscleProgram(profile) {
const { age, gender } = profile;
const days = [
{
day: "Pazartesi",
type: "strength",
title: "Göğüs & Triceps",
exercises: adjustForAge([...EXERCISES.chest.slice(0, 4), ...EXERCISES.arms.filter((e) => e.name.includes("Triceps") || e.name.includes("Skull")).slice(0, 3)], age),
},
{
day: "Salı",
type: "strength",
title: "Sırt & Biceps",
exercises: adjustForAge([...EXERCISES.back.slice(0, 4), ...EXERCISES.arms.filter((e) => e.name.includes("Curl") || e.name.includes("Hammer")).slice(0, 3)], age),
},
{
day: "Çarşamba",
type: "strength",
title: "Bacak Günü",
exercises: adjustForAge(EXERCISES.legs, age),
},
{
day: "Perşembe",
type: "rest",
title: "Aktif Dinlenme",
exercises: adjustForAge([
{ name: "Hafif Yürüyüş", sets: 1, reps: "20 dakika", rest_seconds: 0, notes: "Kan dolaşımı için" },
...pickRandom(EXERCISES.flexibility, 5),
], age),
},
{
day: "Cuma",
type: "strength",
title: "Omuz & Trapez",
exercises: adjustForAge([...EXERCISES.shoulders, ...pickRandom(EXERCISES.core, 3)], age),
},
{
day: "Cumartesi",
type: "strength",
title: "Kol Günü (Biceps & Triceps)",
exercises: adjustForAge([...EXERCISES.arms, ...pickRandom(EXERCISES.core, 2)], age),
},
{
day: "Pazar",
type: "rest",
title: "Tam Dinlenme",
exercises: adjustForAge(EXERCISES.flexibility, age),
},
];
return days;
}
function generateGainWeightProgram(profile) {
const { age, gender } = profile;
const days = [
{
day: "Pazartesi",
type: "strength",
title: "Compound Güç - Üst Vücut",
exercises: adjustForAge([...EXERCISES.compound.filter((e) => e.name.includes("Bench") || e.name.includes("Overhead") || e.name.includes("Row")), ...pickRandom(EXERCISES.chest, 2), ...pickRandom(EXERCISES.shoulders, 1)], age),
},
{
day: "Salı",
type: "strength",
title: "Compound Güç - Alt Vücut",
exercises: adjustForAge([...EXERCISES.compound.filter((e) => e.name.includes("Squat") || e.name.includes("Deadlift")), ...pickRandom(EXERCISES.legs, 3)], age),
},
{
day: "Çarşamba",
type: "rest",
title: "Dinlenme & Toparlanma",
exercises: adjustForAge(pickRandom(EXERCISES.flexibility, 6), age),
},
{
day: "Perşembe",
type: "strength",
title: "Göğüs, Sırt & Omuz",
exercises: adjustForAge([...pickRandom(EXERCISES.chest, 3), ...pickRandom(EXERCISES.back, 3), ...pickRandom(EXERCISES.shoulders, 2)], age),
},
{
day: "Cuma",
type: "strength",
title: "Bacak & Core",
exercises: adjustForAge([...pickRandom(EXERCISES.legs, 5), ...pickRandom(EXERCISES.core, 3)], age),
},
{
day: "Cumartesi",
type: "strength",
title: "Kollar & Yardımcı Kaslar",
exercises: adjustForAge([...EXERCISES.arms, ...pickRandom(EXERCISES.core, 2)], age),
},
{
day: "Pazar",
type: "rest",
title: "Tam Dinlenme",
exercises: adjustForAge(EXERCISES.flexibility, age),
},
];
return days;
}
function generateMaintainProgram(profile) {
const { age, gender } = profile;
const days = [
{
day: "Pazartesi",
type: "strength",
title: "Üst Vücut Kuvvet",
exercises: adjustForAge([...pickRandom(EXERCISES.chest, 2), ...pickRandom(EXERCISES.back, 2), ...pickRandom(EXERCISES.shoulders, 2)], age),
},
{
day: "Salı",
type: "cardio",
title: "Kardiyo & Core",
exercises: adjustForAge([...pickRandom(EXERCISES.cardio, 2), ...pickRandom(EXERCISES.core, 4)], age),
},
{
day: "Çarşamba",
type: "strength",
title: "Alt Vücut Kuvvet",
exercises: adjustForAge(pickRandom(EXERCISES.legs, 5), age),
},
{
day: "Perşembe",
type: "flexibility",
title: "Esneklik & Mobilite",
exercises: adjustForAge(EXERCISES.flexibility, age),
},
{
day: "Cuma",
type: "strength",
title: "Tüm Vücut",
exercises: adjustForAge([...pickRandom(EXERCISES.compound, 3), ...pickRandom(EXERCISES.arms, 2), ...pickRandom(EXERCISES.core, 2)], age),
},
{
day: "Cumartesi",
type: "cardio",
title: "Açık Hava Aktivitesi",
exercises: adjustForAge([
{ name: "Yürüyüş veya Koşu", sets: 1, reps: "30-45 dakika", rest_seconds: 0, notes: "Doğada, orta tempo" },
{ name: "Bisiklet veya Yüzme", sets: 1, reps: "30 dakika", rest_seconds: 0, notes: "Keyifli tempo" },
...pickRandom(EXERCISES.flexibility, 3),
], age),
},
{
day: "Pazar",
type: "rest",
title: "Tam Dinlenme",
exercises: adjustForAge(pickRandom(EXERCISES.flexibility, 5), age),
},
];
return days;
}
function generateWorkoutProgram(profile) {
const goalMap = {
lose_weight: generateLoseWeightProgram,
build_muscle: generateBuildMuscleProgram,
gain_weight: generateGainWeightProgram,
maintain: generateMaintainProgram,
};
const generator = goalMap[profile.goal] || goalMap.maintain;
const program = generator(profile);
const goalNames = {
lose_weight: "Yağ Yakma & Zayıflama",
build_muscle: "Kas Geliştirme",
gain_weight: "Kilo Alma & Güçlenme",
maintain: "Formu Koruma",
};
const activityNotes = {
sedentary: "Başlangıç seviyesi - yavaş başlayın, formu öğrenin",
light: "Hafif seviye - ağırlıkları kademeli artırın",
moderate: "Orta seviye - standart program",
active: "İleri seviye - ağırlıkları artırabilirsiniz",
very_active: "Elit seviye - süper setler ve drop setler ekleyebilirsiniz",
};
return {
title: `${goalNames[profile.goal] || "Fitness"} Programı`,
goal: profile.goal,
level: profile.activity_level,
note: activityNotes[profile.activity_level] || "",
general_tips: [
"Her antrenmandan önce 5-10 dakika ısınma yapın",
"Her antrenmandan sonra 5-10 dakika soğuma ve germe yapın",
"Günde en az 2-3 litre su için",
"Uyku düzeninize dikkat edin (7-9 saat)",
"Ağrı hissederseniz hareketi durdurun",
"Haftada en az 1 gün tam dinlenme yapın",
],
days: program,
};
}
module.exports = { generateWorkoutProgram };