Initial commit: Angular frontend and Expressjs backend

This commit is contained in:
chamikaJ
2024-05-17 09:32:30 +05:30
parent eb0a0d77d6
commit 298ca6beeb
3548 changed files with 193558 additions and 3 deletions

View File

@@ -0,0 +1,23 @@
import {NextFunction} from "express";
import sharp from "sharp";
import {IWorkLenzRequest} from "../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../interfaces/worklenz-response";
import {ServerResponse} from "../models/server-response";
export default async function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction) {
if (!req.body.file) return next();
try {
const buffer = Buffer.from(req.body.file.replace(/^data:(.*?);base64,/, ""), "base64");
const out = await sharp(buffer)
.webp({quality: 50})
.toBuffer();
req.body.type = "webp";
req.body.buffer = out;
return next();
} catch (error) {
return res.status(200).send(new ServerResponse(false, null, "Upload failed"));
}
}

View File

@@ -0,0 +1,9 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../interfaces/worklenz-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction) {
// Map string[] -> Array<{ id: string; }>
req.body.tasks = req.body.tasks.map((id: string) => ({id}));
return next();
}

View File

@@ -0,0 +1,15 @@
import {NextFunction, Request, Response} from "express";
import {Schema, Validator} from "jsonschema";
import {ServerResponse} from "../models/server-response";
export default function (schema: Schema) {
return (req: Request, res: Response, next: NextFunction) => {
const validator = new Validator();
const result = validator.validate(req.body, schema);
if (!result.valid)
return res.status(400).send(new ServerResponse(false, null, (result.errors[0]?.schema as any).message || null));
return next();
};
}

View File

@@ -0,0 +1,26 @@
import session from "express-session";
import db from "../config/db";
// eslint-disable-next-line @typescript-eslint/no-var-requires
const pgSession = require("connect-pg-simple")(session);
export default session({
name: process.env.SESSION_NAME,
secret: process.env.SESSION_SECRET || [], // session secret
proxy: false,
resave: false,
saveUninitialized: true,
rolling: true,
store: new pgSession({
pool: db.pool,
tableName: "pg_sessions"
}),
cookie: {
path: "/",
// secure: true,
// httpOnly: true,
// sameSite: true,
// domain: process.env.HOSTNAME,
maxAge: 30 * 24 * 60 * 60 * 1000 // 30 days
}
});

View File

@@ -0,0 +1 @@
Validators are expressjs middlewares that validate the request body.

View File

@@ -0,0 +1,22 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {file, file_name, size} = req.body;
if (!file || !file_name || !size)
return res.status(200).send(new ServerResponse(false, null, "Upload failed"));
if (size > 200000)
return res.status(200).send(new ServerResponse(false, null, "Max file size 200kb.").withTitle("Upload failed!"));
req.body.type = file_name.split(".").pop();
if (req.body.type !== "png" && req.body.type !== "jpg" && req.body.type !== "jpeg")
return res.status(200).send(new ServerResponse(false, null, "Invalid file type"));
return next();
}

View File

@@ -0,0 +1,12 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {name} = req.body;
if (!name?.trim())
return res.status(200).send(new ServerResponse(false, null, "Name is required"));
return next();
}

View File

@@ -0,0 +1,13 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {phase_id, tasks} = req.body;
if (!phase_id || !Array.isArray(tasks))
return res.status(400).send(new ServerResponse(false, null));
return next();
}

View File

@@ -0,0 +1,13 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {priority_id, tasks} = req.body;
if (!priority_id || !Array.isArray(tasks))
return res.status(400).send(new ServerResponse(false, null));
return next();
}

View File

@@ -0,0 +1,13 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {status_id, tasks} = req.body;
if (!status_id || !Array.isArray(tasks))
return res.status(400).send(new ServerResponse(false, null));
return next();
}

View File

@@ -0,0 +1,15 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {tasks} = req.body;
if (!Array.isArray(tasks))
return res.status(400).send(new ServerResponse(false, null));
req.body.labels = Array.isArray(req.body.labels) ? req.body.labels : [];
return next();
}

View File

@@ -0,0 +1,15 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {name} = req.body;
if (!name || name.trim() === "")
return res.status(200).send(new ServerResponse(false, null, "Name is required"));
req.body.name = req.body.name.trim();
return next();
}

View File

