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. 59
      app/controllers/lib/populators.js
  8. 5
      app/controllers/manifest.js
  9. 36
      app/controllers/user.js
  10. 42
      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 ChatImage = mongoose.model('Image');
const Video = mongoose.model('Video'); const Video = mongoose.model('Video');
import express from 'express';
import { SiteController } from '../../lib/site-lib.js'; import { SiteController } from '../../lib/site-lib.js';
export default class AdminController extends SiteController { 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 authCheck = sessionService.authCheckMiddleware({ requireLogin: true, requireAdmin: true });
const router = express.Router(); const router = this.createRouter('/admin');
this.dtp.app.use('/admin', authCheck, router); router.use(authCheck);
router.use('/user', await this.loadChild(path.join(__dirname, 'admin', 'user.js'))); 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 express from 'express';
import { SiteController, SiteError } from '../../../lib/site-lib.js'; import { SiteController, SiteError } from '../../../lib/site-lib.js';
import {
populateUserId,
} from '../lib/populators.js';
export default class UserAdminController extends SiteController { export default class UserAdminController extends SiteController {
@ -20,7 +23,7 @@ export default class UserAdminController extends SiteController {
async start ( ) { async start ( ) {
const router = express.Router(); const router = express.Router();
router.param('userId', this.populateUserId.bind(this)); router.param('userId', populateUserId(this));
router.post( router.post(
'/:userId', '/:userId',
@ -40,33 +43,19 @@ export default class UserAdminController extends SiteController {
return router; 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) { async postUserUpdate (req, res, next) {
const { user: userService } = this.dtp.services; const { user: userService } = this.dtp.services;
try { try {
switch (req.body.action) { switch (req.body.action) {
case 'update': case 'update':
await userService.updateForAdmin(res.locals.userAccount, req.body); await userService.updateForAdmin(res.locals.userProfile, req.body);
break; break;
case 'ban': 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"); throw new SiteError(400, "You can't ban yourself");
} }
await userService.banUser(res.locals.userAccount); await userService.banUser(res.locals.userProfile);
break; break;
} }
res.redirect(`/admin/user`); res.redirect(`/admin/user`);

4
app/controllers/auth.js

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

4
app/controllers/email.js

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

5
app/controllers/home.js

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

25
app/controllers/image.js

@ -5,10 +5,11 @@
'use strict'; 'use strict';
import fs from 'node:fs'; import fs from 'node:fs';
import express from 'express';
import mongoose from 'mongoose';
import { SiteController, SiteError } from '../../lib/site-lib.js'; import { SiteController, SiteError } from '../../lib/site-lib.js';
import {
populateImageId,
} from './lib/populators.js';
export default class ImageController extends SiteController { export default class ImageController extends SiteController {
@ -24,8 +25,7 @@ export default class ImageController extends SiteController {
const { limiter: limiterService } = dtp.services; const { limiter: limiterService } = dtp.services;
const limiterConfig = limiterService.config.image; const limiterConfig = limiterService.config.image;
const router = express.Router(); const router = this.createRouter('/image');
dtp.app.use('/image', router);
const imageUpload = this.createMulter(ImageController.slug, { const imageUpload = this.createMulter(ImageController.slug, {
limits: { limits: {
@ -38,7 +38,7 @@ export default class ImageController extends SiteController {
return next(); return next();
}); });
router.param('imageId', this.populateImage.bind(this)); router.param('imageId', populateImageId(this));
router.post('/', router.post('/',
limiterService.create(limiterConfig.postCreateImage), 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) { async postCreateImage (req, res, next) {
const { image: imageService } = this.dtp.services; const { image: imageService } = this.dtp.services;
try { try {

59
app/controllers/lib/populators.js

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

5
app/controllers/manifest.js

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

36
app/controllers/user.js

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

42
app/controllers/video.js

@ -4,11 +4,12 @@
'use strict'; 'use strict';
import express from 'express';
import { pipeline } from 'node:stream'; 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 { export default class VideoController extends SiteController {
@ -26,21 +27,20 @@ export default class VideoController extends SiteController {
} = this.dtp.services; } = this.dtp.services;
const limiterConfig = limiterService.config.video; const limiterConfig = limiterService.config.video;
this.templates = {
passwordResetComplete: this.loadViewTemplate('auth/password-reset-complete.pug'),
};
const authRequired = sessionService.authCheckMiddleware({ requireLogin: true }); const authRequired = sessionService.authCheckMiddleware({ requireLogin: true });
const router = express.Router(); const router = this.createRouter('/video');
this.dtp.app.use('/video', authRequired, router); this.dtp.app.use('/video', router);
router.use(async (req, res, next) => { router.use(
res.locals.currentView = 'video'; authRequired,
return next(); async (req, res, next) => {
}); res.locals.currentView = 'video';
return next();
},
);
router.param('videoId', this.populateVideoId.bind(this)); router.param('videoId', populateVideoId(this));
router.get( router.get(
'/:videoId/media', '/:videoId/media',
@ -51,20 +51,6 @@ export default class VideoController extends SiteController {
return router; 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) { async getVideoMedia (req, res, next) {
const { minio: minioService } = this.dtp.services; const { minio: minioService } = this.dtp.services;
try { try {

6
app/controllers/welcome.js

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

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

@ -3,49 +3,49 @@ block admin-content
include ../../user/components/profile-picture 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.uk-card-default.uk-card-small.uk-border-rounded
.uk-card-header .uk-card-header
div(uk-grid).uk-grid-small div(uk-grid).uk-grid-small
.uk-width-auto .uk-width-auto
+renderProfilePicture(userAccount) +renderProfilePicture(userProfile)
.uk-width-expand .uk-width-expand
.uk-margin .uk-margin
.uk-text-lead= userAccount.displayName .uk-text-lead= userProfile.displayName
div(uk-grid).uk-grid-small.uk-grid-divider div(uk-grid).uk-grid-small.uk-grid-divider
.uk-width-auto @#{userAccount.username} .uk-width-auto @#{userProfile.username}
.uk-width-auto= userAccount.email .uk-width-auto= userProfile.email
.uk-width-auto Created #{dayjs(userAccount.created).fromNow()} on #{dayjs(userAccount.created).format('MMMM D, YYYY')} .uk-width-auto Created #{dayjs(userProfile.created).fromNow()} on #{dayjs(userProfile.created).format('MMMM D, YYYY')}
.uk-card-body .uk-card-body
.uk-margin .uk-margin
label(for="profile-bio").uk-form-label bio 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 .uk-margin
label(for="profile-badges").uk-form-label Profile Badges 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 .uk-margin
form(method="POST", action=`/admin/user/${userAccount._id}`).uk-form form(method="POST", action=`/admin/user/${userProfile._id}`).uk-form
.uk-margin .uk-margin
label.uk-form-label Flags label.uk-form-label Flags
.uk-margin-small .uk-margin-small
div(uk-grid).uk-grid-small div(uk-grid).uk-grid-small
.uk-width-auto .uk-width-auto
.pretty.p-default .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 .state.p-success
label Admin label Admin
.uk-width-auto .uk-width-auto
.pretty.p-default .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 .state.p-success
label Moderator label Moderator
.uk-width-auto .uk-width-auto
.pretty.p-default .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 .state.p-success
label Email Verified label Email Verified
@ -55,17 +55,17 @@ block admin-content
div(uk-grid).uk-grid-small div(uk-grid).uk-grid-small
.uk-width-auto .uk-width-auto
.pretty.p-default .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 .state.p-success
label Can Login label Can Login
.uk-width-auto .uk-width-auto
.pretty.p-default .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 .state.p-success
label Can Report label Can Report
.uk-width-auto .uk-width-auto
.pretty.p-default .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 .state.p-success
label Can Share Links label Can Share Links
@ -75,12 +75,12 @@ block admin-content
div(uk-grid).uk-grid-small div(uk-grid).uk-grid-small
.uk-width-auto .uk-width-auto
.pretty.p-default .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 .state.p-success
label System Messages label System Messages
.uk-width-auto .uk-width-auto
.pretty.p-default .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 .state.p-success
label Marketing label Marketing

7
lib/site-controller.js

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

Loading…
Cancel
Save