Skip to main content
FitAiid provides 8 powerful features to create a complete AI-powered fitness experience.

Feature Overview

User Authentication

Secure email/password and Google OAuth with JWT

AI Workout Generation

Personalized routines powered by OpenAI

Progress Tracking

Real-time statistics and workout history

Achievement System

Gamified rewards and streak tracking

Routine Management

Automatic day cycling and workout scheduling

Push Notifications

Real-time workout reminders and achievements

Email Verification

Secure account creation with code verification

User Profiles

Comprehensive fitness profiles and preferences

1. User Authentication & Authorization

FitAiid implements enterprise-grade authentication with multiple methods:
  • Email/Password: Secure registration with bcrypt hashing (12 salt rounds)
  • Google OAuth: Firebase Admin SDK integration
  • JWT Tokens: 30-day expiration with role-based access
  • Rate Limiting: 5 login attempts before 30-minute lockout
  • Email Verification: 6-digit codes with 15-minute expiration
All passwords must contain at least 8 characters, including uppercase, lowercase, and numbers.

Security Features

// From backend/src/models/User.js:607-621
userSchema.pre('save', async function(next) {
  if (!this.isModified('password')) {
    return next();
  }
  
  try {
    const saltRounds = 12;
    this.password = await bcrypt.hash(this.password, saltRounds);
    console.log(`🔒 Contraseña encriptada para: ${this.email}`);
    next();
  } catch (error) {
    console.error(`❌ Error encriptando contraseña:`, error.message);
    next(error);
  }
});
// From backend/src/models/User.js:653-669
userSchema.methods.incrementLoginAttempts = function() {
  if (this.lockUntil && this.lockUntil < Date.now()) {
    return this.updateOne({
      $unset: { lockUntil: 1 },
      $set: { loginAttempts: 1 }
    });
  }
  
  const updates = { $inc: { loginAttempts: 1 } };
  
  if (this.loginAttempts + 1 >= 5 && !this.isLocked) {
    updates.$set = { lockUntil: Date.now() + 30 * 60 * 1000 }; // 30 minutes
    console.log(`🔒 Cuenta bloqueada temporalmente: ${this.email}`);
  }
  
  return this.updateOne(updates);
};

2. AI-Powered Workout Generation

FitAiid uses OpenAI GPT models to generate personalized workout routines based on user profiles.
Users complete a comprehensive fitness questionnaire:
// User fitness profile schema (from User.js:275-341)
const fitnessProfile = {
  gender: 'hombre' | 'mujer',
  age: 28,                          // Min: 14, Max: 100
  height: 175,                      // cm, Min: 100, Max: 250
  weight: 75,                       // kg, Min: 30, Max: 300
  fitnessLevel: 'intermedio',       // 'principiante' | 'intermedio' | 'avanzado'
  mainGoal: 'tonificar',            // 'tonificar' | 'ganar masa muscular' | 'bajar de peso'
  medicalConditions: '',            // Optional, max 500 chars
  trainingLocation: 'gym',          // 'casa' | 'gym'
  trainingDaysPerWeek: 4,           // 1-7 days
  sessionDuration: '1 hr',          // '30 min' | '45 min' | '1 hr' | '+1 hr'
  questionnaireCompleted: true,
  questionnaireCompletedAt: new Date()
};
The fitness profile is used to generate contextual prompts for OpenAI, ensuring workouts match the user’s capabilities and goals.

BMI Calculation

// Automatic BMI calculation (User.js:565-581)
userSchema.virtual('bmi').get(function() {
  if (!this.fitnessProfile?.weight || !this.fitnessProfile?.height) {
    return null;
  }
  const heightInMeters = this.fitnessProfile.height / 100;
  const bmi = this.fitnessProfile.weight / (heightInMeters * heightInMeters);
  return Math.round(bmi * 10) / 10;
});

userSchema.virtual('bmiCategory').get(function() {
  const bmi = this.bmi;
  if (!bmi) return null;
  if (bmi < 18.5) return 'Bajo peso';
  if (bmi < 25) return 'Peso normal';
  if (bmi < 30) return 'Sobrepeso';
  return 'Obesidad';
});

3. Automatic Routine Cycling

FitAiid automatically cycles through workout days, skipping rest days and resetting when complete.
// GET /api/rutina/:userId/dia-actual
const response = await fetch(`/api/rutina/${userId}/dia-actual`, {
  headers: { 'Authorization': `Bearer ${token}` }
});

const data = await response.json();
Response:
{
  "success": true,
  "data": {
    "diaActual": {
      "indice": 0,
      "nombre": "lunes",
      "enfoque": "Pecho y Tríceps",
      "duracionTotal": 60,
      "caloriasEstimadas": 450,
      "ejercicios": [...],
      "completado": false
    },
    "progreso": {
      "cicloActual": 1,
      "diaActualIndex": 0,
      "diasCompletadosEsteCiclo": 0,
      "porcentajeCiclo": 0
    }
  }
}
Source: backend/src/controllers/rutinaController.js:13-37
When a user completes the last day of their routine, the system automatically starts a new cycle and resets all progress flags.

4. Progress Tracking & Statistics

