Browse Source

major refactor of populators

develop
Rob Colbert 12 months ago
parent
commit
d8035352f2
  1. 6
      app/controllers/admin.js
  2. 25
      app/controllers/admin/user.js
  3. 4
      app/controllers/auth.js
  4. 4
      app/controllers/email.js
  5. 5
      app/controllers/home.js
  6. 25
      app/controllers/image.js
  7. 57
      app/controllers/lib/populators.js
  8. 5
      app/controllers/manifest.js
  9. 36
      app/controllers/user.js
  10. 38
      app/controllers/video.js
  11. 6
      app/controllers/welcome.js
  12. 34
      app/views/admin/user/view.pug
  13. 7
      lib/site-controller.js

6
app/controllers/admin.js

@ -13,8 +13,6 @@ const User = mongoose.model('User');
const ChatImage = mongoose.model('Image');
const Video = mongoose.model('Video');
import express from 'express';
import { SiteController } from '../../lib/site-lib.js';
export default class AdminController extends SiteController {
@ -31,8 +29,8 @@ export default class AdminController extends SiteController {
const authCheck = sessionService.authCheckMiddleware({ requireLogin: true, requireAdmin: true });
const router = express.Router();
this.dtp.app.use('/admin', authCheck, router);
const router = this.createRouter('/admin');
router.use(authCheck);
router.use('/user', await this.loadChild(path.join(__dirname, 'admin', 'user.js')));

25
app/controllers/admin/user.js

@ -7,6 +7,9 @@
import express from 'express';
import { SiteController, SiteError } from '../../../lib/site-lib.js';
import {
populateUserId,
} from '../lib/populators.js';
export default class UserAdminController extends SiteController {
@ -20,7 +23,7 @@ export default class UserAdminController extends SiteController {
async start ( ) {
const router = express.Router();
router.param('userId', this.populateUserId.bind(this));
router.param('userId', populateUserId(this));
router.post(
'/:userId',
@ -40,33 +43,19 @@ export default class UserAdminController extends SiteController {
return router;
}
async populateUserId (req, res, next, userId) {
const { user: userService } = this.dtp.services;
try {
res.locals.userAccount = await userService.getUserAccount(userId);
if (!res.locals.userAccount) {
throw new SiteError(404, 'User not found');
}
return next();
} catch (error) {
this.log.error('failed to populate user account', { userId, error });
return next(error);
}
}
async postUserUpdate (req, res, next) {
const { user: userService } = this.dtp.services;
try {
switch (req.body.action) {
case 'update':
await userService.updateForAdmin(res.locals.userAccount, req.body);
await userService.updateForAdmin(res.locals.userProfile, req.body);
break;
case 'ban':
if (res.locals.userAccount._id.equals(req.user._id)) {
if (res.locals.userProfile._id.equals(req.user._id)) {
throw new SiteError(400, "You can't ban yourself");
}
await userService.banUser(res.locals.userAccount);
await userService.banUser(res.locals.userProfile);
break;
}
res.redirect(`/admin/user`);

4
app/controllers/auth.js

@ -4,7 +4,6 @@
'use strict';
import express from 'express';
import multer from 'multer';
import mongoose from 'mongoose';
@ -38,8 +37,7 @@ export default class AuthController extends SiteController {
const upload = multer({ });
const authRequired = sessionService.authCheckMiddleware({ requireLogin: true });
const router = express.Router();
this.dtp.app.use('/auth', router);
const router = this.createRouter('/auth');
router.use(async (req, res, next) => {
res.locals.currentView = 'auth';

4
app/controllers/email.js

@ -5,7 +5,6 @@
'use strict';
import path from 'node:path';
import express from 'express';
import SvgCaptcha from 'svg-captcha';
@ -29,8 +28,7 @@ export default class EmailController extends SiteController {
attempts: 3
});
const router = express.Router();
this.dtp.app.use('/email', router);
const router = this.createRouter('/email');
router.post(
'/verify',

5
app/controllers/home.js

@ -4,8 +4,6 @@
'use strict';
import express from 'express';
import { SiteController } from '../../lib/site-controller.js';
export default class HomeController extends SiteController {
@ -19,8 +17,7 @@ export default class HomeController extends SiteController {
}
async start ( ) {
const router = express.Router();
this.dtp.app.use('/', router);
const router = this.createRouter('/');
router.get('/', this.getHome.bind(this));

25
app/controllers/image.js

@ -5,10 +5,11 @@
'use strict';
import fs from 'node:fs';
import express from 'express';
import mongoose from 'mongoose';
import { SiteController, SiteError } from '../../lib/site-lib.js';
import {
populateImageId,
} from './lib/populators.js';
export default class ImageController extends SiteController {
@ -24,8 +25,7 @@ export default class ImageController extends SiteController {
const { limiter: limiterService } = dtp.services;
const limiterConfig = limiterService.config.image;
const router = express.Router();
dtp.app.use('/image', router);
const router = this.createRouter('/image');
const imageUpload = this.createMulter(ImageController.slug, {
limits: {
@ -38,7 +38,7 @@ export default class ImageController extends SiteController {
return next();
});
router.param('imageId', this.populateImage.bind(this));
router.param('imageId', populateImageId(this));
router.post('/',
limiterService.create(limiterConfig.postCreateImage),
@ -63,21 +63,6 @@ export default class ImageController extends SiteController {
);
}
async populateImage (req, res, next, imageId) {
const { image: imageService } = this.dtp.services;
try {
res.locals.imageId = mongoose.Types.ObjectId.createFromHexString(imageId);
res.locals.image = await imageService.getImageById(res.locals.imageId);
if (!res.locals.image) {
throw new SiteError(404, 'Image not found');
}
return next();
} catch (error) {
this.log.error('failed to populate image', { error });
return next(error);
}
}
async postCreateImage (req, res, next) {
const { image: imageService } = this.dtp.services;
try {

57
app/controllers/lib/populators.js

@ -1,23 +1,56 @@
'use strict';
import mongoose from 'mongoose';
import { SiteError } from '../../../lib/site-lib.js';
/*
* This is a sample populator. It doesn't run. There is no sample service, etc.
* This is simply the pattern you follow to declare a new ExpressJS parameter
* populator and export it from your populators library.
*/
export function populateSampleParameter (controller) {
return async function (req, res, next, sampleParameter) {
const { sample: sampleService } = controller.dtp.services;
export function populateImageId (controller) {
const { image: imageService } = controller.dtp.services;
return async function (req, res, next, imageId) {
try {
res.locals.imageId = mongoose.Types.ObjectId.createFromHexString(imageId);
res.locals.image = await imageService.getImageById(res.locals.imageId);
if (!res.locals.image) {
throw new SiteError(404, 'Image not found');
}
return next();
} catch (error) {
controller.log.error('failed to populate imageId', { imageId, error });
return next(error);
}
};
}
export function populateUserId (controller) {
const { user: userService } = controller.dtp.services;
return async function (req, res, next, userId) {
if (!mongoose.Types.ObjectId.isValid(userId)) {
return next(new SiteError(406, 'Invalid User'));
}
try {
res.locals.userProfile = await userService.getUserAccount(userId);
if (!res.locals.userProfile) {
throw new SiteError(404, 'User not found');
}
return next();
} catch (error) {
controller.log.error('failed to populate userId', { userId, error });
return next(error);
}
};
}
export function populateVideoId (controller) {
const { video: videoService } = controller.dtp.services;
return async function (req, res, next, videoId) {
try {
res.locals.sample = await sampleService.getSample(sampleParameter);
if (!res.locals.sample) {
throw new SiteError(404, 'Sample not found');
res.locals.video = await videoService.getVideoById(videoId);
if (!res.locals.video) {
throw new SiteError(404, 'Video not found');
}
return next();
} catch (error) {
controller.log.error('failed to populate sampleParameter', { sampleParamater, error });
controller.log.error('failed to populate video', { videoId, error });
return next(error);
}
};

5
app/controllers/manifest.js

@ -6,8 +6,6 @@
const DTP_COMPONENT_NAME = 'manifest';
import express from 'express';
import { SiteController } from '../../lib/site-controller.js';
export default class ManifestController extends SiteController {
@ -28,8 +26,7 @@ export default class ManifestController extends SiteController {
const { dtp } = this;
const { limiter: limiterService } = dtp.services;
const router = express.Router();
dtp.app.use('/manifest.json', router);
const router = this.createRouter('/manifest.json');
router.use(async (req, res, next) => {
res.locals.currentView = DTP_COMPONENT_NAME;

36
app/controllers/user.js

@ -4,11 +4,10 @@
'use strict';
import express from 'express';
import mongoose from 'mongoose';
import multer from 'multer';
import { SiteController, SiteError } from '../../lib/site-lib.js';
import {
populateUserId,
} from './lib/populators.js';
export default class UserController extends SiteController {
@ -28,9 +27,13 @@ export default class UserController extends SiteController {
session: sessionService,
} = dtp.services;
const upload = multer({ dest: `/tmp/dtp/${this.dtp.config.site.domainKey}/uploads/${UserController.name}` });
const router = express.Router();
dtp.app.use('/user', router);
const upload = this.createMulter(UserController.slug, {
limits: {
fileSize: 1024 * 1000 * 15,
},
});
const router = this.createRouter('/user');
const authCheck = sessionService.authCheckMiddleware({ requireLogin: true });
@ -64,7 +67,7 @@ export default class UserController extends SiteController {
return next();
}
router.param('userId', this.populateUser.bind(this));
router.param('userId', populateUserId(this));
router.post(
'/:userId/profile-photo',
@ -173,23 +176,6 @@ export default class UserController extends SiteController {
);
}
async populateUser (req, res, next, userId) {
const { user: userService } = this.dtp.services;
if (!req.user) {
return res.redirect('/welcome');
}
if (!mongoose.Types.ObjectId.isValid(userId)) {
return next(new SiteError(406, 'Invalid User'));
}
try {
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 {

38
app/controllers/video.js

@ -4,11 +4,12 @@
'use strict';
import express from 'express';
import { pipeline } from 'node:stream';
import { SiteController, SiteError } from '../../lib/site-lib.js';
import { SiteController } from '../../lib/site-lib.js';
import {
populateVideoId,
} from './lib/populators.js';
export default class VideoController extends SiteController {
@ -26,21 +27,20 @@ export default class VideoController extends SiteController {
} = this.dtp.services;
const limiterConfig = limiterService.config.video;
this.templates = {
passwordResetComplete: this.loadViewTemplate('auth/password-reset-complete.pug'),
};
const authRequired = sessionService.authCheckMiddleware({ requireLogin: true });
const router = express.Router();
this.dtp.app.use('/video', authRequired, router);
const router = this.createRouter('/video');
this.dtp.app.use('/video', router);
router.use(async (req, res, next) => {
router.use(
authRequired,
async (req, res, next) => {
res.locals.currentView = 'video';
return next();
});
},
);
router.param('videoId', this.populateVideoId.bind(this));
router.param('videoId', populateVideoId(this));
router.get(
'/:videoId/media',
@ -51,20 +51,6 @@ export default class VideoController extends SiteController {
return router;
}
async populateVideoId (req, res, next, videoId) {
const { video: videoService } = this.dtp.services;
try {
res.locals.video = await videoService.getVideoById(videoId);
if (!res.locals.video) {
throw new SiteError(404, 'Video not found');
}
return next();
} catch (error) {
this.log.error('failed to populate video', { videoId, error });
return next(error);
}
}
async getVideoMedia (req, res, next) {
const { minio: minioService } = this.dtp.services;
try {

6
app/controllers/welcome.js

@ -4,8 +4,6 @@
'use strict';
import express from 'express';
import { SiteController } from '../../lib/site-controller.js';
export default class WelcomeController extends SiteController {
@ -18,13 +16,11 @@ export default class WelcomeController extends SiteController {
}
async start ( ) {
const { dtp } = this;
// const { limiter: limiterService } = dtp.services;
this.log.info('WelcomeController starting');
const router = express.Router();
dtp.app.use('/welcome', router);
const router = this.createRouter('/welcome');
router.use(async (req, res, next) => {
res.locals.currentView = WelcomeController.name;

34
app/views/admin/user/view.pug

@ -3,49 +3,49 @@ block admin-content
include ../../user/components/profile-picture
form(method="POST", action=`/admin/user/${userAccount._id}`).uk-form
form(method="POST", action=`/admin/user/${userProfile._id}`).uk-form
.uk-card.uk-card-default.uk-card-small.uk-border-rounded
.uk-card-header
div(uk-grid).uk-grid-small
.uk-width-auto
+renderProfilePicture(userAccount)
+renderProfilePicture(userProfile)
.uk-width-expand
.uk-margin
.uk-text-lead= userAccount.displayName
.uk-text-lead= userProfile.displayName
div(uk-grid).uk-grid-small.uk-grid-divider
.uk-width-auto @#{userAccount.username}
.uk-width-auto= userAccount.email
.uk-width-auto Created #{dayjs(userAccount.created).fromNow()} on #{dayjs(userAccount.created).format('MMMM D, YYYY')}
.uk-width-auto @#{userProfile.username}
.uk-width-auto= userProfile.email
.uk-width-auto Created #{dayjs(userProfile.created).fromNow()} on #{dayjs(userProfile.created).format('MMMM D, YYYY')}
.uk-card-body
.uk-margin
label(for="profile-bio").uk-form-label bio
#profile-bio.markdown-block!= marked.parse(userAccount.bio || '(no bio provided)', { renderer: fullMarkdownRenderer })
#profile-bio.markdown-block!= marked.parse(userProfile.bio || '(no bio provided)', { renderer: fullMarkdownRenderer })
.uk-margin
label(for="profile-badges").uk-form-label Profile Badges
input(id="profile-badges", type= "text", placeholder= "Comma-separated list of badge names", value= userAccount.badges.join(',')).uk-input
input(id="profile-badges", type= "text", placeholder= "Comma-separated list of badge names", value= userProfile.badges.join(',')).uk-input
.uk-margin
form(method="POST", action=`/admin/user/${userAccount._id}`).uk-form
form(method="POST", action=`/admin/user/${userProfile._id}`).uk-form
.uk-margin
label.uk-form-label Flags
.uk-margin-small
div(uk-grid).uk-grid-small
.uk-width-auto
.pretty.p-default
input(type="checkbox", name="flags.isAdmin", checked= userAccount.flags.isAdmin)
input(type="checkbox", name="flags.isAdmin", checked= userProfile.flags.isAdmin)
.state.p-success
label Admin
.uk-width-auto
.pretty.p-default
input(type="checkbox", name="flags.isModerator", checked= userAccount.flags.isModerator)
input(type="checkbox", name="flags.isModerator", checked= userProfile.flags.isModerator)
.state.p-success
label Moderator
.uk-width-auto
.pretty.p-default
input(type="checkbox", name="flags.isEmailVerified", checked= userAccount.flags.isEmailVerified)
input(type="checkbox", name="flags.isEmailVerified", checked= userProfile.flags.isEmailVerified)
.state.p-success
label Email Verified
@ -55,17 +55,17 @@ block admin-content
div(uk-grid).uk-grid-small
.uk-width-auto
.pretty.p-default
input(type="checkbox", name="permissions.canLogin", checked= userAccount.permissions.canLogin)
input(type="checkbox", name="permissions.canLogin", checked= userProfile.permissions.canLogin)
.state.p-success
label Can Login
.uk-width-auto
.pretty.p-default
input(type="checkbox", name="permissions.canReport", checked= userAccount.permissions.canReport)
input(type="checkbox", name="permissions.canReport", checked= userProfile.permissions.canReport)
.state.p-success
label Can Report
.uk-width-auto
.pretty.p-default
input(type="checkbox", name="permissions.canShareLinks", checked= userAccount.permissions.canShareLinks)
input(type="checkbox", name="permissions.canShareLinks", checked= userProfile.permissions.canShareLinks)
.state.p-success
label Can Share Links
@ -75,12 +75,12 @@ block admin-content
div(uk-grid).uk-grid-small
.uk-width-auto
.pretty.p-default
input(type="checkbox", name="optIn.system", checked= userAccount.optIn.system)
input(type="checkbox", name="optIn.system", checked= userProfile.optIn.system)
.state.p-success
label System Messages
.uk-width-auto
.pretty.p-default
input(type="checkbox", name="optIn.marketing", checked= userAccount.optIn.marketing)
input(type="checkbox", name="optIn.marketing", checked= userProfile.optIn.marketing)
.state.p-success
label Marketing

7
lib/site-controller.js

@ -6,6 +6,7 @@
import path from 'node:path';
import express from 'express';
import multer from 'multer';
import slugTool from 'slug';
@ -57,6 +58,12 @@ export class SiteController extends SiteCommon {
return pagination;
}
createRouter (route) {
const router = express.Router();
this.dtp.app.use(route, router);
return router;
}
createMulter (slug, options) {
if (!!slug && (typeof slug === 'object')) {
options = slug;

Loading…
Cancel
Save