diff --git a/app/workers/sample-worker.js b/app/workers/sample-worker.js deleted file mode 100644 index c5298ec..0000000 --- a/app/workers/sample-worker.js +++ /dev/null @@ -1,91 +0,0 @@ -// sample-worker.js -// Copyright (C) 2022 DTP Technologies, LLC -// License: Apache-2.0 - -'use strict'; - -const path = require('path'); -require('dotenv').config({ path: path.resolve(__dirname, '..', '..', '.env') }); - -const { - SiteLog, - SiteWorker, -} = require(path.join(__dirname, '..', '..', 'lib', 'site-lib')); - -const { CronJob } = require('cron'); - -module.rootPath = path.resolve(__dirname, '..', '..'); -module.pkg = require(path.resolve(__dirname, '..', '..', 'package.json')); - -module.config = { - environment: process.env.NODE_ENV, - root: module.rootPath, - component: { name: 'sampleWorker', slug: 'sample-worker' }, -}; - -module.config.site = require(path.join(module.rootPath, 'config', 'site')); -module.config.http = require(path.join(module.rootPath, 'config', 'http')); - -class SampleWorker extends SiteWorker { - - constructor (dtp) { - super(dtp, { }); - } - - async start ( ) { - const CRON_TIMEZONE = 'America/New_York'; - - await super.start(); - - this.log.info('starting worker job'); - this.job = new CronJob( - '*/5 * * * * *', - this.runJob.bind(this), - null, true, CRON_TIMEZONE, - ); - - const { jobQueue: jobQueueService } = this.dtp.services; - this.sampleJobQueue = jobQueueService.getJobQueue('dtp-sample', this.dtp.config.jobQueues['dtp-sample']); - this.sampleJobQueue.process('dtp-sample', 1, this.processDtpSample.bind(this)); - } - - async stop ( ) { - this.log.info('stopping sample worker job'); - this.job.stop(); - delete this.job; - - await super.stop(); - } - - async runJob ( ) { - this.log.alert('sample job starting'); - - /* - * Your worker will do interesting things here - */ - - this.log.alert('sample job ending'); - } - - async processDtpSample (job) { - this.log.info('received sample job', { id: job.id, data: job.data }); - } -} - -(async ( ) => { - try { - module.log = new SiteLog(module, module.config.component); - - module.worker = new SampleWorker(module); - await module.worker.start(); - - module.log.info(`${module.pkg.name} v${module.pkg.version} ${module.config.component.name} started`); - } catch (error) { - module.log.error('failed to start worker', { - component: module.config.component, - error, - }); - process.exit(-1); - } - -})(); \ No newline at end of file diff --git a/docs/samples/controller.js b/docs/samples/controller.js new file mode 100644 index 0000000..3a55f4d --- /dev/null +++ b/docs/samples/controller.js @@ -0,0 +1,97 @@ +// samples/controller.js +// Copyright (C) 2022 DTP Technologies, LLC +// License: Apache-2.0 + +'use strict'; + +const express = require('express'); + +const { SiteController, SiteError } = require('../../lib/site-lib'); + +class HomeController extends SiteController { + + constructor (dtp) { + super(dtp, module.exports); + } + + async start ( ) { + const { dtp } = this; + const { limiter: limiterService } = dtp.services; + + const upload = this.createMulter(); + + const router = express.Router(); + dtp.app.use('/your-route', router); + + router.use(async (req, res, next) => { + res.locals.currentView = 'your-view'; + return next(); + }); + + router.param('itemId', this.populateItemId.bind(this)); + + router.post( + '/item', + limiterService.createMiddleware(limiterService.config.home.postItemCreate), + upload.none(), + this.postItemCreate.bind(this), + ); + + router.get( + '/item/:itemId', + limiterService.createMiddleware(limiterService.config.sample.getItemView), + this.getItemView.bind(this), + ); + + router.get('/', + limiterService.createMiddleware(limiterService.config.sample.getHome), + this.getHome.bind(this), + ); + } + + async populateItemId (req, res, next, itemId) { + const { item: itemService } = this.dtp.services; + try { + res.locals.item = await itemService.getById(itemId); + if (!res.locals.item) { + throw new SiteError(404, 'Item not found'); + } + return next(); + } catch (error) { + return next(error); + } + } + + async postItemCreate (req, res, next) { + const { item: itemService } = this.dtp.services; + try { + const item = await itemService.create(req.user, req.body); + res.redirect(`/item/${item._id}`); + } catch (error) { + this.log.error('failed to create item', { error }); + return next(error); + } + } + + async getItemView (req, res) { + res.render('item/view'); + } + + async getHome (req, res, next) { + const { announcement: announcementService } = this.dtp.services; + try { + res.locals.announcements = await announcementService.getLatest(req.user); + res.render('index'); + } catch (error) { + this.log.error('failed to render home view', { error }); + return next(error); + } + } +} + +module.exports = { + slug: 'home', + name: 'home', + isHome: true, + create: async (dtp) => { return new HomeController(dtp); }, +}; \ No newline at end of file diff --git a/docs/samples/service.js b/docs/samples/service.js new file mode 100644 index 0000000..0126acd --- /dev/null +++ b/docs/samples/service.js @@ -0,0 +1,68 @@ +// samples/service.js +// Copyright (C) 2022 DTP Technologies, LLC +// License: Apache-2.0 + +'use strict'; + +const mongoose = require('mongoose'); + +const Item = mongoose.model('Item'); + +const { SiteService } = require('../../lib/site-lib'); + +class SampleService extends SiteService { + + constructor (dtp) { + super(dtp, module.exports); + } + + async start ( ) { + await super.start(); + + this.queue = this.getJobQueue('sample'); + } + + async stop ( ) { + // do your shutdown here + + await super.stop(); + } + + async create (owner, itemDefinition) { + const NOW = new Date(); + const item = new Item(); + + item.created = NOW; + item.title = itemDefinition.title; + item.content = itemDefinition.content; + + await item.save(); + + return item.toObject(); + } + + async getItems (search, pagination) { + const items = await Item + .find(search) + .sort({ created: -1 }) + .skip(pagination.skip) + .limit(pagination.cpp) + .lean(); + return items; + } + + async getById (itemId) { + const item = await Item.findById(itemId).lean(); + return item; + } + + async deleteItem (item) { + await Item.deleteOne({ _id: item._id }); + } +} + +module.exports = { + name: 'sample', + slug: 'sample', + create: (dtp) => { return new SampleService(dtp); }, +}; \ No newline at end of file diff --git a/docs/samples/worker.js b/docs/samples/worker.js new file mode 100644 index 0000000..d3c725c --- /dev/null +++ b/docs/samples/worker.js @@ -0,0 +1,56 @@ +// samples/worker.js +// Copyright (C) 2022 DTP Technologies, LLC +// License: Apache-2.0 + +'use strict'; + +const path = require('path'); +require('dotenv').config({ path: path.resolve(__dirname, '..', '..', '.env') }); + +const { SiteLog, SiteWorker } = require(path.join(__dirname, '..', '..', 'lib', 'site-lib')); + +class SampleWorker extends SiteWorker { + + constructor (dtp) { + super(dtp, dtp.config.component); + } + + async start ( ) { + await super.start(); + + await this.loadProcessor(path.join(__dirname, 'your-worker', 'cron', 'expire-things.js')); + await this.loadProcessor(path.join(__dirname, 'your-worker', 'job', 'process-things.js')); + + await this.startProcessors(); + } + + async stop ( ) { + await super.stop(); + } +} + +(async ( ) => { + try { + module.rootPath = path.resolve(__dirname, '..', '..'); + module.pkg = require(path.resolve(__dirname, '..', '..', 'package.json')); + module.component = { name: 'theWorkerName', slug: 'the-worker-name' }; + + module.config = { + environment: process.env.NODE_ENV, + root: module.rootPath, + component: module.component, + }; + + module.config.site = require(path.join(module.rootPath, 'config', 'site')); + module.log = new SiteLog(module, module.component); + + module.worker = new SampleWorker(module); + await module.worker.start(); + + module.log.info(`${module.pkg.name} v${module.pkg.version} ${module.component.name} started`); + } catch (error) { + module.log.error('failed to start worker', { component: module.component.name, error }); + process.exit(-1); + } + +})(); \ No newline at end of file diff --git a/lib/site-controller.js b/lib/site-controller.js index 37e487e..28185f7 100644 --- a/lib/site-controller.js +++ b/lib/site-controller.js @@ -5,6 +5,7 @@ 'use strict'; const path = require('path'); +const multer = require('multer'); const { SiteCommon } = require(path.join(__dirname, 'site-common')); @@ -35,6 +36,11 @@ class SiteController extends SiteCommon { return pagination; } + createMulter (slug) { + slug = slug || 'uploads'; + return multer({ dest: `/tmp/${this.dtp.config.site.domainKey}/${slug}/${this.component.slug}` }); + } + createDisplayList (name) { const { displayEngine: displayEngineService } = this.dtp.services; return displayEngineService.createDisplayList(name);