Skip to content

Commit 8dd75b1

Browse files
authored
perf: cache files with redis to reduce registry load (#3)
* refactor: cleanup dependencies / SSL with @interactivetraining/acme-client * feat: add directory listing (#2) * fix(getPackageFileList): move downloadPackage() call outside promise * feat: add redis to reduce registry load
1 parent 0defad6 commit 8dd75b1

File tree

13 files changed

+276
-210
lines changed

13 files changed

+276
-210
lines changed

.dockerignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
.gitignore
44
Dockerfile
55
README.md
6-
.env
76
node_modules
87
npm-debug.log
8+
build-for-run.sh

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,5 @@ dist/
2424
*.pem
2525
acme/
2626
cache/
27+
28+
build-for-run.sh

Dockerfile

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
FROM node:10
22

3-
LABEL version="0.0.1"
3+
LABEL version="0.0.2"
44

55
WORKDIR /usr/src/app
66

77
COPY . .
88

99
RUN npm install
1010

11-
EXPOSE 443
12-
EXPOSE 80
13-
14-
VOLUME /usr/src/app/cache
11+
VOLUME /usr/src/app
1512

1613
CMD [ "npm", "run", "start" ]

README.md

Lines changed: 11 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,17 @@
11
# self-hosted-unpkg
22

3-
## Run locally
4-
Create .env file. (see sample.env) and run:
3+
## Requirements
4+
- Registry token or credentials (if you want to allow access to private packages)
5+
- Redis (if you want short-term file caching to reduce registry load)
56

6-
```bash
7-
docker run -it --rm -p 443:443 -p 80:80 -v /local/path/to/cache:/usr/src/app/cache --env-file ./.env --name self-hosted-unpkg interactivetraining/self-hosted-unpkg
8-
```
7+
### For HTTPS
8+
- Cloudflare credentials (used to verify domain with Lets Encrypt)
9+
- Google Cloud Storage + service account (for storing SSL cert/key)
910

10-
## Kubernetes Deployment
1111

12-
### Create a secret from your env file
13-
```bash
14-
kubectl create secret generic __MY_SECRET_NAME__ --from-env-file ./path/to/.env
15-
```
12+
## Example
13+
Create .env file. (see http.sample.env) and run:
1614

17-
### Add each variable to your deployment
18-
```yaml
19-
apiVersion: extensions/v1beta1
20-
kind: Deployment
21-
spec:
22-
template:
23-
spec:
24-
containers:
25-
- env:
26-
- name: DOMAIN
27-
valueFrom:
28-
secretKeyRef:
29-
key: DOMAIN
30-
name: __MY_SECRET_NAME__
31-
- name: NPM_REGISTRY
32-
valueFrom:
33-
secretKeyRef:
34-
key: NPM_REGISTRY
35-
name: __MY_SECRET_NAME__
36-
- name: NPM_TOKEN
37-
valueFrom:
38-
secretKeyRef:
39-
key: NPM_TOKEN
40-
name: __MY_SECRET_NAME__
41-
- name: CLOUDFLARE_EMAIL
42-
valueFrom:
43-
secretKeyRef:
44-
key: CLOUDFLARE_EMAIL
45-
name: __MY_SECRET_NAME__
46-
- name: CLOUDFLARE_API_KEY
47-
valueFrom:
48-
secretKeyRef:
49-
key: CLOUDFLARE_API_KEY
50-
name: __MY_SECRET_NAME__
51-
- name: LETS_ENCRYPT_EMAIL
52-
valueFrom:
53-
secretKeyRef:
54-
key: LETS_ENCRYPT_EMAIL
55-
name: __MY_SECRET_NAME__
56-
- name: LETS_ENCRYPT_AGREE_TO_TOS
57-
valueFrom:
58-
secretKeyRef:
59-
key: LETS_ENCRYPT_AGREE_TO_TOS
60-
name: __MY_SECRET_NAME__
61-
- name: GOOGLE_CLOUD_BUCKET_NAME
62-
valueFrom:
63-
secretKeyRef:
64-
key: GOOGLE_CLOUD_BUCKET_NAME
65-
name: __MY_SECRET_NAME__
66-
- name: GOOGLE_CLOUD_PROJECT_ID
67-
valueFrom:
68-
secretKeyRef:
69-
key: GOOGLE_CLOUD_PROJECT_ID
70-
name: __MY_SECRET_NAME__
71-
- name: GOOGLE_CLOUD_CERT_DB_FILE
72-
valueFrom:
73-
secretKeyRef:
74-
key: GOOGLE_CLOUD_CERT_DB_FILE
75-
name: __MY_SECRET_NAME__
76-
- name: GOOGLE_CLOUD_CLIENT_EMAIL
77-
valueFrom:
78-
secretKeyRef:
79-
key: GOOGLE_CLOUD_CLIENT_EMAIL
80-
name: __MY_SECRET_NAME__
81-
- name: GOOGLE_CLOUD_PRIVATE_KEY
82-
valueFrom:
83-
secretKeyRef:
84-
key: GOOGLE_CLOUD_PRIVATE_KEY
85-
name: __MY_SECRET_NAME__
86-
- name: ENABLE_SSL
87-
valueFrom:
88-
secretKeyRef:
89-
key: ENABLE_SSL
90-
name: __MY_SECRET_NAME__
91-
image: interactivetraining/self-hosted-unpkg:latest
92-
imagePullPolicy: Always
93-
name: self-hosted-unpkg
94-
```
15+
```bash
16+
docker run -it --rm -p 443:443 -p 80:80 --env-file ./.env --name self-hosted-unpkg interactivetraining/self-hosted-unpkg
17+
```

build.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env bash
2+
3+
while [[ "$#" -gt 0 ]]; do case $1 in
4+
-v|--version) version="$2"; shift;;
5+
*) echo "Unknown parameter passed: $1"; exit 1;;
6+
esac; shift; done
7+
8+
npm run build
9+
docker build -t "interactivetraining/self-hosted-unpkg:$version" .
10+
docker push "interactivetraining/self-hosted-unpkg:$version"

