Skip to content

Commit

Permalink
add first api tests and new environment
Browse files Browse the repository at this point in the history
  • Loading branch information
Sigura committed Nov 27, 2019
1 parent 2c92629 commit 5fcd007
Show file tree
Hide file tree
Showing 15 changed files with 242 additions and 99 deletions.
13 changes: 13 additions & 0 deletions bin/dev.js
@@ -0,0 +1,13 @@
#!/usr/bin/env node
'use strict';

if (!require('piping')({
hook: true,
ignore: /(\/\.|~$|\.json$)/i,
})) {
return;
}

require('@babel/polyfill/noConflict');
require('@babel/register')();
require('../dev.server');
17 changes: 17 additions & 0 deletions bin/server.js
@@ -0,0 +1,17 @@
#!/usr/bin/env node
'use strict';

const isProduction = process.env.NODE_ENV === 'production';

if (!isProduction) {
if (!require('piping')({
hook: true,
ignore: /(\/\.|~$|\.json$)/i,
})) {
return;
}
}

require('@babel/polyfill/noConflict');
require('@babel/register')();
require('../src/server');
91 changes: 91 additions & 0 deletions dev.server.js
@@ -0,0 +1,91 @@
import path from 'path';
import express from 'express';
import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import httpProxy from 'http-proxy';

import webpackConfig from './webpack.config';

const devPort = process.env.DEV_PORT || 8080;
const port = process.env.DEV_PORT || 9000;
const app = express();
const compiler = webpack(webpackConfig);
const make = (apiAddress) => {
const proxy = apiAddress
? httpProxy.createProxyServer({
target: apiAddress,
}) : httpProxy.createProxyServer();

proxy.on('error', (error, req, res) => {
if (error.code !== 'ECONNRESET') {
console.error('proxy error', error);
}
if (!res.headersSent) {
res.writeHead(500, { 'content-type': 'application/json' });
}

res.status(500).json({ error: 'proxy_error', reason: error.message });
});

return proxy;
};
const register = (app, proxy, path, apiAddress) => {
console.log(`Server ${app.name} will proxy ${path} to ${apiAddress}`);

app.use(path, (req, res) => {
proxy.web(req, res, {
target: apiAddress,
});
});
};
const middleware = [
webpackDevMiddleware(compiler, {
port: devPort,
contentBase: path.join(__dirname, 'src', 'client'),
hot: true,
stats: {
colors: true,
},
compress: true,
}),
webpackHotMiddleware(compiler, {
// eslint-disable-next-line no-console
log: console.log,
heartbeat: 2000,
path: '/__webpack_hmr',
}),
];

app.use(middleware);

[
{ address: `http://localhost:${port}/v1`, path: '/v1' },
{ address: `http://localhost:${port}/v2`, path: '/v2' },
].forEach(cfg => {
const proxy = make(cfg.address);
app.on('stop', () => proxy.close());
register(app, proxy, cfg.path, cfg.address);
});

app.get('*', (req, res, next) => {
const filename = path.join(compiler.outputPath, 'index.html');
compiler.outputFileSystem.readFile(filename, (err, result) => {
if (err) {
return next(err);
}
res.set('content-type', 'text/html');
res.send(result);
res.end();
});
});

app.listen(devPort, () => {
console.log('Developer Server | port: %s', devPort);
});

process
.on('dev server Uncaught Exception', (err) => {
// eslint-disable-next-line no-console
console.error('<!> Exception %s: ', err.message, err.stack);
});
7 changes: 5 additions & 2 deletions jest.config.js
@@ -1,10 +1,10 @@
const { defaults } = require('jest-config');

