mirror of
https://github.com/farcasclaudiu/learn-build-apps-copilot-agent.git
synced 2026-06-22 05:01:34 +03:00
feat: implement backend API with models and seed data for OctoFit application
This commit is contained in:
+93
@@ -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
@@ -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);
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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();
|
||||
+5
-1
@@ -1,9 +1,12 @@
|
||||
{
|
||||
"name": "backend",
|
||||
"name": "octofit-tracker-backend",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "octofit-tracker-backend",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"express": "^5.2.1",
|
||||
"mongoose": "^9.7.1"
|
||||
@@ -11,6 +14,7 @@
|
||||
"devDependencies": {
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/node": "^26.0.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"typescript": "^6.0.3"
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"scripts": {
|
||||
"dev": "ts-node-dev --respawn --transpile-only src/index.ts",
|
||||
"build": "tsc -p tsconfig.json",
|
||||
"start": "node dist/index.js"
|
||||
"start": "node dist/index.js",
|
||||
"seed": "ts-node src/scripts/seed.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^5.2.1",
|
||||
@@ -15,6 +16,7 @@
|
||||
"devDependencies": {
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/node": "^26.0.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"typescript": "^6.0.3"
|
||||
}
|
||||
|
||||
@@ -1,21 +1,87 @@
|
||||
import express from "express";
|
||||
import mongoose from "mongoose";
|
||||
import { ActivityModel } from "./models/Activity";
|
||||
import { LeaderboardModel } from "./models/Leaderboard";
|
||||
import { TeamModel } from "./models/Team";
|
||||
import { UserModel } from "./models/User";
|
||||
import { WorkoutModel } from "./models/Workout";
|
||||
|
||||
const app = express();
|
||||
const port = Number(process.env.PORT) || 8000;
|
||||
const mongoUri = process.env.MONGO_URI || "mongodb://127.0.0.1:27017/octofit_tracker";
|
||||
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.json());
|
||||
|
||||
app.get("/health", (_req, res) => {
|
||||
res.status(200).json({ status: "ok" });
|
||||
res.status(200).json({ status: "ok", baseUrl });
|
||||
});
|
||||
|
||||
app.get("/api/users/", async (_req, res) => {
|
||||
try {
|
||||
const items = await 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 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 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 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 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.connect(mongoUri);
|
||||
app.listen(port, () => {
|
||||
console.log(`OctoFit backend listening on http://localhost:${port}`);
|
||||
console.log(`OctoFit backend listening on ${baseUrl}`);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to start backend:", error);
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Schema, model, type InferSchemaType, Types } from "mongoose";
|
||||
|
||||
const activitySchema = new Schema(
|
||||
{
|
||||
user: { type: Types.ObjectId, ref: "User", required: true },
|
||||
team: { type: 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,
|
||||
},
|
||||
);
|
||||
|
||||
export type ActivityDocument = InferSchemaType<typeof activitySchema>;
|
||||
export const ActivityModel = model("Activity", activitySchema);
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Schema, model, type InferSchemaType, Types } from "mongoose";
|
||||
|
||||
const leaderboardEntrySchema = new Schema(
|
||||
{
|
||||
user: { type: Types.ObjectId, ref: "User", required: true },
|
||||
team: { type: 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 Schema(
|
||||
{
|
||||
period: { type: String, required: true, trim: true },
|
||||
generatedAt: { type: Date, required: true },
|
||||
entries: { type: [leaderboardEntrySchema], required: true },
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
},
|
||||
);
|
||||
|
||||
export type LeaderboardDocument = InferSchemaType<typeof leaderboardSchema>;
|
||||
export const LeaderboardModel = model("Leaderboard", leaderboardSchema);
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Schema, model, type InferSchemaType, Types } from "mongoose";
|
||||
|
||||
const teamSchema = new Schema(
|
||||
{
|
||||
name: { type: String, required: true, trim: true },
|
||||
city: { type: String, required: true, trim: true },
|
||||
captain: { type: Types.ObjectId, ref: "User", required: true },
|
||||
members: [{ type: Types.ObjectId, ref: "User", required: true }],
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
},
|
||||
);
|
||||
|
||||
export type TeamDocument = InferSchemaType<typeof teamSchema>;
|
||||
export const TeamModel = model("Team", teamSchema);
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Schema, model, type InferSchemaType } from "mongoose";
|
||||
|
||||
const userSchema = new 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,
|
||||
},
|
||||
);
|
||||
|
||||
export type UserDocument = InferSchemaType<typeof userSchema>;
|
||||
export const UserModel = model("User", userSchema);
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Schema, model, type InferSchemaType } from "mongoose";
|
||||
|
||||
const workoutSchema = new 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,
|
||||
},
|
||||
);
|
||||
|
||||
export type WorkoutDocument = InferSchemaType<typeof workoutSchema>;
|
||||
export const WorkoutModel = model("Workout", workoutSchema);
|
||||
@@ -0,0 +1,166 @@
|
||||
import mongoose from "mongoose";
|
||||
import { ActivityModel } from "../models/Activity";
|
||||
import { LeaderboardModel } from "../models/Leaderboard";
|
||||
import { TeamModel } from "../models/Team";
|
||||
import { UserModel } from "../models/User";
|
||||
import { WorkoutModel } from "../models/Workout";
|
||||
|
||||
const mongoUri = process.env.MONGO_URI || "mongodb://127.0.0.1:27017/octofit_db";
|
||||
|
||||
const seed = async () => {
|
||||
try {
|
||||
await mongoose.connect(mongoUri);
|
||||
|
||||
console.log("Seed the octofit_db database with test data");
|
||||
|
||||
await Promise.all([
|
||||
ActivityModel.deleteMany({}),
|
||||
LeaderboardModel.deleteMany({}),
|
||||
TeamModel.deleteMany({}),
|
||||
UserModel.deleteMany({}),
|
||||
WorkoutModel.deleteMany({}),
|
||||
]);
|
||||
|
||||
const users = await 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 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 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 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 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 UserModel.countDocuments(),
|
||||
teams: await TeamModel.countDocuments(),
|
||||
activities: await ActivityModel.countDocuments(),
|
||||
leaderboard: await LeaderboardModel.countDocuments(),
|
||||
workouts: await WorkoutModel.countDocuments(),
|
||||
};
|
||||
|
||||
console.log("Seed complete", counts);
|
||||
} catch (error) {
|
||||
console.error("Seed failed", error);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
await mongoose.disconnect();
|
||||
}
|
||||
};
|
||||
|
||||
void seed();
|
||||
@@ -3,6 +3,7 @@
|
||||
"target": "ES2020",
|
||||
"module": "Node16",
|
||||
"moduleResolution": "Node16",
|
||||
"types": ["node"],
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"strict": true,
|
||||
|
||||
Reference in New Issue
Block a user