Compare commits
10 Commits
f821e794f8
...
fa1c796f3c
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fa1c796f3c | ||
![]() |
96e53a0462 | ||
![]() |
8320343218 | ||
![]() |
6f014d499b | ||
![]() |
dbd9c683f9 | ||
![]() |
7c8b90ff6b | ||
![]() |
e2932d3afb | ||
![]() |
2346dbaf87 | ||
![]() |
588ec835df | ||
![]() |
62e64e5154 |
12
cli/list.go
12
cli/list.go
@ -3,7 +3,7 @@ package cli
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
npmproxy "github.com/emeralt/npm-cache-proxy/proxy"
|
||||
npmproxy "github.com/pkgems/npm-cache-proxy/proxy"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -12,13 +12,11 @@ var listCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List all cached paths",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
proxy := getProxy(func() (npmproxy.Options, error) {
|
||||
return npmproxy.Options{
|
||||
DatabasePrefix: persistentOptions.RedisPrefix,
|
||||
}, nil
|
||||
})
|
||||
proxy := getProxy()
|
||||
|
||||
metadatas, err := proxy.ListCachedPaths()
|
||||
metadatas, err := proxy.ListCachedPaths(npmproxy.Options{
|
||||
DatabasePrefix: persistentOptions.RedisPrefix,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
npmproxy "github.com/emeralt/npm-cache-proxy/proxy"
|
||||
npmproxy "github.com/pkgems/npm-cache-proxy/proxy"
|
||||
"github.com/go-redis/redis"
|
||||
)
|
||||
|
||||
@ -25,7 +25,7 @@ func init() {
|
||||
rootCmd.PersistentFlags().StringVar(&persistentOptions.RedisPrefix, "redis-prefix", getEnvString("REDIS_PREFIX", "ncp-"), "Redis prefix")
|
||||
}
|
||||
|
||||
func getProxy(getOptions func() (npmproxy.Options, error)) *npmproxy.Proxy {
|
||||
func getProxy() *npmproxy.Proxy {
|
||||
return &npmproxy.Proxy{
|
||||
Database: npmproxy.DatabaseRedis{
|
||||
Client: redis.NewClient(&redis.Options{
|
||||
@ -37,7 +37,6 @@ func getProxy(getOptions func() (npmproxy.Options, error)) *npmproxy.Proxy {
|
||||
HttpClient: &http.Client{
|
||||
Transport: http.DefaultTransport,
|
||||
},
|
||||
GetOptions: getOptions,
|
||||
}
|
||||
}
|
||||
|
||||
|
12
cli/purge.go
12
cli/purge.go
@ -1,7 +1,7 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
npmproxy "github.com/emeralt/npm-cache-proxy/proxy"
|
||||
npmproxy "github.com/pkgems/npm-cache-proxy/proxy"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -10,13 +10,11 @@ var purgeCmd = &cobra.Command{
|
||||
Use: "purge",
|
||||
Short: "Purge all cached paths",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
proxy := getProxy(func() (npmproxy.Options, error) {
|
||||
return npmproxy.Options{
|
||||
DatabasePrefix: persistentOptions.RedisPrefix,
|
||||
}, nil
|
||||
})
|
||||
proxy := getProxy()
|
||||
|
||||
err := proxy.PurgeCachedPaths()
|
||||
err := proxy.PurgeCachedPaths(npmproxy.Options{
|
||||
DatabasePrefix: persistentOptions.RedisPrefix,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
27
cli/root.go
27
cli/root.go
@ -1,9 +1,10 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
npmproxy "github.com/emeralt/npm-cache-proxy/proxy"
|
||||
npmproxy "github.com/pkgems/npm-cache-proxy/proxy"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -31,16 +32,24 @@ func init() {
|
||||
}
|
||||
|
||||
func run(cmd *cobra.Command, args []string) {
|
||||
proxy := getProxy(func() (npmproxy.Options, error) {
|
||||
return npmproxy.Options{
|
||||
DatabasePrefix: persistentOptions.RedisPrefix,
|
||||
DatabaseExpiration: time.Duration(rootOptions.CacheTTL) * time.Second,
|
||||
UpstreamAddress: rootOptions.UpstreamAddress,
|
||||
}, nil
|
||||
})
|
||||
proxy := getProxy()
|
||||
|
||||
proxy.Server(npmproxy.ServerOptions{
|
||||
log.Print("Listening on " + rootOptions.ListenAddress)
|
||||
|
||||
err := proxy.Server(npmproxy.ServerOptions{
|
||||
ListenAddress: rootOptions.ListenAddress,
|
||||
Silent: rootOptions.Silent,
|
||||
|
||||
GetOptions: func() (npmproxy.Options, error) {
|
||||
return npmproxy.Options{
|
||||
DatabasePrefix: persistentOptions.RedisPrefix,
|
||||
DatabaseExpiration: time.Duration(rootOptions.CacheTTL) * time.Second,
|
||||
UpstreamAddress: rootOptions.UpstreamAddress,
|
||||
}, nil
|
||||
},
|
||||
}).ListenAndServe()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
npmproxy "github.com/emeralt/npm-cache-proxy/proxy"
|
||||
npmproxy "github.com/pkgems/npm-cache-proxy/proxy"
|
||||
"github.com/go-redis/redis"
|
||||
)
|
||||
|
||||
@ -18,6 +18,10 @@ func main() {
|
||||
}),
|
||||
},
|
||||
HttpClient: &http.Client{},
|
||||
}
|
||||
|
||||
proxy.Server(npmproxy.ServerOptions{
|
||||
ListenAddress: "localhost:8080",
|
||||
GetOptions: func() (npmproxy.Options, error) {
|
||||
return npmproxy.Options{
|
||||
DatabasePrefix: "ncp-",
|
||||
@ -25,9 +29,5 @@ func main() {
|
||||
UpstreamAddress: "https://registry.npmjs.org",
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
||||
proxy.Server(npmproxy.ServerOptions{
|
||||
ListenAddress: "localhost:8080",
|
||||
}).ListenAndServe()
|
||||
}
|
||||
|
3
go.mod
3
go.mod
@ -1,4 +1,4 @@
|
||||
module github.com/emeralt/npm-cache-proxy
|
||||
module github.com/pkgems/npm-cache-proxy
|
||||
|
||||
go 1.12
|
||||
|
||||
@ -6,6 +6,7 @@ require (
|
||||
github.com/gin-contrib/zap v0.0.0-20190405225521-7c4b822813e7
|
||||
github.com/gin-gonic/gin v1.3.0
|
||||
github.com/go-redis/redis v6.15.2+incompatible
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/spf13/cobra v0.0.3
|
||||
github.com/spf13/pflag v1.0.3 // indirect
|
||||
go.uber.org/zap v1.9.1
|
||||
|
2
go.sum
2
go.sum
@ -9,6 +9,8 @@ github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDA
|
||||
github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
|
2
main.go
2
main.go
@ -1,7 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/emeralt/npm-cache-proxy/cli"
|
||||
"github.com/pkgems/npm-cache-proxy/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -10,12 +10,7 @@ import (
|
||||
)
|
||||
|
||||
// GetCachedPath returns cached upstream response for a given url path.
|
||||
func (proxy Proxy) GetCachedPath(path string, request *http.Request) ([]byte, error) {
|
||||
options, err := proxy.GetOptions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (proxy Proxy) GetCachedPath(options Options, path string, request *http.Request) ([]byte, error) {
|
||||
key := options.DatabasePrefix + path
|
||||
|
||||
// get package from database
|
||||
@ -41,7 +36,6 @@ func (proxy Proxy) GetCachedPath(path string, request *http.Request) ([]byte, er
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.Header.Get("Content-Encoding") == "gzip" {
|
||||
zr, err := gzip.NewReader(res.Body)
|
||||
@ -52,14 +46,17 @@ func (proxy Proxy) GetCachedPath(path string, request *http.Request) ([]byte, er
|
||||
res.Body = zr
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pkg = string(body)
|
||||
|
||||
// TODO: avoid calling MustCompile every time
|
||||
// find "dist": "https?://.*/ and replace to "dist": "{localurl}/
|
||||
pkg = regexp.MustCompile(`(?U)"tarball":"https?://.*/`).ReplaceAllString(string(body), `"dist": "http://localhost:8080/`)
|
||||
pkg = regexp.MustCompile(`(?U)"tarball":"https?://.*/`).ReplaceAllString(string(body), `"tarball": "http://`+request.Host+"/")
|
||||
|
||||
// save to redis
|
||||
err = proxy.Database.Set(key, pkg, options.DatabaseExpiration)
|
||||
@ -72,12 +69,7 @@ func (proxy Proxy) GetCachedPath(path string, request *http.Request) ([]byte, er
|
||||
}
|
||||
|
||||
// ListCachedPaths returns list of all cached url paths.
|
||||
func (proxy Proxy) ListCachedPaths() ([]string, error) {
|
||||
options, err := proxy.GetOptions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (proxy Proxy) ListCachedPaths(options Options) ([]string, error) {
|
||||
metadata, err := proxy.Database.Keys(options.DatabasePrefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -92,12 +84,7 @@ func (proxy Proxy) ListCachedPaths() ([]string, error) {
|
||||
}
|
||||
|
||||
// PurgeCachedPaths deletes all cached url paths.
|
||||
func (proxy Proxy) PurgeCachedPaths() error {
|
||||
options, err := proxy.GetOptions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func (proxy Proxy) PurgeCachedPaths(options Options) error {
|
||||
metadata, err := proxy.Database.Keys(options.DatabasePrefix)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1,6 +1,6 @@
|
||||
//
|
||||
// Package proxy implements a HTTP caching proxy for Node package registry (NPM).
|
||||
// See https://github.com/emeralt/npm-cache-proxy/ for more information about proxy.
|
||||
// See https://github.com/pkgems/npm-cache-proxy/ for more information about proxy.
|
||||
//
|
||||
package proxy
|
||||
|
||||
@ -14,8 +14,6 @@ import (
|
||||
type Proxy struct {
|
||||
Database Database
|
||||
HttpClient *http.Client
|
||||
|
||||
GetOptions func() (Options, error)
|
||||
}
|
||||
|
||||
// Options provides dynamic options for Proxy.
|
||||
|
@ -2,6 +2,7 @@ package proxy
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -14,10 +15,14 @@ import (
|
||||
type ServerOptions struct {
|
||||
ListenAddress string
|
||||
Silent bool
|
||||
|
||||
GetOptions func() (Options, error)
|
||||
}
|
||||
|
||||
// Server creates http proxy server
|
||||
func (proxy Proxy) Server(options ServerOptions) *http.Server {
|
||||
gin.SetMode("release")
|
||||
|
||||
router := gin.New()
|
||||
|
||||
if options.Silent {
|
||||
@ -28,9 +33,9 @@ func (proxy Proxy) Server(options ServerOptions) *http.Server {
|
||||
router.Use(ginzap.RecoveryWithZap(logger, true))
|
||||
}
|
||||
|
||||
router.GET("/:scope/:name", proxy.getPackageHandler)
|
||||
router.GET("/:scope", proxy.getPackageHandler)
|
||||
router.NoRoute(proxy.noRouteHandler)
|
||||
router.GET("/:scope/:name", proxy.getPackageHandler(options))
|
||||
router.GET("/:scope", proxy.getPackageHandler(options))
|
||||
router.NoRoute(proxy.noRouteHandler(options))
|
||||
|
||||
return &http.Server{
|
||||
Handler: router,
|
||||
@ -38,45 +43,50 @@ func (proxy Proxy) Server(options ServerOptions) *http.Server {
|
||||
}
|
||||
}
|
||||
|
||||
func (proxy Proxy) getPackageHandler(c *gin.Context) {
|
||||
pkg, err := proxy.GetCachedPath(c.Request.URL.Path, c.Request)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
} else {
|
||||
// c.Header("Content-Encoding", "gzip")
|
||||
c.Data(200, "application/json", pkg)
|
||||
}
|
||||
}
|
||||
|
||||
func (proxy Proxy) getTarballHabdler(c *gin.Context) {
|
||||
pkg, err := proxy.GetCachedPath(c.Request.URL.Path, c.Request)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
} else {
|
||||
c.Data(200, "application/json", pkg)
|
||||
}
|
||||
}
|
||||
|
||||
func (proxy Proxy) noRouteHandler(c *gin.Context) {
|
||||
if strings.Contains(c.Request.URL.Path, ".tgz") {
|
||||
proxy.getTarballHabdler(c)
|
||||
} else if c.Request.URL.Path == "/" {
|
||||
err := proxy.Database.Health()
|
||||
func (proxy Proxy) getPackageHandler(options ServerOptions) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
options, err := options.GetOptions()
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(503, err)
|
||||
c.AbortWithError(500, err)
|
||||
} else {
|
||||
c.AbortWithStatusJSON(200, gin.H{"ok": true})
|
||||
}
|
||||
} else {
|
||||
options, err := proxy.GetOptions()
|
||||
pkg, err := proxy.GetCachedPath(options, c.Request.URL.Path, c.Request)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(500, err)
|
||||
} else {
|
||||
c.Redirect(http.StatusTemporaryRedirect, options.UpstreamAddress+c.Request.URL.Path)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
} else {
|
||||
c.Header("Cache-Control", "public, max-age="+strconv.Itoa(int(options.DatabaseExpiration.Seconds())))
|
||||
c.Data(200, "application/json", pkg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (proxy Proxy) noRouteHandler(options ServerOptions) gin.HandlerFunc {
|
||||
tarballHandler := proxy.getPackageHandler(options)
|
||||
|
||||
return func(c *gin.Context) {
|
||||
if strings.Contains(c.Request.URL.Path, ".tgz") {
|
||||
// get tarball
|
||||
tarballHandler(c)
|
||||
} else if c.Request.URL.Path == "/" {
|
||||
// get health
|
||||
err := proxy.Database.Health()
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(503, err)
|
||||
} else {
|
||||
c.AbortWithStatusJSON(200, gin.H{"ok": true})
|
||||
}
|
||||
} else {
|
||||
// redirect
|
||||
options, err := options.GetOptions()
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(500, err)
|
||||
} else {
|
||||
c.Redirect(http.StatusTemporaryRedirect, options.UpstreamAddress+c.Request.URL.Path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
210
readme.md
210
readme.md
@ -1,104 +1,135 @@
|
||||
<h1 align="center">
|
||||
<img width="400" src="./logo.png">
|
||||
<div align="center">
|
||||
<img width="450" src="./logo.png">
|
||||
|
||||
npm-cache-proxy
|
||||
</h1>
|
||||
<h1>npm-cache-proxy</h1>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://hub.docker.com/r/emeralt/npm-cache-proxy/tags">
|
||||
<img src="https://img.shields.io/github/release/emeralt/npm-cache-proxy.svg" alt="Current Release" />
|
||||
<a href="https://hub.docker.com/r/pkgems/npm-cache-proxy/tags">
|
||||
<img src="https://img.shields.io/github/release/pkgems/npm-cache-proxy.svg" alt="Current Release" />
|
||||
</a>
|
||||
<a href="https://hub.docker.com/r/emeralt/npm-cache-proxy/builds">
|
||||
<img src="https://img.shields.io/docker/cloud/build/emeralt/npm-cache-proxy.svg" alt="CI Build">
|
||||
<a href="https://hub.docker.com/r/pkgems/npm-cache-proxy/builds">
|
||||
<img src="https://img.shields.io/docker/cloud/build/pkgems/npm-cache-proxy.svg" alt="CI Build">
|
||||
</a>
|
||||
<a href="https://github.com/emeralt/npm-cache-proxy/blob/master/liscense">
|
||||
<img src="https://img.shields.io/github/license/emeralt/npm-cache-proxy.svg" alt="Licence">
|
||||
<a href="https://github.com/pkgems/npm-cache-proxy/blob/master/license">
|
||||
<img src="https://img.shields.io/github/license/pkgems/npm-cache-proxy.svg" alt="Licence">
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
> `npm-cache-proxy` is a lightweight npm caching proxy written in Go that achieves warp speeds by using Redis for data storage. B-b-blazing fast!
|
||||
<br />
|
||||
<br />
|
||||
|
||||
## Introduction
|
||||
|
||||
#### ⚡️ Performance
|
||||
NCP is a tiny but very fast caching proxy written in Go. It uses Redis for data storage, which in combination with the speed of Go makes it incredibly fast. NCP is well-optimized and can be run on almost any platform, so if you have a Raspberry Pi, you can install NCP as your local cache there.
|
||||
|
||||
#### ✨ Modularity
|
||||
NCP is modular. Now it has only one database adapter which is Redis. If you need support for any other database, feel free to open an issue or implement it [on your own](https://github.com/pkgems/npm-cache-proxy/blob/7c8b90ff6ba0656f60e3de915b9fb4eaabfb467b/proxy/proxy.go#L29) and then open a pull request (_bonus points_).
|
||||
|
||||
#### 💡 Simplicity
|
||||
NCP is very simple. It just proxies requests to an upstream registry, caches response and returns cached response for next requests to the same package. Cached data are stored in Redis with an original request URL as a key.
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Table of Contents</summary>
|
||||
<p>
|
||||
<br />
|
||||
|
||||
- [Getting started](#getting-started)
|
||||
- [CLI](#cli)
|
||||
- [`ncp`](#ncp)
|
||||
- [`ncp list`](#ncp-list)
|
||||
- [`ncp purge`](#ncp-purge)
|
||||
- [Programmatic usage](#programmatic-usage)
|
||||
- [Example](#example)
|
||||
- [Deployment](#deployment)
|
||||
- [Benchmark](#benchmark)
|
||||
- [License](#license)
|
||||
|
||||
</p>
|
||||
</details>
|
||||
## Installation
|
||||
NCP binaries for different paltforms can be downloaded can be downloaded on the [Releases](https://github.com/pkgems/npm-cache-proxy/releases) page. Also, Docker image is provided on [Docker Hub](https://cloud.docker.com/u/pkgems/repository/docker/pkgems/npm-cache-proxy).
|
||||
|
||||
## Getting started
|
||||
Release binaries for different platforms can be downloaded on the [Releases](https://github.com/emeralt/npm-cache-proxy/releases) page. Also, [Docker image](https://cloud.docker.com/u/emeralt/repository/docker/emeralt/npm-cache-proxy) is provided.
|
||||
#### 💫 Quick Start
|
||||
The quickies way to get started with NCP is to use Docker.
|
||||
|
||||
The fastest way to get started with NCP is to use Docker image:
|
||||
```bash
|
||||
# run proxy inside of docker container in background
|
||||
docker run -e REDIS_ADDRESS=host.docker.internal:6379 -p 8080:8080 -it emeralt/npm-cache-proxy -d
|
||||
docker run -e REDIS_ADDRESS=host.docker.internal:6379 -p 8080:8080 -it -d pkgems/npm-cache-proxy
|
||||
|
||||
# configure npm to use caching proxy as registry
|
||||
npm config set registry http://localhost:8080
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
## CLI
|
||||
Additionally, NCP provides a command line utility for proxy configuration and data management.
|
||||
NCP provides command line interface for interaction with a cached data.
|
||||
|
||||
---
|
||||
<details>
|
||||
<summary>Options</summary>
|
||||
|
||||
| Global Options | Env | Default | Description |
|
||||
| ----------------------------- | ---------------- | ----------------------- | ----------------- |
|
||||
| `--redis-address <address>` | `REDIS_ADDRESS` | `http://localhost:6379` | Redis address |
|
||||
| `--redis-database <database>` | `REDIS_DATABASE` | `0` | Redis database |
|
||||
| `--redis-password <password>` | `REDIS_PASSWORD` | - | Redis password |
|
||||
| `--redis-prefix <prefix>` | `REDIS_PREFIX` | `ncp-` | Redis keys prefix |
|
||||
| Options | Env | Default | Description |
|
||||
| ----------------------------- | ------------------ | ---------------------------- | ----------------------------------- |
|
||||
| `--listen <address>` | `LISTEN_ADDRESS` | `locahost:8080` | Address to listen |
|
||||
| `--upstream <address>` | `UPSTREAM_ADDRESS` | `https://registry.npmjs.org` | Upstream registry address |
|
||||
| `--silent <address>` | `SILENT` | `0` | Disable logs |
|
||||
| `--cache-limit <count>` | `CACHE_LIMIT` | - | Cached packages count limit |
|
||||
| `--cache-ttl <timeout>` | `CACHE_TTL` | `3600` | Cache expiration timeout in seconds |
|
||||
| `--redis-address <address>` | `REDIS_ADDRESS` | `http://localhost:6379` | Redis address |
|
||||
| `--redis-database <database>` | `REDIS_DATABASE` | `0` | Redis database |
|
||||
| `--redis-password <password>` | `REDIS_PASSWORD` | - | Redis password |
|
||||
| `--redis-prefix <prefix>` | `REDIS_PREFIX` | `ncp-` | Redis keys prefix |
|
||||
|
||||
---
|
||||
</details>
|
||||
|
||||
### `ncp`
|
||||
#### `ncp`
|
||||
Start NCP server.
|
||||
|
||||
Start proxy server.
|
||||
#### `ncp list`
|
||||
List cached url paths.
|
||||
|
||||
#### `ncp purge`
|
||||
Purge cached url paths.
|
||||
|
||||
|
||||
<br />
|
||||
|
||||
|
||||
## Benchmark
|
||||
Benchmark is run on Macbook Pro 15″ 2017, Intel Core i7-7700HQ.
|
||||
|
||||
#### 1️⃣ 1 process
|
||||
|
||||
```bash
|
||||
ncp --listen "localhost:1234" # listen on port 1234
|
||||
# GOMAXPROCS=1 ncp --silent
|
||||
|
||||
$ go-wrk -c 100 -d 6 http://localhost:8080/tiny-tarball
|
||||
Running 6s test @ http://localhost:8080/tiny-tarball
|
||||
100 goroutine(s) running concurrently
|
||||
|
||||
70755 requests in 5.998378587s, 91.16MB read
|
||||
|
||||
Requests/sec: 11795.69
|
||||
Transfer/sec: 15.20MB
|
||||
Avg Req Time: 8.477674ms
|
||||
Fastest Request: 947.743µs
|
||||
Slowest Request: 815.787409ms
|
||||
Number of Errors: 0
|
||||
```
|
||||
|
||||
| Options | Env | Default | Description |
|
||||
| ----------------------- | ------------------ | ---------------------------- | ----------------------------------- |
|
||||
| `--listen <address>` | `LISTEN_ADDRESS` | `locahost:8080` | Address to listen |
|
||||
| `--upstream <address>` | `UPSTREAM_ADDRESS` | `https://registry.npmjs.org` | Upstream registry address |
|
||||
| `--cache-limit <count>` | `CACHE_LIMIT` | - | Cached packages count limit |
|
||||
| `--cache-ttl <timeout>` | `CACHE_TTL` | `3600` | Cache expiration timeout in seconds |
|
||||
| `--silent <address>` | `SILENT` | `0` | Disable logs |
|
||||
|
||||
---
|
||||
|
||||
### `ncp list`
|
||||
List all cached packages.
|
||||
|
||||
---
|
||||
|
||||
### `ncp purge`
|
||||
Purge all cached packages.
|
||||
|
||||
---
|
||||
|
||||
## Programmatic usage
|
||||
Along with the CLI, go package is provided. Documentation is available on [godoc.org](https://godoc.org/github.com/emeralt/npm-cache-proxy/proxy).
|
||||
#### ♾ unlimited processes
|
||||
|
||||
```bash
|
||||
go get -u github.com/emeralt/npm-cache-proxy/proxy
|
||||
# ncp --silent
|
||||
|
||||
$ go-wrk -c 100 -d 6 http://localhost:8080/tiny-tarball
|
||||
Running 6s test @ http://localhost:8080/tiny-tarball
|
||||
100 goroutine(s) running concurrently
|
||||
|
||||
115674 requests in 5.98485984s, 149.04MB read
|
||||
|
||||
Requests/sec: 19327.77
|
||||
Transfer/sec: 24.90MB
|
||||
Avg Req Time: 5.173902ms
|
||||
Fastest Request: 273.015µs
|
||||
Slowest Request: 34.777963ms
|
||||
Number of Errors: 0
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
<br />
|
||||
|
||||
|
||||
## Programmatic Usage
|
||||
NCP provides `proxy` go package that can be used programmatically. Docs are available on [godoc.org](https://godoc.org/github.com/pkgems/npm-cache-proxy/proxy).
|
||||
|
||||
#### 🤖 Example
|
||||
```golang
|
||||
package main
|
||||
|
||||
@ -106,24 +137,30 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
npmproxy "github.com/emeralt/npm-cache-proxy/proxy"
|
||||
redis "github.com/go-redis/redis"
|
||||
npmproxy "github.com/pkgems/npm-cache-proxy/proxy"
|
||||
"github.com/go-redis/redis"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// create proxy
|
||||
proxy := npmproxy.Proxy{
|
||||
// you can provide you own Database
|
||||
// or use an existing one
|
||||
// use redis as database
|
||||
Database: npmproxy.DatabaseRedis{
|
||||
// see github.com/go-redis/redis
|
||||
Client: redis.NewClient(&redis.Options{
|
||||
Addr: "localhost:6379",
|
||||
}),
|
||||
},
|
||||
|
||||
// allows to reuse tcp connections
|
||||
// reuse connections
|
||||
HttpClient: &http.Client{},
|
||||
}
|
||||
|
||||
// allows to get options dynamically
|
||||
// create and start server
|
||||
proxy.Server(npmproxy.ServerOptions{
|
||||
ListenAddress: "localhost:8080",
|
||||
|
||||
// allow fetching options dynamically on each request
|
||||
GetOptions: func() (npmproxy.Options, error) {
|
||||
return npmproxy.Options{
|
||||
DatabasePrefix: "ncp-",
|
||||
@ -131,36 +168,13 @@ func main() {
|
||||
UpstreamAddress: "https://registry.npmjs.org",
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
||||
// listen on http://localhost:8080
|
||||
proxy.Server(npmproxy.ServerOptions{
|
||||
ListenAddress: "localhost:8080",
|
||||
}).ListenAndServe()
|
||||
}
|
||||
```
|
||||
|
||||
## Deployment
|
||||
NCP can be deployed using Kubernetes, Docker Compose or any other container orchestration platform. NCP supports running indefinite amount of instances simultaneously.
|
||||
|
||||
## Benchmark
|
||||
Macbook Pro 15″ 2017, Intel Core i7-7700HQ. Note `GOMAXPROCS=1`.
|
||||
<br />
|
||||
|
||||
```bash
|
||||
# SILENT=1 GIN_MODE=release GOMAXPROCS=1 ncp
|
||||
|
||||
$ go-wrk -c 100 -d 10 http://localhost:8080/ascii
|
||||
Running 10s test @ http://localhost:8080/ascii
|
||||
100 goroutine(s) running concurrently
|
||||
84216 requests in 10.000196326s, 535.30MB read
|
||||
Requests/sec: 8421.43
|
||||
Transfer/sec: 53.53MB
|
||||
Avg Req Time: 11.874461ms
|
||||
Fastest Request: 2.213324ms
|
||||
Slowest Request: 745.874068ms
|
||||
Number of Errors: 0
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[MIT](./license)
|
||||
|
Loading…
x
Reference in New Issue
Block a user