diff --git a/backend/api/index.ts b/backend/api/index.ts index b7502d9..546482f 100644 --- a/backend/api/index.ts +++ b/backend/api/index.ts @@ -1,7 +1,6 @@ // Explicitly tell module-alias where to find package.json in backend directory -// @ts-ignore - module-alias doesn't have types +// @ts-expect-error - module-alias doesn't have types import moduleAlias from "module-alias"; - moduleAlias(require.resolve("../package.json")); import type { VercelRequest, VercelResponse } from "@vercel/node"; import mongoose from "mongoose"; diff --git a/backend/src/controllers/InterviewQuestionController.ts b/backend/src/controllers/InterviewQuestionController.ts index d0f6ade..50c8231 100644 --- a/backend/src/controllers/InterviewQuestionController.ts +++ b/backend/src/controllers/InterviewQuestionController.ts @@ -5,13 +5,13 @@ import asyncHandler from "express-async-handler"; import createHttpError from "http-errors"; import Company from "../models/Company"; import mongoose from "mongoose"; -import User from "../models/User"; // Interface for creating/updating an interview question interface InterviewQuestionCreate { company: mongoose.Types.ObjectId; + userId: string; question: string; - date: Date; + date?: Date; } interface InterviewQuestionUpdate extends Partial {} @@ -25,7 +25,6 @@ export const getAllInterviewQuestions = asyncHandler(async (req, res, _) => { // TODO: Implement with paginated date, for now it will just return all interview questions for testing const interviewQuestions = await InterviewQuestion.find() .populate({ path: "company", model: Company }) - .populate({ path: "user", model: User }) .lean() .exec(); @@ -63,7 +62,6 @@ export const createInterviewQuestion = asyncHandler(async (req, res, next) => { question: interviewQuestionData.question, }) .populate({ path: "company", model: Company }) - .populate({ path: "user", model: User }) .lean() .exec(); @@ -93,7 +91,6 @@ export const getInterviewQuestionById = asyncHandler(async (req, res, next) => { // Find interview question by ID const interviewQuestion = await InterviewQuestion.findById(id) .populate({ path: "company", model: Company }) - .populate({ path: "user", model: User }) .lean() .exec(); @@ -125,7 +122,6 @@ export const getInterviewQuestionsByCompanyId = asyncHandler( company: companyId, }) .populate({ path: "company", model: Company }) - .populate({ path: "user", model: User }) .lean() .exec(); @@ -176,7 +172,6 @@ export const updateInterviewQuestion = asyncHandler(async (req, res, next) => { { new: true, runValidators: true }, ) .populate({ path: "company", model: Company }) - .populate({ path: "user", model: User }) .lean() .exec(); diff --git a/backend/src/controllers/applicationController.ts b/backend/src/controllers/applicationController.ts index 7e87d3a..dedc87e 100644 --- a/backend/src/controllers/applicationController.ts +++ b/backend/src/controllers/applicationController.ts @@ -15,6 +15,7 @@ import mongoose, { PipelineStage } from "mongoose"; interface ApplicationCreate { userId: string; company: mongoose.Types.ObjectId; + companyName: string; position: string; link?: string; location?: string; diff --git a/backend/src/controllers/leetcodeQuestionController.ts b/backend/src/controllers/leetcodeQuestionController.ts index 2416b94..9780ff8 100644 --- a/backend/src/controllers/leetcodeQuestionController.ts +++ b/backend/src/controllers/leetcodeQuestionController.ts @@ -6,12 +6,11 @@ import validationErrorParser from "../util/validationErrorParser"; import createHttpError from "http-errors"; import Company from "../models/Company"; import mongoose from "mongoose"; -import User from "../models/User"; // interface for creating/updating leetcodeQuestions interface leetcodeQuestionCreate { company: mongoose.Types.ObjectId; - user: mongoose.Types.ObjectId; + userId: string; title: string; url: string; difficulty: Difficulty; @@ -61,7 +60,6 @@ export const getLeetcodeQuestions = asyncHandler(async (req, res, next) => { .skip(page * perPage) .limit(perPage) .populate({ path: "company", model: Company }) - .populate({ path: "user", model: User }) .lean() .exec(), ]); @@ -106,7 +104,6 @@ export const createLeetcodeQuestion = asyncHandler(async (req, res, next) => { newLeetcodeQuestion._id, ) .populate({ path: "company", model: Company }) - .populate({ path: "user", model: User }) .lean() .exec(); @@ -134,7 +131,6 @@ export const getLeetcodeQuestionById = asyncHandler(async (req, res, next) => { // Find leetcodeQuestion by ID const leetcodeQuestion = await LeetcodeQuestion.findById(id) .populate({ path: "company", model: Company }) - .populate({ path: "user", model: User }) .lean() .exec(); @@ -176,7 +172,6 @@ export const updateLeetcodeQuestion = asyncHandler(async (req, res, next) => { { new: true, runValidators: true }, ) .populate({ path: "company", model: Company }) - .populate({ path: "user", model: User }) .lean() .exec(); @@ -226,7 +221,6 @@ export const getLeetcodeQuestionByCompanyId = asyncHandler( company: companyId, }) .populate({ path: "company", model: Company }) - .populate({ path: "user", model: User }) .lean() .exec(); diff --git a/backend/src/controllers/savedApplicationController.ts b/backend/src/controllers/savedApplicationController.ts index 3b6a749..b6a1627 100644 --- a/backend/src/controllers/savedApplicationController.ts +++ b/backend/src/controllers/savedApplicationController.ts @@ -11,6 +11,7 @@ import mongoose, { PipelineStage } from "mongoose"; interface SavedApplicationCreate { userId: string; company: mongoose.Types.ObjectId; + companyName: string; position: string; location?: string; link?: string; diff --git a/backend/src/controllers/tipController.ts b/backend/src/controllers/tipController.ts index db0e415..1b4b864 100644 --- a/backend/src/controllers/tipController.ts +++ b/backend/src/controllers/tipController.ts @@ -5,11 +5,10 @@ import asyncHandler from "express-async-handler"; import createHttpError from "http-errors"; import { Schema } from "mongoose"; import Company from "../models/Company"; -import User from "../models/User"; // Interface for creating/updating a tip interface TipCreate { - user: string; + userId: string; company: Schema.Types.ObjectId; text: string; } @@ -25,7 +24,6 @@ export const getAllTips = asyncHandler(async (req, res, _) => { // Retrieve all tips from the database const tips = await Tip.find() .populate({ path: "company", model: Company }) - .populate({ path: "user", model: User }) .lean() .exec(); @@ -55,7 +53,6 @@ export const createTip = asyncHandler(async (req, res, next) => { const populatedTip = await Tip.findById(newTip._id) .populate({ path: "company", model: Company }) - .populate({ path: "user", model: User }) .lean() .exec(); @@ -82,7 +79,6 @@ export const getTipById = asyncHandler(async (req, res, next) => { // Find the tip by ID const tip = await Tip.findById(id) .populate({ path: "company", model: Company }) - .populate({ path: "user", model: User }) .lean() .exec(); @@ -130,7 +126,6 @@ export const updateTipById = asyncHandler(async (req, res, next) => { { new: true, runValidators: true }, ) .populate({ path: "company", model: Company }) - .populate({ path: "user", model: User }) .lean() .exec(); @@ -189,7 +184,6 @@ export const getTipsByCompanyId = asyncHandler(async (req, res, next) => { // Find tips for the specified company with pagination const tips = await Tip.find({ company: id }) .populate({ path: "company", model: Company }) - .populate({ path: "user", model: User }) .lean() .exec(); diff --git a/backend/src/middlewares/logger.ts b/backend/src/middlewares/logger.ts index afa0d87..574905a 100644 --- a/backend/src/middlewares/logger.ts +++ b/backend/src/middlewares/logger.ts @@ -8,8 +8,14 @@ export const logEvents = async (message: string, logFileName: string) => { const dateTime = new Date(); const logItem = `${dateTime}\t${message}\n`; + // Skip file logging on Vercel (read-only filesystem) + if (process.env.VERCEL === "1") { + console.log(logItem); + return; + } + try { - // Create logs directory if it doesn't exist + // Create logs directory if it doesn't exist (local only) if (!fs.existsSync(path.join(__dirname, "..", "logs"))) { await fsPromises.mkdir(path.join(__dirname, "..", "logs")); } diff --git a/backend/src/models/Application.ts b/backend/src/models/Application.ts index 4717efe..7d6183d 100644 --- a/backend/src/models/Application.ts +++ b/backend/src/models/Application.ts @@ -44,6 +44,11 @@ const applicationSchema = new Schema( type: Schema.Types.ObjectId, required: true, }, + companyName: { + type: String, + required: true, + trim: true, + }, position: { type: String, required: true, diff --git a/backend/src/models/InterviewQuestion.ts b/backend/src/models/InterviewQuestion.ts index 8b77edb..cf1fd72 100644 --- a/backend/src/models/InterviewQuestion.ts +++ b/backend/src/models/InterviewQuestion.ts @@ -5,7 +5,7 @@ const interviewQuestionSchema = new Schema({ type: Schema.Types.ObjectId, required: true, }, - user: { + userId: { type: String, required: true, }, diff --git a/backend/src/models/LeetcodeQuestion.ts b/backend/src/models/LeetcodeQuestion.ts index 8a292ce..ba3e333 100644 --- a/backend/src/models/LeetcodeQuestion.ts +++ b/backend/src/models/LeetcodeQuestion.ts @@ -12,7 +12,7 @@ const leetcodeQuestionSchema = new Schema({ type: Schema.Types.ObjectId, required: true, }, - user: { + userId: { type: String, required: true, }, @@ -40,7 +40,7 @@ type LeetcodeQuestion = InferSchemaType; // Add indexes for better performance leetcodeQuestionSchema.index({ difficulty: 1 }); // Difficulty filtering leetcodeQuestionSchema.index({ company: 1 }); // Company filtering -leetcodeQuestionSchema.index({ user: 1 }); // User's questions +leetcodeQuestionSchema.index({ userId: 1 }); // User's questions leetcodeQuestionSchema.index({ title: "text" }); // Title search export default model( diff --git a/backend/src/models/SavedApplication.ts b/backend/src/models/SavedApplication.ts index 15beecb..8509e7b 100644 --- a/backend/src/models/SavedApplication.ts +++ b/backend/src/models/SavedApplication.ts @@ -10,6 +10,11 @@ const savedApplicationSchema = new Schema( type: Schema.Types.ObjectId, required: true, }, + companyName: { + type: String, + required: true, + trim: true, + }, location: { type: String, required: false, diff --git a/backend/src/models/Tips.ts b/backend/src/models/Tips.ts index 9fc3f62..aa0c956 100644 --- a/backend/src/models/Tips.ts +++ b/backend/src/models/Tips.ts @@ -2,7 +2,7 @@ import { InferSchemaType, Schema, model } from "mongoose"; const tipSchema = new Schema( { - user: { + userId: { type: String, required: true, }, diff --git a/backend/src/validators/applicationValidator.ts b/backend/src/validators/applicationValidator.ts index df743d6..b68f350 100644 --- a/backend/src/validators/applicationValidator.ts +++ b/backend/src/validators/applicationValidator.ts @@ -68,6 +68,13 @@ const validateCompany = body("company") .withMessage("invalid company. (Must be a Mongo ObjectID.)") .trim(); +const validateCompanyName = body("companyName") + .isString() + .withMessage("companyName must be a string.") + .trim() + .notEmpty() + .withMessage("companyName must be a non-empty string."); + const validatePosition = body("position") .isString() .withMessage("position must be a string.") @@ -116,6 +123,7 @@ const validateProcess = [ export const createApplicationValidator = [ validateUserId, validateCompany, + validateCompanyName, validatePosition, validateLink, validateLocation, @@ -128,6 +136,7 @@ export const updateApplicationValidator = [ validateId, validateUserId.optional(), validateCompany.optional(), + validateCompanyName.optional(), validatePosition.optional(), validateLocation.optional(), validateLink.optional(), diff --git a/backend/src/validators/interviewQuestionValidator.ts b/backend/src/validators/interviewQuestionValidator.ts index 39e78c4..fe33fa4 100644 --- a/backend/src/validators/interviewQuestionValidator.ts +++ b/backend/src/validators/interviewQuestionValidator.ts @@ -19,10 +19,12 @@ const validateCompanyIdParam = param("companyId") .withMessage("invalid company id. (Must be a Mongo ObjectID.)") .trim(); -const validateUser = body("user") +const validateUserId = body("userId") .isString() - .withMessage("invalid user id. (Must be a string)") - .trim(); + .withMessage("invalid userId. (Must be a string)") + .trim() + .notEmpty() + .withMessage("userId must be a non-empty string."); const validateQuestion = body("question") .isString() @@ -68,7 +70,7 @@ export const getInterviewQuestionsValidator = [ export const createInterviewQuestionValidator = [ validateCompany, - validateUser, + validateUserId, validateQuestion, validateDate, ]; @@ -77,7 +79,7 @@ export const getInterviewQuestionByIdValidator = [validateIdParam]; export const updateInterviewQuestionValidator = [ validateIdParam, - validateUser.optional(), + validateUserId.optional(), validateCompany.optional(), validateQuestion.optional(), validateDate, diff --git a/backend/src/validators/leetcodeQuestionValidator.ts b/backend/src/validators/leetcodeQuestionValidator.ts index 5e5901f..f811c97 100644 --- a/backend/src/validators/leetcodeQuestionValidator.ts +++ b/backend/src/validators/leetcodeQuestionValidator.ts @@ -20,10 +20,12 @@ const validateCompanyIdParam = param("companyId") .withMessage("invalid company id. (Must be a Mongo ObjectID.)") .trim(); -const validateUser = body("user") +const validateUserId = body("userId") .isString() - .withMessage("invalid user id. (Must be a string)") - .trim(); + .withMessage("invalid userId. (Must be a string)") + .trim() + .notEmpty() + .withMessage("userId must be a non-empty string."); const validateTitle = body("title") .isString() @@ -105,7 +107,7 @@ export const getLeetcodeQuestionsValidator = [ export const createLeetcodeQuestionValidator = [ validateCompany, - validateUser, + validateUserId, validateTitle, validateURL, validateDifficulty, @@ -116,7 +118,7 @@ export const getLeetcodeQuestionByIdValidator = [validateIdParam]; export const updateLeetcodeQuestionValidator = [ validateIdParam, - validateUser.optional(), + validateUserId.optional(), validateCompany.optional(), validateTitle.optional(), validateURL.optional(), diff --git a/backend/src/validators/savedApplicationValidator.ts b/backend/src/validators/savedApplicationValidator.ts index d9cffcb..c79cdff 100644 --- a/backend/src/validators/savedApplicationValidator.ts +++ b/backend/src/validators/savedApplicationValidator.ts @@ -21,6 +21,13 @@ const validateCompany = body("company") .withMessage("invalid company id. (Must be a Mongo ObjectID.)") .trim(); +const validateCompanyName = body("companyName") + .isString() + .withMessage("companyName must be a string.") + .trim() + .notEmpty() + .withMessage("companyName must be a non-empty string."); + const validateLocation = body("location") .optional() .isString() @@ -81,6 +88,7 @@ const validatePerPage = query("perPage") export const createSavedApplicationValidator = [ validateUserId, validateCompany, + validateCompanyName, validatePosition, validateLocation, validateLink, @@ -93,6 +101,7 @@ export const getSavedApplicationValidator = [validateParamId]; export const updateSavedApplicationValidator = [ validateParamId, validateCompany.optional(), + validateCompanyName.optional(), validatePosition.optional(), validateLink.optional(), validateLocation.optional(), diff --git a/backend/src/validators/tipValidator.ts b/backend/src/validators/tipValidator.ts index f0e5349..d9dd148 100644 --- a/backend/src/validators/tipValidator.ts +++ b/backend/src/validators/tipValidator.ts @@ -9,12 +9,12 @@ const validateId = param("id") .withMessage("Invalid tip id. (Must be a Mongo ObjectID.)") .trim(); -const validateUser = body("user") +const validateUserId = body("userId") .isString() - .withMessage("user must be a string.") + .withMessage("userId must be a string.") .trim() .notEmpty() - .withMessage("user must be a non-empty string."); + .withMessage("userId must be a non-empty string."); const validateCompany = body("company") .isMongoId() @@ -45,13 +45,17 @@ const validateCompanyId = param("id") // .toInt() // .withMessage("per page must be an integer greater than 1"); -export const createTipValidator = [validateUser, validateCompany, validateText]; +export const createTipValidator = [ + validateUserId, + validateCompany, + validateText, +]; export const getTipValidator = [validateId]; export const updateTipValidator = [ validateId, - validateUser.optional(), + validateUserId.optional(), validateCompany.optional(), validateText.optional(), ]; diff --git a/frontend/index.html b/frontend/index.html index 540cc85..32d6173 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,4 +1,4 @@ - +