Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • kevinc97/project_cresendo
  • hci/project_cresendo
2 results
Show changes
Commits on Source (38)
Showing
with 2950 additions and 175 deletions
File deleted
node_modules
/database/
/database/journal/
/database/diagnostic.data/
/database/*.lock
/database/WiredTiger
/database/WiredTiger.*
/*.log
*.pid
*.sock
.env
database_backup/
## Capstone Feedback project ## Capstone Feedback project(Undergraduate Research)
- Frontend - Frontend
- Using React - Using React
...@@ -15,6 +15,10 @@ ...@@ -15,6 +15,10 @@
- To run backend - To run backend
- cd to 'backend' directory and run 'npm i' in the terminal. After installed, run 'nrpm run dev' to connect the web to the server. - cd to 'backend' directory and run 'npm i' in the terminal. After installed, run 'nrpm run dev' to connect the web to the server.
============================================
I have dockerized both backend and frontend so you don't have to run front and back manually in the local terminal. If I host the container for this app, this app will run in localhost and you can use both front and backend at the same time.
MONGO_URI_PROD=mongodb://kevjayjeong906:crescendodbuser-1@localhost:27017/crescendo?authSource=crescendo
MONGO_URI_DEV=mongodb://localhost:27017/crescendo
profile_uri=https://t4.ftcdn.net/jpg/04/10/43/77/360_F_410437733_hdq4Q3QOH9uwh0mcqAhRFzOKfrCR24Ta.jpg
SSL_KEY_PATH = /home/sangwonlee/cert/key.pem
SSL_CERT_PATH = /home/sangwonlee/cert/crescendo.cs.vt.edu.crt
NODE_ENV=production
echo "HOST=crescendo.cs.vt.edu" >> .env
FROM node:18-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD [ "npm", "start" ]
\ No newline at end of file
const mongoose = require('mongoose');
exports.connectDB = () => {
mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log("Connected to db"))
.catch((err) => console.log(err));
};
\ No newline at end of file
const MongoStore = require('connect-mongo');
module.exports = {
secret: 'your_secret_key',
resave: false,
saveUninitialized: false,
store: MongoStore.create({ mongoUrl: process.env.MONGO_URI }),
cookie: { secure: true }
};
\ No newline at end of file
const axios = require('axios');
const xml2js = require('xml2js');
const User = require('../models/user');
const { CAS_VALIDATE_URL, CAS_SERVICE_URL, getUserRole } = require('../utils/casUtils');
exports.handleDashboard = async (req, res) => {
if (req.session.user) {
return redirectUser(req, res, req.session.user.role);
}
const { ticket } = req.query;
if (!ticket) {
return res.redirect(`https://login.vt.edu/cas/login?service=${encodeURIComponent(CAS_SERVICE_URL)}`);
}
try {
const response = await axios.get(CAS_VALIDATE_URL, {
params: {
ticket,
service: CAS_SERVICE_URL
}
});
const user = await parseCasResponse(response.data);
const pid = user['cas:user'][0];
const attributes = user['cas:attributes'][0];
const email = attributes['cas:eduPersonPrincipalName'][0];
const role = getUserRole(attributes);
req.session.user = { pid, email, role };
req.session.user_id = pid;
let dbUser = await User.findOne({ email: email });
if (!dbUser) {
dbUser = new User({ pid, email, isFirstLogin: true });
await dbUser.save();
req.session.user.isFirstLogin = true;
} else {
req.session.user.name = dbUser.name;
req.session.user.isFirstLogin = false;
}
redirectUser(req, res, role);
} catch (error) {
console.error('CAS ticket validation failed:', error);
res.status(500).send('Ticket validation failed');
}
};
exports.checkSession = (req, res) => {
if (!req.session.user) {
return res.status(401).send('User not authenticated');
}
res.status(200).json({
user: req.session.user
});
};
const parseCasResponse = (data) => {
return new Promise((resolve, reject) => {
const parser = new xml2js.Parser();
parser.parseString(data, (err, result) => {
if (err) {
return reject(err);
}
try {
const user = result['cas:serviceResponse']['cas:authenticationSuccess'][0];
resolve(user);
} catch (error) {
reject(new Error('CAS authentication failed'));
}
});
});
};
const redirectUser = (req, res, role) => {
if (role === 'student') {
console.log("Redirecting to student page....");
return res.redirect('https://crescendo.cs.vt.edu/Courses');
} else if (role === 'students') {
console.log("Redirecting to faculty page....");
return res.redirect('https://crescendo.cs.vt.edu/Dashboard');
} else {
return res.status(403).send('Access denied');
}
};
// This function is only used in development environment (NODE_ENV=development)
exports.fakeLogin = async (req, res) => {
if (req.session.user) {
console.log("Session already exists:", req.session.user);
return res.status(200).json({
message: 'Already logged in',
user: req.session.user
});
}
const fakeUser = {
pid: '12345678',
role: 'student',
name: 'John Doe',
email: 'johndoe@local.dev',
isFirstLogin: false,
group: 'Group A'
};
req.session.user = fakeUser;
req.session.user_id = fakeUser.pid;
let dbUser = await User.findOne({ email: fakeUser.email });
if (!dbUser) {
dbUser = new User({ pid: fakeUser.pid, email: fakeUser.email, name: fakeUser.name, isFirstLogin: fakeUser.isFirstLogin });
await dbUser.save();
}
res.status(200).json({
message: 'Fake login successful',
user: fakeUser
});
};
\ No newline at end of file
const Course = require('../models/course');
const User = require('../models/user');
const { v4: uuidv4 } = require('uuid');
exports.createCourse = async (req, res) => {
const { name, term, crn } = req.body;
if (!req.session.user) {
return res.status(401).send('User not authenticated');
}
try {
const uniqueCode = uuidv4();
const newCourse = new Course({ name, term, crn, uniqueCode});
const savedCourse = await newCourse.save();
res.status(201).json(savedCourse);
} catch (error) {
console.error('Error creating course:', error);
res.status(500).send('Failed to create course');
}
};
exports.register = async (req, res) => {
const { uniqueCode } = req.body;
const userPid = req.session.user.pid;
try {
const course = await Course.findOne({ uniqueCode });
if (!course) {
return res.status(404).send('Course not found');
}
if (course.students.includes(userPid)) {
return res.status(400).send('User already registered for this course');
}
course.students.push(userPid);
await course.save();
res.status(200).json({ message: 'Successfully registered for the course', course });
} catch (error) {
console.error('Error registering course:', error);
res.status(500).json({ message: 'Failed to register course' });
}
};
exports.getCourses = async (req, res) => {
try {
const courses = await Course.find();
res.status(200).json(courses);
} catch (error) {
console.error('Error fetching courses:', error);
res.status(500).send('Failed to fetch courses');
}
};
exports.getRegisteredCourses = async (req, res) => {
const userId = req.session.user_id;
try {
const courses = await Course.find({ students: userId });
res.status(200).json(courses);
} catch(error) {
onsole.error('Error fetching user courses:', error);
res.status(500).send('Failed to fetch user courses');
}
};
exports.getCourse = async (req, res) => {
const courseId = req.params.courseId;
try {
const course = await Course.findById(courseId);
if (!course) {
return res.status(404).json({ error: 'Course not found' });
}
res.json(course);
} catch (error) {
console.error(error);
res.status(500).json({ error: 'Server error' });
}
};
exports.updateCourse = async (req, res) => {
const courseId = req.params.courseId;
const { name, term, crn } = req.body;
try {
const updatedCourse = await Course.findByIdAndUpdate(courseId, { name, term, crn }, { new: true });
if (!updatedCourse) {
return res.status(404).send('Course not found');
}
res.status(200).json(updatedCourse);
} catch (error) {
console.error('Error updating course:', error);
res.status(500).send('Failed to update course');
}
};
exports.deleteCourse = async (req, res) => {
const courseId = req.params.courseId;
if (!req.session.user) {
return res.status(401).send('User not authenticated');
}
try {
const course = await Course.findById(courseId);
if (!course) {
return res.status(404).send('Course not found');
}
await Course.deleteOne({ _id: courseId });
res.status(200).json({ message: 'Course deleted successfully' });
} catch (error) {
console.error('Error deleting course:', error);
res.status(500).send('Failed to delete course');
}
};
exports.getStudentsInCourse = async(req, res) => {
const courseId = req.params.courseId;
try{
const course = await Course.findById(courseId);
if (!course) {
return res.status(404).json({message : 'course not found'});
}
const students = await User.find({ pid : { $in : course.students}}, 'name email');
res.status(200).json(students);
} catch (error) {
console.error('Error fetching students:', error);
res.status(500).json({ message: 'Failed to fetch students' });
}
}
exports.deleteStudentFromCourse = async (req, res) => {
const { courseId, studentId } = req.params;
try {
const student = await User.findById(studentId)
if (!student) {
return res.status(404).json({ message: 'Student not found' });
}
const studentPid = student.pid;
const course = await Course.findById(courseId);
if (!course) {
return res.status(404).json({ message: 'Course not found' });
}
course.students = course.students.filter(pid => pid !== studentPid);
await course.save();
res.status(200).json({ message: 'Student removed from course successfully' });
} catch (error) {
console.error('Error removing student from course:', error);
res.status(500).json({ message: 'Failed to remove student from course' });
}
};
exports.createGroups = async (req, res) => {
const { courseId } = req.params;
const { groupSize } = req.body //This refers the # of students in each group
try {
const course = await Course.findById(courseId).populate('students');
if (!course) {
return res.status(404).json({ message: 'Course not found' });
}
//Fetch student id
const studentIds = course.students;
// shuffle students
const shuffledStudents = studentIds.sort(() => Math.random() - 0.5);
// splits into groups
let groups = [];
for (let i = 0; i < shuffledStudents.length; i += groupSize) {
groups.push(shuffledStudents.slice(i, i + groupSize));
}
// save the group in the db
course.groups = groups.map((group, index) => ({
groupNumber: index + 1,
members: group
}));
await course.save();
res.status(200).json({ message: 'Groups created successfully', groups: course.groups });
} catch (error) {
console.error('Error creating groups:', error);
res.status(500).json({ message: 'Failed to create groups' });
}
};
exports.getGroups = async (req, res) => {
const { courseId } = req.params;
try {
const course = await Course.findById(courseId).populate('groups.members', 'name email');
if (!course) {
return res.status(404).json({ message: 'Course not found' });
}
res.status(200).json({ groups: course.groups });
} catch (error) {
console.error('Error fetching groups:', error);
res.status(500).json({ message: 'Failed to fetch groups' });
}
};
exports.saveGroups = async (req, res) => {
const { courseId } = req.params;
const { groups } = req.body;
try {
const course = await Course.findById(courseId);
if (!course) {
return res.status(404).json({ message: 'Course not found' });
}
course.groups = groups;
await course.save();
res.status(200).json({ message: 'Groups saved successfully' });
} catch (error) {
console.error('Error saving groups:', error);
res.status(500).json({ message: 'Failed to save groups' });
}
};
const Feedback = require('../models/feedback');
exports.saveFeedback = async (req, res) => {
try {
if (!req.body.title || !req.body.message) {
return res.status(400).json({ success: false, message: "Title and message are required fields" });
}
const data = new Feedback(req.body);
await data.save();
res.status(200).json({ success: true, message: "Feedback sent successfully" });
} catch (error) {
console.error('Error processing request:', error);
res.status(500).json({ success: false, message: "Internal server error" });
}
};
exports.getFeedback = async (req, res) => {
try {
const data = await Feedback.find({}, { title: 1, message: 1 });
res.status(200).json(data);
} catch (error) {
console.error(error);
res.status(500).json({ success: false, message: "Internal server error" });
}
};
const User = require('../models/user');
exports.saveName = async (req, res) => {
const { firstName, lastName } = req.body;
const email = req.session.user.email;
await User.updateOne({ email: email }, { name: `${firstName} ${lastName}` });
req.session.user.name = `${firstName} ${lastName}`;
req.session.user.isFirstLogin = false;
res.status(200).json({ message: 'Name updated successfully' });
};
exports.resetSession = (req, res) => {
req.session.destroy((err) => {
if (err) {
console.error('Error destroying session:', err);
return res.status(500).send('Failed to reset session');
}
res.send('Session reset');
});
};
exports.getUser = (req, res) => {
if (!req.session.user) {
return res.status(401).send('User not authenticated');
}
res.json({
pid: req.session.user.pid,
email: req.session.user.email,
role: req.session.user.role,
name: req.session.user.name,
isFirstLogin: req.session.user.isFirstLogin
});
};
exports.getStudents = async (req, res) => {
try {
const students = await User.find({}, 'name email');
const studentsWithRole = students.map(student => {
const role = req.session.user.role || 'unknown';
return { ...student._doc, role };
});
res.status(200).json(studentsWithRole);
} catch (error) {
console.error('Error fetching students:', error);
res.status(500).json({ message: 'Failed to fetch students' });
}
};
const express = require('express');
const cors = require('cors');
const mongoose = require('mongoose');
const app = express();
app.use(cors());
app.use(express.json());
const PORT = process.env.PORT || 8080;
const schemaData = mongoose.Schema({
title: String,
message: String,
}, {
timestamps: true
});
const feedbackModel = mongoose.model("feedbacks", schemaData);
mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => {
console.log("Connected to db")
app.listen(PORT, () => console.log(`Server is running on port ${PORT}`));
})
.catch((err) => console.log(err))
app.post("/saveFeedback", async (req, res) => {
try {
console.log(req.body);
if (!req.body.title || !req.body.message) {
return res.status(400).json({ success: false, message: "Title and message are required fields" });
}
const data = new feedbackModel(req.body);
await data.save();
res.status(200).json({ success: true, message: "Data save successful" });
} catch (error) {
console.error(error);
res.status(500).json({ success: false, message: "Internal server error" });
}
});
app.get("/getFeedback", async (req, res) => {
try {
const data = await feedbackModel.find({}, { title: 1, message: 1 });
res.status(200).json(data);
} catch (error) {
console.error(error);
res.status(500).json({ success: false, message: "Internal server error" });
}
});
module.exports = (req, res, next) => {
if (!req.session.user) {
return res.status(401).send('User not authenticated');
}
next();
};
\ No newline at end of file
const mongoose = require('mongoose');
const courseSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
term: {
type: String,
required: true
},
crn: {
type: String,
required: true
},
uniqueCode: {
type: String,
required: true,
unique: true
},
students : [{
type: String
}],
groups: [{
groupNumber: Number,
members: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}]
}]
}, {
timestamps: true
});
module.exports = mongoose.model('Course', courseSchema);
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
pid: { type: String, required: true, unique: true },
role: { type: String},
name: { type: String },
email: { type: String, required: false, unique: true },
isFirstLogin: { type: Boolean, default: true },
createdAt: { type: Date, default: Date.now },
group: {type: String, required: false}
});
const User = mongoose.model('User', userSchema);
module.exports = User;
This diff is collapsed.
...@@ -2,18 +2,21 @@ ...@@ -2,18 +2,21 @@
"name": "backend", "name": "backend",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"main": "index.js", "main": "server.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js", "start": "node server.js",
"dev": "nodemon index.js" "dev": "nodemon server.js"
}, },
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"axios": "^1.6.7", "axios": "^1.6.7",
"cas-authentication": "^0.0.8",
"connect-mongo": "^5.1.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"express": "^4.18.2", "express": "^4.18.2",
"express-session": "^1.18.0",
"mongoose": "^8.1.1", "mongoose": "^8.1.1",
"nodemon": "^3.0.3", "nodemon": "^3.0.3",
"react-router-dom": "^6.22.2" "react-router-dom": "^6.22.2"
......
This diff is collapsed.
const express = require('express');
const router = express.Router();
const authController = require('../controllers/authController');
router.get('/fakeLogin', authController.fakeLogin);
router.get('/Dashboard', authController.handleDashboard);
router.get('/checkSession', authController.checkSession);
module.exports = router;