Default exercises are created in selected language. Initial GUI added
This commit is contained in:
@@ -1,43 +1,43 @@
|
||||
name,type,bodyWeightPercentage,isUnilateral
|
||||
Air Squats,BODYWEIGHT,1.0,false
|
||||
Barbell Row,STRENGTH,0,false
|
||||
Bench Press,STRENGTH,0,false
|
||||
Bicep Curl,STRENGTH,0,true
|
||||
Bulgarian Split-Squat Jumps,BODYWEIGHT,1.0,true
|
||||
Bulgarian Split-Squats,BODYWEIGHT,1.0,true
|
||||
Burpees,BODYWEIGHT,1.0,false
|
||||
Calf Raise,STRENGTH,0,true
|
||||
Chin-Ups,BODYWEIGHT,1.0,false
|
||||
Cycling,CARDIO,0,false
|
||||
Deadlift,STRENGTH,0,false
|
||||
Dips,BODYWEIGHT,1.0,false
|
||||
Dumbbell Curl,STRENGTH,0,true
|
||||
Dumbbell Shoulder Press,STRENGTH,0,true
|
||||
Face Pull,STRENGTH,0,false
|
||||
Front Squat,STRENGTH,0,false
|
||||
Hammer Curl,STRENGTH,0,true
|
||||
Handstand,BODYWEIGHT,1.0,false
|
||||
Hip Thrust,STRENGTH,0,false
|
||||
Jump Rope,CARDIO,0,false
|
||||
Lat Pulldown,STRENGTH,0,false
|
||||
Leg Extension,STRENGTH,0,true
|
||||
Leg Press,STRENGTH,0,false
|
||||
Lunges,BODYWEIGHT,1.0,true
|
||||
Mountain Climbers,CARDIO,0,false
|
||||
Muscle-Up,BODYWEIGHT,1.0,false
|
||||
Overhead Press,STRENGTH,0,false
|
||||
Plank,STATIC,0,false
|
||||
Pull-Ups,BODYWEIGHT,1.0,false
|
||||
Push-Ups,BODYWEIGHT,0.65,false
|
||||
Romanian Deadlift,STRENGTH,0,false
|
||||
Rowing,CARDIO,0,false
|
||||
Running,CARDIO,0,false
|
||||
Russian Twist,BODYWEIGHT,0,false
|
||||
Seated Cable Row,STRENGTH,0,false
|
||||
Side Plank,STATIC,0,true
|
||||
Sissy Squats,BODYWEIGHT,1.0,false
|
||||
Sprint,CARDIO,0,false
|
||||
Squat,STRENGTH,0,false
|
||||
Treadmill,CARDIO,0,false
|
||||
Tricep Extension,STRENGTH,0,false
|
||||
Wall-Sit,STATIC,0,false
|
||||
name,name_ru,type,bodyWeightPercentage,isUnilateral
|
||||
Air Squats,Приседания,BODYWEIGHT,1.0,false
|
||||
Barbell Row,Тяга штанги в наклоне,STRENGTH,0,false
|
||||
Bench Press,Жим лежа,STRENGTH,0,false
|
||||
Bicep Curl,Подъем на бицепс,STRENGTH,0,true
|
||||
Bulgarian Split-Squat Jumps,Болгарские сплит-прыжки,BODYWEIGHT,1.0,true
|
||||
Bulgarian Split-Squats,Болгарские сплит-приседания,BODYWEIGHT,1.0,true
|
||||
Burpees,Берпи,BODYWEIGHT,1.0,false
|
||||
Calf Raise,Подъем на носки,STRENGTH,0,true
|
||||
Chin-Ups,Подтягивания обратным хватом,BODYWEIGHT,1.0,false
|
||||
Cycling,Велосипед,CARDIO,0,false
|
||||
Deadlift,Становая тяга,STRENGTH,0,false
|
||||
Dips,Отжимания на брусьях,BODYWEIGHT,1.0,false
|
||||
Dumbbell Curl,Сгибания рук с гантелями,STRENGTH,0,true
|
||||
Dumbbell Shoulder Press,Жим гантелей сидя,STRENGTH,0,true
|
||||
Face Pull,Тяга к лицу,STRENGTH,0,false
|
||||
Front Squat,Фронтальный присед,STRENGTH,0,false
|
||||
Hammer Curl,Сгибания "Молот",STRENGTH,0,true
|
||||
Handstand,Стойка на руках,BODYWEIGHT,1.0,false
|
||||
Hip Thrust,Ягодичный мостик,STRENGTH,0,false
|
||||
Jump Rope,Скакалка,CARDIO,0,false
|
||||
Lat Pulldown,Тяга верхнего блока,STRENGTH,0,false
|
||||
Leg Extension,Разгибание ног в тренажере,STRENGTH,0,true
|
||||
Leg Press,Жим ногами,STRENGTH,0,false
|
||||
Lunges,Выпады,BODYWEIGHT,1.0,true
|
||||
Mountain Climbers,Альпинист,CARDIO,0,false
|
||||
Muscle-Up,Выход силой,BODYWEIGHT,1.0,false
|
||||
Overhead Press,Армейский жим,STRENGTH,0,false
|
||||
Plank,Планка,STATIC,0,false
|
||||
Pull-Ups,Подтягивания,BODYWEIGHT,1.0,false
|
||||
Push-Ups,Отжимания,BODYWEIGHT,0.65,false
|
||||
Romanian Deadlift,Румынская тяга,STRENGTH,0,false
|
||||
Rowing,Гребля,CARDIO,0,false
|
||||
Running,Бег,CARDIO,0,false
|
||||
Russian Twist,Русский твист,BODYWEIGHT,0,false
|
||||
Seated Cable Row,Тяга блока к поясу,STRENGTH,0,false
|
||||
Side Plank,Боковая планка,STATIC,0,true
|
||||
Sissy Squats,Сисси-приседания,BODYWEIGHT,1.0,false
|
||||
Sprint,Спринт,CARDIO,0,false
|
||||
Squat,Приседания со штангой,STRENGTH,0,false
|
||||
Treadmill,Беговая дорожка,CARDIO,0,false
|
||||
Tricep Extension,Разгибание рук на трицепс,STRENGTH,0,false
|
||||
Wall-Sit,Стульчик у стены,STATIC,0,false
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 18 and column 30.
|
Binary file not shown.
@@ -83,6 +83,21 @@ export class AuthController {
|
||||
}
|
||||
}
|
||||
|
||||
static async initializeAccount(req: any, res: Response) {
|
||||
try {
|
||||
const userId = req.user.userId;
|
||||
const { language } = req.body;
|
||||
if (!language) {
|
||||
return sendError(res, 'Language is required', 400);
|
||||
}
|
||||
const user = await AuthService.initializeUser(userId, language);
|
||||
return sendSuccess(res, { user });
|
||||
} catch (error: any) {
|
||||
logger.error('Error in initializeAccount', { error });
|
||||
return sendError(res, error.message || 'Server error', 500);
|
||||
}
|
||||
}
|
||||
|
||||
static async getAllUsers(req: any, res: Response) {
|
||||
try {
|
||||
if (req.user.role !== 'ADMIN') {
|
||||
|
||||
@@ -65,7 +65,7 @@ async function ensureAdminUser() {
|
||||
console.info(`ℹ️ Admin user exists but with different email: ${existingAdmin.email}. Expected: ${adminEmail}`);
|
||||
}
|
||||
// Even if admin exists, ensure exercises are seeded (will skip if already has them)
|
||||
await AuthService.seedDefaultExercises(existingAdmin.id);
|
||||
await AuthService.seedDefaultExercises(existingAdmin.id, 'en');
|
||||
await prisma.$disconnect();
|
||||
return;
|
||||
}
|
||||
@@ -77,12 +77,12 @@ async function ensureAdminUser() {
|
||||
email: adminEmail,
|
||||
password: hashed,
|
||||
role: 'ADMIN',
|
||||
profile: { create: { weight: 70 } },
|
||||
profile: { create: { weight: 70, language: 'en' } },
|
||||
},
|
||||
});
|
||||
|
||||
// Seed exercises for new admin
|
||||
await AuthService.seedDefaultExercises(admin.id);
|
||||
await AuthService.seedDefaultExercises(admin.id, 'en');
|
||||
|
||||
console.info(`✅ Admin user created and exercises seeded (email: ${adminEmail})`);
|
||||
await prisma.$disconnect();
|
||||
|
||||
@@ -16,6 +16,7 @@ router.use(authenticateToken); // Standard middleware now
|
||||
router.get('/me', AuthController.getCurrentUser);
|
||||
router.post('/change-password', validate(changePasswordSchema), AuthController.changePassword);
|
||||
router.patch('/profile', validate(updateProfileSchema), AuthController.updateProfile);
|
||||
router.post('/initialize', AuthController.initializeAccount);
|
||||
|
||||
// Admin routes
|
||||
router.get('/users', AuthController.getAllUsers);
|
||||
|
||||
@@ -67,17 +67,13 @@ export class AuthService {
|
||||
include: { profile: true }
|
||||
});
|
||||
|
||||
// Seed default exercises
|
||||
// Seed default exercises
|
||||
await this.seedDefaultExercises(user.id);
|
||||
|
||||
const token = jwt.sign({ userId: user.id, role: user.role }, JWT_SECRET);
|
||||
const { password: _, ...userSafe } = user;
|
||||
|
||||
return { user: userSafe, token };
|
||||
}
|
||||
|
||||
static async seedDefaultExercises(userId: string) {
|
||||
static async seedDefaultExercises(userId: string, language: string = 'en') {
|
||||
try {
|
||||
// Ensure env is loaded from root (in case server didn't restart)
|
||||
if (!process.env.DEFAULT_EXERCISES_CSV_PATH) {
|
||||
@@ -110,6 +106,8 @@ export class AuthService {
|
||||
const headers = lines[0].split(',').map(h => h.trim());
|
||||
const exercisesToCreate = [];
|
||||
|
||||
const nameColumn = language === 'ru' ? 'name_ru' : 'name';
|
||||
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
const cols = lines[i].split(',').map(c => c.trim());
|
||||
if (cols.length < headers.length) continue;
|
||||
@@ -117,10 +115,12 @@ export class AuthService {
|
||||
const row: any = {};
|
||||
headers.forEach((h, idx) => row[h] = cols[idx]);
|
||||
|
||||
if (row.name && row.type) {
|
||||
const exerciseName = row[nameColumn] || row['name'];
|
||||
|
||||
if (exerciseName && row.type) {
|
||||
exercisesToCreate.push({
|
||||
userId,
|
||||
name: row.name,
|
||||
name: exerciseName,
|
||||
type: row.type,
|
||||
bodyWeightPercentage: row.bodyWeightPercentage ? parseFloat(row.bodyWeightPercentage) : 0,
|
||||
isUnilateral: row.isUnilateral === 'true',
|
||||
@@ -153,14 +153,34 @@ export class AuthService {
|
||||
}
|
||||
}
|
||||
|
||||
static async initializeUser(userId: string, language: string) {
|
||||
// Update profile language
|
||||
await prisma.userProfile.upsert({
|
||||
where: { userId },
|
||||
update: { language },
|
||||
create: { userId, language, weight: 70 }
|
||||
});
|
||||
|
||||
// Seed exercises in that language
|
||||
await this.seedDefaultExercises(userId, language);
|
||||
|
||||
// Mark as first login done
|
||||
await prisma.user.update({
|
||||
where: { id: userId },
|
||||
data: { isFirstLogin: false }
|
||||
});
|
||||
|
||||
// Return updated user
|
||||
return this.getUser(userId);
|
||||
}
|
||||
|
||||
static async changePassword(userId: string, newPassword: string) {
|
||||
const hashed = await bcrypt.hash(newPassword, 10);
|
||||
|
||||
await prisma.user.update({
|
||||
where: { id: userId },
|
||||
data: {
|
||||
password: hashed,
|
||||
isFirstLogin: false
|
||||
password: hashed
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user