resend 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,26 +1,11 @@
1
1
  {
2
2
  "name": "resend",
3
- "version": "0.1.1",
4
- "description": "Web server for Redis data including Refile support",
5
- "main": "lib/index.js",
3
+ "version": "0.1.2",
4
+ "description": "",
5
+ "main": "index.js",
6
6
  "scripts": {
7
- "lint": "eslint --cache src",
8
- "start": "node --harmony lib/index.js"
7
+ "test": "echo \"Error: no test specified\" && exit 1"
9
8
  },
10
- "author": "@evanxsummers",
11
- "license": "ISC",
12
- "dependencies": {
13
- "redis-koa-app-rpf": "^0.6.5"
14
- },
15
- "devDependencies": {
16
- "eslint": "^3.12.2"
17
- },
18
- "repository": {
19
- "type": "git",
20
- "url": "git+https://github.com/evanx/resend.git"
21
- },
22
- "bugs": {
23
- "url": "https://github.com/evanx/resend/issues"
24
- },
25
- "homepage": "https://github.com/evanx/resend#readme"
9
+ "author": "",
10
+ "license": "ISC"
26
11
  }
package/.eslintrc DELETED
@@ -1,6 +0,0 @@
1
- {
2
- "parserOptions": {
3
- "ecmaVersion": 2017,
4
- "sourceType": "module"
5
- }
6
- }
package/.npmignore DELETED
@@ -1,7 +0,0 @@
1
- /node_modules
2
- /npm-debug.log
3
- /config
4
- /tmp
5
- /.eslintcache
6
- /.idea
7
- /.vscode
package/Dockerfile DELETED
@@ -1,5 +0,0 @@
1
- FROM mhart/alpine-node
2
- ADD package.json .
3
- RUN npm install
4
- ADD lib lib
5
- CMD ["node", "--harmony", "lib/index.js"]
package/README.md DELETED
@@ -1,74 +0,0 @@
1
- # resend
2
-
3
- Containerized utility to scan and print llen of Redis keys.
4
-
5
- <img src="https://raw.githubusercontent.com/evanx/resend/master/docs/readme/main.png"/>
6
-
7
- ## Use case
8
-
9
- We wish to expire a set of keys in Redis matching some pattern.
10
-
11
- ```
12
- ```
13
-
14
- ## Config
15
-
16
- See `lib/config.js`
17
- ```javascript
18
- ```
19
-
20
- ## Docker
21
-
22
- You can build as follows:
23
- ```shell
24
- docker build -t resend https://github.com/evanx/resend.git
25
- ```
26
-
27
- See `bin/test.sh` https://github.com/evanx/resend/blob/master/bin/test.sh
28
-
29
- Builds:
30
- - isolated network `resend-network`
31
- - isolated Redis instance named `resend-redis`
32
- - this utility `evanx/resend`
33
-
34
- We populate our test keys:
35
- ```
36
- populate() {
37
- }
38
- ```
39
-
40
- We build a container image for this service:
41
- ```
42
- docker build -t resend https://github.com/evanx/resend.git
43
- ```
44
-
45
- We interactively run the service on our test Redis container:
46
- ```
47
- docker run --name resend-instance --rm -i \
48
- --network=resend-network \
49
- -e host=$redisHost \
50
- -e pattern='*' \
51
- resend
52
- ```
53
-
54
- ## Implementation
55
-
56
- See `lib/main.js`
57
-
58
- ```javascript
59
- ```
60
-
61
- ### Appication archetype
62
-
63
- Incidently `lib/index.js` uses the `redis-koa-app-rpf` application archetype.
64
- ```
65
- require('redis-koa-app-rpf')(require('./spec'), require('./main'));
66
- ```
67
- where we extract the `config` from `process.env` according to the `spec` and invoke our `main` function.
68
-
69
- See https://github.com/evanx/redis-koa-app-rpf.
70
-
71
- This provides lifecycle boilerplate to reuse across similar applications.
72
-
73
- <hr>
74
- https://twitter.com/@evanxsummers
package/bin/redeploy.sh DELETED
@@ -1,13 +0,0 @@
1
-
2
- echo "NODE_ENV $NODE_ENV"
3
- docker build -t geo-cache https://github.com/evanx/geo-cache.git
4
- docker rm -f `docker ps -q -f name=geo-cache`
5
- container=`docker run --name geo-cache -d \
6
- --network=host --restart unless-stopped \
7
- -e httpPort=8851 \
8
- -e NODE_ENV=$NODE_ENV \
9
- geo-cache`
10
- sleep 1
11
- echo "docker logs $container"
12
- docker logs $container
13
-
@@ -1,9 +0,0 @@
1
-
2
- set -u -e
3
-
4
- echo $# | grep '^0$\|^1$'
5
-
6
- git add -A
7
- git commit -m "${1-initial}"
8
- git push
9
-
@@ -1,24 +0,0 @@
1
-
2
- set -u -x
3
-
4
- mkdir -p tmp
5
-
6
- redis-cli keys cache-geo-cache:* | xargs -n 1 redis-cli del
7
-
8
- echo '
9
- redis-cli keys cache-geo-cache:* | head
10
- redis-cli keys cache-geo-cache:* | wc -l
11
- ' | sed '/^$/d' | tee tmp/keys.sh | dash -x
12
- cat tmp/keys.sh
13
- ls -l tmp/keys.sh
14
-
15
- (
16
- echo 'client sleep'
17
- sleep 1
18
- echo 'client curl'
19
- curl 'http://localhost:8851/maps/api/geocode/json' -G --data-urlencode 'address=20 Falstaff Close, Eynsham OX29 4QA'
20
- redis-cli keys cache-geo-cache:*
21
- ) &
22
-
23
- echo 'start server'
24
- apiKey=$MAPS_API_KEY node --harmony lib/index.js
Binary file
Binary file
package/lib/index.js DELETED
@@ -1,2 +0,0 @@
1
-
2
- require('redis-koa-app-rpf')(require('./spec'), require('./main'));
package/lib/main.js DELETED
@@ -1,132 +0,0 @@
1
- const assert = require('assert');
2
- const crypto = require('crypto');
3
- const fetch = require('node-fetch');
4
- const h = require('render-html-rpf');
5
- const lodash = require('lodash');
6
- const multiExecAsync = require('multi-exec-async');
7
-
8
- const reduceAllProperties = (object, fn) => Object.keys(object).reduce((result, key) => {
9
- result[key] = fn(object[key]);
10
- return result;
11
- }, {});
12
-
13
- module.exports = async appx => {
14
- const {config, logger, client, api} = appx;
15
- logger.info({config}, process.env.NODE_ENV);
16
- api.get('/stats', async ctx => {
17
- ctx.redirect('/metrics');
18
- });
19
- api.get('/metrics', async ctx => {
20
- const [getCountRes, setCountRes, migrateCountRes] = await multiExecAsync(client, multi => {
21
- multi.hgetall([config.redisNamespace, 'metric:get:path:count:h'].join(':'));
22
- multi.hgetall([config.redisNamespace, 'metric:set:path:count:h'].join(':'));
23
- multi.hgetall([config.redisNamespace, 'metric:migrate:path:count:h'].join(':'));
24
- });
25
- const getCount = reduceAllProperties(getCountRes || {}, value => parseInt(value));
26
- const setCount = reduceAllProperties(setCountRes || {}, value => parseInt(value));
27
- const migrateCount = reduceAllProperties(migrateCountRes || {}, value => parseInt(value));
28
- const metrics = {getCount, setCount, migrateCount};
29
- if (/(Mobile)/.test(ctx.get('user-agent'))) {
30
- ctx.body = h.page({
31
- title: 'gcache',
32
- heading: 'Metrics',
33
- content: [{
34
- name: 'pre',
35
- content: JSON.stringify(metrics, null, 2)}
36
- ],
37
- footerLink: 'https://github.com/evanx/geo-cache'
38
- });
39
- } else {
40
- ctx.body = metrics;
41
- }
42
- });
43
- api.get('/maps/api/*', async ctx => {
44
- const path = ctx.params[0];
45
- const url = 'https://maps.googleapis.com/maps/api/' + path;
46
- const query = Object.keys(ctx.query)
47
- .filter(key => key !== 'key')
48
- .reduce((query, key) => {
49
- query[key] = ctx.query[key];
50
- return query;
51
- }, {});
52
- assert(!query.key);
53
- const queryString = Object.keys(query).slice(0).sort().map(
54
- key => [key, encodeURIComponent(query[key])].join('=')
55
- ).join('&');
56
- const urlString = [url, queryString].join('?');
57
- const authQuery = Object.assign({}, {key: config.apiKey}, ctx.query);
58
- if (!authQuery.key) {
59
- ctx.statusCode = 401;
60
- const statusText = 'Unauthorized';
61
- ctx.body = statusText + '\n';
62
- return;
63
- }
64
- logger.debug({url, query, urlString});
65
- const sha = crypto.createHash('sha1').update(urlString).digest('hex');
66
- const cacheKey = [config.redisNamespace, sha, 'j'].join(':');
67
- if (true) {
68
- const migrateSha = crypto.createHash('sha1').update(
69
- [url, JSON.stringify(query)].join('#')
70
- ).digest('hex');
71
- const migrateKey = ['cache-geo-cache', migrateSha, 'json'].join(':');
72
- const [migrateContent] = await multiExecAsync(client, multi => {
73
- multi.get(migrateKey);
74
- });
75
- if (migrateContent) {
76
- await multiExecAsync(client, multi => {
77
- multi.set(cacheKey, JSON.stringify(JSON.parse(migrateContent)));
78
- multi.del(migrateKey);
79
- multi.hincrby([config.redisNamespace, 'metric:migrate:path:count:h'].join(':'), path, 1);
80
- });
81
- }
82
- }
83
- let [cachedContent] = await multiExecAsync(client, multi => {
84
- multi.get(cacheKey);
85
- multi.expire(cacheKey, config.expireSeconds);
86
- multi.hincrby([config.redisNamespace, 'metric:get:path:count:h'].join(':'), path, 1);
87
- });
88
- if (cachedContent) {
89
- logger.debug('hit', {url, sha, cacheKey});
90
- const parsedContent = JSON.parse(cachedContent);
91
- const formattedContent = JSON.stringify(parsedContent);
92
- if (cachedContent !== formattedContent) {
93
- logger.debug('reformat', cacheKey, cachedContent);
94
- await multiExecAsync(client, multi => {
95
- multi.set(cacheKey, formattedContent);
96
- });
97
- }
98
- if (lodash.includes(['OK', 'ZERO_RESULTS'], parsedContent.status)) {
99
- logger.warn('hit', {url, sha, cacheKey, parsedContent});
100
- ctx.set('Content-Type', 'application/json');
101
- ctx.body = JSON.stringify(parsedContent, null, 2) + '\n';
102
- return;
103
- }
104
- }
105
- const urlQuery = url + '?' + Object.keys(authQuery)
106
- .map(key => [key, encodeURIComponent(authQuery[key])].join('='))
107
- .join('&');
108
- const res = await fetch(urlQuery);
109
- if (res.status !== 200) {
110
- logger.debug('statusCode', url, res.status, res.statusText, query);
111
- ctx.statusCode = res.status;
112
- ctx.body = res.statusText + '\n';
113
- return;
114
- }
115
- const fetchedContent = await res.json();
116
- const formattedContent = JSON.stringify(fetchedContent, null, 2) + '\n';
117
- ctx.set('Content-Type', 'application/json');
118
- ctx.body = formattedContent;
119
- if (!lodash.includes(['OK', 'ZERO_RESULTS'], fetchedContent.status)) {
120
- logger.debug('status', fetchedContent.status, url);
121
- } else {
122
- const expireSeconds = lodash.includes(['ZERO_RESULTS'], fetchedContent.status)?
123
- config.shortExpireSeconds:
124
- config.expireSeconds;
125
- logger.debug('expireSeconds', expireSeconds, fetchedContent.status, url);
126
- await multiExecAsync(client, multi => {
127
- multi.setex(cacheKey, expireSeconds, JSON.stringify(fetchedContent));
128
- multi.hincrby([config.redisNamespace, 'metric:set:path:count:h'].join(':'), path, 1);
129
- });
130
- }
131
- });
132
- }
package/lib/spec.js DELETED
@@ -1,50 +0,0 @@
1
- module.exports = {
2
- description: 'Redis-based caching proxy for Google Maps API queries.',
3
- required: {
4
- redisHost: {
5
- description: 'the Redis host',
6
- default: 'localhost'
7
- },
8
- redisPort: {
9
- description: 'the Redis port',
10
- default: 6379
11
- },
12
- redisPassword: {
13
- description: 'the Redis password',
14
- required: false
15
- },
16
- redisNamespace: {
17
- description: 'the Redis namespace',
18
- default: 'cache-geo'
19
- },
20
- expireSeconds: {
21
- description: 'the TTL for the cached content',
22
- default: 21*24*3600
23
- },
24
- shortExpireSeconds: {
25
- description: 'the TTL for the cached content',
26
- default: 3*24*3600
27
- },
28
- httpPort: {
29
- description: 'the HTTP port',
30
- default: 8851
31
- },
32
- apiKey: {
33
- description: 'our Google Maps API key',
34
- required: false
35
- },
36
- loggerLevel: {
37
- description: 'the logging level',
38
- default: 'info',
39
- example: 'debug'
40
- }
41
- },
42
- test: {
43
- loggerLevel: 'debug',
44
- debug: true
45
- },
46
- development: {
47
- loggerLevel: 'debug',
48
- debug: true
49
- }
50
- }
package/test/curl.sh DELETED
@@ -1,9 +0,0 @@
1
-
2
- set -u -x
3
-
4
- curl 'http://localhost:8851/maps/api/geocode/json' -G --data-urlencode 'address=20 Falstaff Close, Eynsham OX29 4QA'
5
- curl 'http://localhost:8851/maps/api/geocode/json' -G --data-urlencode 'address=14 Grays Inn Road, Chancery Lane, London'
6
- sleep 1
7
- redis-cli keys cache-geo-cache:*
8
- redis-cli hkeys cache-geo-cache:url:h
9
- redis-cli hgetall cache-geo-cache:url:h