Every completed workout is automatically logged:
// From backend/src/models/User.js:711-743
userSchema.methods.registrarEntrenamiento = async function(workoutData) {
  console.log(`📝 Registrando entrenamiento para ${this.email}`);
  
  const workout = {
    date: new Date(),
    nombre: workoutData.nombre,
    enfoque: workoutData.enfoque,
    duracionTotal: workoutData.duracionTotal,
    caloriasEstimadas: workoutData.caloriasEstimadas || 0,
    ejerciciosCompletados: workoutData.ejercicios?.length || 0,
    ejerciciosDetalles: workoutData.ejercicios || []
  };
  
  // Add to history
  this.fitnessStats.workoutHistory.push(workout);
  
  // Update totals
  this.fitnessStats.totalWorkouts += 1;
  this.fitnessStats.totalExercises += workout.ejerciciosCompletados;
  this.fitnessStats.totalMinutes += workout.duracionTotal;
  this.fitnessStats.totalCalories += workout.caloriasEstimadas;
  this.fitnessStats.lastWorkoutDate = workout.date;
  this.fitnessStats.lastStatsUpdate = new Date();
  
  // Calculate streak and check achievements
  this.calcularRacha();
  this.verificarLogros();
  
  await this.save();
  
  return workout;
};

5. Achievement System

Gamified achievements unlock automatically based on user progress.
// From backend/src/models/User.js:797-848
userSchema.methods.verificarLogros = function() {
  const stats = this.fitnessStats;
  const logros = [
    {
      id: 'first_workout',
      nombre: 'Primera Rutina',
      condicion: stats.totalWorkouts >= 1
    },
    {
      id: 'week_streak',
      nombre: 'Racha de 7 días',
      condicion: stats.currentStreak >= 7
    },
    {
      id: 'ten_workouts',
      nombre: 'Dedicación',
      condicion: stats.totalWorkouts >= 10
    },
    {
      id: 'fifty_workouts',
      nombre: 'Guerrero',
      condicion: stats.totalWorkouts >= 50
    },
    {
      id: 'month_streak',
      nombre: 'Leyenda',
      condicion: stats.currentStreak >= 30
    },
    {
      id: 'hundred_exercises',
      nombre: 'Incansable',
      condicion: stats.totalExercises >= 100
    }
  ];
  
  logros.forEach(logro => {
    if (logro.condicion) {
      const yaDesbloqueado = stats.achievements.some(
        a => a.achievementId === logro.id
      );
      
      if (!yaDesbloqueado) {
        stats.achievements.push({
          achievementId: logro.id,
          nombre: logro.nombre,
          unlockedAt: new Date()
        });
        console.log(`🏆 ¡Logro desbloqueado! ${logro.nombre}`);
      }
    }
  });
};

Available Achievements

Primera Rutina

Complete your first workout

Racha de 7 días

Workout 7 consecutive days

Dedicación

Complete 10 total workouts

Guerrero

Complete 50 total workouts

Leyenda

Maintain a 30-day streak

Incansable

Complete 100 total exercises
Achievements are checked automatically every time registrarEntrenamiento() is called.

6. Push Notifications

Real-time Web Push notifications using VAPID protocol.
// Step 1: Get VAPID public key
const keyResponse = await fetch('/api/notifications/vapid-public-key');
const { publicKey } = await keyResponse.json();

// Step 2: Request permission and subscribe
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: publicKey
});

// Step 3: Save subscription to backend
await fetch('/api/notifications/subscribe', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    userId: currentUser.id,
    subscription
  })
});
Source: backend/src/routes/notifications.js:33-79
VAPID keys must be generated and configured in your .env file. See the quickstart guide for setup instructions.

7. Email System

Secure email delivery using Nodemailer with Gmail SMTP.
// From backend/src/controllers/authController.js:525-547
const sendVerificationCodeEmail = async (email, firstName, code) => {
  const mailOptions = {
    from: `"FitAiid 💪" <${process.env.EMAIL_USER}>`,
    to: email,
    subject: "Código de Verificación - FitAiid",
    html: `
      <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
        <h2 style="color: #333;">¡Bienvenido a FitAiid!</h2>
        <p>Hola ${firstName},</p>
        <p>Gracias por registrarte. Tu código de verificación es:</p>
        <div style="background-color: #f4f4f4; padding: 20px; text-align: center; border-radius: 5px; margin: 20px 0;">
          <h1 style="color: #667eea; font-size: 36px; letter-spacing: 5px; margin: 0;">
            ${code}
          </h1>
        </div>
        <p>Este código expira en <strong>15 minutos</strong>.</p>
        <p style="color: #999; font-size: 14px;">Si no te registraste, ignora este correo.</p>
      </div>
    `
  };

  await transporter.sendMail(mailOptions);
};
Email configuration requires Gmail App Passwords. Regular Gmail passwords won’t work with SMTP authentication.

8. User Profile Management

// GET /api/auth/profile
const response = await fetch('/api/auth/profile', {
  headers: { 'Authorization': `Bearer ${token}` }
});
Response includes:
  • Basic info (name, email, phone)
  • Fitness profile (age, weight, height, goals)
  • Statistics (workouts, calories, streak)
  • BMI calculation
  • Achievements
  • Customer level (bronze/silver/gold/platinum)
Source: backend/src/controllers/authController.js:265-296

Complete Feature Integration

Here’s how all features work together:
1

User Registration

User signs up → Email verification → JWT token issued
2

Profile Setup

User completes fitness questionnaire → AI generates personalized routine
3

Daily Workout

User gets current day workout → Completes exercises → Marks day complete
4

Progress Tracking

System logs workout → Updates statistics → Calculates streak
5

Achievement Unlock

System checks achievements → Unlocks new badges → Sends push notification
6

Automatic Cycling

Next day is set → Rest days skipped → Routine resets when complete

API Reference

Complete endpoint documentation

Quickstart

Get started in 5 minutes

Build docs developers (and LLMs) love