feat: implement backend API with models and seed data for OctoFit application

This commit is contained in:
2026-06-20 01:49:15 +00:00
parent 39ef098873
commit f8c4a57e78
17 changed files with 669 additions and 5 deletions
+93
View File
@@ -0,0 +1,93 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = __importDefault(require("express"));
const mongoose_1 = __importDefault(require("mongoose"));
const Activity_1 = require("./models/Activity");
const Leaderboard_1 = require("./models/Leaderboard");
const Team_1 = require("./models/Team");
const User_1 = require("./models/User");
const Workout_1 = require("./models/Workout");
const app = (0, express_1.default)();
const port = Number(process.env.PORT) || 8000;
const mongoUri = process.env.MONGO_URI || "mongodb://127.0.0.1:27017/octofit_db";
const codespaceName = process.env.CODESPACE_NAME;
const baseUrl = codespaceName
? `https://${codespaceName}-8000.app.github.dev`
: `http://localhost:${port}`;
app.use(express_1.default.json());
app.get("/health", (_req, res) => {
res.status(200).json({ status: "ok", baseUrl });
});
app.get("/api/users/", async (_req, res) => {
try {
const items = await User_1.UserModel.find().sort({ createdAt: -1 }).lean();
res.status(200).json({ resource: "users", count: items.length, items });
}
catch (error) {
res.status(500).json({ message: "Failed to fetch users", error });
}
});
app.get("/api/teams/", async (_req, res) => {
try {
const items = await Team_1.TeamModel.find()
.populate("captain", "username email")
.populate("members", "username email")
.sort({ createdAt: -1 })
.lean();
res.status(200).json({ resource: "teams", count: items.length, items });
}
catch (error) {
res.status(500).json({ message: "Failed to fetch teams", error });
}
});
app.get("/api/activities/", async (_req, res) => {
try {
const items = await Activity_1.ActivityModel.find()
.populate("user", "username email")
.populate("team", "name city")
.sort({ completedAt: -1 })
.lean();
res.status(200).json({ resource: "activities", count: items.length, items });
}
catch (error) {
res.status(500).json({ message: "Failed to fetch activities", error });
}
});
app.get("/api/leaderboard/", async (_req, res) => {
try {
const items = await Leaderboard_1.LeaderboardModel.find()
.populate("entries.user", "username email")
.populate("entries.team", "name city")
.sort({ generatedAt: -1 })
.lean();
res.status(200).json({ resource: "leaderboard", count: items.length, items });
}
catch (error) {
res.status(500).json({ message: "Failed to fetch leaderboard", error });
}
});
app.get("/api/workouts/", async (_req, res) => {
try {
const items = await Workout_1.WorkoutModel.find().sort({ createdAt: -1 }).lean();
res.status(200).json({ resource: "workouts", count: items.length, items });
}
catch (error) {
res.status(500).json({ message: "Failed to fetch workouts", error });
}
});
const start = async () => {
try {
await mongoose_1.default.connect(mongoUri);
app.listen(port, () => {
console.log(`OctoFit backend listening on ${baseUrl}`);
});
}
catch (error) {
console.error("Failed to start backend:", error);
process.exit(1);
}
};
void start();
+15
View File
@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ActivityModel = void 0;
const mongoose_1 = require("mongoose");
const activitySchema = new mongoose_1.Schema({
user: { type: mongoose_1.Types.ObjectId, ref: "User", required: true },
team: { type: mongoose_1.Types.ObjectId, ref: "Team", required: true },
type: { type: String, required: true, trim: true },
durationMinutes: { type: Number, required: true, min: 1 },
caloriesBurned: { type: Number, required: true, min: 1 },
completedAt: { type: Date, required: true },
}, {
timestamps: true,
});
exports.ActivityModel = (0, mongoose_1.model)("Activity", activitySchema);
+18
View File
@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LeaderboardModel = void 0;
const mongoose_1 = require("mongoose");
const leaderboardEntrySchema = new mongoose_1.Schema({
user: { type: mongoose_1.Types.ObjectId, ref: "User", required: true },
team: { type: mongoose_1.Types.ObjectId, ref: "Team", required: true },
points: { type: Number, required: true, min: 0 },
rank: { type: Number, required: true, min: 1 },
}, { _id: false });
const leaderboardSchema = new mongoose_1.Schema({
period: { type: String, required: true, trim: true },
generatedAt: { type: Date, required: true },
entries: { type: [leaderboardEntrySchema], required: true },
}, {
timestamps: true,
});
exports.LeaderboardModel = (0, mongoose_1.model)("Leaderboard", leaderboardSchema);
+13
View File
@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TeamModel = void 0;
const mongoose_1 = require("mongoose");
const teamSchema = new mongoose_1.Schema({
name: { type: String, required: true, trim: true },
city: { type: String, required: true, trim: true },
captain: { type: mongoose_1.Types.ObjectId, ref: "User", required: true },
members: [{ type: mongoose_1.Types.ObjectId, ref: "User", required: true }],
}, {
timestamps: true,
});
exports.TeamModel = (0, mongoose_1.model)("Team", teamSchema);
+15
View File
@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserModel = void 0;
const mongoose_1 = require("mongoose");
const userSchema = new mongoose_1.Schema({
username: { type: String, required: true, trim: true },
email: { type: String, required: true, unique: true, lowercase: true, trim: true },
age: { type: Number, required: true, min: 13 },
heightCm: { type: Number, required: true, min: 100 },
weightKg: { type: Number, required: true, min: 30 },
fitnessGoal: { type: String, required: true, trim: true },
}, {
timestamps: true,
});
exports.UserModel = (0, mongoose_1.model)("User", userSchema);
+15
View File
@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WorkoutModel = void 0;
const mongoose_1 = require("mongoose");
const workoutSchema = new mongoose_1.Schema({
title: { type: String, required: true, trim: true },
category: { type: String, required: true, trim: true },
difficulty: { type: String, required: true, trim: true },
durationMinutes: { type: Number, required: true, min: 1 },
equipment: { type: [String], required: true },
targetMuscles: { type: [String], required: true },
}, {
timestamps: true,
});
exports.WorkoutModel = (0, mongoose_1.model)("Workout", workoutSchema);
+161
View File
@@ -0,0 +1,161 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const mongoose_1 = __importDefault(require("mongoose"));
const Activity_1 = require("../models/Activity");
const Leaderboard_1 = require("../models/Leaderboard");
const Team_1 = require("../models/Team");
const User_1 = require("../models/User");
const Workout_1 = require("../models/Workout");
const mongoUri = process.env.MONGO_URI || "mongodb://127.0.0.1:27017/octofit_db";
const seed = async () => {
try {
await mongoose_1.default.connect(mongoUri);
console.log("Seed the octofit_db database with test data");
await Promise.all([
Activity_1.ActivityModel.deleteMany({}),
Leaderboard_1.LeaderboardModel.deleteMany({}),
Team_1.TeamModel.deleteMany({}),
User_1.UserModel.deleteMany({}),
Workout_1.WorkoutModel.deleteMany({}),
]);
const users = await User_1.UserModel.insertMany([
{
username: "alex_runner",
email: "alex.runner@example.com",
age: 29,
heightCm: 178,
weightKg: 74,
fitnessGoal: "Half marathon in under 1h40",
},
{
username: "maya_lifter",
email: "maya.lifter@example.com",
age: 33,
heightCm: 165,
weightKg: 62,
fitnessGoal: "Increase deadlift max to 140kg",
},
{
username: "liam_hiker",
email: "liam.hiker@example.com",
age: 26,
heightCm: 182,
weightKg: 79,
fitnessGoal: "Improve endurance for alpine trekking",
},
{
username: "sofia_cycle",
email: "sofia.cycle@example.com",
age: 31,
heightCm: 170,
weightKg: 66,
fitnessGoal: "Ride a 100km gran fondo",
},
]);
const teams = await Team_1.TeamModel.insertMany([
{
name: "Pulse Pacers",
city: "Cluj-Napoca",
captain: users[0]._id,
members: [users[0]._id, users[2]._id],
},
{
name: "Iron Orbit",
city: "Bucharest",
captain: users[1]._id,
members: [users[1]._id, users[3]._id],
},
]);
await Workout_1.WorkoutModel.insertMany([
{
title: "Tempo Run 8K",
category: "Cardio",
difficulty: "Intermediate",
durationMinutes: 50,
equipment: ["Running shoes", "Sports watch"],
targetMuscles: ["Quadriceps", "Hamstrings", "Calves", "Core"],
},
{
title: "Barbell Strength Circuit",
category: "Strength",
difficulty: "Advanced",
durationMinutes: 60,
equipment: ["Barbell", "Weight plates", "Bench"],
targetMuscles: ["Glutes", "Back", "Chest", "Shoulders"],
},
{
title: "Mobility and Recovery Flow",
category: "Mobility",
difficulty: "Beginner",
durationMinutes: 30,
equipment: ["Yoga mat", "Resistance band"],
targetMuscles: ["Hips", "Lower back", "Shoulders"],
},
]);
await Activity_1.ActivityModel.insertMany([
{
user: users[0]._id,
team: teams[0]._id,
type: "Interval Run",
durationMinutes: 42,
caloriesBurned: 520,
completedAt: new Date("2026-06-18T06:40:00.000Z"),
},
{
user: users[1]._id,
team: teams[1]._id,
type: "Heavy Strength Session",
durationMinutes: 64,
caloriesBurned: 610,
completedAt: new Date("2026-06-18T18:20:00.000Z"),
},
{
user: users[2]._id,
team: teams[0]._id,
type: "Hill Hike",
durationMinutes: 95,
caloriesBurned: 780,
completedAt: new Date("2026-06-19T07:10:00.000Z"),
},
{
user: users[3]._id,
team: teams[1]._id,
type: "Road Cycling",
durationMinutes: 88,
caloriesBurned: 845,
completedAt: new Date("2026-06-19T16:05:00.000Z"),
},
]);
await Leaderboard_1.LeaderboardModel.insertMany([
{
period: "weekly-2026-W25",
generatedAt: new Date("2026-06-20T00:00:00.000Z"),
entries: [
{ user: users[3]._id, team: teams[1]._id, points: 1280, rank: 1 },
{ user: users[2]._id, team: teams[0]._id, points: 1225, rank: 2 },
{ user: users[1]._id, team: teams[1]._id, points: 1160, rank: 3 },
{ user: users[0]._id, team: teams[0]._id, points: 1095, rank: 4 },
],
},
]);
const counts = {
users: await User_1.UserModel.countDocuments(),
teams: await Team_1.TeamModel.countDocuments(),
activities: await Activity_1.ActivityModel.countDocuments(),
leaderboard: await Leaderboard_1.LeaderboardModel.countDocuments(),
workouts: await Workout_1.WorkoutModel.countDocuments(),
};
console.log("Seed complete", counts);
}
catch (error) {
console.error("Seed failed", error);
process.exitCode = 1;
}
finally {
await mongoose_1.default.disconnect();
}
};
void seed();