@@ -0,0 +1,12 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {project_id} = req.query;
if (!project_id)
return res.status(200).send(new ServerResponse(false, null, "Project ID is required"));
return next();
}

View File

@@ -0,0 +1,19 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {project_id, start_date, end_date} = req.query;
if (!project_id)
return res.status(200).send(new ServerResponse(false, null, "Project ID is required"));
if (!start_date)
return res.status(200).send(new ServerResponse(false, null, "Start date is required"));
if (!end_date)
return res.status(200).send(new ServerResponse(false, null, "End date is required"));
return next();
}

View File

@@ -0,0 +1,24 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {name, end_date, project_id} = req.body;
if (!name?.trim().length)
return res.status(200).send(new ServerResponse(false, null, "Name is required"));
if (!end_date?.trim().length)
return res.status(200).send(new ServerResponse(false, null, "Due date is required"));
if (!project_id)
return res.status(200).send(new ServerResponse(false, null, "Project is required"));
req.body.reporter_id = req.user?.id ?? null;
req.body.team_id = req.user?.team_id ?? null;
req.body.inline = req.query.inline || false;
if (req.body.name.length > 100)
return res.status(200).send(new ServerResponse(false, null, "Task name length exceeded!"));
return next();
}

View File

@@ -0,0 +1,11 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
if (!req.params.id)
return res.status(400).send(new ServerResponse(false, null));
return next();
}

View File

@@ -0,0 +1,15 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
if (!req.params.id)
return res.status(400).send(new ServerResponse(false, null));
if (!req.body.length)
return res.status(400).send(new ServerResponse(false, null, "Tasks are required!"));
return next();
}

View File

@@ -0,0 +1,15 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {name} = req.body;
if (!name || name.trim() === "")
return res.status(200).send(new ServerResponse(false, null, "Name is required"));
req.body.name = req.body.name.trim();
return next();
}

View File

@@ -0,0 +1,13 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {status_id, task_id} = req.params;
if (!status_id || !task_id)
return res.status(200).send(new ServerResponse(false, null, "Updating status failed!"));
return next();
}

View File

@@ -0,0 +1,11 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
if (req.user?.owner)
return next();
return res.status(401).send(new ServerResponse(false, null, "You are not authorized to perform this action"));
}

View File

@@ -0,0 +1,12 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {name} = req.body;
if (!name || name.trim() === "")
return res.status(200).send(new ServerResponse(false, null, "Name is required"));
return next();
}

View File

@@ -0,0 +1,34 @@
import {NextFunction, Request, Response} from "express";
import {ServerResponse} from "../../models/server-response";
import {isProduction} from "../../shared/utils";
import {PasswordStrengthChecker} from "../../shared/password-strength-check";
import {PASSWORD_POLICY} from "../../shared/constants";
function isStrongPassword(password: string) {
if (!isProduction()) return true;
const strength = PasswordStrengthChecker.validate(password);
return strength.value >= 2 && strength.length < 32;
}
export default function (req: Request, res: Response, next: NextFunction) {
const {confirm_password, new_password, password} = req.body;
const psw = (password || "").trim();
const newPws = (new_password || "").trim();
if (!psw)
return res.status(200).send(new ServerResponse(false, null, "Password is required"));
if (newPws) {
if (!isStrongPassword(newPws))
return res.status(200).send(new ServerResponse(false, null, PASSWORD_POLICY).withTitle("Please use a strong new password."));
if (newPws !== confirm_password)
return res.status(200).send(new ServerResponse(false, null, "Passwords do not match"));
} else if (!isStrongPassword(psw)) {
return res.status(200).send(new ServerResponse(false, null, PASSWORD_POLICY).withTitle("Please use a strong password."));
}
return next();
}

View File

@@ -0,0 +1,12 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {name} = req.body;
if (!name || name.trim() === "")
return res.status(200).send(new ServerResponse(false, null, "Name is required"));
return next();
}

View File

@@ -0,0 +1,22 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
import ProjectsController from "../../controllers/projects-controller";
export default async function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): Promise<IWorkLenzResponse | void> {
let is_project_manager = false;
if (req.query.current_project_id) {
const result = await ProjectsController.getProjectManager(req.query.current_project_id as string);
if (result.length)
if (req.user && (result[0].team_member_id === req.user?.team_member_id)) is_project_manager = true;
}
if (req.user && (req.user.owner || req.user.is_admin || is_project_manager))
return next();
return res.status(401).send(new ServerResponse(false, null, "You are not authorized to perform this action"));
}

