Skip to content
Draft
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
679 changes: 345 additions & 334 deletions backend/routes/auth.js

Large diffs are not rendered by default.

61 changes: 61 additions & 0 deletions backend/routes/users.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Module Imports
const express = require('express');

// Model Imports
const User = require('../models/user');

// Function Imports
const { verifyAdmin } = require('../utils/verify_token.js');

// Router instance
const router = express.Router();

/**
* ! GET Get User by Username
*
* Gets a user by their username
*
* @async
* @returns {JSON} Responds with the unit and its details in JSON format
* @throws {500} If an error occurs whilst getting the singular user from the database
*/
router.get('/:username', async function (req, res) {
try {
const user = await User.findOne({ username: req.params.username });

if (!user)
return res.status(404).json({ error: 'User not found'});

return res.status(200).json(user);
}
catch (error) {
return res.status(500).json({ error: `An error occured whilst getting the singular user: ${error.message}`});
}
});

/**
* ! GET Get All Users
*
* Gets all users from the database.
*
* @async
* @returns {JSON} Responds with a list of all users in JSON format.
* @throws {500} If an error occurs whilst fetching users from the database.
*/
router.get('/', async function (req, res) {
try {
// Find all the users
const users = await User.find({});

// Respond 200 with JSON list containing all Users.
return res.status(200).json(users);
}
catch (error) {
// Handle general errors 500
return res.status(500).json({ error: `An error occured while getting all Users: ${error.message}` });
}
});


// Export the router
module.exports = router;
270 changes: 135 additions & 135 deletions backend/server.js
Original file line number Diff line number Diff line change
@@ -1,135 +1,135 @@
// Load environment variables
require("dotenv").config();

// Module Imports
const express = require("express");
const mongoose = require("mongoose");
const cron = require("node-cron");
const cors = require("cors");
const app = express();
const cookieParser = require('cookie-parser');
const tagManager = require('./services/tagManager.service');
const aiOverviewService = require("./services/aiOverview.service");
const { exec } = require('child_process');
const path = require("path");

// Router Imports
const UnitRouter = require('./routes/units');
const ReviewRouter = require('./routes/reviews');
const AuthRouter = require('./routes/auth');
const NotificationRouter = require('./routes/notifications');
const GitHubRouter = require("./routes/github");
const SetuRouter = require("./routes/setus");

// === Environment Configuration ===
const isDevelopment = process.env.DEVELOPMENT === 'true';
console.log(`Running in ${isDevelopment ? 'DEVELOPMENT' : 'PRODUCTION'} mode`);

// === Middleware ===
if (isDevelopment) {
app.use(
cors({
origin: "http://localhost:4200",
credentials: true,
})
);
}

app.use(express.json({ limit: "50mb" })); // Increased payload limit for JSON requests.
app.use(express.urlencoded({ limit: "50mb", extended: true })); // Increased payload limit for URL-encoded requests.
app.use(cookieParser());

// Response handler middlware
app.use((obj, req, res, next) => {
const statusCode = obj.status || 500;
const message = obj.message || "Internal server error";
return res.status(statusCode, {
success: [200, 201, 204].some((a) => a === obj.status) ? true : false,
status: statusCode,
message: message,
data: obj.data,
});
});

// === Routes ===
app.use('/api/v1/units', UnitRouter);
app.use('/api/v1/reviews', ReviewRouter);
app.use('/api/v1/auth', AuthRouter);
app.use('/api/v1/notifications', NotificationRouter);
app.use('/api/v1/github', GitHubRouter);
app.use('/api/v1/setus', SetuRouter);

// === Serving Static Files (Production Mode) ===
if (!isDevelopment) {
app.use(express.static(path.join(__dirname, '../frontend/dist/frontend/browser')));
}

// === Connect to MongoDB ===
const url = process.env.MONGODB_CONN_STRING;
async function connect(url) {
await mongoose.connect(url);
}
connect(url)
.then(() => {
console.log("Connected to MongoDB Database");
tagManager.updateMostReviewsTag(1);
})
.catch((error) => console.log(error));

// === Services ===
// Update the most reviews tag every hour
cron.schedule("0 * * * *", async function () {
await tagManager.updateMostReviewsTag(1);
});

// Generate sitemaps daily at 3:00 AM
cron.schedule("0 3 * * *", function () {
console.log("[Cron] Running daily sitemap generation...");

// Path to the sitemap generator script
const scriptPath = path.join(__dirname, "utils", "generate-sitemap.js");

// Use Node to execute the script
exec(`node ${scriptPath}`, (error, stdout, stderr) => {
if (error) {
console.error(`[Cron] Sitemap generation error: ${error.message}`);
return;
}

if (stderr) {
console.error(`[Cron] Sitemap stderr: ${stderr}`);
return;
}

console.log(`[Cron] Sitemap generation complete: ${stdout}`);
});
});


// Regenerate AI unit overviews ahead of each semester (Feb 1 & Jun 1 at 02:00)
cron.schedule('0 2 1 2 *', async function () {
console.log('[Cron] Running Semester 1 AI overview refresh');
await aiOverviewService.generateOverviewsForAllUnits({ force: true, delayMs: 750 });
});

