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.
299 lines
8.7 KiB
299 lines
8.7 KiB
// user.js
|
|
// Copyright (C) 2021 Digital Telepresence, LLC
|
|
// License: Apache-2.0
|
|
|
|
'use strict';
|
|
|
|
const DTP_COMPONENT_NAME = 'user';
|
|
|
|
const express = require('express');
|
|
const mongoose = require('mongoose');
|
|
const multer = require('multer');
|
|
|
|
const { SiteController, SiteError } = require('../../lib/site-lib');
|
|
|
|
class UserController extends SiteController {
|
|
|
|
constructor (dtp) {
|
|
super(dtp, DTP_COMPONENT_NAME);
|
|
}
|
|
|
|
async start ( ) {
|
|
const { dtp } = this;
|
|
const {
|
|
limiter: limiterService,
|
|
otpAuth: otpAuthService,
|
|
session: sessionService,
|
|
} = dtp.services;
|
|
|
|
const upload = multer({ dest: `/tmp/${this.dtp.config.site.domainKey}/uploads/${DTP_COMPONENT_NAME}` });
|
|
const router = express.Router();
|
|
dtp.app.use('/user', router);
|
|
|
|
const authRequired = sessionService.authCheckMiddleware({ requireLogin: true });
|
|
const otpMiddleware = otpAuthService.middleware('Account', {
|
|
adminRequired: false,
|
|
otpRequired: false,
|
|
otpRedirectURL: async (req) => { return `/user/${req.user._id}`; },
|
|
});
|
|
|
|
router.use(
|
|
async (req, res, next) => {
|
|
try {
|
|
res.locals.currentView = 'user';
|
|
res.locals.pageTitle = 'Manage your user account.';
|
|
return next();
|
|
} catch (error) {
|
|
return next(error);
|
|
}
|
|
},
|
|
);
|
|
|
|
async function checkProfileOwner (req, res, next) {
|
|
if (!req.user || !req.user._id.equals(res.locals.userProfile._id)) {
|
|
return next(new SiteError(403, 'This is not your user account or profile'));
|
|
}
|
|
return next();
|
|
}
|
|
|
|
router.param('userId', this.populateUser.bind(this));
|
|
|
|
router.post('/:userId/profile-photo',
|
|
limiterService.create(limiterService.config.user.postProfilePhoto),
|
|
upload.single('imageFile'),
|
|
this.postProfilePhoto.bind(this),
|
|
);
|
|
|
|
router.post('/:userId/header-image',
|
|
limiterService.create(limiterService.config.user.postHeaderImage),
|
|
upload.single('imageFile'),
|
|
this.postHeaderImage.bind(this),
|
|
);
|
|
|
|
router.post('/:userId/settings',
|
|
limiterService.create(limiterService.config.user.postUpdateSettings),
|
|
upload.none(),
|
|
this.postUpdateSettings.bind(this),
|
|
);
|
|
|
|
router.post('/',
|
|
limiterService.create(limiterService.config.user.postCreate),
|
|
this.postCreateUser.bind(this),
|
|
);
|
|
|
|
router.get('/:userId/settings',
|
|
limiterService.create(limiterService.config.user.getSettings),
|
|
authRequired,
|
|
otpMiddleware,
|
|
checkProfileOwner,
|
|
this.getUserSettingsView.bind(this),
|
|
);
|
|
router.get('/:userId',
|
|
limiterService.create(limiterService.config.user.getUserProfile),
|
|
authRequired,
|
|
otpMiddleware,
|
|
checkProfileOwner,
|
|
this.getUserView.bind(this),
|
|
);
|
|
|
|
router.delete('/:userId/profile-photo',
|
|
limiterService.create(limiterService.config.user.deleteProfilePhoto),
|
|
authRequired,
|
|
checkProfileOwner,
|
|
this.deleteProfilePhoto.bind(this),
|
|
);
|
|
|
|
router.delete('/:userId/header-image',
|
|
limiterService.create(limiterService.config.user.deleteHeaderImage),
|
|
authRequired,
|
|
checkProfileOwner,
|
|
this.deleteHeaderImage.bind(this),
|
|
);
|
|
}
|
|
|
|
async populateUser (req, res, next, userId) {
|
|
const { user: userService } = this.dtp.services;
|
|
try {
|
|
userId = mongoose.Types.ObjectId(userId);
|
|
} catch (error) {
|
|
return next(new SiteError(406, 'Invalid User'));
|
|
}
|
|
try {
|
|
if (!req.user._id.equals(userId)) {
|
|
return next(new Error('Invalid account ID'));
|
|
}
|
|
res.locals.userProfile = await userService.getUserAccount(userId);
|
|
return next();
|
|
} catch (error) {
|
|
this.log.error('failed to populate userId', { userId, error });
|
|
return next(error);
|
|
}
|
|
}
|
|
|
|
async postCreateUser (req, res, next) {
|
|
const { user: userService } = this.dtp.services;
|
|
try {
|
|
// verify that the request has submitted a captcha
|
|
if ((typeof req.body.captcha !== 'string') || req.body.captcha.length === 0) {
|
|
throw new SiteError(403, 'Invalid signup attempt');
|
|
}
|
|
// verify that the session has a signup captcha
|
|
if (!req.session.captcha || !req.session.captcha.signup) {
|
|
throw new SiteError(403, 'Invalid signup attempt');
|
|
}
|
|
// verify that the captcha from the form matches the captcha in the signup session flow
|
|
if (req.body.captcha !== req.session.captcha.signup) {
|
|
throw new SiteError(403, 'The captcha value is not correct');
|
|
}
|
|
|
|
// create the user account
|
|
res.locals.user = await userService.create(req.body);
|
|
|
|
// log the user in
|
|
req.login(res.locals.user, (error) => {
|
|
if (error) {
|
|
return next(error);
|
|
}
|
|
res.redirect(`/user/${res.locals.user._id}`);
|
|
});
|
|
} catch (error) {
|
|
this.log.error('failed to create new user', { error });
|
|
return next(error);
|
|
}
|
|
}
|
|
|
|
async postProfilePhoto (req, res) {
|
|
const { user: userService } = this.dtp.services;
|
|
try {
|
|
const displayList = this.createDisplayList('profile-photo');
|
|
await userService.updatePhoto(req.user, req.file);
|
|
displayList.showNotification(
|
|
'Profile photo updated successfully.',
|
|
'success',
|
|
'bottom-center',
|
|
2000,
|
|
);
|
|
res.status(200).json({ success: true, displayList });
|
|
} catch (error) {
|
|
this.log.error('failed to update profile photo', { error });
|
|
return res.status(error.statusCode || 500).json({
|
|
success: false,
|
|
message: error.message,
|
|
});
|
|
}
|
|
}
|
|
|
|
async postHeaderImage (req, res) {
|
|
const { user: userService } = this.dtp.services;
|
|
try {
|
|
const displayList = this.createDisplayList('header-image');
|
|
await userService.updateHeaderImage(req.user, req.file);
|
|
displayList.showNotification(
|
|
'Header image updated successfully.',
|
|
'success',
|
|
'bottom-center',
|
|
2000,
|
|
);
|
|
res.status(200).json({ success: true, displayList });
|
|
} catch (error) {
|
|
this.log.error('failed to update header image', { error });
|
|
return res.status(error.statusCode || 500).json({
|
|
success: false,
|
|
message: error.message,
|
|
});
|
|
}
|
|
}
|
|
|
|
async postUpdateSettings (req, res) {
|
|
const { user: userService } = this.dtp.services;
|
|
try {
|
|
const displayList = this.createDisplayList('app-settings');
|
|
|
|
await userService.updateSettings(req.user, req.body);
|
|
|
|
displayList.showNotification(
|
|
'Member account settings updated successfully.',
|
|
'success',
|
|
'bottom-center',
|
|
6000,
|
|
);
|
|
res.status(200).json({ success: true, displayList });
|
|
} catch (error) {
|
|
this.log.error('failed to update account settings', { error });
|
|
return res.status(error.statusCode || 500).json({
|
|
success: false,
|
|
message: error.message,
|
|
});
|
|
}
|
|
}
|
|
|
|
async getUserSettingsView (req, res, next) {
|
|
try {
|
|
res.locals.startTab = req.query.st || 'watch';
|
|
res.render('user/settings');
|
|
} catch (error) {
|
|
this.log.error('failed to produce user settings view', { error });
|
|
return next(error);
|
|
}
|
|
}
|
|
|
|
async getUserView (req, res, next) {
|
|
try {
|
|
res.render('user/profile');
|
|
} catch (error) {
|
|
this.log.error('failed to produce user profile view', { error });
|
|
return next(error);
|
|
}
|
|
}
|
|
|
|
async deleteProfilePhoto (req, res) {
|
|
const { user: userService } = this.dtp.services;
|
|
try {
|
|
const displayList = this.createDisplayList('app-settings');
|
|
await userService.removePhoto(req.user);
|
|
displayList.showNotification(
|
|
'Profile photo removed successfully.',
|
|
'success',
|
|
'bottom-center',
|
|
2000,
|
|
);
|
|
res.status(200).json({ success: true, displayList });
|
|
} catch (error) {
|
|
this.log.error('failed to remove profile photo', { error });
|
|
return res.status(error.statusCode || 500).json({
|
|
success: false,
|
|
message: error.message,
|
|
});
|
|
}
|
|
}
|
|
|
|
async deleteHeaderImage (req, res) {
|
|
const { user: userService } = this.dtp.services;
|
|
try {
|
|
const displayList = this.createDisplayList('remove-header-image');
|
|
await userService.removeHeaderImage(req.user);
|
|
displayList.showNotification(
|
|
'Header image removed successfully.',
|
|
'success',
|
|
'bottom-center',
|
|
2000,
|
|
);
|
|
res.status(200).json({ success: true, displayList });
|
|
} catch (error) {
|
|
this.log.error('failed to remove header image', { error });
|
|
return res.status(error.statusCode || 500).json({
|
|
success: false,
|
|
message: error.message,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
slug: 'user',
|
|
name: 'user',
|
|
create: async (dtp) => {
|
|
let controller = new UserController(dtp);
|
|
return controller;
|
|
},
|
|
};
|