Skip to content

Commit

Permalink
Can't use backend without mongo url env secret (#478)
Browse files Browse the repository at this point in the history
* lints

* roadmap

* h

* Update api/src/app.ts

Co-authored-by: Jacob Sommer <[email protected]>

* conflict

---------

Co-authored-by: Jacob Sommer <[email protected]>
  • Loading branch information
ptruong0 and js0mmer authored Sep 23, 2024
1 parent 820ed90 commit 57b0f3f
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 144 deletions.
54 changes: 32 additions & 22 deletions api/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,34 +35,43 @@ const app = express();
// Setup mongo store for sessions
const mongoStore = MongoDBStore(session);

let store: undefined | MongoDBStore.MongoDBStore;
if (process.env.MONGO_URL) {
const store = new mongoStore({
store = new mongoStore({
uri: process.env.MONGO_URL,
databaseName: DB_NAME,
collection: COLLECTION_NAMES.SESSIONS,
});
// Catch errors
mongoose.connection.on('error', function (error) {
console.log(error);
});
store.on('error', function (error) {
console.log(error);
});
// Setup Passport and Sessions
app.use(
session({
secret: process.env.SESSION_SECRET!,
resave: false,
saveUninitialized: false,
cookie: { maxAge: SESSION_LENGTH },
store: store,
}),
);
} else {
console.log('MONGO_URL env var is not defined!');
}
// Catch errors
mongoose.connection.on('error', function (error) {
console.log(error);
});
store?.on('error', function (error) {
console.log(error);
});
// Setup Passport and Sessions
if (!process.env.SESSION_SECRET) {
console.log('SESSION_SECRET env var is not defined!');
}
app.use(
session({
secret: process.env.SESSION_SECRET ?? 'secret',
resave: false,
saveUninitialized: false,
cookie: { maxAge: SESSION_LENGTH },
store: store,
}),
);

if (process.env.GOOGLE_CLIENT && process.env.GOOGLE_SECRET) {
app.use(passport.initialize());
app.use(passport.session());
require('./config/passport');
} else {
console.log('MONGO_URL env var is not defined!');
console.log('GOOGLE_CLIENT and/or GOOGLE_SECRET env var(s) not defined! Google login will not be available.');
}

/**
Expand Down Expand Up @@ -109,10 +118,11 @@ app.use(function (req, res) {
res.status(500).json({ error: `Internal Serverless Error - '${req}'` });
});

let conn: null | Mongoose = null;
const uri = process.env.MONGO_URL;
export const connect = async () => {
if (conn == null) {
let conn: null | Mongoose = null;
const uri = process.env.MONGO_URL;

if (conn == null && uri) {
conn = await mongoose.connect(uri!, {
dbName: DB_NAME,
serverSelectionTimeoutMS: 5000,
Expand Down
56 changes: 37 additions & 19 deletions api/src/controllers/reports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,20 @@ const router = express.Router();
*/
router.get('/', async (req, res) => {
// get all reports in collection
const reports = await Report.find({});
if (!req.session.passport) return res.status(401).send('Unathenticated');
if (!req.session.passport.admin) return res.status(403).send('Unauthorized');

res.json(reports);
Report.find({})
.then((reports) => {
if (reports) {
res.json(reports);
} else {
res.json({ error: 'No reports found!' });
}
})
.catch(() => {
res.json({ error: 'Cannot get reports' });
});
});

/**
Expand All @@ -25,32 +34,41 @@ router.get('/', async (req, res) => {
router.post('/', async (req, res) => {
console.log(`Adding Report: ${JSON.stringify(req.body)}`);
const report = new Report(req.body);
await report.save();

res.json(req.body);
report
.save()
.then(() => {
res.json(req.body);
})
.catch(() => {
res.json({ error: 'Cannot add report' });
});
});

/**
* Delete a report
*/
router.delete('/', async (req, res) => {
let status;
if (!req.session.passport) return res.status(401).send('Unathenticated');
if (!req.session.passport.admin) return res.status(403).send('Unauthorized');
if (req.body.id) {
console.log(`Deleting report ${req.body.id}`);
status = await Report.deleteOne({ _id: req.body.id });
} else {
console.log(`Deleting reports with reviewID ${req.body.reviewID}`);
const query: GenericObject = {};
if (req.body.reviewID) query['reviewID'] = req.body.reviewID;
try {
let status;
if (!req.session.passport) return res.status(401).send('Unathenticated');
if (!req.session.passport.admin) return res.status(403).send('Unauthorized');
if (req.body.id) {
console.log(`Deleting report ${req.body.id}`);
status = await Report.deleteOne({ _id: req.body.id });
} else {
console.log(`Deleting reports with reviewID ${req.body.reviewID}`);
const query: GenericObject = {};
if (req.body.reviewID) query['reviewID'] = req.body.reviewID;

if (Object.keys(query).length === 0) return; // avoid deleting all documents if no filters are specified
if (Object.keys(query).length === 0) return; // avoid deleting all documents if no filters are specified

status = await Report.deleteMany(query);
}
status = await Report.deleteMany(query);
}

res.json(status);
res.json(status);
} catch {
res.json({ error: 'Cannot delete report' });
}
});

export default router;
149 changes: 88 additions & 61 deletions api/src/controllers/reviews.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,22 @@ router.get('/scores', async function (req: Request<never, unknown, never, Scores

// execute aggregation on the reviews collection

const aggreg = await Review.aggregate([
Review.aggregate([
{ $match: { [matchField]: req.query.id } },
{ $group: { _id: groupField, score: { $avg: '$rating' } } },
]);

// returns the results in an array
const array = aggreg as ReviewData[];
// rename _id to name
const results = array.map((v) => {
return { name: v._id, score: v.score };
});
res.json(results);
])
.then((aggreg) => {
// returns the results in an array
const array = aggreg as ReviewData[];
// rename _id to name
const results = array.map((v) => {
return { name: v._id, score: v.score };
});
res.json(results);
})
.catch(() => {
res.json({ error: 'Cannot aggregate reviews' });
});
});

/**
Expand All @@ -63,14 +67,19 @@ router.get('/featured', async function (req: Request<never, unknown, never, Feat
}

// find first review with the highest score
const reviewsCollection = await Review.find({ [field]: req.query.id })
Review.find({ [field]: req.query.id })
.sort({ score: -1 })
.limit(1);
if (reviewsCollection) {
res.json(reviewsCollection);
} else {
res.json([]);
}
.limit(1)
.then((reviewsCollection) => {
if (reviewsCollection) {
res.json(reviewsCollection);
} else {
res.json([]);
}
})
.catch(() => {
res.json({ error: 'Cannot find review' });
});
});

interface ReviewFilter {
Expand Down Expand Up @@ -179,41 +188,45 @@ router.post('/', async function (req, res) {
if (req.session.passport) {
//^ this should be a middleware check smh

// check if user is trusted
const verifiedCount = await Review.find({
userID: req.session.passport.user.id,
verified: true,
})
.countDocuments()
.exec();
try {
// check if user is trusted
const verifiedCount = await Review.find({
userID: req.session.passport.user.id,
verified: true,
})
.countDocuments()
.exec();

// Set on server so the client can't automatically verify their own review.
req.body.verified = verifiedCount >= 3; // auto-verify if use has posted 3+ reviews
// Set on server so the client can't automatically verify their own review.
req.body.verified = verifiedCount >= 3; // auto-verify if use has posted 3+ reviews

// Verify the captcha
const verifyResponse = await verifyCaptcha(req.body);
if (!verifyResponse?.success)
return res.status(400).json({ error: 'ReCAPTCHA token is invalid', data: verifyResponse });
delete req.body.captchaToken; // so it doesn't get stored in DB
// Verify the captcha
const verifyResponse = await verifyCaptcha(req.body);
if (!verifyResponse?.success)
return res.status(400).json({ error: 'ReCAPTCHA token is invalid', data: verifyResponse });
delete req.body.captchaToken; // so it doesn't get stored in DB

//check if review already exists for same professor, course, and user
const query: ReviewFilter = {
courseID: req.body.courseID,
professorID: req.body.professorID,
userID: req.session.passport.user.id,
};
//check if review already exists for same professor, course, and user
const query: ReviewFilter = {
courseID: req.body.courseID,
professorID: req.body.professorID,
userID: req.session.passport.user.id,
};

const reviews = await Review.find(query);
if (reviews?.length > 0)
return res.status(400).json({ error: 'Review already exists for this professor and course!' });
// add review to mongo
req.body.userDisplay =
req.body.userDisplay === 'Anonymous Peter' ? 'Anonymous Peter' : req.session.passport.user.name;
req.body.userID = req.session.passport.user.id;
await new Review(req.body).save();
const reviews = await Review.find(query);
if (reviews?.length > 0)
return res.status(400).json({ error: 'Review already exists for this professor and course!' });
// add review to mongo
req.body.userDisplay =
req.body.userDisplay === 'Anonymous Peter' ? 'Anonymous Peter' : req.session.passport.user.name;
req.body.userID = req.session.passport.user.id;
await new Review(req.body).save();

// echo back body
res.json(req.body);
// echo back body
res.json(req.body);
} catch {
res.json({ error: 'Cannot add review' });
}
} else {
res.json({ error: 'Must be logged in to add a review!' });
}
Expand All @@ -223,17 +236,21 @@ router.post('/', async function (req, res) {
* Delete a review
*/
router.delete('/', async (req, res) => {
const checkUser = async () => {
return await Review.findOne({ _id: req.body.id as string, userID: req.session.passport?.user.id }).exec();
};
try {
const checkUser = async () => {
return await Review.findOne({ _id: req.body.id as string, userID: req.session.passport?.user.id }).exec();
};

if (req.session.passport?.admin || (await checkUser())) {
await Review.deleteOne({ _id: req.body.id });
await Vote.deleteMany({ reviewID: req.body.id });
await Report.deleteMany({ reviewID: req.body.id });
res.status(200).send();
} else {
res.json({ error: 'Must be an admin or review author to delete reviews!' });
if (req.session.passport?.admin || (await checkUser())) {
await Review.deleteOne({ _id: req.body.id });
await Vote.deleteMany({ reviewID: req.body.id });
await Report.deleteMany({ reviewID: req.body.id });
res.status(200).send();
} else {
res.json({ error: 'Must be an admin or review author to delete reviews!' });
}
} catch {
res.json({ error: 'Cannot delete review' });
}
});

Expand Down Expand Up @@ -287,8 +304,13 @@ router.patch('/vote', async function (req, res) {
router.patch('/verify', async function (req, res) {
if (req.session.passport?.admin) {
console.log(`Verifying review ${req.body.id}`);
const status = await Review.updateOne({ _id: req.body.id }, { verified: true });
res.json(status);
Review.updateOne({ _id: req.body.id }, { verified: true })
.then((status) => {
res.json(status);
})
.catch(() => {
res.json({ error: 'Cannot verify review' });
});
} else {
res.json({ error: 'Must be an admin to verify reviews!' });
}
Expand All @@ -299,8 +321,13 @@ router.patch('/verify', async function (req, res) {
*/
router.delete('/clear', async function (req, res) {
if (process.env.NODE_ENV != 'production') {
const status = await Review.deleteMany({});
res.json(status);
Review.deleteMany({})
.then((status) => {
res.json(status);
})
.catch(() => {
res.json({ error: 'Cannot clear reviews' });
});
} else {
res.json({ error: 'Can only clear on development environment' });
}
Expand Down
Loading

0 comments on commit 57b0f3f

Please sign in to comment.