View File

@@ -0,0 +1,13 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
import {isValidateEmail} from "../../shared/utils";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {project_id, email} = req.body;
if (!project_id || !isValidateEmail(email))
return res.status(400).send(new ServerResponse(false, null));
return next();
}

View File

@@ -0,0 +1,36 @@
import { NextFunction } from "express";
import { IWorkLenzRequest } from "../../interfaces/worklenz-request";
import { IWorkLenzResponse } from "../../interfaces/worklenz-response";
import { ServerResponse } from "../../models/server-response";
import ProjectMembersController from "../../controllers/project-members-controller";
export default async function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): Promise<IWorkLenzResponse | void> {
const projectId = req.body.project_id;
const teamMemberId = req.user?.team_member_id;
const defaultView = req.body.default_view;
if (!req.body.project_id || !defaultView || !teamMemberId) {
return res.status(401).send(new ServerResponse(false, null, "Unknown error has occured"));
}
const isProjectMember = await ProjectMembersController.checkIfMemberExists(projectId, teamMemberId as string);
if (isProjectMember) {
return next();
}
req.body.team_member_id = teamMemberId;
req.body.user_id = req.user?.id;
req.body.team_id = req.user?.team_id;
req.body.access_level = req.body.access_level ? req.body.access_level : "MEMBER";
const isProjectMemberAssigned = await ProjectMembersController.createOrInviteMembers(req.body);
if (isProjectMemberAssigned) {
return next();
}
return res.status(401).send(new ServerResponse(false, null, "Cannot assign as Project Member"));
}

View File

@@ -0,0 +1,34 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {name, color_code, status_id} = req.body;
if (!status_id || status_id.trim() === "")
return res.status(400).send(new ServerResponse(false, null));
if (!name || name.trim() === "")
return res.status(200).send(new ServerResponse(false, null, "Project name is required"));
if (!color_code || color_code.trim() === "")
return res.status(200).send(new ServerResponse(false, null, "Color code is required"));
req.body.name = req.body.name.trim();
if (req.body.name.length > 100)
return res.status(200).send(new ServerResponse(false, null, "Project name length exceeded!"));
if (req.body.notes && req.body.notes.length > 200)
return res.status(200).send(new ServerResponse(false, null, "Project note length exceeded!"));
if (req.body.working_days && !(Number.isInteger(req.body.working_days)))
return res.status(200).send(new ServerResponse(false, null, "Please use integer values"));
if (req.body.man_days && !(Number.isInteger(req.body.man_days)))
return res.status(200).send(new ServerResponse(false, null, "Please use integer values"));
if (req.body.hours_per_day && !(Number.isInteger(req.body.hours_per_day)))
return res.status(200).send(new ServerResponse(false, null, "Please use integer values"));
return next();
}

View File

@@ -0,0 +1,22 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {name, template_id, category_id} = req.body;
if (!name || name.trim() === "")
return res.status(200).send(new ServerResponse(false, null, "Name is required"));
if (!template_id || template_id.trim() === "")
return res.status(400).send(new ServerResponse(false, null));
if (!category_id || category_id.trim() === "")
return res.status(400).send(new ServerResponse(false, null));
req.body.color_code = req.body.color_code || "#a9a9a9";
req.body.default_status = !!req.body.default_status;
req.body.name = req.body.name.trim();
return next();
}

View File

@@ -0,0 +1,15 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {name, project_id} = req.body;
if (!name)
return res.status(200).send(new ServerResponse(false, null, "Name is required"));
if (!project_id)
return res.status(200).send(new ServerResponse(false, null, "Project is required"));
return next();
}

View File

@@ -0,0 +1,17 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
import {isValidateEmail} from "../../shared/utils";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {email} = req.body;
if (!email)
return res.status(200).send(new ServerResponse(false, null, "Email is required"));
if (!isValidateEmail(email))
return res.status(200).send(new ServerResponse(false, null, "Invalid email address"));
return next();
}

View File

