From cdc46845c50cd4dd0bbbecb3ef5d24d832aea3b0 Mon Sep 17 00:00:00 2001 From: Ulf Seltmann <seltmann@ub.uni-leipzig.de> Date: Thu, 24 Aug 2017 17:07:35 +0200 Subject: [PATCH] * added executable * added test-structure * added documentation --- .gitignore | 7 +- .npmignore | 4 +- Readme.md | 48 ++++++- bin/dacap | 23 ++++ docs/index.md | 64 +++++++-- package-lock.json | 334 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 18 ++- src/index.ts | 105 --------------- src/server.ts | 108 +++++++++++++++ test/cache.ts | 16 +++ 10 files changed, 596 insertions(+), 131 deletions(-) create mode 100755 bin/dacap delete mode 100644 src/index.ts create mode 100644 src/server.ts diff --git a/.gitignore b/.gitignore index e09321a..39e650f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /node_modules -/dist/* -/public/* -/data/* +/dist +/public +/data +/site \ No newline at end of file diff --git a/.npmignore b/.npmignore index e1b5565..54fbaab 100644 --- a/.npmignore +++ b/.npmignore @@ -6,4 +6,6 @@ /mkdocs.yml /tsconfig.json /webpack.*.js -/src \ No newline at end of file +/src +/site +/.tmp \ No newline at end of file diff --git a/Readme.md b/Readme.md index 52900b7..1da33b5 100644 --- a/Readme.md +++ b/Readme.md @@ -2,18 +2,56 @@ *Dynamic And Compressing Api Proxy* is a service which lets you proxy arbitrary API-Endpoints which are by themself to slow to be used in production environments. All cached Responses are held in memory, so be sure to provide enough. -## requirements +## Requirements * node >= v8.2.1 * npm >= v5.3.0 -## file a bug +## Install the service +```bash +$# npm install -g dacap +``` +## Start the service +``` bash +$# dacap +``` -# register new endpoint +The Admin Interface is now available under http://localhost:3000/admin -New Endpoints are registered using the admin interface +You can customize multiple values, see [#Advanced Configuration] +## Usage + +First of all one has to define a *Cache* where the requests to an API-Endpoint are stored. This is done from within the Admin Interface. + +One has to define the URL to the desired *API-Endpoint* (e.g. `http://foo.example.com/`) and a *Cache-Name* for this API-Endpoint (e.g. `foo`). All API-Requests which normally would go to + + http://foo.example.com/?query=foo&offset=0&limit=10 + +now you can request by accessing + + http://localhost:3000/ep/foo/?query=foo&offset=0&limit=10 + +* *Be sure to append the Slash after the Cache-Name, otherwise the Endpoint cannot be resolved.* +* *Everything after the Cache-Name and Slash will be passed to the API-Endpoint.* + +## Advanced Configuration + +The service can be configured by environment variables. The following are available: + +* `data_dir`: specifies the folder where the cache is stored in intervals. This is only used to + make it more easy to restart the service without adding all Endpoints again. After restart the + service reads the saved cache-file and keeps it in memory. + By default this points to the `data`-folder where the process was invoked. Make sure it can be created if not existing and written if already existing. +* `proxy_path`: this is the path where the Endpoints will be accessable after registering. By default this is `/ep/` +* `proxy_port`: this is the port number where the service is listening. Defaults to `3000` +* `proxy_url`: this is the absolute url of the service with protocol and port if differing from the defaults (e.g. `http://api.example.com:8080`). Set this if you use a different domain name than `localhost` to connect to this service. +* `cache_ttl`: the time-to-live in seconds of a cached request. after this time the cache will be refreshed. Defaults to `600` +* `default_check_period`: specifies the interval in seconds the cache is checked for expiry. Defaults to `60` +* `array_value_size`: unkown configuration. Defaults to `40` +* `object_value_size`: unkown configuration. Defaults to `80` +* `autosave_interval`: specifies the interval the cache is stored to harddisk. defaults to `60`; +* `register_name`: specifies the name of the register where the cache is stored. Defaults to `api-cache`; -/admin/ \ No newline at end of file diff --git a/bin/dacap b/bin/dacap new file mode 100755 index 0000000..3d31f4d --- /dev/null +++ b/bin/dacap @@ -0,0 +1,23 @@ +#!/usr/bin/env node + +const debug = require('debug')('dacap'); +const pjson = require('../package.json'); +const path = require('path'); +const Server = require('../dist/server').Server; + +const storagePath = process.env.data_dir || path.resolve(process.cwd(), 'data'); +const proxyPath = process.env.proxy_path || '/ep/'; +const proxyPort = parseInt(process.env.proxy_port) || 3000; +const proxyUrl = process.env.proxy_url || `http://localhost:${proxyPort}`; +const defaultTtl = parseInt(process.env.cache_ttl) || 600; +const defaultCheckPeriod = parseInt(process.env.default_check_period) || 60; +const defaultArrayValueSize = parseInt(process.env.array_value_size) || 40; +const defaultObjectValueSize = parseInt(process.env.object_value_size) || 80; +const autosaveInterval = parseInt(process.env.autosave_interval) || 60; +const registerName = process.env.register_name || 'api-cache'; + +const server = new Server(storagePath, proxyPath, proxyUrl, defaultTtl, defaultCheckPeriod, defaultArrayValueSize, defaultObjectValueSize, autosaveInterval, registerName); + +server.listen(proxyPort, () => { + debug('server up and running'); +}); \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index da37213..1da33b5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,17 +1,57 @@ -# Welcome to MkDocs +# dacap -For full documentation visit [mkdocs.org](http://mkdocs.org). +*Dynamic And Compressing Api Proxy* is a service which lets you proxy arbitrary API-Endpoints which are by themself to slow to be used in production environments. All cached Responses are held in memory, so be sure to provide enough. -## Commands +## Requirements -* `mkdocs new [dir-name]` - Create a new project. -* `mkdocs serve` - Start the live-reloading docs server. -* `mkdocs build` - Build the documentation site. -* `mkdocs help` - Print this help message. +* node >= v8.2.1 +* npm >= v5.3.0 -## Project layout +## Install the service + +```bash +$# npm install -g dacap +``` + +## Start the service +``` bash +$# dacap +``` + +The Admin Interface is now available under http://localhost:3000/admin + +You can customize multiple values, see [#Advanced Configuration] + +## Usage + +First of all one has to define a *Cache* where the requests to an API-Endpoint are stored. This is done from within the Admin Interface. + +One has to define the URL to the desired *API-Endpoint* (e.g. `http://foo.example.com/`) and a *Cache-Name* for this API-Endpoint (e.g. `foo`). All API-Requests which normally would go to + + http://foo.example.com/?query=foo&offset=0&limit=10 + +now you can request by accessing + + http://localhost:3000/ep/foo/?query=foo&offset=0&limit=10 + +* *Be sure to append the Slash after the Cache-Name, otherwise the Endpoint cannot be resolved.* +* *Everything after the Cache-Name and Slash will be passed to the API-Endpoint.* + +## Advanced Configuration + +The service can be configured by environment variables. The following are available: + +* `data_dir`: specifies the folder where the cache is stored in intervals. This is only used to + make it more easy to restart the service without adding all Endpoints again. After restart the + service reads the saved cache-file and keeps it in memory. + By default this points to the `data`-folder where the process was invoked. Make sure it can be created if not existing and written if already existing. +* `proxy_path`: this is the path where the Endpoints will be accessable after registering. By default this is `/ep/` +* `proxy_port`: this is the port number where the service is listening. Defaults to `3000` +* `proxy_url`: this is the absolute url of the service with protocol and port if differing from the defaults (e.g. `http://api.example.com:8080`). Set this if you use a different domain name than `localhost` to connect to this service. +* `cache_ttl`: the time-to-live in seconds of a cached request. after this time the cache will be refreshed. Defaults to `600` +* `default_check_period`: specifies the interval in seconds the cache is checked for expiry. Defaults to `60` +* `array_value_size`: unkown configuration. Defaults to `40` +* `object_value_size`: unkown configuration. Defaults to `80` +* `autosave_interval`: specifies the interval the cache is stored to harddisk. defaults to `60`; +* `register_name`: specifies the name of the register where the cache is stored. Defaults to `api-cache`; - mkdocs.yml # The configuration file. - docs/ - index.md # The documentation homepage. - ... # Other markdown pages, images and other files. diff --git a/package-lock.json b/package-lock.json index 161dda4..61e2dda 100644 --- a/package-lock.json +++ b/package-lock.json @@ -127,6 +127,12 @@ "integrity": "sha512-rek8twk9C58gHYqIrUlJsx8NQMhlxqHzln9Z9ODqiNgv3/s+ZwIrfr+djqzsnVM12xe9hL98iJ20lj2RvCBv6A==", "dev": true }, + "@types/mocha": { + "version": "2.2.42", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.42.tgz", + "integrity": "sha512-b6gVDoxEbAQGwbV7gSzeFw/hy3/eEAokztktdzl4bHvGgb9K5zW4mVQDlVYch2w31m8t/J7L2iqhQvz3r5edCQ==", + "dev": true + }, "@types/node": { "version": "8.0.20", "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.20.tgz", @@ -177,6 +183,12 @@ "@types/mime": "1.3.1" } }, + "@types/should": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@types/should/-/should-11.2.0.tgz", + "integrity": "sha512-+J77XoXmKIXcLK5fWS5B3j31F4wfdclzk+lRxFcKfXTHzZfd153u8w96W30dQBIT4kwKobjvYa0kIb0BWJX21Q==", + "dev": true + }, "accepts": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", @@ -308,6 +320,12 @@ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, "anymatch": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", @@ -898,6 +916,12 @@ "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", "dev": true }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "dev": true + }, "browserify-aes": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.0.6.tgz", @@ -1791,6 +1815,12 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "diff": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", + "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", + "dev": true + }, "diffie-hellman": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", @@ -2664,6 +2694,18 @@ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + }, + "growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", + "dev": true + }, "har-schema": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", @@ -3366,6 +3408,12 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -3476,6 +3524,12 @@ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, "json5": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", @@ -3581,12 +3635,80 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" }, + "lodash._baseassign": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", + "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", + "dev": true, + "requires": { + "lodash._basecopy": "3.0.1", + "lodash.keys": "3.1.2" + } + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "dev": true + }, + "lodash._basecreate": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", + "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, "lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", "dev": true }, + "lodash.create": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", + "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", + "dev": true, + "requires": { + "lodash._baseassign": "3.2.0", + "lodash._basecreate": "3.0.3", + "lodash._isiterateecall": "3.0.9" + } + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" + } + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -3635,6 +3757,12 @@ "integrity": "sha1-WQTcU3w57G2+/q6QIycTX6hRHxI=", "dev": true }, + "make-error": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.0.tgz", + "integrity": "sha1-Uq06M5zPEM5itAQLcI/nByRLi5Y=", + "dev": true + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -3809,6 +3937,74 @@ "minimist": "0.0.8" } }, + "mocha": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.0.tgz", + "integrity": "sha512-pIU2PJjrPYvYRqVpjXzj76qltO9uBYI7woYAMoxbSefsa+vqAfptjoeevd6bUgwD0mPIO+hv9f7ltvsNreL2PA==", + "dev": true, + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.9.0", + "debug": "2.6.8", + "diff": "3.2.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.1", + "growl": "1.9.2", + "json3": "3.3.2", + "lodash.create": "3.1.1", + "mkdirp": "0.5.1", + "supports-color": "3.1.2" + }, + "dependencies": { + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true, + "requires": { + "graceful-readlink": "1.0.1" + } + }, + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "supports-color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", + "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -5615,6 +5811,60 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "should": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/should/-/should-11.2.1.tgz", + "integrity": "sha1-kPVRRVUtAc/CAGZuToGKHJZw7aI=", + "dev": true, + "requires": { + "should-equal": "1.0.1", + "should-format": "3.0.3", + "should-type": "1.4.0", + "should-type-adaptors": "1.0.1", + "should-util": "1.0.0" + } + }, + "should-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-1.0.1.tgz", + "integrity": "sha1-C26VFvJgGp+wuy3MNpr6HH4gCvc=", + "dev": true, + "requires": { + "should-type": "1.4.0" + } + }, + "should-format": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", + "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=", + "dev": true, + "requires": { + "should-type": "1.4.0", + "should-type-adaptors": "1.0.1" + } + }, + "should-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", + "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=", + "dev": true + }, + "should-type-adaptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.0.1.tgz", + "integrity": "sha1-7+VVPN9oz/ZuXF9RtxLcNRx3vqo=", + "dev": true, + "requires": { + "should-type": "1.4.0", + "should-util": "1.0.0" + } + }, + "should-util": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.0.tgz", + "integrity": "sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM=", + "dev": true + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -6231,6 +6481,43 @@ "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", "dev": true }, + "ts-mocha": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-1.0.3.tgz", + "integrity": "sha1-KSlZ7MVtVrFVgA1TCrLcsxYKN+o=", + "dev": true, + "requires": { + "ts-node": "1.7.3" + } + }, + "ts-node": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-1.7.3.tgz", + "integrity": "sha1-3uf4qEdRcy08Lkl8rFoC+xF9/uc=", + "dev": true, + "requires": { + "arrify": "1.0.1", + "chalk": "1.1.3", + "diff": "3.2.0", + "make-error": "1.3.0", + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "pinkie": "2.0.4", + "source-map-support": "0.4.16", + "tsconfig": "5.0.3", + "v8flags": "2.1.1", + "xtend": "4.0.1", + "yn": "1.3.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, "tsc-watch": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/tsc-watch/-/tsc-watch-1.0.7.tgz", @@ -6243,6 +6530,29 @@ "typescript": "2.4.2" } }, + "tsconfig": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-5.0.3.tgz", + "integrity": "sha1-X0J45wGACWeo/Dg/0ZZIh48qbjo=", + "dev": true, + "requires": { + "any-promise": "1.3.0", + "parse-json": "2.2.0", + "strip-bom": "2.0.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + } + } + }, "tslib": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.7.1.tgz", @@ -6486,6 +6796,12 @@ } } }, + "user-home": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", + "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", + "dev": true + }, "util": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", @@ -6525,6 +6841,15 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" }, + "v8flags": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", + "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", + "dev": true, + "requires": { + "user-home": "1.1.1" + } + }, "validate-npm-package-license": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", @@ -6837,6 +7162,15 @@ } } }, + "yn": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-1.3.0.tgz", + "integrity": "sha1-GwgSq7jYBdSJZvjfOF3J2syaGdg=", + "dev": true, + "requires": { + "object-assign": "4.1.1" + } + }, "zone.js": { "version": "0.8.16", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.16.tgz", diff --git a/package.json b/package.json index 29d89ee..4109abd 100644 --- a/package.json +++ b/package.json @@ -2,17 +2,20 @@ "name": "dacap", "version": "1.0.0", "description": "proxies,compresses and caches api-requests", - "main": "dist/index.js", + "main": "bin/dacap", + "bin": { + "dacap": "./bin/dacap" + }, "directories": { "test": "test" }, "scripts": { "webpack-watch": "webpack --config webpack.dev.js --watch", - "start": "node dist/index.js", + "start": "node bin/dacap", "tsc": "tsc", - "inspect-watch": "tsc-watch --sourceMap --onSuccess 'node --inspect=0.0.0.0:9229 dist/index.js'", - "inspect-brk-watch": "tsc-watch --onSuccess 'node --inspect-brk=0.0.0.0:9229 dist/index.js'", - "test": "mocha test", + "inspect-watch": "tsc-watch --sourceMap --onSuccess 'node --inspect=0.0.0.0:9229 bin/dacap'", + "inspect-brk-watch": "tsc-watch --onSuccess 'node --inspect-brk=0.0.0.0:9229 bin/dacap'", + "test": "ts-mocha --recursive test/*", "build": "rm -rf dist public && tsc --declaration && webpack --config webpack.prod.js" }, "author": "Ulf Seltmann <ulf.seltmann@uni-leipzig.de>", @@ -45,11 +48,13 @@ "@types/compression": "0.0.33", "@types/debug": "0.0.30", "@types/express": "^4.0.36", + "@types/mocha": "^2.2.42", "@types/node": "^8.0.20", "@types/node-cache": "^4.1.0", "@types/react": "^16.0.2", "@types/react-dom": "^15.5.3", "@types/request": "^2.0.1", + "@types/should": "^11.2.0", "angular2-template-loader": "^0.6.2", "awesome-typescript-loader": "^3.2.3", "bootstrap": "^3.3.7", @@ -60,8 +65,11 @@ "html-loader": "^0.5.1", "html-webpack-plugin": "^2.30.1", "jquery": "^3.2.1", + "mocha": "^3.5.0", + "should": "^11.2.1", "source-map-loader": "^0.2.1", "style-loader": "^0.18.2", + "ts-mocha": "^1.0.3", "tsc-watch": "^1.0.7", "typescript": "^2.4.2", "webpack": "^3.5.5", diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index 1c4a21d..0000000 --- a/src/index.ts +++ /dev/null @@ -1,105 +0,0 @@ -import * as express from 'express'; -import * as compression from 'compression'; -import * as debugFactory from 'debug'; -import * as path from 'path'; -import * as cache from './cache'; -import * as bodyparser from 'body-parser'; - -const debug = debugFactory('dacap:index'); -const app = express(); -const storagePath = process.env.data_dir || path.resolve(process.cwd(), 'data'); -const proxyPath = process.env.proxy_path || '/ep/'; -const proxyPort = process.env.proxy_port || 3000; -const proxyUrl = process.env.proxy_url || `http://localhost:${proxyPort}${proxyPath}`; -const defaultTtl = process.env.cache_ttl || 600; -const defaultCheckPeriod = process.env.default_check_period || 60; -const defaultArrayValueSize = process.env.array_value_size || 40; -const defaultObjectValueSize = process.env.object_value_size || 80; -const autosaveInterval = process.env.autosave_interval || 60; - -const register = new cache.Register(storagePath, 'apis'); - -register.restore(); - -app.get(/favicon.ico/, (req, res, next) => { - res.send(); -}); - -app.use(compression()); -app.use(bodyparser.json()); - -app.post('/api/add/cache/:name', (req, res, next) => { - if (!req.body || !req.body.apiEndPoint) return res.status(400).send(`you need to specify at least an url`); - if (register.has(req.body.name)) return res.send(`already registered endpoint "${req.params.name}"`); - register.add(req.params.name, req.body.apiEndPoint, { - stdTTL: req.body.cacheOptions.ttl || defaultTtl, - checkperiod: req.body.cacheOptions.checkPeriod || defaultCheckPeriod, - objectValueSize: req.body.cacheOptions.objectValueSize || defaultObjectValueSize, - arrayValueSize: req.body.cacheOptions.arrayValueSize || defaultArrayValueSize - }); - res.json(register.getInfo(proxyUrl, req.params.name)); -}); - -app.get('/api/list/cache', async (req, res, next) => { - try { - res.json(register.getInfo(proxyUrl)); - } catch(err) { - next(err); - } -}); - -app.get('/api/delete/cache/:name', (req, res, next) => { - try { - register.delete(req.params.name); - res.json({}); - } catch (err) { - next(err); - } -}) - -app.get('/api/flush/cache/:name', (req, res, next) => { - try { - const cache = register.get(req.params.name) - cache.flush(); - res.json(register.getInfo(proxyUrl, req.params.name)); - } catch(err) { - next(err); - } -}); - -app.get('/api/delete/cache/:name/key/:hash', (req, res, next) => { - try { - const cache = register.get(req.params.name) - cache.del(req.params.hash); - res.json({}); - } catch(err) { - next(err); - } -}) - -app.get('/api/refresh/cache/:name/key/:hash', async (req, res, next) => { - try { - const cache = register.get(req.params.name); - const value = cache.getCache().get(req.params.hash); - await cache.refresh(req.params.hash, value); - res.json(cache.getDetails(req.params.hash)); - } catch(err) { - next(err); - } -}); - -app.use('/admin/', express.static(path.resolve(__dirname,'..', 'public'))); - -app.use(proxyPath, cache.Middleware(register)); - -const server = app.listen(proxyPort, () => { - debug('server up and running'); -}); -process.stdin.resume(); - -setTimeout(register.save, autosaveInterval); - -process.on('SIGTERM', () => { - register.save(); - process.exit(); -}); \ No newline at end of file diff --git a/src/server.ts b/src/server.ts new file mode 100644 index 0000000..3717e5d --- /dev/null +++ b/src/server.ts @@ -0,0 +1,108 @@ +import * as express from 'express'; +import * as compression from 'compression'; +import * as debugFactory from 'debug'; +import * as path from 'path'; +import * as cache from './cache'; +import * as bodyparser from 'body-parser'; + +const debug = debugFactory('dacap:server'); + +export class Server { + private register: cache.Register; + private expressApp: express.Application; + + constructor(private storagePath: string, + private proxyPath: string, + private proxyUrl: string, + private defaultTtl: number, + private defaultCheckPeriod: number, + private defaultArrayValueSize: number, + private defaultObjectValueSize: number, + private autosaveInterval: number, + private registerName: string) { + + this._init(); + } + + private _init() { + this.register = new cache.Register(this.storagePath, this.registerName); + this.register.restore(); + setTimeout(this.register.save, this.autosaveInterval * 1000); + + this.expressApp = express(); + this.expressApp.get(/favicon.ico/, (req, res, next) => { + res.send(); + }); + + this.expressApp.use(compression()); + this.expressApp.use(bodyparser.json()); + + this.expressApp.post('/api/add/cache/:name', (req, res, next) => { + if (!req.body || !req.body.apiEndPoint) return res.status(400).send(`you need to specify at least an url`); + if (this.register.has(req.body.name)) return res.send(`already registered endpoint "${req.params.name}"`); + this.register.add(req.params.name, req.body.apiEndPoint, { + stdTTL: req.body.cacheOptions.ttl || this.defaultTtl, + checkperiod: req.body.cacheOptions.checkPeriod || this.defaultCheckPeriod, + objectValueSize: req.body.cacheOptions.objectValueSize || this.defaultObjectValueSize, + arrayValueSize: req.body.cacheOptions.arrayValueSize || this.defaultArrayValueSize + }); + res.json(this.register.getInfo(this.proxyUrl + this.proxyPath, req.params.name)); + }); + + this.expressApp.get('/api/list/cache', async (req, res, next) => { + try { + res.json(this.register.getInfo(this.proxyUrl + this.proxyPath)); + } catch (err) { + next(err); + } + }); + + this.expressApp.get('/api/delete/cache/:name', (req, res, next) => { + try { + this.register.delete(req.params.name); + res.json({}); + } catch (err) { + next(err); + } + }) + + this.expressApp.get('/api/flush/cache/:name', (req, res, next) => { + try { + const cache = this.register.get(req.params.name) + cache.flush(); + res.json(this.register.getInfo(this.proxyUrl + this.proxyPath, req.params.name)); + } catch (err) { + next(err); + } + }); + + this.expressApp.get('/api/delete/cache/:name/key/:hash', (req, res, next) => { + try { + const cache = this.register.get(req.params.name) + cache.del(req.params.hash); + res.json({}); + } catch (err) { + next(err); + } + }) + + this.expressApp.get('/api/refresh/cache/:name/key/:hash', async (req, res, next) => { + try { + const cache = this.register.get(req.params.name); + const value = cache.getCache().get(req.params.hash); + await cache.refresh(req.params.hash, value); + res.json(cache.getDetails(req.params.hash)); + } catch (err) { + next(err); + } + }); + + this.expressApp.use('/admin/', express.static(path.resolve(__dirname, '..', 'public'))); + + this.expressApp.use(this.proxyPath, cache.Middleware(this.register)); + } + + listen(port: number, cb) { + return this.expressApp.listen(port, cb); + } +} \ No newline at end of file diff --git a/test/cache.ts b/test/cache.ts index e69de29..fa1dd33 100644 --- a/test/cache.ts +++ b/test/cache.ts @@ -0,0 +1,16 @@ +import {Register, Cache, Middleware} from '../src/cache'; +import * as path from 'path'; +import 'should'; + +describe('register', () => { + it('should create an instance', (done) => { + new Register(path.resolve(__dirname, '..', '.tmp'), 'foobar'); + done(); + }) +}); +describe('cache', () => { + it('should create an instance', (done) => { + new Cache(); + done(); + }); +}) \ No newline at end of file -- GitLab