module.exports = {
// rootDir: 'src/server',
// rootDir: './',
transform: {
...defaults.transform,
'^.+\\.[t|j]sx?$': 'babel-jest',
'^.+\\.[t|j]sx?$': '<rootDir>/jest.transform.js',
},
moduleFileExtensions: [
'js',
Expand All @@ -15,5 +15,8 @@ module.exports = {
defaults.coveragePathIgnorePatterns,
[]
),
setupFiles: [
'<rootDir>/jest.init.js',
],
verbose: true,
};
2 changes: 2 additions & 0 deletions jest.init.js
@@ -0,0 +1,2 @@
require('@babel/polyfill/noConflict');
require('@babel/register')();
3 changes: 3 additions & 0 deletions jest.transform.js
@@ -0,0 +1,3 @@
const babelConfig = require('./babel.config');

module.exports = require('babel-jest').createTransformer(babelConfig);
35 changes: 29 additions & 6 deletions package.json
Expand Up @@ -5,9 +5,11 @@
"main": "index.js",
"scripts": {
"lint": "eslint src/ && flow",
"build": "webpack",
"start": "babel-node server.js",
"server": "babel-node server.js",
"build": "NODE_ENV=production webpack",
"start": "better-npm-run start",
"dev": "better-npm-run dev",
"test": "jest",
"server": "node server.js",
"heroku-postbuild": "rm -rf build/ && webpack -p --progress",
"migrate": "babel-node ./src/server/database/migrate.js"
},
Expand All @@ -17,6 +19,25 @@
"author": "Chris Scott, Transistor Software",
"license": "MIT",
"repository": "https://github.com/transistorsoft/background-geolocation-console",
"betterScripts": {
"dev": {
"command": "concurrently --kill-others \"BABEL_ENV=node node --inspect=9229 ./bin/server.js\" \"BABEL_ENV=webpack node ./bin/dev.js\"",
"env": {
"TZ": "UTC",
"NODE_PATH": "./src/server/",
"DYNO": "1"
}
},
"start": {
"command": "node ./bin/server.js",
"env": {
"TZ": "UTC",
"NODE_PATH": "./src/server/",
"DYNO": "1",
"NODE_ENV": "production"
}
}
},
"dependencies": {
"@babel/cli": "^7.6.0",
"@babel/node": "^7.6.1",
Expand All @@ -41,6 +62,7 @@
"babel-plugin-transform-require-ignore": "0.0.2",
"bluebird": "^3.7.1",
"body-parser": "^1.17.1",
"better-npm-run": "^0.1.1",
"classnames": "^2.2.5",
"colors": "^1.1.2",
"compression": "^1.6.2",
Expand Down Expand Up @@ -74,7 +96,9 @@
"request": "^2.81.0",
"reselect": "^3.0.1",
"rsa-key-gen": "^0.6.1",
"sequelize": "^5.2.7"
"sequelize": "^5.2.7",
"webpack": "^4.39.3",
"webpack-cli": "^3.3.8"
},
"devDependencies": {
"@babel/core": "^7.7.2",
Expand All @@ -83,6 +107,7 @@
"babel-jest": "^24.9.0",
"chai": "^4.2.0",
"chai-http": "^4.3.0",
"concurrently": "^5.0.0",
"copy-webpack-plugin": "^5.0.4",
"css-loader": "^0.26.1",
"ejs-loader": "^0.3.0",
Expand Down Expand Up @@ -118,8 +143,6 @@
"style-loader": "^0.13.1",
"uglifyjs-webpack-plugin": "^2.2.0",
"url-loader": "^2.1.0",
"webpack": "^4.39.3",
"webpack-cli": "^3.3.8",
"webpack-dev-middleware": "^3.7.1",
"webpack-dev-server": "^3.8.0",
"webpack-hot-middleware": "^2.25.0"
Expand Down
8 changes: 7 additions & 1 deletion src/client/constants.js
@@ -1,4 +1,10 @@
export const API_URL = window.location.origin;
const isProduction = process.env.NODE_ENV === 'production';

export const API_URI = isProduction
? ''
: '/v1';

export const API_URL = window.location.origin + API_URI;

// Colors
export const COLORS = {
Expand Down
1 change: 1 addition & 0 deletions src/client/main.js
Expand Up @@ -13,6 +13,7 @@ import WrappedViewport from './components/WrappedViewport';
// view in browser at same url. This is incorrect.
const path = window.location.pathname;
const pathQuery = path.match(/^\/locations\/(.*)$/);

if (pathQuery) {
// Redirect /locations/username -> /username
window.location.pathname = pathQuery[1];
Expand Down
72 changes: 19 additions & 53 deletions server.js → src/server/index.js
@@ -1,30 +1,21 @@
import initializeDatabase from './src/server/database/initializeDatabase';
import initializeDatabase from './database/initializeDatabase';
import express from 'express';
import morgan from 'morgan';
import bodyParser from 'body-parser';
import path from 'path';
import { resolve, extname } from 'path';
import compress from 'compression';
import 'colors';
import opn from 'opn';
import request from 'request';

import obsoleteApi from './src/server/routes/obsolete-api';
import { makeKeys } from './src/server/libs/jwt';
import api from './src/server/routes/api-v2';
import obsoleteApi from './routes/obsolete-api';
import { makeKeys } from './libs/jwt';
import api from './routes/api-v2';

const isProduction = process.env.NODE_ENV === 'production';

// if (!isProduction) {
// if (!require('piping')({
// hook: true,
// ignore: /(\/\.|~$|\.json$)/i,
// })) {
// return;
// }
// }

const app = express();
const port = process.env.PORT || 9000;
const dyno = process.env.DYNO;
const app = express();
const buildPath = resolve(__dirname, '..', '..', 'build');

process
.on('uncaughtException', (err) => {
Expand All @@ -35,7 +26,7 @@ process
process
.on('message', (msg) => {
// eslint-disable-next-line no-console
console.log('Server %s process.on( message = %s )', this.name, msg);
console.log('Server %s process.on( message = %s )', msg);
});

(async function () {
Expand All @@ -50,46 +41,19 @@ process

// default old api
app.use(obsoleteApi);
app.use('/v1', obsoleteApi);
// v2 with jwt auth support
app.use('/v2', api);

if (isProduction) {
app.use(express.static('./build'));
} else {
console.info('adding webpack');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const webpackConfig = require('./webpack.config.babel');
const compiler = webpack(webpackConfig);

const middleware = [
webpackDevMiddleware(compiler, {
publicPath: '/', // Same as `output.publicPath` in most cases.
contentBase: path.join(__dirname, 'src', 'client'),
hot: true,
stats: {
colors: true,
},
}),
webpackHotMiddleware(compiler, {
log: console.log, // eslint-disable-line no-console
heartbeat: 2000,
path: '/__webpack_hmr',
}),
];

app.use(middleware);
app.use(express.static(buildPath));
}

app.use((req, res, next) => {
var ext = path.extname(req.url);
console.info(ext, req.url);
if ((ext === '' || ext === '.html') && req.url !== '/') {
console.info('returning the index.html here');
console.info(req.url);
console.info(`http://localhost:${port}/`);
req.pipe(request(`http://localhost:${port}/`)).pipe(res);
var ext = extname(req.url);
console.info(ext, isProduction, resolve(buildPath, 'index.html'), (ext === '' || ext === '.html') && req.url !== '/', req.url);
if ((!ext || ext === '.html') && req.url !== '/') {
res.sendFile(resolve(buildPath, 'index.html'));
} else {
next();
}
Expand All @@ -99,15 +63,17 @@ process
});
});

app.listen(port, function () {
app.listen(port, () => {
console.log('╔═══════════════════════════════════════════════════════════'.green.bold);
console.log('║ Background Geolocation Server | port: %s'.green.bold, port);
console.log('╚═══════════════════════════════════════════════════════════'.green.bold);

// Spawning dedicated process on opened port.. only if not deployed on heroku
if (!process.env.DYNO) {
if (!dyno) {
opn(`http://localhost:${port}`)
.catch(error => console.log('Optional site open failed:', error));
}
});
})();

module.exports = app;
4 changes: 2 additions & 2 deletions src/server/libs/utils.js
Expand Up @@ -91,8 +91,8 @@ export const checkAuth = (req, res, next) => {
}
};

export const checkCompany = ({ companyToken, model }) => {
if (isDeniedCompany(companyToken)) {
export const checkCompany = ({ org, model }) => {
if (isDeniedCompany(org)) {
throw new AccessDeniedError(
'This is a question from the CEO of Transistor Software.\n' +
'Why are you spamming my demo server1/v2?\n' +
Expand Down

0 comments on commit 5fcd007

Please sign in to comment.