@@ -0,0 +1,34 @@
import { NextFunction } from "express";
import { IWorkLenzRequest } from "../../interfaces/worklenz-request";
import { IWorkLenzResponse } from "../../interfaces/worklenz-response";
import { ServerResponse } from "../../models/server-response";
import { isValidateEmail } from "../../shared/utils";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const { team_name, project_name, template_id } = req.body;
if (template_id && team_name) {
return next();
}
if (!template_id) {
if (!team_name)
return res.status(200).send(new ServerResponse(false, null, "Account name is required"));
if (!project_name)
return res.status(200).send(new ServerResponse(false, null, "Project name is required"));
req.body.tasks = Array.isArray(req.body.tasks) ? req.body.tasks : [];
req.body.team_members = Array.isArray(req.body.team_members) ? req.body.team_members.filter((i: string) => !!i) : [];
if (!req.body.tasks.length)
return res.status(200).send(new ServerResponse(false, null, "At least one task is required"));
for (const email of req.body.team_members) {
if (email && !isValidateEmail(email))
return res.status(200).send(new ServerResponse(false, null, "One or more of your team members has invalid email addresses. Please double check and try again.").withTitle("Account setup failed!"));
}
}
return next();
}

View File

@@ -0,0 +1,12 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {project_id} = req.body;
if (!project_id)
return res.status(400).send(new ServerResponse(false, null));
return next();
}

View File

@@ -0,0 +1,13 @@
import {NextFunction, Request, Response} from "express";
import {ServerResponse} from "../../models/server-response";
import {isValidateEmail} from "../../shared/utils";
export default function (req: Request, res: Response, next: NextFunction) {
const {name, email} = req.body;
if (!name) return res.status(200).send(new ServerResponse(false, null, "Name is required"));
if (!email) return res.status(200).send(new ServerResponse(false, null, "Email is required"));
if (!isValidateEmail(email)) return res.status(200).send(new ServerResponse(false, null, "Invalid email address"));
return next();
}

View File

@@ -0,0 +1,15 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {project, replace} = req.query;
if (!project)
return res.status(400).send(new ServerResponse(false, null));
req.query.replace = /null/.test(replace as string) ? null : replace as any;
return next();
}

View File

@@ -0,0 +1,11 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
if (!req.body.status_order)
return res.status(200).send(new ServerResponse(false, null, "Invalid request. Please try again. "));
return next();
}

View File

@@ -0,0 +1,21 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {file, file_name, project_id, size} = req.body;
if (!file || !file_name || !project_id || !size)
return res.status(200).send(new ServerResponse(false, null, "Upload failed"));
if (size > 5.243e+7)
return res.status(200).send(new ServerResponse(false, null, "Max file size for attachments is 50 MB.").withTitle("Upload failed!"));
req.body.type = file_name.split(".").pop();
req.body.task_id = req.body.task_id || null;
return next();
}

View File

@@ -0,0 +1,21 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {content, task_id} = req.body;
if (!content)
return res.status(200).send(new ServerResponse(false, null, "Comment message is required"));
if (!task_id)
return res.status(200).send(new ServerResponse(false, null, "Unable to create comment"));
if (content.length > 2000)
return res.status(200).send(new ServerResponse(false, null, "Message length exceeded"));
req.body.mentions = Array.isArray(req.body.mentions)
? req.body.mentions
: [];
return next();
}

View File

@@ -0,0 +1,12 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const name = (req.body.name || "").trim();
if (!name)
return res.status(400).send(new ServerResponse(false, null, "Invalid name"));
return next();
}

View File

@@ -0,0 +1,22 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {name, project_id, category_id} = req.body;
if (!name || name.trim() === "")
return res.status(200).send(new ServerResponse(false, null, "Name is required"));
if (!project_id || project_id.trim() === "")
return res.status(400).send(new ServerResponse(false, null));
if (!category_id || category_id.trim() === "")
return res.status(400).send(new ServerResponse(false, null));
req.body.color_code = req.body.color_code || "#a9a9a9";
req.body.default_status = !!req.body.default_status;
req.body.name = req.body.name.trim();
return next();
}

View File

@@ -0,0 +1,13 @@
import { NextFunction } from "express";
import { IWorkLenzRequest } from "../../interfaces/worklenz-request";
import { IWorkLenzResponse } from "../../interfaces/worklenz-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {id, seconds_spent, created_at, formatted_start} = req.body;
if (!id || !seconds_spent || !formatted_start) return res.sendStatus(400);
return next();
}

View File

