diff --git a/.nodemon.json b/.nodemon.json index 44b17e9ee..101104f4a 100644 --- a/.nodemon.json +++ b/.nodemon.json @@ -1,6 +1,5 @@ { "exec": "npm-run-all --parallel generate-introspection start:development", - "verbose": true, "ignore": ["test/*", "client/*", "dist/*", "plugins/*/client"], "ext": "js,json,graphql,yml", "watch": [ diff --git a/app.js b/app.js index 225b5b480..e962e41ff 100644 --- a/app.js +++ b/app.js @@ -5,7 +5,6 @@ const uuid = require('uuid'); const merge = require('lodash/merge'); const helmet = require('helmet'); const plugins = require('./services/plugins'); -const compression = require('compression'); const { HELMET_CONFIGURATION } = require('./config'); const { MOUNT_PATH } = require('./url'); const routes = require('./routes'); @@ -72,9 +71,6 @@ app.use( ) ); -// Compress the responses if appropriate. -app.use(compression()); - //============================================================================== // VIEW CONFIGURATION //============================================================================== diff --git a/middleware/pubsub.js b/middleware/pubsub.js deleted file mode 100644 index 2b7291bf3..000000000 --- a/middleware/pubsub.js +++ /dev/null @@ -1,11 +0,0 @@ -const pubsub = require('../services/pubsub'); - -// To handle dependancy injection safer, we inject the pubsub handle onto the -// request object. -module.exports = (req, res, next) => { - // Attach the pubsub handle to the requests. - req.pubsub = pubsub.getClient(); - - // Forward on the request. - next(); -}; diff --git a/routes/admin/index.js b/routes/admin/index.js index 66f9c123d..ce686bd85 100644 --- a/routes/admin/index.js +++ b/routes/admin/index.js @@ -1,6 +1,12 @@ const express = require('express'); const router = express.Router(); +if (process.env.NODE_ENV !== 'production') { + router.get('/docs', (req, res) => { + res.render('admin/docs'); + }); +} + router.get('/confirm-email', (req, res) => { res.render('admin/confirm-email'); }); diff --git a/routes/api/index.js b/routes/api/index.js index 5f704fcda..f6651a4f5 100644 --- a/routes/api/index.js +++ b/routes/api/index.js @@ -1,17 +1,7 @@ const express = require('express'); -const pkg = require('../../package.json'); - const router = express.Router(); -router.get('/', (req, res) => { - res.json({ version: pkg.version }); -}); - -router.use('/assets', require('./assets')); -router.use('/settings', require('./settings')); -router.use('/auth', require('./auth')); -router.use('/users', require('./users')); -router.use('/account', require('./account')); -router.use('/setup', require('./setup')); +// Return the current version. +router.use('/v1', require('./v1')); module.exports = router; diff --git a/routes/api/settings/index.js b/routes/api/settings/index.js deleted file mode 100644 index 1cad31146..000000000 --- a/routes/api/settings/index.js +++ /dev/null @@ -1,29 +0,0 @@ -const express = require('express'); -const SettingsService = require('../../../services/settings'); -const authorization = require('../../../middleware/authorization'); - -const router = express.Router(); - -router.get( - '/', - authorization.needed('ADMIN', 'MODERATOR'), - async (req, res, next) => { - try { - let settings = await SettingsService.retrieve(); - res.json(settings); - } catch (e) { - return next(e); - } - } -); - -router.put('/', authorization.needed('ADMIN'), async (req, res, next) => { - try { - await SettingsService.update(req.body); - res.status(204).end(); - } catch (e) { - return next(e); - } -}); - -module.exports = router; diff --git a/routes/api/account/index.js b/routes/api/v1/account.js similarity index 89% rename from routes/api/account/index.js rename to routes/api/v1/account.js index c666d9f2f..e1395323d 100644 --- a/routes/api/account/index.js +++ b/routes/api/v1/account.js @@ -5,10 +5,7 @@ const mailer = require('../../../services/mailer'); const authorization = require('../../../middleware/authorization'); const errors = require('../../../errors'); -//============================================================================== -// ROUTES -//============================================================================== - +// Return the current logged in user. router.get('/', authorization.needed(), (req, res, next) => { res.json(req.user); }); @@ -45,9 +42,8 @@ const tokenCheck = (verifier, error) => async (req, res, next) => { next(); }; -// POST /email/confirm takes the password confirmation token available as a -// payload parameter and if it verifies, it updates the confirmed_at date on the -// local profile. +// Takes the password confirmation token available as a payload parameter and if +// it verifies, it updates the confirmed_at date on the local profile. router.post( '/email/verify', tokenCheck( @@ -67,6 +63,7 @@ router.post( } ); +// Sends the password reset email if the user exists. router.post('/password/reset', async (req, res, next) => { const { email, loc } = req.body; @@ -93,6 +90,7 @@ router.post('/password/reset', async (req, res, next) => { } }); +// Executes the password reset. router.put( '/password/reset', tokenCheck( diff --git a/routes/api/assets/index.js b/routes/api/v1/assets.js similarity index 99% rename from routes/api/assets/index.js rename to routes/api/v1/assets.js index 47aca1cc8..e3bd21dd4 100644 --- a/routes/api/assets/index.js +++ b/routes/api/v1/assets.js @@ -1,10 +1,8 @@ const express = require('express'); const router = express.Router(); const authorization = require('../../../middleware/authorization'); - const errors = require('../../../errors'); const AssetsService = require('../../../services/assets'); - const AssetModel = require('../../../models/asset'); const FilterOpenAssets = (query, filter) => { diff --git a/routes/api/auth/index.js b/routes/api/v1/auth.js similarity index 89% rename from routes/api/auth/index.js rename to routes/api/v1/auth.js index 90912a537..369f4278a 100644 --- a/routes/api/auth/index.js +++ b/routes/api/v1/auth.js @@ -4,7 +4,6 @@ const { HandleGenerateCredentials, HandleLogout, } = require('../../../services/passport'); - const router = express.Router(); /** @@ -27,15 +26,14 @@ router.get('/', (req, res, next) => { /** * This blacklists the token used to authenticate. */ - router.delete('/', HandleLogout); -// ============================================================================= +//============================================================================== // PASSPORT ROUTES //============================================================================== /** - * Local auth endpoint, will recieve a email and password + * Local auth endpoint, will receive a email and password */ router.post('/local', (req, res, next) => { // Perform the local authentication. diff --git a/routes/api/v1/graph.js b/routes/api/v1/graph.js new file mode 100644 index 000000000..7d13d4c92 --- /dev/null +++ b/routes/api/v1/graph.js @@ -0,0 +1,19 @@ +const express = require('express'); +const apollo = require('apollo-server-express'); +const { createGraphOptions } = require('../../../graph'); +const staticTemplate = require('../../../middleware/staticTemplate'); +const router = express.Router(); + +router.use('/ql', apollo.graphqlExpress(createGraphOptions)); + +// Only include the graphiql tool if we aren't in production mode. +if (process.env.NODE_ENV !== 'production') { + // Interactive graphiql interface. + router.use('/iql', staticTemplate, (req, res) => { + res.render('graphiql', { + endpointURL: 'api/v1/graph/ql', + }); + }); +} + +module.exports = router; diff --git a/routes/api/v1/index.js b/routes/api/v1/index.js new file mode 100644 index 000000000..e331d09d8 --- /dev/null +++ b/routes/api/v1/index.js @@ -0,0 +1,17 @@ +const express = require('express'); +const pkg = require('../../../package.json'); +const router = express.Router(); + +// Return the current version. +router.get('/', (req, res) => { + res.json({ version: pkg.version }); +}); + +router.use('/account', require('./account')); +router.use('/assets', require('./assets')); +router.use('/auth', require('./auth')); +router.use('/graph', require('./graph')); +router.use('/setup', require('./setup')); +router.use('/users', require('./users')); + +module.exports = router; diff --git a/routes/api/setup/index.js b/routes/api/v1/setup.js similarity index 99% rename from routes/api/setup/index.js rename to routes/api/v1/setup.js index 93b22fda1..c3cf9518e 100644 --- a/routes/api/setup/index.js +++ b/routes/api/v1/setup.js @@ -1,7 +1,5 @@ const express = require('express'); - const SetupService = require('../../../services/setup'); - const router = express.Router(); router.get('/', async (req, res, next) => { diff --git a/routes/api/users/index.js b/routes/api/v1/users.js similarity index 100% rename from routes/api/users/index.js rename to routes/api/v1/users.js diff --git a/routes/index.js b/routes/index.js index aa846a9dc..3727f76bb 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,5 +1,4 @@ const SetupService = require('../services/setup'); -const apollo = require('apollo-server-express'); const authentication = require('../middleware/authentication'); const cookieParser = require('cookie-parser'); const debug = require('debug')('talk:routes'); @@ -8,12 +7,11 @@ const errors = require('../errors'); const express = require('express'); const i18n = require('../middleware/i18n'); const path = require('path'); +const compression = require('compression'); const plugins = require('../services/plugins'); const staticTemplate = require('../middleware/staticTemplate'); -const pubsub = require('../middleware/pubsub'); const staticMiddleware = require('express-static-gzip'); const { DISABLE_STATIC_SERVER } = require('../config'); -const { createGraphOptions } = require('../graph'); const { passport } = require('../services/passport'); const { MOUNT_PATH } = require('../url'); @@ -33,10 +31,9 @@ if (!DISABLE_STATIC_SERVER) { /** * Redirect old embed calls. */ + const oldEmbed = path.resolve(MOUNT_PATH, 'embed.js'); + const newEmbed = path.resolve(MOUNT_PATH, 'static/embed.js'); router.get('/embed.js', (req, res) => { - const oldEmbed = path.resolve(MOUNT_PATH, 'embed.js'); - const newEmbed = path.resolve(MOUNT_PATH, 'static/embed.js'); - console.warn( `deprecation warning: ${oldEmbed} will be phased out soon, please replace calls from ${oldEmbed} to ${newEmbed}` ); @@ -69,6 +66,9 @@ if (!DISABLE_STATIC_SERVER) { // Add the i18n middleware to all routes. router.use(i18n); +// Compress all API responses if appropriate. +router.use(compression()); + //============================================================================== // STATIC ROUTES //============================================================================== @@ -103,37 +103,12 @@ router.use(passport.initialize()); // Attach the authentication middleware, this will be responsible for decoding // (if present) the JWT on the request. -router.use('/api', authentication, pubsub); +router.use('/api', authentication, require('./api')); //============================================================================== -// GraphQL Router +// DEVELOPMENT ROUTES //============================================================================== -// GraphQL endpoint. -router.use('/api/v1/graph/ql', apollo.graphqlExpress(createGraphOptions)); - -// Only include the graphiql tool if we aren't in production mode. -if (process.env.NODE_ENV !== 'production') { - // Interactive graphiql interface. - router.use('/api/v1/graph/iql', staticTemplate, (req, res) => { - res.render('graphiql', { - endpointURL: 'api/v1/graph/ql', - }); - }); - - // GraphQL documentation. - router.get('/admin/docs', (req, res) => { - res.render('admin/docs'); - }); -} - -router.use('/api/v1', require('./api')); - -//============================================================================== -// ROUTES -//============================================================================== - -// Development routes. if (process.env.NODE_ENV !== 'production') { router.use('/assets', staticTemplate, require('./assets')); router.get('/', staticTemplate, async (req, res) => { @@ -161,6 +136,10 @@ if (process.env.NODE_ENV !== 'production') { }); } +//============================================================================== +// PLUGIN ROUTES +//============================================================================== + // Inject server route plugins. plugins.get('server', 'router').forEach(plugin => { debug(`added plugin '${plugin.plugin.name}'`); @@ -178,7 +157,7 @@ router.use((req, res, next) => { next(errors.ErrNotFound); }); -// General api error handler. Respond with the message and error if we have it +// General API error handler. Respond with the message and error if we have it // while returning a status code that makes sense. router.use('/api', (err, req, res, next) => { if (err !== errors.ErrNotFound) { diff --git a/test/server/routes/api/settings/index.js b/test/server/routes/api/settings/index.js deleted file mode 100644 index 52264d800..000000000 --- a/test/server/routes/api/settings/index.js +++ /dev/null @@ -1,56 +0,0 @@ -const passport = require('../../../passport'); - -const app = require('../../../../../app'); - -const chai = require('chai'); -chai.should(); -chai.use(require('chai-http')); -const expect = chai.expect; - -const SettingsService = require('../../../../../services/settings'); -const defaults = { id: '1', moderation: 'PRE' }; - -describe('/api/v1/settings', () => { - beforeEach(() => SettingsService.init(defaults)); - - describe('#get', () => { - it('should return a settings object', async () => { - for (let role of ['ADMIN', 'MODERATOR']) { - const res = await chai - .request(app) - .get('/api/v1/settings') - .set(passport.inject({ role })); - expect(res).to.have.status(200); - expect(res).to.be.json; - expect(res.body).to.have.property('moderation', 'PRE'); - } - }); - }); - - describe('#put', () => { - it('should update the settings', () => { - return chai - .request(app) - .put('/api/v1/settings') - .set(passport.inject({ role: 'ADMIN' })) - .send({ moderation: 'POST' }) - .then(res => { - expect(res).to.have.status(204); - - return SettingsService.retrieve(); - }) - .then(settings => { - expect(settings).to.have.property('moderation', 'POST'); - }); - }); - - it('should require ADMIN role', () => { - const promise = chai - .request(app) - .put('/api/v1/settings') - .set(passport.inject({ role: 'MODERATOR' })) - .send({ moderation: 'POST' }); - return expect(promise).to.eventually.be.rejected; - }); - }); -});