http.sample.env

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
ENABLE_SSL=0
2+
NPM_REGISTRY=https://registry.npmjs.com
3+
NPM_USER=
4+
NPM_PASSWORD=
5+
NPM_TOKEN=
6+
PORT=80
7+
REDIS_HOST=
8+
REDIS_PORT=6379
9+
REDIS_PASSWORD=
10+
CACHE_PREFIX=unpkg
11+
LOG_REQUEST=true
Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
DOMAIN=my-self-hosted-unpkg-domain.com
2-
ENABLE_SSL=0
3-
NPM_REGISTRY=https://private.npm.registry
2+
ENABLE_SSL=1
3+
NPM_REGISTRY=https://registry.npmjs.com
44
NPM_USER=
55
NPM_PASSWORD=
66
NPM_TOKEN=
@@ -10,6 +10,11 @@ LETS_ENCRYPT_EMAIL=
1010
LETS_ENCRYPT_AGREE_TO_TOS=true
1111
GOOGLE_CLOUD_BUCKET_NAME=my-certs
1212
GOOGLE_CLOUD_PROJECT_ID=my-google-cloud-project-id
13-
GOOGLE_CLOUD_CERT_DB_FILE=cert-db.json
1413
GOOGLE_CLOUD_CLIENT_EMAIL=[email protected]
1514
GOOGLE_CLOUD_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nEXAMPLE_PRIVATE_KEY\n-----END PRIVATE KEY-----\n"
15+
PORT=80
16+
REDIS_HOST=
17+
REDIS_PORT=6379
18+
REDIS_PASSWORD=
19+
CACHE_PREFIX=unpkg
20+
LOG_REQUEST=true

package.json

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,34 +12,31 @@
1212
"build": "tsc"
1313
},
1414
"dependencies": {
15-
"@interactivetraining/le-challenge-cloudflare": "1.3.1",
16-
"@interactivetraining/le-store-gcloud-storage": "0.0.2",
15+
"@interactivetraining/acme-client": "0.0.6",
1716
"compression": "^1.7.3",
1817
"cors": "^2.8.5",
1918
"dotenv": "^5.0.1",
20-
"download-npm-package": "^3.1.12",
21-
"download-package-tarball": "^1.0.7",
2219
"express": "^4.16.4",
23-
"get-package-json-from-registry": "^2.2.1",
24-
"got": "^9.6.0",
25-
"greenlock-express": "^2.6.8",
20+
"gunzip-maybe": "^1.4.1",
2621
"helmet": "^3.16.0",
27-
"le-challenge-cloudflare": "^1.0.1",
2822
"mime": "^2.4.0",
23+
"mime-types": "^2.1.24",
2924
"redirect-https": "^1.3.0",
30-
"zlib": "^1.0.5"
25+
"tar-stream": "^2.1.0",
26+
"zlib": "^1.0.5",
27+
"ioredis": "^4.14.0"
3128
},
3229
"devDependencies": {
3330
"@types/compression": "0.0.36",
3431
"@types/cors": "^2.8.4",
3532
"@types/dotenv": "^6.1.0",
3633
"@types/express": "^4.16.1",
3734
"@types/helmet": "0.0.43",
38-
"@types/node": "^11.11.1",
39-
"@types/shelljs": "^0.8.3",
35+
"@types/node": "^12.7.2",
36+
"@types/tar-stream": "^1.6.1",
4037
"nodemon": "^1.18.10",
41-
"shelljs": "^0.8.3",
4238
"ts-node": "^8.0.2",
43-
"typescript": "^3.3.3333"
39+
"typescript": "^3.5.3",
40+
"@types/ioredis": "^4.0.15"
4441
}
4542
}

src/cache.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import * as Redis from 'ioredis';
2+
import {IPackageParams} from './interfaces';
3+
4+
const redis = (process.env.REDIS_PORT && process.env.REDIS_HOST) ? new Redis({
5+
port: parseInt(process.env.REDIS_PORT),
6+
host: process.env.REDIS_HOST,
7+
password: process.env.REDIS_PASSWORD
8+
}) : undefined;
9+
10+
export class Cache {
11+
static get(key: string): Promise<string | null> {
12+
return new Promise((resolve, reject) => {
13+
if (!redis) resolve(null);
14+
redis.get(key, (err, result) => {
15+
(err) ? reject(err) : resolve(result);
16+
});
17+
})
18+
}
19+
20+
static set(key: string, value: any, expireSeconds: number = 0): Promise<string> {
21+
if (!redis) return;
22+
return redis.set(key, value, 'EX', expireSeconds);
23+
}
24+
25+
static buildKey(pkg: IPackageParams): string {
26+
return `${process.env.CACHE_PREFIX}/cache/${(pkg.scope) ? `${pkg.scope}/` : ''}${pkg.package}/${pkg.version}/${pkg['0']}`;
27+
}
28+
}

0 commit comments

Comments
 (0)