Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions backend/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import emailRoutes from "./routes/emailRoutes";
import speakerRoutes from "./routes/speakerRoutes";
import pdfRoutes from "./routes/pdfRoutes";
import emailTemplateRoutes from "./routes/emailTemplateRoutes";
import zoomRoutes from "./routes/zoomRoutes";

// Import middleware
import { notFound, errorHandler } from "./middlewares/errorMiddleware";
Expand Down Expand Up @@ -93,6 +94,7 @@ app.use("/api/emails", verifyFirebaseAuth, emailRoutes);
app.use("/api/emailTemplates", verifyFirebaseAuth, emailTemplateRoutes);
app.use("/api/speakers", verifyFirebaseAuth, speakerRoutes);
app.use("/api/upload", verifyFirebaseAuth, uploadRoutes);
app.use("/api/zoom", verifyFirebaseAuth, zoomRoutes)
app.use("/api/certificatePDFs", verifyFirebaseAuth, pdfRoutes);

app.use(notFound);
Expand Down
28 changes: 5 additions & 23 deletions backend/controllers/courseController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,9 @@ export const getCourses = async (
): Promise<void> => {
try {
const filters = req.query;

// Populate ratings and components fields as needed
const courseResponses = await Course.find(filters)
.populate(["ratings", "components"])
.populate(["ratings"])
.exec();

res.status(200).json({
success: true,
count: courseResponses.length,
Expand All @@ -56,7 +53,7 @@ export const getCourseById = async (
if (id) {
// Find course by ID and populate related fields
const course = await Course.findById(id)
.populate(["ratings", "components", "managers"])
.populate(["ratings", "managers"])
.exec();

if (!course) {
Expand Down Expand Up @@ -105,8 +102,6 @@ export const createCourse = async (
ratings,
className,
discussion,
components,
isLive,
categories,
creditNumber,
courseDescription,
Expand All @@ -115,17 +110,14 @@ export const createCourse = async (
cost,
instructorDescription,
instructorRole,
lengthCourse,
time,
instructorName,
isInPerson,
students,
managers,
speakers,
courseType,
regStart,
regEnd,
productType,
productInfo,
shortUrl,
draft,
} = req.body;
Expand All @@ -134,23 +126,18 @@ export const createCourse = async (
if (!draft)
if (
!className ||
isLive === undefined ||
creditNumber === undefined ||
!courseDescription ||
!thumbnailPath ||
cost === undefined ||
!lengthCourse ||
!time ||
!instructorName ||
isInPerson === undefined ||
!courseType ||
!regEnd
) {
// console.log("[createCourse] Validation failed. Missing required fields");
res.status(400).json({
success: false,
message:
"Please provide className, isLive, creditNumber, thumbnailPath, cost, lengthCourse, time, instructorName, isInPerson, courseType, and regStart",
"Please provide className, isLive, creditNumber, thumbnailPath, cost, lengthCourse, instructorName, and regStart",
});
return;
}
Expand All @@ -172,8 +159,6 @@ export const createCourse = async (
ratings,
className,
discussion,
components,
isLive,
categories,
creditNumber,
courseDescription,
Expand All @@ -182,17 +167,14 @@ export const createCourse = async (
cost,
instructorDescription,
instructorRole,
lengthCourse,
time,
instructorName,
isInPerson,
students,
managers,
speakers,
courseType,
regStart,
regEnd,
productType,
productInfo,
shortUrl,
draft,
});
Expand Down
145 changes: 145 additions & 0 deletions backend/controllers/zoomController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import {Request, Response} from "express";
import dotenv from "dotenv";
dotenv.config();
// @desc Get a bearer token
// @route GET /api/zoom/token
// @access Public
async function getToken(){
try {
const credentials = btoa(`${process.env.ZOOM_CLIENT_ID}:${process.env.ZOOM_CLIENT_SECRET}`);

const token = await fetch(`https://zoom.us/oauth/token?grant_type=account_credentials&account_id=${process.env.ZOOM_ACCOUNT_ID}`, {
method: "POST",
headers: {
"Authorization": `Basic ${credentials}`,
"Content-Type": "application/x-www-form-urlencoded"
}
})
return (await token.json()).access_token
} catch (error) {
console.error(error);
return undefined
}
}

export const getMeetings = async (
req: Request,
res: Response
): Promise<void> => {
try {
await getToken().then(async (token) => {
const response = await fetch(`https://api.zoom.us/v2/users/${process.env.ZOOM_USER_ID}/meetings`, {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/x-www-form-urlencoded",
}
})
let meetings = await response.json()
res.status(200).json(meetings)
})
} catch (error) {
console.error(error);
res.status(500).json({
success: false,
message: "Internal service error",
});
}
};

export const getWebinars = async (
req: Request,
res: Response
): Promise<void> => {
try {
await getToken().then(async (token) => {

const response = await fetch(`https://api.zoom.us/v2/users/${process.env.ZOOM_USER_ID}/webinars`, {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/x-www-form-urlencoded",
}
})
let webinars = (await response.json()).webinars
res.status(200).json({
webinars: webinars
})
})
} catch (error) {
console.error(error);
res.status(500).json({
success: false,
message: "Internal service error",
});
}
};

export const createMeeting = async (
req: Request,
res: Response
): Promise<void> => {
const { topic, startTime, duration } = req.body;
try {
await getToken().then(async (token) => {

const response = await fetch(`https://api.zoom.us/v2/users/${process.env.ZOOM_USER_ID}/meetings`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
"Authorization": `Bearer ${token}`,
},
body: JSON.stringify({
topic,
start_time: startTime,
duration: duration
})
});
let meeting = await response.json()
console.log(meeting)
res.status(200).json({
meeting: meeting
})
})
} catch (error) {
console.error(error);
res.status(500).json({
success: false,
message: "Internal service error",
});
}
};

export const createWebinar = async (
req: Request,
res: Response
): Promise<void> => {
const { topic, startTime, duration } = req.body;
try {
await getToken().then(async (token) => {
const response = await fetch(`https://api.zoom.us/v2/users/${process.env.ZOOM_USER_ID}/webinars`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
"Authorization": `Bearer ${token}`,
},
body: JSON.stringify({
topic,
start_time: startTime,
duration: duration
})
});
let webinar = await response.json()
console.log(webinar)
res.status(200).json({
webinar: webinar
})
})
} catch (error) {
console.error(error);
res.status(500).json({
success: false,
message: "Internal service error",
});
}
};
20 changes: 5 additions & 15 deletions backend/models/courseModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export interface ICourse extends Document {
ratings: (mongoose.Types.ObjectId | IRating)[];
className: string;
discussion: string;
components: (mongoose.Types.ObjectId | Object)[];
isLive: boolean;
categories: string[];
creditNumber: number;
Expand All @@ -20,16 +19,14 @@ export interface ICourse extends Document {
instructorName: string;
instructorDescription: string;
instructorRole: string;
courseType: "webinar" | "course" | "meeting";
lengthCourse: number;
time: Date;
isInPerson: boolean;
students: (mongoose.Types.ObjectId | IUser)[]; //for the users
managers: (mongoose.Types.ObjectId | IUser)[];
speakers: (mongoose.Types.ObjectId | ISpeaker)[];
regStart: Date;
regEnd: Date;
productType: string[];
//Virtual Training - Live Meeting, In-Person Training, Virtual Training - On Demand, Virtual Training - Live Webinar
productType: string;
productInfo: string
shortUrl: string;
draft: boolean;
}
Expand All @@ -50,8 +47,6 @@ const CourseSchema: Schema = new Schema(
],
className: { type: String, required: true },
discussion: { type: String, required: false },
components: [{ type: Schema.Types.Mixed, required: false }],
isLive: { type: Boolean, required: false },
categories: [{ type: String, required: false }],
creditNumber: { type: Number, required: false },
courseDescription: { type: String, required: false },
Expand All @@ -60,7 +55,6 @@ const CourseSchema: Schema = new Schema(
cost: { type: Number, required: false },
instructorDescription: { type: String, required: false },
instructorRole: { type: String, required: false },
lengthCourse: { type: Number, required: false },
time: { type: Date, required: false },
instructorName: { type: String, required: false },
isInPerson: { type: Boolean, required: false },
Expand All @@ -70,11 +64,6 @@ const CourseSchema: Schema = new Schema(
ref: "User",
},
],
courseType: {
type: String,
enum: ["webinar", "course", "meeting"],
required: true,
},
managers: [
{
type: Schema.Types.ObjectId,
Expand All @@ -89,7 +78,8 @@ const CourseSchema: Schema = new Schema(
],
regStart: { type: Date, required: false },
regEnd: { type: Date, required: false },
productType: [{ type: String, required: false }],
productType: { type: String, required: false },
productInfo: {type:String, required: false},
shortUrl: { type: String, required: false },
draft: { type: Boolean, required: true, default: true },
},
Expand Down
15 changes: 15 additions & 0 deletions backend/routes/zoomRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import express from "express";
import {
createMeeting, createWebinar,
getMeetings, getWebinars
} from "../controllers/zoomController";

const router = express.Router();

router.get("/meetings", getMeetings);
router.get("/webinars", getWebinars);

router.post("/meeting", createMeeting)
router.post("/webinar", createWebinar)

export default router;
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,6 @@ function AdminCoursePreview({
return null;
}

if (isLive !== null && product.course.isLive !== isLive) {
return null;
}

if (
category !== null &&
(!product.course.categories ||
Expand Down Expand Up @@ -138,7 +134,6 @@ function AdminCoursePreview({

<Calendar className="w-12" />
<span className="text-gray-500 w-24">
{product.course.isLive ? "Live" : "Virtual"} Event{" "}
{product.startTime.getMonth() + 1}/{product.startTime.getDate()}/
{product.startTime.getFullYear()} at {product.startTime.getHours()}:
{product.startTime.getMinutes().toString().padStart(2, "0")}{" "}
Expand Down
3 changes: 0 additions & 3 deletions frontend/src/pages/Admin/AdminPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,6 @@ export default function AdminPage() {
status: "Ongoing",
avgRating: calculateAverageRating(course.ratings),
startTime: new Date(course.time),
endTime: new Date(
new Date(course.time).getTime() + course.lengthCourse * 60000
),
timeZone: "(CST)",
selected: false,
})
Expand Down
3 changes: 0 additions & 3 deletions frontend/src/pages/Admin/ProductPage/ProductPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,6 @@ export default function ProductPage() {
status: "Ongoing",
avgRating: calculateAverageRating(course.ratings),
startTime: new Date(course.time),
endTime: new Date(
new Date(course.time).getTime() + course.lengthCourse * 60000
),
timeZone: "(CST)",
selected: false,
categories: categories,
Expand Down
Loading