@@ -0,0 +1,51 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
import {getRandomColorCode, sanitize, toMinutes, toRound} from "../../shared/utils";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {name, priority_id, status_id, assignees, project_id, labels} = req.body;
if (!name?.trim()?.length)
return res.status(200).send(new ServerResponse(false, null, "Name is required"));
if (!priority_id)
return res.status(200).send(new ServerResponse(false, null, "Priority is required"));
if (!status_id)
return res.status(200).send(new ServerResponse(false, null, "Status is required"));
if (!project_id)
return res.status(200).send(new ServerResponse(false, null, "Project is required"));
req.body.total_hours = isNaN(+req.body.total_hours) || req.body.total_hours > 1000 ? 0 : toRound(req.body.total_hours);
req.body.total_minutes = isNaN(+req.body.total_minutes) || req.body.total_minutes > 1000 ? 0 : toRound(req.body.total_minutes);
req.body.assignees = Array.isArray(assignees) ? assignees : [];
req.body.labels = Array.isArray(labels) ? labels : [];
req.body.reporter_id = req.user?.id || null;
req.body.total_minutes = toMinutes(req.body.total_hours, req.body.total_minutes);
req.body.team_id = req.user?.team_id || null;
req.body.inline = req.query.inline || false;
const labelsJson = [];
for (const label of req.body.labels) {
labelsJson.push({
name: label,
color: getRandomColorCode()
});
}
req.body.labels = labelsJson;
if (req.body.description) {
if (req.body.description.length > 4000)
return res.status(200).send(new ServerResponse(false, null, "Task description length exceeded!"));
req.body.description = sanitize(req.body.description);
}
if (req.body.name.length > 100)
return res.status(200).send(new ServerResponse(false, null, "Task name length exceeded!"));
return next();
}

View File

@@ -0,0 +1,20 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
import {isValidateEmail} from "../../shared/utils";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {emails} = req.body;
if (!Array.isArray(emails) || !emails.length)
return res.status(200).send(new ServerResponse(false, null, "Email addresses cannot be empty"));
for (const email of emails) {
if (!isValidateEmail(email))
return res.status(200).send(new ServerResponse(false, null, "Invalid email address"));
}
return next();
}

View File

@@ -0,0 +1,11 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
if (req.user && (req.user.owner || req.user.is_admin))
return next();
return res.status(401).send(new ServerResponse(false, null, "You are not authorized to perform this action"));
}

View File

@@ -0,0 +1,12 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {name} = req.body;
if (!name || name.trim() === "")
return res.status(200).send(new ServerResponse(false, null, "Name is required"));
return next();
}

View File

@@ -0,0 +1,12 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {id} = req.body;
if (!id)
return res.status(200).send(new ServerResponse(false, null, "Invalid team identifier"));
return next();
}

View File

@@ -0,0 +1,16 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {name, color_code} = req.body;
if (!name)
return res.status(200).send(new ServerResponse(false, null, "Name is required"));
if (!color_code)
req.body.color_code = "#767676";
req.body.done = !!req.body.done;
return next();
}

View File

@@ -0,0 +1,15 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../../interfaces/worklenz-response";
import {ServerResponse} from "../../models/server-response";
export default function (req: IWorkLenzRequest, res: IWorkLenzResponse, next: NextFunction): IWorkLenzResponse | void {
const {hash, password, user} = req.body;
if (!password)
return res.status(200).send(new ServerResponse(false, null, "Password is required"));
if (!hash || !user)
return res.status(200).send(new ServerResponse(false, null, "An unknown error has occurred. Please try again."));
return next();
}

View File

@@ -0,0 +1,28 @@
import {NextFunction} from "express";
import {IWorkLenzRequest} from "../interfaces/worklenz-request";
import {IWorkLenzResponse} from "../interfaces/worklenz-response";
import createHttpError from "http-errors";
import db from "../config/db";
export default function (projectId: string) {
return async (req: IWorkLenzRequest, _res: IWorkLenzResponse, next: NextFunction) => {
const userId = req.user?.id;
const teamId = req.user?.team_id;
try {
const q = `
SELECT 1
FROM project_members
WHERE project_id = $1
AND team_member_id = (SELECT id FROM team_members WHERE team_id = $2 AND user_id = $3);
`;
const result = await db.query(q, [projectId, teamId, userId]);
if (result.rowCount)
return next();
} catch (error) {
// ignored
}
return next(createHttpError(401));
};
}