commit
eed5dadc01
26 changed files with 392 additions and 207 deletions
16
.eslintrc
16
.eslintrc
|
@ -1,16 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "eslint-config-dabapps/base/.eslintrc",
|
|
||||||
"env": {
|
|
||||||
"node": true,
|
|
||||||
"browser": false,
|
|
||||||
"mocha": true,
|
|
||||||
"es6": true
|
|
||||||
},
|
|
||||||
"parserOptions": {
|
|
||||||
"sourceType": "module",
|
|
||||||
"ecmaVersion": 6
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"no-console": 0
|
|
||||||
}
|
|
||||||
}
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -31,3 +31,5 @@ node_modules
|
||||||
|
|
||||||
# Optional REPL history
|
# Optional REPL history
|
||||||
.node_repl_history
|
.node_repl_history
|
||||||
|
typings/
|
||||||
|
dist/
|
||||||
|
|
48
README.md
48
README.md
|
@ -2,29 +2,55 @@
|
||||||
The only static-file server you'll ever need!
|
The only static-file server you'll ever need!
|
||||||
|
|
||||||
### Features:
|
### Features:
|
||||||
- Logging
|
- Logging - [`winston`](https://www.npmjs.com/package/winston)
|
||||||
- Basic-Auth _(optional)_
|
- Basic-Auth - [`basic-auth`](https://www.npmjs.com/package/basic-auth)
|
||||||
- Custom 404 page
|
- Custom 404 page
|
||||||
- Optimum Compression - [`compression`](https://www.npmjs.com/package/compression)
|
- Optimum Compression - [`compression`](https://www.npmjs.com/package/compression)
|
||||||
- Security checks / headers - [`helmet`](https://www.npmjs.com/package/helmet)
|
- Security checks / headers - [`helmet`](https://www.npmjs.com/package/helmet)
|
||||||
- Opbeat error-reporting - [docs](https://opbeat.com/docs/articles/get-started-with-express/)
|
- Opbeat error-reporting - [docs](https://opbeat.com/docs/articles/get-started-with-express/)
|
||||||
|
- Whitelist IP Addresses - [`express-ip-access-control`](https://www.npmjs.com/package/express-ip-access-control)
|
||||||
|
- Directory Listing - [`serve-index`](https://www.npmjs.com/package/serve-index)
|
||||||
|
|
||||||
### Usage / Configuration
|
### Usage
|
||||||
```bash
|
```bash
|
||||||
tstatic <directory>
|
tstatic <dir> [options]
|
||||||
|
|
||||||
|
-h --help Show this screen.
|
||||||
|
--version Show version.
|
||||||
|
-p <port> --port=<port> Port to listen on.
|
||||||
|
-b <auth> --basic-auth=<auth> Enable basic-auth.
|
||||||
|
-i <ips> --ips=<ips> Allowed IP addresses.
|
||||||
|
-l --list-dir List Directory.
|
||||||
|
--opbeat Enable Opbeat.
|
||||||
|
-o --open Open in browser after start.
|
||||||
|
|
||||||
```
|
```
|
||||||
`directory` is where your static files are.
|
`dir` is where your static files are.
|
||||||
|
|
||||||
404 errors will return with `<directory>/.404.html`, with status code 404. If this file doesnt exist, plain error page will be shown.
|
404 errors will return with `<dir>/.404.html`, with status code 404. If this file doesnt exist, the default error page will be shown.
|
||||||
|
|
||||||
|
|
||||||
#### Environment
|
### Configuration
|
||||||
Make sure to set `NODE_ENV` to `production`!
|
|
||||||
|
|
||||||
`PORT`: The port you want the server to listen on. Default: `5000`.
|
##### `port`
|
||||||
|
The port for the server to listen on. Currently supports plain HTTP only
|
||||||
|
|
||||||
`BASIC_AUTH_USERNAME` / `BASIC_AUTH_PASSWORD`: Credentials for built-in basic auth
|
##### `basic-auth`
|
||||||
|
Enable basic-auth for all paths. Currently only supports single credentals.
|
||||||
|
|
||||||
Opbeat middleware is configured using documented variables [here](https://opbeat.com/docs/articles/opbeat-for-nodejs-api/#appid). _Requires production `NODE_ENV`!_
|
Format:`-b username:password`
|
||||||
|
|
||||||
|
##### `ips`
|
||||||
|
IP addresses that are allowed to connect to the server.
|
||||||
|
|
||||||
|
Format: `-i 192.168.1.100,192.168.1.101`
|
||||||
|
|
||||||
|
##### `list-dir`
|
||||||
|
Enables directory listing. Allow browseing
|
||||||
|
|
||||||
|
##### `opbeat`
|
||||||
|
Enable opbeat error reporting. `--opbeat` only enables this, configuration is done using [environment varables](https://opbeat.com/docs/articles/get-started-with-express/#appId).
|
||||||
|
|
||||||
|
##### `open`
|
||||||
|
Open the server in the browser one started. It will open in your default browser, and use url `http://0.0.0.0:<port>`.
|
||||||
|
|
||||||
|
|
38
package.json
38
package.json
|
@ -2,15 +2,17 @@
|
||||||
"name": "tstatic",
|
"name": "tstatic",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Container to host simple static applications using a node server, so files can be deployed using rsync",
|
"description": "Container to host simple static applications using a node server, so files can be deployed using rsync",
|
||||||
"main": "./src/server.js",
|
"main": "node ./dist/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tstatic": "./src/server.js"
|
"tstatic": "node ./dist/index.js"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "npm run mocha && npm run lint && nsp check",
|
"start": "node ./dist/index.js",
|
||||||
"lint": "eslint src/ tests/",
|
"postinstall": "typings install",
|
||||||
"start": "./src/server.js site/",
|
"build": "tsc",
|
||||||
"mocha": "NODE_ENV=test mocha tests/**.test.js"
|
"test": "npm run build && npm run mocha && nsp check",
|
||||||
|
"mocha": "mocha --compilers ts:ts-node/register --require scripts/test-helper.js tests/**.test.ts",
|
||||||
|
"lint": "tslint src/**/*.ts"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "6.9.4"
|
"node": "6.9.4"
|
||||||
|
@ -25,22 +27,30 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/RealOrangeOne/tstatic#readme",
|
"homepage": "https://github.com/RealOrangeOne/tstatic#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"basic-auth": "^1.1.0",
|
"basic-auth": "=1.1.0",
|
||||||
"compression": "=1.6.2",
|
"compression": "=1.6.2",
|
||||||
"connect-static-file": "=1.1.2",
|
"connect-static-file": "=1.1.2",
|
||||||
"express": "=4.14.0",
|
"docopt": "=0.6.2",
|
||||||
"express-basic-auth": "=0.2.3",
|
"express": "=4.14.1",
|
||||||
|
"express-basic-auth": "=0.3.2",
|
||||||
"express-ip-access-control": "=1.0.5",
|
"express-ip-access-control": "=1.0.5",
|
||||||
"express-winston": "=2.1.2",
|
"express-winston": "=2.2.0",
|
||||||
"helmet": "=3.4.0",
|
"helmet": "=3.4.0",
|
||||||
"opbeat": "=4.7.0",
|
"opbeat": "=4.11.0",
|
||||||
|
"open": "=0.0.5",
|
||||||
"serve-index": "=1.8.0",
|
"serve-index": "=1.8.0",
|
||||||
"winston": "=2.3.0"
|
"winston": "=2.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint-config": "dabapps/eslint-config#2.0.5",
|
"chai": "=3.5.0",
|
||||||
|
"chai-as-promised": "=6.0.0",
|
||||||
"mocha": "=3.2.0",
|
"mocha": "=3.2.0",
|
||||||
|
"node-fetch": "=1.6.3",
|
||||||
"nsp": "=2.6.2",
|
"nsp": "=2.6.2",
|
||||||
"supertest": "=2.0.1"
|
"supertest": "=3.0.0",
|
||||||
|
"ts-node": "=2.1.0",
|
||||||
|
"tslint": "=4.4.2",
|
||||||
|
"typescript": "=2.1.6",
|
||||||
|
"typings": "=2.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
7
scripts/test-helper.js
Normal file
7
scripts/test-helper.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
const chai = require('chai');
|
||||||
|
const chaiAsPromised = require('chai-as-promised');
|
||||||
|
|
||||||
|
chai.expect();
|
||||||
|
chai.use(chaiAsPromised);
|
||||||
|
|
||||||
|
process.env.NODE_ENV = 'test';
|
|
@ -1,9 +0,0 @@
|
||||||
const staticFile = require('connect-static-file');
|
|
||||||
const path = require('path');
|
|
||||||
const { SERVE_DIR } = require('./consts');
|
|
||||||
|
|
||||||
const handle404 = staticFile(path.join(SERVE_DIR, '.404.html'));
|
|
||||||
module.exports = function (request, response, next) {
|
|
||||||
response.statusCode = 404;
|
|
||||||
return handle404(request, response, next);
|
|
||||||
};
|
|
|
@ -1,17 +0,0 @@
|
||||||
const basicAuth = require('express-basic-auth');
|
|
||||||
const { BASIC_AUTH_ENABLED } = require('./consts');
|
|
||||||
|
|
||||||
function basicAuthHandler(username, password) {
|
|
||||||
return process.env.BASIC_AUTH_USERNAME === username && process.env.BASIC_AUTH_PASSWORD === password;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BASIC_AUTH_ENABLED) {
|
|
||||||
module.exports = basicAuth({
|
|
||||||
authorizer: basicAuthHandler,
|
|
||||||
challenge: true
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
module.exports = (req, res, next) => next();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
40
src/cli.ts
Normal file
40
src/cli.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { docopt } from 'docopt';
|
||||||
|
import { Options } from './types';
|
||||||
|
|
||||||
|
const PKG = require('../package.json');
|
||||||
|
|
||||||
|
const ARG_DATA = `
|
||||||
|
${PKG.name}.
|
||||||
|
${PKG.description}
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
tstatic <dir> [options]
|
||||||
|
tstatic -h | --help
|
||||||
|
tstatic --version
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h --help Show this screen.
|
||||||
|
--version Show version.
|
||||||
|
-p <port> --port=<port> Port to listen on.
|
||||||
|
-b <auth> --basic-auth=<auth> Enable basic-auth.
|
||||||
|
-i <ips> --ips=<ips> Allowed IP addresses.
|
||||||
|
-l --list-dir List Directory.
|
||||||
|
--opbeat Enable Opbeat.
|
||||||
|
-o --open Open in browser after start.
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default function getArgs() : Options {
|
||||||
|
const rawArgs = docopt(ARG_DATA, {
|
||||||
|
version: PKG.version,
|
||||||
|
help: true
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
port: rawArgs['--port'] || process.env.PORT || 5000,
|
||||||
|
allowed_ips: rawArgs['--ips'] ? rawArgs['--ips'].split(',') : [],
|
||||||
|
basicAuth: rawArgs['--basic-auth'] ? rawArgs['--basic-auth'].split(':') : [],
|
||||||
|
dirList: rawArgs['--list-dir'],
|
||||||
|
serveDir: rawArgs['<dir>'],
|
||||||
|
opbeat: rawArgs['--opbeat'],
|
||||||
|
open: rawArgs['--open']
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,11 +0,0 @@
|
||||||
const IN_TEST = process.env.NODE_ENV === 'test';
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
SERVE_DIR: IN_TEST ? 'site/' : process.argv[process.argv.length - 1],
|
|
||||||
PORT: process.env.PORT || 5000,
|
|
||||||
ALLOWED_IPS: process.env.ALLOWED_IPS ? process.env.ALLOWED_IPS.split(',') : undefined,
|
|
||||||
IN_TEST,
|
|
||||||
IN_PRODUCTION: process.env.NODE_ENV === 'production',
|
|
||||||
DIR_LIST: process.env.DIR_LIST,
|
|
||||||
BASIC_AUTH_ENABLED: process.env.BASIC_AUTH_USERNAME && process.env.BASIC_AUTH_PASSWORD
|
|
||||||
};
|
|
17
src/index.ts
Normal file
17
src/index.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { docopt } from 'docopt';
|
||||||
|
import createServer from './server';
|
||||||
|
import getArgs from './cli';
|
||||||
|
import * as open from 'open';
|
||||||
|
|
||||||
|
console.log("Starting Server...");
|
||||||
|
|
||||||
|
const ARGS = getArgs();
|
||||||
|
const app = createServer(ARGS);
|
||||||
|
|
||||||
|
export const server = app.listen(ARGS.port, function () {
|
||||||
|
const port = server.address().port;
|
||||||
|
console.log("Server started on port " + port);
|
||||||
|
if (ARGS.open) {
|
||||||
|
open('http://0.0.0.0:' + port);
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,17 +0,0 @@
|
||||||
const winston = require('winston');
|
|
||||||
const expressWinston = require('express-winston');
|
|
||||||
|
|
||||||
module.exports = expressWinston.logger({
|
|
||||||
transports: [
|
|
||||||
new winston.transports.Console({
|
|
||||||
colorize: true
|
|
||||||
})
|
|
||||||
],
|
|
||||||
meta: false,
|
|
||||||
msg: '{{ req.url }} '
|
|
||||||
.concat('status:{{ res.statusCode }} ')
|
|
||||||
.concat('useragent:{{ req.headers["user-agent"] }} ')
|
|
||||||
.concat('time:{{ res.responseTime }}ms'),
|
|
||||||
colorize: true,
|
|
||||||
statusLevels: true
|
|
||||||
});
|
|
11
src/middleware/404.ts
Normal file
11
src/middleware/404.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { Request, Response } from 'express';
|
||||||
|
import * as staticFile from 'connect-static-file'
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
export default function handle404(serveDir : string) {
|
||||||
|
const handle404Middleware = staticFile(path.join(serveDir, '.404.html'));
|
||||||
|
return function (request : Request, response : Response, next : Function) {
|
||||||
|
response.statusCode = 404;
|
||||||
|
return handle404Middleware(request, response, next);
|
||||||
|
}
|
||||||
|
}
|
8
src/middleware/basic-auth.ts
Normal file
8
src/middleware/basic-auth.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import * as basicAuth from 'express-basic-auth';
|
||||||
|
|
||||||
|
export default function basicAuthHandler(username : string, password : string) {
|
||||||
|
return basicAuth({
|
||||||
|
authorizer: (req_username : string, req_password : string) => req_username === username && req_password === password,
|
||||||
|
challenge: true
|
||||||
|
});
|
||||||
|
}
|
17
src/middleware/logging.ts
Normal file
17
src/middleware/logging.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import * as winston from 'winston';
|
||||||
|
import * as expressWinston from 'express-winston';
|
||||||
|
|
||||||
|
export default expressWinston.logger({
|
||||||
|
transports: [
|
||||||
|
new winston.transports.Console({
|
||||||
|
colorize: true
|
||||||
|
})
|
||||||
|
],
|
||||||
|
meta: false,
|
||||||
|
msg: '{{ req.url }} '
|
||||||
|
.concat('status:{{ res.statusCode }} ')
|
||||||
|
.concat('useragent:{{ req.headers["user-agent"] }} ')
|
||||||
|
.concat('time:{{ res.responseTime }}ms'),
|
||||||
|
colorize: true,
|
||||||
|
statusLevels: true
|
||||||
|
});
|
28
src/middleware/static-files.ts
Normal file
28
src/middleware/static-files.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import * as express from 'express';
|
||||||
|
import * as serveIndex from 'serve-index';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
function isDirectory(url : string) : boolean {
|
||||||
|
return /\/$/.test(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function indexHandle(request : express.Request, response : express.Response, next : Function) {
|
||||||
|
if (isDirectory(request.url)) {
|
||||||
|
request.url = path.join(request.url, 'index.html');
|
||||||
|
}
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function staticFileHandle(serveDir : string) {
|
||||||
|
return express.static(serveDir, {
|
||||||
|
dotfiles: 'ignore',
|
||||||
|
index: false,
|
||||||
|
redirect: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serveIndexHandle(serveDir : string) {
|
||||||
|
return serveIndex(serveDir, {
|
||||||
|
icons: true
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,54 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
console.log('Starting Server...');
|
|
||||||
|
|
||||||
const app = require('express')();
|
|
||||||
const consts = require('./consts');
|
|
||||||
|
|
||||||
const compression = require('compression');
|
|
||||||
const helmet = require('helmet');
|
|
||||||
const serveIndex = require('serve-index');
|
|
||||||
const AccessControl = require('express-ip-access-control');
|
|
||||||
const opbeat = require('opbeat').start({
|
|
||||||
active: consts.IN_PRODUCTION
|
|
||||||
});
|
|
||||||
|
|
||||||
const logging = require('./logging');
|
|
||||||
const staticFiles = require('./static-files');
|
|
||||||
const handle404 = require('./404');
|
|
||||||
const basicAuth = require('./basic-auth');
|
|
||||||
|
|
||||||
if (consts.ALLOWED_IPS) {
|
|
||||||
app.set('trust proxy', true);
|
|
||||||
app.use(AccessControl({
|
|
||||||
mode: 'allow',
|
|
||||||
allows: consts.ALLOWED_IPS,
|
|
||||||
statusCode: 404
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Custom Middleware
|
|
||||||
app.use(logging);
|
|
||||||
app.use(basicAuth);
|
|
||||||
|
|
||||||
if (consts.DIR_LIST) {
|
|
||||||
app.use(serveIndex(consts.SERVE_DIR, {
|
|
||||||
icons: true
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
app.use(staticFiles.indexHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
app.use(staticFiles.static);
|
|
||||||
app.use(handle404);
|
|
||||||
|
|
||||||
// Library
|
|
||||||
app.use(compression({ level: 9 }));
|
|
||||||
app.use(helmet());
|
|
||||||
app.use(opbeat.middleware.express());
|
|
||||||
|
|
||||||
const server = app.listen(consts.PORT, function () {
|
|
||||||
console.log('Server started on ' + server.address().port);
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = server;
|
|
53
src/server.ts
Normal file
53
src/server.ts
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import * as express from 'express';
|
||||||
|
|
||||||
|
import * as AccessControl from 'express-ip-access-control';
|
||||||
|
import * as compression from 'compression';
|
||||||
|
import * as helmet from 'helmet';
|
||||||
|
import * as opbeat from 'opbeat';
|
||||||
|
|
||||||
|
import logging from './middleware/logging';
|
||||||
|
import basicAuthHandler from './middleware/basic-auth';
|
||||||
|
import { serveIndexHandle, indexHandle, staticFileHandle } from './middleware/static-files';
|
||||||
|
import handle404 from './middleware/404';
|
||||||
|
|
||||||
|
import { Options } from './types';
|
||||||
|
|
||||||
|
export default function createServer(opts : Options) : express.Application {
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'test') {
|
||||||
|
app.use(logging);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.allowed_ips.length) {
|
||||||
|
app.set('trust proxy', true);
|
||||||
|
app.use(AccessControl({
|
||||||
|
mode: 'allow',
|
||||||
|
allows: opts.allowed_ips,
|
||||||
|
statusCode: 404
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.basicAuth.length) {
|
||||||
|
app.use(basicAuthHandler(opts.basicAuth[0], opts.basicAuth[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.dirList) {
|
||||||
|
app.use(serveIndexHandle(opts.serveDir));
|
||||||
|
} else {
|
||||||
|
app.use(indexHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
app.use(staticFileHandle(opts.serveDir));
|
||||||
|
app.use(handle404(opts.serveDir));
|
||||||
|
|
||||||
|
app.use(compression({ level: 9 }));
|
||||||
|
app.use(helmet());
|
||||||
|
if (opts.opbeat) {
|
||||||
|
app.use(opbeat.start({
|
||||||
|
active: opts.opbeat
|
||||||
|
}).middleware.express());
|
||||||
|
}
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
|
@ -1,16 +0,0 @@
|
||||||
const express = require('express');
|
|
||||||
const path = require('path');
|
|
||||||
const { SERVE_DIR } = require('./consts');
|
|
||||||
|
|
||||||
module.exports.indexHandle = function (request, response, next) {
|
|
||||||
if (request.url.endsWith('/')) {
|
|
||||||
request.url = path.join(request.url, 'index.html');
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.static = express.static(SERVE_DIR, {
|
|
||||||
dotfiles: 'ignore',
|
|
||||||
index: false,
|
|
||||||
redirect: true
|
|
||||||
});
|
|
12
src/types/fakes.d.ts
vendored
Normal file
12
src/types/fakes.d.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/* Mock types that dont exist */
|
||||||
|
|
||||||
|
declare module 'express-ip-access-control';
|
||||||
|
declare module 'connect-static-file';
|
||||||
|
declare module 'express-basic-auth';
|
||||||
|
declare module 'winston'; // doesnt like console transport
|
||||||
|
declare module 'express-winston';
|
||||||
|
declare module 'opbeat';
|
||||||
|
declare module 'docopt';
|
||||||
|
declare module 'open';
|
||||||
|
declare module 'node-fetch';
|
||||||
|
declare module 'chai';
|
10
src/types/index.ts
Normal file
10
src/types/index.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
export interface Options {
|
||||||
|
port: number;
|
||||||
|
allowed_ips: string[];
|
||||||
|
basicAuth: string[];
|
||||||
|
dirList: boolean;
|
||||||
|
serveDir: string;
|
||||||
|
opbeat: boolean;
|
||||||
|
open: boolean;
|
||||||
|
}
|
14
tests/helpers.ts
Normal file
14
tests/helpers.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import createServer from '../src/server';
|
||||||
|
import { Options } from '../src/types';
|
||||||
|
import fetch from 'node-fetch';
|
||||||
|
|
||||||
|
|
||||||
|
export function runServer(opts: Object, url : string, callback: Function) {
|
||||||
|
const app = createServer(opts as Options);
|
||||||
|
const server = app.listen(1234, function () {
|
||||||
|
return fetch('http://0.0.0.0:1234' + url).then(function (response : any) {
|
||||||
|
server.close();
|
||||||
|
callback(response);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,42 +0,0 @@
|
||||||
const request = require('supertest');
|
|
||||||
const fs = require('fs');
|
|
||||||
|
|
||||||
|
|
||||||
describe('Server', function () {
|
|
||||||
var server;
|
|
||||||
before(function () {
|
|
||||||
server = require('../src/server');
|
|
||||||
});
|
|
||||||
|
|
||||||
after(function () {
|
|
||||||
server.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('responds to /', function (done) {
|
|
||||||
request(server)
|
|
||||||
.get('/')
|
|
||||||
.expect(200, done);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns 404 on bad path', function (done) {
|
|
||||||
request(server)
|
|
||||||
.get('/foo/bar')
|
|
||||||
.expect(404, done);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('index route', function () {
|
|
||||||
const body = fs.readFileSync(__dirname + '/../site/index.html').toString();
|
|
||||||
|
|
||||||
it('should render /index.html', function (done) {
|
|
||||||
request(server)
|
|
||||||
.get('/index.html')
|
|
||||||
.expect(200, body, done);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render /', function (done) {
|
|
||||||
request(server)
|
|
||||||
.get('/')
|
|
||||||
.expect(200, body, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
61
tests/server.test.ts
Normal file
61
tests/server.test.ts
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import { runServer } from './helpers';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
|
||||||
|
describe('Server', function () {
|
||||||
|
it('should test', function () {
|
||||||
|
expect(2 + 2).to.equal(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be usable', function (done) {
|
||||||
|
runServer({
|
||||||
|
allowed_ips: [],
|
||||||
|
basicAuth: [],
|
||||||
|
dirList: false,
|
||||||
|
serveDir: 'site/',
|
||||||
|
opbeat: false,
|
||||||
|
open: false
|
||||||
|
}, '/index.html', function (response : any) {
|
||||||
|
expect(response.status).to.equal(200);
|
||||||
|
expect(response.url).to.include('/index.html');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should respond with 404 on bad path', function (done) {
|
||||||
|
runServer({
|
||||||
|
allowed_ips: [],
|
||||||
|
basicAuth: [],
|
||||||
|
dirList: false,
|
||||||
|
serveDir: 'site/',
|
||||||
|
opbeat: false,
|
||||||
|
open: false
|
||||||
|
}, '/foo/bar', function (response : any) {
|
||||||
|
expect(response.ok).to.be.false;
|
||||||
|
expect(response.status).to.equal(404);
|
||||||
|
expect(response.text()).to.eventually.include('Cannot GET').notify(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('index route', function () {
|
||||||
|
const body = fs.readFileSync(path.join(__dirname, '..', 'site', 'index.html')).toString();
|
||||||
|
|
||||||
|
['', '/', '/index.html'].forEach(function (path : string) {
|
||||||
|
it('should render ' + path, function (done) {
|
||||||
|
runServer({
|
||||||
|
allowed_ips: [],
|
||||||
|
basicAuth: [],
|
||||||
|
dirList: false,
|
||||||
|
serveDir: 'site/',
|
||||||
|
opbeat: false,
|
||||||
|
open: false
|
||||||
|
}, path, function (response : any) {
|
||||||
|
expect(response.status).to.equal(200);
|
||||||
|
expect(response.text()).to.eventually.equal(body).notify(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
28
tsconfig.json
Normal file
28
tsconfig.json
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"preserveConstEnums": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"pretty": true
|
||||||
|
},
|
||||||
|
"filesGlob": [
|
||||||
|
"typings/index.d.ts",
|
||||||
|
"src/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"dist",
|
||||||
|
"node_modules",
|
||||||
|
"scripts/",
|
||||||
|
"tests/"
|
||||||
|
],
|
||||||
|
"typeRoots": [
|
||||||
|
"node_modules",
|
||||||
|
"typings",
|
||||||
|
"src/types"
|
||||||
|
],
|
||||||
|
"compileOnSave": false
|
||||||
|
}
|
7
tslint.json
Normal file
7
tslint.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"extends": "tslint:recommended",
|
||||||
|
"rules": {
|
||||||
|
"quotemark": [true, "single"],
|
||||||
|
"trailing-comma": [false]
|
||||||
|
}
|
||||||
|
}
|
16
typings.json
Normal file
16
typings.json
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"compression": "registry:dt/compression#0.0.0+20160725212620",
|
||||||
|
"debug": "registry:npm/debug#2.0.0+20160723033700",
|
||||||
|
"express": "registry:npm/express#4.14.0+20160925001530",
|
||||||
|
"helmet": "registry:dt/helmet#0.0.0+20161005184000",
|
||||||
|
"serve-index": "registry:dt/serve-index#1.7.2+20160428043022"
|
||||||
|
},
|
||||||
|
"globalDependencies": {
|
||||||
|
"node": "registry:dt/node#7.0.0+20170204020307"
|
||||||
|
},
|
||||||
|
"globalDevDependencies": {
|
||||||
|
"chai": "registry:dt/chai#3.4.0+20170217154556",
|
||||||
|
"mocha": "registry:dt/mocha#2.2.5+20170204022515"
|
||||||
|
}
|
||||||
|
}
|
Reference in a new issue