DTP Base provides a scalable and secure Node.js application development harness ready for production service.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

224 lines
6.4 KiB

// client.js
// Copyright (C) 2024 DTP Technologies, LLC
// All Rights Reserved
'use strict';
import express from 'express';
import { SiteController, SiteError } from '../../lib/site-lib.js';
export default class ClientController extends SiteController {
static get name ( ) { return 'ClientController'; }
static get slug ( ) { return 'client'; }
constructor (dtp) {
super(dtp, ClientController.slug);
}
async start ( ) {
const { dtp } = this;
const {
limiter: limiterService,
session: sessionService,
} = dtp.services;
const limiterConfig = limiterService.config.client;
const authCheck = sessionService.authCheckMiddleware({ requireLogin: true });
function checkClientOwnership (req, res, next) {
if (!res.locals.client.user._id.equals(req.user._id)) {
throw new SiteError(401, 'This is not your client');
}
return next();
}
function checkProjectOwnership (req, res, next) {
if (!res.locals.project.user._id.equals(req.user._id)) {
throw new SiteError(401, 'This is not your client');
}
return next();
}
const router = express.Router();
dtp.app.use('/client', authCheck, router);
router.param('clientId', this.populateClientId.bind(this));
router.param('projectId', this.populateProjectId.bind(this));
router.post(
'/:clientId/project/:projectId',
limiterService.create(limiterConfig.postProjectUpdate),
checkClientOwnership,
this.postProjectUpdate.bind(this),
);
router.post(
'/:clientId/project',
limiterService.create(limiterConfig.postProjectCreate),
checkClientOwnership,
this.postProjectCreate.bind(this),
);
router.post(
'/',
limiterService.create(limiterConfig.postCreateClient),
this.postCreateClient.bind(this),
);
router.get(
'/:clientId/project/create',
limiterService.create(limiterConfig.getProjectCreate),
checkClientOwnership,
this.getProjectCreate.bind(this),
);
router.get(
'/:clientId/project/:projectId/edit',
limiterService.create(limiterConfig.getProjectEditor),
checkProjectOwnership,
this.getProjectEditor.bind(this),
);
router.get(
'/:clientId/project/:projectId',
limiterService.create(limiterConfig.getProjectView),
checkProjectOwnership,
this.getProjectView.bind(this),
);
router.get(
'/create',
limiterService.create(limiterConfig.getClientCreate),
this.getClientCreate.bind(this),
);
router.get(
'/:clientId',
limiterService.create(limiterConfig.getClientView),
checkClientOwnership,
this.getClientView.bind(this),
);
router.get(
'/',
limiterService.create(limiterConfig.getHome),
this.getHome.bind(this),
);
return router;
}
async populateClientId (req, res, next, clientId) {
const { client: clientService } = this.dtp.services;
try {
res.locals.client = await clientService.getClientById(clientId);
if (!res.locals.client) {
throw new SiteError(404, 'Client not found');
}
if (!res.locals.client.user._id.equals(req.user._id)) {
throw new SiteError(401, 'This is not your client');
}
return next();
} catch (error) {
this.log.error('failed to populate client', { error });
return next(error);
}
}
async populateProjectId (req, res, next, projectId) {
const { client: clientService } = this.dtp.services;
try {
res.locals.project = await clientService.getProjectById(projectId);
if (!res.locals.project) {
throw new SiteError(404, 'Project not found');
}
if (!res.locals.project.user._id.equals(req.user._id)) {
throw new SiteError(401, 'This is not your project');
}
return next();
} catch (error) {
this.log.error('failed to populate project', { error });
return next(error);
}
}
async postProjectUpdate (req, res, next) {
const { client: clientService } = this.dtp.services;
try {
await clientService.updateProject(res.locals.project, req.body);
res.redirect(`/client/${res.locals.client._id}/project/${res.locals.project._id}`);
} catch (error) {
this.log.error('failed to update client project', { error });
return next(error);
}
}
async postProjectCreate (req, res, next) {
const { client: clientService } = this.dtp.services;
try {
res.locals.project = await clientService.createProject(res.locals.client, req.body);
res.redirect(`/client/${res.locals.client._id}/project/${res.locals.project._id}`);
} catch (error) {
this.log.error('failed to create client project', { error });
return next(error);
}
}
async postCreateClient (req, res, next) {
const { client: clientService } = this.dtp.services;
try {
res.locals.client = await clientService.createClient(req.user, req.body);
res.redirect(`/client/${res.locals.client._id}`);
} catch (error) {
this.log.error('failed to create client', { error });
return next(error);
}
}
async getProjectCreate (req, res) {
res.render('client/project/editor');
}
async getProjectEditor (req, res) {
res.render('client/project/editor');
}
async getProjectView (req, res, next) {
const { task: taskService } = this.dtp.services;
try {
res.locals.taskGrid = await taskService.getTaskGridForProject(res.locals.project);
res.render('client/project/view');
} catch (error) {
this.log.error('failed to present project view', { error });
return next(error);
}
}
async getClientCreate (req, res) {
res.render('client/create');
}
async getClientView (req, res, next) {
const { client: clientService } = this.dtp.services;
try {
res.locals.projects = await clientService.getProjectsForClient(res.locals.client);
res.render('client/view');
} catch (error) {
this.log.error('failed to present client home view', { error });
return next(error);
}
}
async getHome (req, res, next) {
const { client: clientService } = this.dtp.services;
try {
res.locals.clients = await clientService.getClientsForUser(req.user);
res.render('client/dashboard');
} catch (error) {
this.log.error('failed to present client home view', { error });
return next(error);
}
}
}