cron.schedule('0 2 1 6 *', async function () {
console.log('[Cron] Running Semester 2 AI overview refresh');
await aiOverviewService.generateOverviewsForAllUnits({ force: true, delayMs: 750 });
});


// === Catch all route (Production Mode) ===
if (!isDevelopment) {
app.get('*', (req, res) => {
return res.sendFile(path.join(__dirname, '../frontend/dist/frontend/browser/index.html'));
});
}


// === Start Server ===
const PORT = process.env.PORT || 8080; // Default to 8080 if no port specified
app.listen(PORT, (error) => {
if (error) console.log(error);

console.log(`Server running on port ${PORT}`);
});
// Load environment variables
require("dotenv").config();
// Module Imports
const express = require("express");
const mongoose = require("mongoose");
const cron = require("node-cron");
const cors = require("cors");
const app = express();
const cookieParser = require('cookie-parser');
const tagManager = require('./services/tagManager.service');
const aiOverviewService = require("./services/aiOverview.service");
const { exec } = require('child_process');
const path = require("path");
// Router Imports
const UnitRouter = require('./routes/units');
const ReviewRouter = require('./routes/reviews');
const AuthRouter = require('./routes/auth');
const NotificationRouter = require('./routes/notifications');
const GitHubRouter = require("./routes/github");
const SetuRouter = require("./routes/setus");
// === Environment Configuration ===
const isDevelopment = process.env.DEVELOPMENT === 'true';
console.log(`Running in ${isDevelopment ? 'DEVELOPMENT' : 'PRODUCTION'} mode`);
// === Middleware ===
if (isDevelopment) {
app.use(
cors({
origin: "http://localhost:4200",
credentials: true,
})
);
}
app.use(express.json({ limit: "50mb" })); // Increased payload limit for JSON requests.
app.use(express.urlencoded({ limit: "50mb", extended: true })); // Increased payload limit for URL-encoded requests.
app.use(cookieParser());
// Response handler middlware
app.use((obj, req, res, next) => {
const statusCode = obj.status || 500;
const message = obj.message || "Internal server error";
return res.status(statusCode, {
success: [200, 201, 204].some((a) => a === obj.status) ? true : false,
status: statusCode,
message: message,
data: obj.data,
});
});
// === Routes ===
app.use('/api/v1/units', UnitRouter);
app.use('/api/v1/reviews', ReviewRouter);
app.use('/api/v1/auth', AuthRouter);
app.use('/api/v1/notifications', NotificationRouter);
app.use('/api/v1/github', GitHubRouter);
app.use('/api/v1/setus', SetuRouter);
// === Serving Static Files (Production Mode) ===
if (!isDevelopment) {
app.use(express.static(path.join(__dirname, '../frontend/dist/frontend/browser')));
}
// === Connect to MongoDB ===
const url = process.env.MONGODB_CONN_STRING;
async function connect(url) {
await mongoose.connect(url);
}
connect(url)
.then(() => {
console.log("Connected to MongoDB Database");
tagManager.updateMostReviewsTag(1);
})
.catch((error) => console.log(error));
// === Services ===
// Update the most reviews tag every hour
cron.schedule("0 * * * *", async function () {
await tagManager.updateMostReviewsTag(1);
});
// Generate sitemaps daily at 3:00 AM
cron.schedule("0 3 * * *", function () {
console.log("[Cron] Running daily sitemap generation...");
// Path to the sitemap generator script
const scriptPath = path.join(__dirname, "utils", "generate-sitemap.js");
// Use Node to execute the script
exec(`node ${scriptPath}`, (error, stdout, stderr) => {
if (error) {
console.error(`[Cron] Sitemap generation error: ${error.message}`);
return;
}
if (stderr) {
console.error(`[Cron] Sitemap stderr: ${stderr}`);
return;
}
console.log(`[Cron] Sitemap generation complete: ${stdout}`);
});
});
// Regenerate AI unit overviews ahead of each semester (Feb 1 & Jun 1 at 02:00)
cron.schedule('0 2 1 2 *', async function () {
console.log('[Cron] Running Semester 1 AI overview refresh');
await aiOverviewService.generateOverviewsForAllUnits({ force: true, delayMs: 750 });
});
cron.schedule('0 2 1 6 *', async function () {
console.log('[Cron] Running Semester 2 AI overview refresh');
await aiOverviewService.generateOverviewsForAllUnits({ force: true, delayMs: 750 });
});
// === Catch all route (Production Mode) ===
if (!isDevelopment) {
app.get('*', (req, res) => {
return res.sendFile(path.join(__dirname, '../frontend/dist/frontend/browser/index.html'));
});
}
// === Start Server ===
const PORT = process.env.PORT || 8080; // Default to 8080 if no port specified
app.listen(PORT, (error) => {
if (error) console.log(error);
console.log(`Server running on port ${PORT}`);
});
Loading