server/routes/router.js

Summary

Maintainability
A
3 hrs
Test Coverage
//
//   Copyright 2009-2014 Ilkka Oksanen <iao@iki.fi>
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing,
//   software distributed under the License is distributed on an "AS
//   IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//   express or implied.  See the License for the specific language
//   governing permissions and limitations under the License.
//

const path = require('path');
const Router = require('@koa/router');
const send = require('koa-send');
const body = require('koa-body');
const conf = require('../lib/conf');
const log = require('../lib/log');
const passport = require('../lib/passport');
const registerController = require('../controllers/register');
const loginController = require('../controllers/login');
const clientController = require('../controllers/client');
const uploadController = require('../controllers/upload');
const userFilesController = require('../controllers/userFiles');
const forgotPasswordController = require('../controllers/forgotPassword');
const confirmEmailController = require('../controllers/confirmEmail');

const ONE_YEAR_IN_MS = 1000 * 60 * 60 * 24 * 365;
const TWO_DAYS_IN_MS = 1000 * 60 * 60 * 24 * 2;
const fingerPrintRe = /^assets\/\S+-.{32}\.\w+$/;
const devMode = process.env.NODE_ENV !== 'production';

module.exports = function buildRouter() {
  log.info('Registering app routes');

  const router = new Router();

  // Passport authentication routes
  if (conf.get('googleauth:enabled') && conf.get('googleauth:openid_realm')) {
    router.get(
      '/auth/google',
      passport.authenticate('google', {
        scope: 'email profile',
        openIDRealm: conf.get('googleauth:openid_realm')
      })
    );
    router.get('/auth/google/oauth2callback', loginController.externalLogin('google'));
  }

  if (conf.get('yahooauth:enabled')) {
    router.get('/auth/yahoo', passport.authenticate('yahoo'));
    router.get('/auth/yahoo/callback', loginController.externalLogin('yahoo'));
  }

  if (conf.get('cloudronauth:enabled')) {
    router.get('/auth/cloudron', passport.authenticate('cloudron'));
    router.get('/auth/cloudron/callback', loginController.externalLogin('cloudron'));
  }

  router.post('/api/v1/login', body(), loginController.localLogin);

  // File upload endpoint
  router.post('/api/v1/upload', body({ multipart: true }), uploadController);

  // Registration routes
  router.get('/api/v1/register', registerController.index);
  router.post('/api/v1/register', body(), registerController.create);
  router.post('/api/v1/register-ext', registerController.createExt);
  router.post('/api/v1/register-reset', registerController.createReset);

  // Forgot password
  router.post('/api/v1/forgot-password', body(), forgotPasswordController.create);
  router.get('/app/reset-password/:token', registerController.indexReset);

  // Confirm email
  router.get('/app/confirm-email/:token', confirmEmailController.show);

  // Public uploaded files
  router.get('/files/:uuid/:slug*', userFilesController);

  // Client
  router.get('/app', clientController);

  // TODO: Improve when V1 client (route below) is removed
  router.get(/^\/app\/c\/(.+)/, clientController);

  // V2 Client assets
  router.get(/^\/app\/client-assets\/(.+)/, async ctx => {
    const maxage = devMode ? 0 : ONE_YEAR_IN_MS;
    await sendFile(ctx, 'new-client/dist/', ctx.params[0], { maxage });
  });

  // V1 Client assets
  router.get(/^\/app\/(.+)/, async ctx => {
    const subPath = ctx.params[0];
    let maxage = TWO_DAYS_IN_MS;

    if (devMode) {
      maxage = 0;
    } else if (fingerPrintRe.test(subPath)) {
      maxage = ONE_YEAR_IN_MS;
    }

    await sendFile(ctx, 'client/dist/', subPath, { maxage });
  });

  return router;
};

async function sendFile(ctx, prefix, filePath, options = {}) {
  const sendOptions = { ...options, root: path.join(conf.root(), prefix) };

  await send(ctx, filePath === '' ? '/' : filePath, sendOptions);
}