// task.js // Copyright (C) 2024 DTP Technologies, LLC // All Rights Reserved 'use strict'; import express from 'express'; import { SiteController, SiteError } from '../../lib/site-lib.js'; import { populateTaskId, populateSessionId } from './lib/populators.js'; export default class TaskController extends SiteController { static get name ( ) { return 'TaskController'; } static get slug ( ) { return 'task'; } constructor (dtp) { super(dtp, TaskController); } async start ( ) { const { dtp } = this; const { limiter: limiterService, session: sessionService, } = dtp.services; const limiterConfig = limiterService.config.task; const multer = this.createMulter(TaskController.slug, { limits: { fileSize: 1024 * 1000 * 5, }, }); const authCheck = sessionService.authCheckMiddleware({ requireLogin: true }); const router = express.Router(); dtp.app.use('/task', authCheck, router); router.use(async (req, res, next) => { res.locals.currentView = TaskController.name; return next(); }); async function checkTaskOwnership (req, res, next) { if (Array.isArray(res.locals.task.project.managers) && (res.locals.task.project.managers.length > 0)) { res.locals.manager = res.locals.task.project.managers.find((manager) => manager._id.equals(req.user._id)); } if (!res.locals.manager && !res.locals.task.user._id.equals(req.user._id)) { return next(new SiteError(401, 'This is not your task')); } return next(); } async function checkSessionOwnership (req, res, next) { if (Array.isArray(res.locals.task.project.managers) && (res.locals.task.project.managers.length > 0)) { res.locals.manager = res.locals.task.project.managers.find((manager) => manager._id.equals(req.user._id)); } if (!res.locals.manager && !res.locals.session.user._id.equals(req.user._id)) { throw new SiteError(401, 'This is not your session'); } return next(); } router.param('taskId', populateTaskId(this)); router.param('sessionId', populateSessionId(this)); router.post( '/:taskId/session/start', limiterService.create(limiterConfig.postStartTaskSession), this.postStartTaskSession.bind(this), ); router.post( '/:taskId/session/:sessionId/screenshot', checkSessionOwnership, limiterService.create(limiterConfig.postTaskSessionScreenshot), multer.single('image'), this.postTaskSessionScreenshot.bind(this), ); router.post( '/:taskId/session/:sessionId/status', limiterService.create(limiterConfig.postTaskSessionStatus), checkSessionOwnership, this.postTaskSessionStatus.bind(this), ); router.post( '/:taskId/session/:sessionId/close', limiterService.create(limiterConfig.postCloseTaskSession), checkSessionOwnership, this.postCloseTaskSession.bind(this), ); router.post( '/:taskId/start', limiterService.create(limiterConfig.postStartTask), checkTaskOwnership, this.postStartTask.bind(this), ); router.post( '/:taskId/close', limiterService.create(limiterConfig.postCloseTask), checkTaskOwnership, this.postCloseTask.bind(this), ); router.post( '/', limiterService.create(limiterConfig.postCreateTask), this.postCreateTask.bind(this), ); router.get( '/:taskId/session/:sessionId', limiterService.create(limiterConfig.getTaskSessionView), checkSessionOwnership, this.getTaskSessionView.bind(this), ); router.get( '/:taskId', limiterService.create(limiterConfig.getTaskView), checkTaskOwnership, this.getTaskView.bind(this), ); } async postStartTaskSession (req, res) { const { task: taskService } = this.dtp.services; try { res.locals.session = await taskService.createTaskSession(res.locals.task); res.status(200).json({ success: true, session: res.locals.session, }); } catch (error) { this.log.error('failed to create task session', { error }); res.status(error.statusCode || 500).json({ success: false, message: error.message, }); } } async postTaskSessionScreenshot (req, res) { const { task: taskService } = this.dtp.services; try { await taskService.addTaskSessionScreenshot(res.locals.session, req.file); res.status(200).json({ success: true }); } catch (error) { this.log.error('failed to add task session screenshot', { error }); res.status(error.statusCode || 500).json({ success: false, message: error.message, }); } } async postTaskSessionStatus (req, res) { const { task: taskService } = this.dtp.services; try { this.log.debug('updating task session status', { sessionId: res.locals.session._id, params: req.body, }); await taskService.setTaskSessionStatus(res.locals.session, req.body.status); const displayList = this.createDisplayList('set-session-status'); displayList.showNotification( 'Session status updated', 'success', 'bottom-center', 5000, ); res.status(200).json({ success: true, displayList }); } catch (error) { this.log.error('failed to update task session status', { error }); res.status(error.statusCode || 500).json({ success: false, message: error.message, }); } } async postCloseTaskSession (req, res) { const { task: taskService } = this.dtp.services; try { await taskService.closeTaskSession(res.locals.session); res.status(200).json({ success: true }); } catch (error) { this.log.error('failed to close task session', { error }); res.status(error.statusCode || 500).json({ success: false, message: error.message, }); } } async postStartTask (req, res, next) { const { task: taskService } = this.dtp.services; try { await taskService.startTask(res.locals.task); res.redirect(`/task/${res.locals.task._id}`); } catch (error) { this.log.error('failed to close task session', { error }); return next(error); } } async postCloseTask (req, res, next) { const { task: taskService } = this.dtp.services; try { await taskService.closeTask(res.locals.task); res.redirect('/'); } catch (error) { this.log.error('failed to close task', { error }); return next(error); } } async postCreateTask (req, res, next) { const { task: taskService } = this.dtp.services; try { res.locals.task = await taskService.createTask(req.user, req.body); res.redirect(`/task/${res.locals.task._id}`); } catch (error) { this.log.error('failed to create new task', { error }); return next(error); } } async getTaskSessionView (req, res) { res.render('task/session/view'); } async getTaskView (req, res, next) { const { report: reportService, task: taskService } = this.dtp.services; try { res.locals.pagination = this.getPaginationParameters(req, 50); res.locals.sessions = await taskService.getSessionsForTask( res.locals.task, res.locals.pagination, ); res.locals.weekStartDate = reportService.startOfWeek(); res.render('task/view'); } catch (error) { this.log.error('failed to present the Task view', { error }); return next(error); } } }