diff --git a/template/_package.json b/template/_package.json index 4a3e83841..095e2995b 100644 --- a/template/_package.json +++ b/template/_package.json @@ -3,7 +3,12 @@ "version": "1.0.0", "description": "<%= description %>", "author": "<%= author %>", - "private": true, + "private": true,<% if (server === 'feathers') { %> + "directories": { + "lib": "server", + "config": "server/config", + "test": "server/test" + }, <% } %> "scripts": {<% if (server === 'adonis') { %> "serve:dev": "<%= pm %> run dev", "dev": "nodemon --watch app --watch bootstrap --watch config --watch .env -x node server.js", @@ -15,7 +20,8 @@ "generate": "nuxt generate"<% } %><% if (eslint === 'yes') { %>, "lint": "eslint --ext .js,.vue --ignore-path .gitignore .", "precommit": "<%= pm %> run lint"<% } %><% if (test !== 'none') { %>, - "test": "<%= test %>"<% } %> + "test": "<%= test %>"<% } %><% if (server === 'feathers') { %>, + "feathers": "feathers"<% } %> }, "dependencies": { "cross-env": "^5.2.0"<% if (edge) { %>, @@ -27,9 +33,16 @@ "micro": "^9.3.3", "micro-route": "^2.5.0"<% } else if (server === 'fastify') { %>, "fastify": "^1.13.3"<% } else if (server === 'feathers') { %>, - "@feathersjs/feathers": "^3.3.1", - "@feathersjs/express": "^1.3.1", - "@feathersjs/configuration": "^2.0.6"<% } else if (server === 'adonis') { %>, + "@feathersjs/client": "^3.7.3", + "@feathersjs/configuration": "^2.0.4", + "@feathersjs/errors": "^3.3.4", + "@feathersjs/express": "^1.2.7", + "@feathersjs/feathers": "^3.2.3", + "@feathersjs/socketio": "^3.2.6", + "compression": "^1.7.3", + "consola": "^1.4.3", + "cors": "^2.8.4", + "helmet": "^3.13.0"<% } else if (server === 'adonis') { %>, "@adonisjs/ace": "^4.0.7", "@adonisjs/auth": "^2.0.10", "@adonisjs/bodyparser": "^1.0.8", @@ -54,7 +67,8 @@ "tachyons": "^4.11.1"<% } %> }, "devDependencies": { - "nodemon": "^1.18.9"<% if (eslint === 'yes') { %>, + "nodemon": "^1.18.9"<% if (server === 'feathers') { %>, + "@feathersjs/cli": "^3.8.5"<% } %><% if (eslint === 'yes') { %>, "@nuxtjs/eslint-config": "^0.0.1", "babel-eslint": "^10.0.1", "eslint": "^5.15.1", diff --git a/template/frameworks/feathers/server/app.hooks.js b/template/frameworks/feathers/server/app.hooks.js new file mode 100644 index 000000000..df0de5eca --- /dev/null +++ b/template/frameworks/feathers/server/app.hooks.js @@ -0,0 +1,34 @@ +// Application hooks that run for every service +const log = require('./hooks/log'); + +module.exports = { + before: { + all: [log()], + find: [], + get: [], + create: [], + update: [], + patch: [], + remove: [] + }, + + after: { + all: [log()], + find: [], + get: [], + create: [], + update: [], + patch: [], + remove: [] + }, + + error: { + all: [log()], + find: [], + get: [], + create: [], + update: [], + patch: [], + remove: [] + } +}; diff --git a/template/frameworks/feathers/server/app.js b/template/frameworks/feathers/server/app.js new file mode 100644 index 000000000..f3930cef2 --- /dev/null +++ b/template/frameworks/feathers/server/app.js @@ -0,0 +1,46 @@ +const compress = require('compression'); +const helmet = require('helmet'); +const cors = require('cors'); +const logger = require('./logger'); + +const path = require('path'); +process.env['NODE_CONFIG_DIR'] = path.join(__dirname, 'config/'); + +const feathers = require('@feathersjs/feathers'); +const configuration = require('@feathersjs/configuration'); +const express = require('@feathersjs/express'); +const socketio = require('@feathersjs/socketio'); + +const middleware = require('./middleware'); +const services = require('./services'); +const appHooks = require('./app.hooks'); +const channels = require('./channels'); + +const app = express(feathers()); + +// Load app configuration +app.configure(configuration()); +// Enable security, CORS and body parsing +app.use(helmet()); +app.use(cors()); +app.use(compress()); +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); + +// Set up Plugins and providers +app.configure(express.rest()); +app.configure(socketio()); + +// Configure other middleware (see `middleware/index.js`) +app.configure(middleware); +// Set up our services (see `services/index.js`) +app.configure(services); +// Set up event channels (see channels.js) +app.configure(channels); + +// Configure a middleware for error handler +app.use(express.errorHandler({ logger })); + +app.hooks(appHooks); + +module.exports = app; diff --git a/template/frameworks/feathers/server/channels.js b/template/frameworks/feathers/server/channels.js new file mode 100644 index 000000000..ad36c2850 --- /dev/null +++ b/template/frameworks/feathers/server/channels.js @@ -0,0 +1,61 @@ +module.exports = function(app) { + if (typeof app.channel !== 'function') { + // If no real-time functionality has been configured just return + return; + } + + app.on('connection', connection => { + // On a new real-time connection, add it to the anonymous channel + app.channel('anonymous').join(connection); + }); + + app.on('login', (authResult, { connection }) => { + // connection can be undefined if there is no + // real-time connection, e.g. when logging in via REST + if (connection) { + // Obtain the logged in user from the connection + // const user = connection.user; + + // The connection is no longer anonymous, remove it + app.channel('anonymous').leave(connection); + + // Add it to the authenticated user channel + app.channel('authenticated').join(connection); + + // Channels can be named anything and joined on any condition + + // E.g. to send real-time events only to admins use + // if(user.isAdmin) { app.channel('admins').join(connection); } + + // If the user has joined e.g. chat rooms + // if(Array.isArray(user.rooms)) user.rooms.forEach(room => app.channel(`rooms/${room.id}`).join(channel)); + + // Easily organize users by email and userid for things like messaging + // app.channel(`emails/${user.email}`).join(channel); + // app.channel(`userIds/$(user.id}`).join(channel); + } + }); + + // eslint-disable-next-line no-unused-vars + app.publish((data, hook) => { + // Here you can add event publishers to channels set up in `channels.js` + // To publish only for a specific event use `app.publish(eventname, () => {})` + + console.log('Publishing all events to all authenticated users. See `channels.js` and https://docs.feathersjs.com/api/channels.html for more information.'); // eslint-disable-line + + // e.g. to publish all service events to all authenticated users use + return app.channel('authenticated'); + }); + + // Here you can also add service specific event publishers + // e.g. the publish the `users` service `created` event to the `admins` channel + // app.service('users').publish('created', () => app.channel('admins')); + + // With the userid and email organization from above you can easily select involved users + // app.service('messages').publish(() => { + // return [ + // app.channel(`userIds/${data.createdBy}`), + // app.channel(`emails/${data.recipientEmail}`) + // ]; + // }); +}; diff --git a/template/frameworks/feathers/server/config/default.json b/template/frameworks/feathers/server/config/default.json index 728827262..c5b8103a4 100644 --- a/template/frameworks/feathers/server/config/default.json +++ b/template/frameworks/feathers/server/config/default.json @@ -1,4 +1,7 @@ { - "host": "localhost", - "port": 3000 + "port": 3000, + "paginate": { + "default": 10, + "max": 50 + } } diff --git a/template/frameworks/feathers/server/config/production.json b/template/frameworks/feathers/server/config/production.json index 751347e58..2c63c0851 100644 --- a/template/frameworks/feathers/server/config/production.json +++ b/template/frameworks/feathers/server/config/production.json @@ -1,4 +1,2 @@ { - "host": "with-featherjs-app.feathersjs.com", - "port": 80 } diff --git a/template/frameworks/feathers/server/hooks/log.js b/template/frameworks/feathers/server/hooks/log.js new file mode 100644 index 000000000..6806c9194 --- /dev/null +++ b/template/frameworks/feathers/server/hooks/log.js @@ -0,0 +1,21 @@ +// A hook that logs service method before, after and error +const util = require('util'); +const consola = require('../logger'); + +module.exports = function() { + return context => { + // This debugs the service call and a stringified version of the hook context + // You can customize the message to your needs + consola.debug( + `${context.type} app.service('${context.path}').${context.method}()` + ); + + if (typeof context.toJSON === 'function') { + consola.debug('Hook Context', util.inspect(context, { colors: false })); + } + + if (context.error && !context.result) { + consola.error(context.error); + } + }; +}; diff --git a/template/frameworks/feathers/server/index.js b/template/frameworks/feathers/server/index.js index 79ce87390..8faaa14c0 100644 --- a/template/frameworks/feathers/server/index.js +++ b/template/frameworks/feathers/server/index.js @@ -1,42 +1,34 @@ 'use strict' -const path = require('path') -const consola = require('consola') -const feathers = require('@feathersjs/feathers') -const express = require('@feathersjs/express') +const consola = require('consola'); +const { Nuxt, Builder } = require('nuxt'); +const app = require('./app'); +const host = process.env.HOST || '127.0.0.1'; +const port = process.env.PORT || 3000; -process.env.NODE_CONFIG_DIR = path.join(__dirname, 'config/') +app.set('port', port); -async function start() { - const app = express(feathers()) - - const { Nuxt, Builder } = require('nuxt<% if (edge) { %>-edge<% } %>') +// Import and Set Nuxt.js options +let config = require('../nuxt.config.js'); +config.dev = !(process.env.NODE_ENV === 'production'); - // Setup nuxt.js - const config = require('../nuxt.config.js') - config.rootDir = path.resolve(__dirname, '..') - config.dev = process.env.NODE_ENV !== 'production' +async function start() { + // Init Nuxt.js + const nuxt = new Nuxt(config); - const nuxt = new Nuxt(config) + // Build only in dev mode if (config.dev) { - const builder = new Builder(nuxt) - await builder.build() - } else { - await nuxt.ready() + const builder = new Builder(nuxt); + await builder.build(); } - const configuration = require('@feathersjs/configuration') - app.configure(configuration()).use(nuxt.render) - - const host = app.get('host') - const port = app.get('port') - - app.listen(port) + // Give nuxt middleware to express + app.use(nuxt.render); + // Listen the server + app.listen(port, host); consola.ready({ - message: `Feathers application started on ${host}:${port}`, + message: `Server listening on http://${host}:${port}`, badge: true - }) + }); } - -start() - +start(); diff --git a/template/frameworks/feathers/server/logger.js b/template/frameworks/feathers/server/logger.js new file mode 100644 index 000000000..cfe8009ff --- /dev/null +++ b/template/frameworks/feathers/server/logger.js @@ -0,0 +1,9 @@ +const { Consola, FancyReporter } = require('consola'); + +const logger = new Consola({ + level: 3, // Info level + reporters: [new FancyReporter()], + types: [] +}); + +module.exports = logger; diff --git a/template/frameworks/feathers/server/middleware/index.js b/template/frameworks/feathers/server/middleware/index.js new file mode 100644 index 000000000..4455535cd --- /dev/null +++ b/template/frameworks/feathers/server/middleware/index.js @@ -0,0 +1,5 @@ +// eslint-disable-next-line no-unused-vars +module.exports = function(app) { + // Add your custom middleware here. Remember that + // in Express, the order matters. +}; diff --git a/template/frameworks/feathers/server/services/index.js b/template/frameworks/feathers/server/services/index.js new file mode 100644 index 000000000..82032bd96 --- /dev/null +++ b/template/frameworks/feathers/server/services/index.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line no-unused-vars +module.exports = function(app) {}; diff --git a/test/snapshots/index.test.js.md b/test/snapshots/index.test.js.md index 9401446ab..2b9af2acc 100644 --- a/test/snapshots/index.test.js.md +++ b/test/snapshots/index.test.js.md @@ -311,9 +311,16 @@ Generated by [AVA](https://ava.li). 'pages/README.md', 'pages/index.vue', 'plugins/README.md', + 'server/app.hooks.js', + 'server/app.js', + 'server/channels.js', 'server/config/default.json', 'server/config/production.json', + 'server/hooks/log.js', 'server/index.js', + 'server/logger.js', + 'server/middleware/index.js', + 'server/services/index.js', 'static/README.md', 'static/favicon.ico', 'store/README.md', @@ -323,19 +330,33 @@ Generated by [AVA](https://ava.li). { dependencies: { - '@feathersjs/configuration': '^2.0.6', - '@feathersjs/express': '^1.3.1', - '@feathersjs/feathers': '^3.3.1', + '@feathersjs/client': '^3.7.3', + '@feathersjs/configuration': '^2.0.4', + '@feathersjs/errors': '^3.3.4', + '@feathersjs/express': '^1.2.7', + '@feathersjs/feathers': '^3.2.3', + '@feathersjs/socketio': '^3.2.6', + compression: '^1.7.3', + consola: '^1.4.3', + cors: '^2.8.4', 'cross-env': '^5.2.0', + helmet: '^3.13.0', nuxt: '^2.4.0', }, devDependencies: { + '@feathersjs/cli': '^3.8.5', nodemon: '^1.18.9', }, + directories: { + config: 'server/config', + lib: 'server', + test: 'server/test', + }, private: true, scripts: { build: 'nuxt build', dev: 'cross-env NODE_ENV=development nodemon server/index.js --watch server', + feathers: 'feathers', generate: 'nuxt generate', start: 'cross-env NODE_ENV=production node server/index.js', }, diff --git a/test/snapshots/index.test.js.snap b/test/snapshots/index.test.js.snap index dd2cf462f..672eca487 100644 Binary files a/test/snapshots/index.test.js.snap and b/test/snapshots/index.test.js.snap differ