6 Commits

Author SHA1 Message Date
83dfcba4ae move x-accel into local storage configuration
All checks were successful
build container / build-container (push) Successful in 5m45s
run go test / test (push) Successful in 3m15s
2025-03-03 09:15:37 +08:00
504a809c16 add pypi to config
Some checks failed
build container / build-container (push) Failing after 21s
2025-03-03 09:07:26 +08:00
f9ff71f62a add pprof handler
Some checks failed
build container / build-container (push) Failing after 22s
run go test / test (push) Failing after 19s
2025-03-03 00:26:29 +08:00
629c095bbe update golang sdk to 1.24
All checks were successful
build container / build-container (push) Successful in 6m19s
2025-02-25 09:56:30 +08:00
9b34fd29c0 fix goproxy on gitea action
All checks were successful
build container / build-container (push) Successful in 5m21s
2025-02-23 12:13:39 +08:00
3cfa6c6116 improve container build
Some checks failed
build container / build-container (push) Failing after 4m43s
2025-02-23 12:07:05 +08:00
10 changed files with 100 additions and 50 deletions

View File

@ -3,9 +3,6 @@ name: build container
run-name: build container on ${{ gitea.actor }} run-name: build container on ${{ gitea.actor }}
on: [push] on: [push]
env:
GOPROXY: ${{ vars.GOPROXY }}
jobs: jobs:
build-container: build-container:
runs-on: docker runs-on: docker
@ -24,7 +21,7 @@ jobs:
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Build container - name: Build container
run: REGISTRY=${{ github.server_url}}; REGISTRY=${REGISTRY##https://}; REGISTRY=${REGISTRY##http://}; podman build -t $REGISTRY/${{ github.repository }} . run: REGISTRY=${{ github.server_url}}; REGISTRY=${REGISTRY##https://}; REGISTRY=${REGISTRY##http://}; podman build --build-arg GOPROXY=${{ vars.GOPROXY }} -t $REGISTRY/${{ github.repository }} .
- name: Login to Container Registry - name: Login to Container Registry
run: echo "${{ secrets.ACTION_PACKAGE_WRITE_TOKEN }}" | podman login ${{ github.server_url }} -u ${{ github.repository_owner }} --password-stdin run: echo "${{ secrets.ACTION_PACKAGE_WRITE_TOKEN }}" | podman login ${{ github.server_url }} -u ${{ github.repository_owner }} --password-stdin
- name: Push Container Image - name: Push Container Image

3
.gitignore vendored
View File

@ -2,3 +2,6 @@
data data
__debug* __debug*
.env
.envrc

View File

@ -1,10 +1,12 @@
FROM docker.io/library/golang:1.23-alpine FROM docker.io/library/golang:1.24-alpine
ARG GOPROXY ARG GOPROXY=direct
ARG TAGS=""
ARG ALPINE_MIRROR=https://mirrors.ustc.edu.cn
WORKDIR /src WORKDIR /src
COPY go.mod go.sum ./ COPY go.mod go.sum ./
RUN apk add git && env GOPROXY=${GOPROXY:-direct} go mod download RUN sed -i "s,https://dl-cdn.alpinelinux.org,${ALPINE_MIRROR}," /etc/apk/repositories; apk add git && env GOPROXY=${GOPROXY:-direct} go mod download
ADD . /src ADD . /src
RUN go build -o /cache-proxy ./cmd/proxy RUN go build -o /cache-proxy -tags "${TAGS}" ./cmd/proxy
FROM docker.io/library/alpine:3.21 AS runtime FROM docker.io/library/alpine:3.21 AS runtime
COPY --from=0 /cache-proxy /bin/cache-proxy COPY --from=0 /cache-proxy /bin/cache-proxy

17
cmd/proxy/debug.go Normal file
View File

@ -0,0 +1,17 @@
//go:build pprof
package main
import (
"flag"
"os"
)
func init() {
flag.BoolVar(&pprofEnabled, "pprof", true, "")
if v, ok := os.LookupEnv("SENTRY_DSN"); ok {
sentrydsn = v
}
flag.StringVar(&sentrydsn, "sentry", sentrydsn, "sentry dsn to report errors")
}

View File

@ -4,6 +4,7 @@ import (
"flag" "flag"
"log/slog" "log/slog"
"net/http" "net/http"
"net/http/pprof"
"os" "os"
"path/filepath" "path/filepath"
"time" "time"
@ -22,6 +23,7 @@ var (
configFilePath = "config.yaml" configFilePath = "config.yaml"
logLevel = "info" logLevel = "info"
sentrydsn = "" sentrydsn = ""
pprofEnabled = false
) )
func init() { func init() {
@ -31,12 +33,8 @@ func init() {
if v, ok := os.LookupEnv("LOG_LEVEL"); ok { if v, ok := os.LookupEnv("LOG_LEVEL"); ok {
logLevel = v logLevel = v
} }
if v, ok := os.LookupEnv("SENTRY_DSN"); ok {
sentrydsn = v
}
flag.StringVar(&configFilePath, "config", configFilePath, "path to config file") flag.StringVar(&configFilePath, "config", configFilePath, "path to config file")
flag.StringVar(&logLevel, "log-level", logLevel, "log level. (trace, debug, info, warn, error)") flag.StringVar(&logLevel, "log-level", logLevel, "log level. (trace, debug, info, warn, error)")
flag.StringVar(&sentrydsn, "sentry", sentrydsn, "sentry dsn to report errors")
} }
func configFromFile(path string) (*cacheproxy.Config, error) { func configFromFile(path string) (*cacheproxy.Config, error) {
@ -57,9 +55,9 @@ func configFromFile(path string) (*cacheproxy.Config, error) {
Local: &cacheproxy.LocalStorage{ Local: &cacheproxy.LocalStorage{
Path: "./data", Path: "./data",
TemporaryFilePattern: "temp.*", TemporaryFilePattern: "temp.*",
},
Accel: cacheproxy.Accel{ Accel: cacheproxy.Accel{
ResponseWithHeaders: []string{"X-Sendfile", "X-Accel-Redirect"}, RespondWithHeaders: []string{"X-Sendfile", "X-Accel-Redirect"},
},
}, },
}, },
Misc: cacheproxy.MiscConfig{ Misc: cacheproxy.MiscConfig{
@ -124,6 +122,14 @@ func main() {
mux.HandleFunc("GET /", server.HandleRequestWithCache) mux.HandleFunc("GET /", server.HandleRequestWithCache)
if pprofEnabled {
mux.HandleFunc("GET /debug/pprof/", pprof.Index)
mux.HandleFunc("GET /debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("GET /debug/pprof/profile", pprof.Profile)
mux.HandleFunc("GET /debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("GET /debug/pprof/trace", pprof.Trace)
}
slog.With("addr", ":8881").Info("serving app") slog.With("addr", ":8881").Info("serving app")
if err := http.ListenAndServe(":8881", middleware.Use(mux, if err := http.ListenAndServe(":8881", middleware.Use(mux,
recover.Recover(), recover.Recover(),

20
compose.release.yaml Normal file
View File

@ -0,0 +1,20 @@
services:
cache-proxy:
image: ${REPOSITORY:-git.jeffthecoder.xyz}/${OWNER:-public}/cache-proxy:${VERSION:-latest}
build:
args:
GOPROXY: ${GOPROXY:-direct}
ALPINE_MIRROR: ${ALPINE_MIRROR:-https://mirrors.ustc.edu.cn}
restart: always
volumes:
- ./config.yaml:/config.yaml:ro
- ./data:/data
ports:
- 8881:8881
environment:
- LOG_LEVEL=info
env_file:
- path: .env
required: false

View File

@ -1,11 +1,12 @@
services: services:
cache-proxy: cache-proxy:
image: 100.64.0.2:3000/guochao/cache-proxy image: ${REPOSITORY:-git.jeffthecoder.xyz}/${OWNER:-public}/cache-proxy:${VERSION:-latest}
build: build:
args: args:
GOPROXY: ${GOPROXY:-direct} GOPROXY: ${GOPROXY:-direct}
ALPINE_MIRROR: ${ALPINE_MIRROR:-https://mirrors.ustc.edu.cn}
TAGS: pprof
pull_policy: always
restart: always restart: always
volumes: volumes:
@ -14,8 +15,8 @@ services:
ports: ports:
- 8881:8881 - 8881:8881
environment: environment:
- HTTPS_PROXY=http://100.64.0.23:7890 - SENTRY_DSN=${SENTRY_DSN}
- HTTP_PROXY=http://100.64.0.23:7890
- ALL_PROXY=http://100.64.0.23:7890
- SENTRY_DSN=http://3b7336602c77427d96318074cd86ee04@100.64.0.2:8000/2
- LOG_LEVEL=debug - LOG_LEVEL=debug
env_file:
- path: .env
required: false

View File

@ -36,17 +36,18 @@ func (upstream Upstream) GetPath(orig string) (string, bool, error) {
type LocalStorage struct { type LocalStorage struct {
Path string `yaml:"path"` Path string `yaml:"path"`
TemporaryFilePattern string `yaml:"temporary-file-pattern"` TemporaryFilePattern string `yaml:"temporary-file-pattern"`
Accel Accel `yaml:"accel"`
} }
type Accel struct { type Accel struct {
EnableByHeader string `yaml:"enable-by-header"` EnableByHeader string `yaml:"enable-by-header"`
ResponseWithHeaders []string `yaml:"response-with-headers"` RespondWithHeaders []string `yaml:"respond-with-headers"`
} }
type Storage struct { type Storage struct {
Type string `yaml:"type"` Type string `yaml:"type"`
Local *LocalStorage `yaml:"local"` Local *LocalStorage `yaml:"local"`
Accel Accel `yaml:"accel"`
} }
type CachePolicyOnPath struct { type CachePolicyOnPath struct {

View File

@ -1,7 +1,7 @@
upstream: upstream:
- server: https://mirrors.aliyun.com - server: https://mirrors.aliyun.com
match: match:
match: /(debian|ubuntu|ubuntu-releases|alpine|archlinux|kali|manjaro|msys2|almalinux|rocky|centos|centos-stream|centos-vault|fedora|epel|gnu)/(.*) match: /(debian|ubuntu|ubuntu-releases|alpine|archlinux|kali|manjaro|msys2|almalinux|rocky|centos|centos-stream|centos-vault|fedora|epel|gnu|pypi)/(.*)
replace: '/$1/$2' replace: '/$1/$2'
- server: https://mirrors.tencent.com - server: https://mirrors.tencent.com
match: match:
@ -9,7 +9,7 @@ upstream:
replace: '/$1/$2' replace: '/$1/$2'
- server: https://mirrors.ustc.edu.cn - server: https://mirrors.ustc.edu.cn
match: match:
match: /(debian|ubuntu|ubuntu-releases|alpine|archlinux|kali|manjaro|msys2|almalinux|rocky|centos|centos-stream|centos-vault|fedora|epel|elrepo|remi|rpmfusion|tailscale|gnu|rust-static)/(.*) match: /(debian|ubuntu|ubuntu-releases|alpine|archlinux|kali|manjaro|msys2|almalinux|rocky|centos|centos-stream|centos-vault|fedora|epel|elrepo|remi|rpmfusion|tailscale|gnu|rust-static|pypi)/(.*)
replace: '/$1/$2' replace: '/$1/$2'
- server: https://packages.microsoft.com/repos/code - server: https://packages.microsoft.com/repos/code
match: match:
@ -88,21 +88,21 @@ upstream:
allowed-redirect: "^https?://cf-builds.garudalinux.org/.*/chaotic-aur/.*$" allowed-redirect: "^https?://cf-builds.garudalinux.org/.*/chaotic-aur/.*$"
misc: misc:
first-chunk-bytes: 31457280 # 1024*1024*30, all upstreams with 200 response will wait for the first chunks to select a upstream by bandwidth, instead of by latency. defaults to 50M first-chunk-bytes: 31457280 ## 1024*1024*30, all upstreams with 200 response will wait for the first chunks to select a upstream by bandwidth, instead of by latency. defaults to 50M
# chunk-bytes: 1048576 # 1024*1024 # chunk-bytes: 1048576 ## 1024*1024
cache: cache:
timeout: 1h timeout: 1h
policies: policies:
- match: '.*\.(rpm|deb|apk|iso)$' # rpm/deb/apk/iso won't change, create only - match: '.*\.(rpm|deb|apk|iso)$' ## rpm/deb/apk/iso won't change, create only
refresh-after: never refresh-after: never
- match: '^/.*-security/.*' # mostly ubuntu/debian security - match: '^/.*-security/.*' ## mostly ubuntu/debian security
refresh-after: 1h refresh-after: 1h
- match: '^/archlinux-localaur/.*' # archlinux local repo - match: '^/archlinux-localaur/.*' ## archlinux local repo
refresh-after: never refresh-after: never
- match: '^/(archlinux.*|chaotic-aur)/*.tar.*' # archlinux packages - match: '^/(archlinux.*|chaotic-aur)/*.tar.*' ## archlinux packages
refresh-after: never refresh-after: never
- match: '/chaotic-aur/.*\.db$' # archlinux chaotic-aur database - match: '/chaotic-aur/.*\.db$' ## archlinux chaotic-aur database
refresh-after: 24h refresh-after: 24h
- match: '/centos/7' - match: '/centos/7'
refresh-after: never refresh-after: never
@ -110,12 +110,12 @@ cache:
refresh-after: never refresh-after: never
storage: storage:
type: local # ignored type: local ## ignored for now
local: local:
path: ./data # defaults to ./data path: ./data ## defaults to ./data
accel: accel:
# example nginx config: ## example nginx config:
## location /i { ## location /i {
## internal; ## internal;
## alias /path/to/data; ## alias /path/to/data;
@ -125,5 +125,8 @@ storage:
## proxy_set_header X-Accel-Path /i; ## proxy_set_header X-Accel-Path /i;
## } ## }
## ##
## then cache proxy will respond to backend a header to indicate sendfile(join(X-Accel-Path, relpath))
# enable-by-header: "X-Accel-Path" # enable-by-header: "X-Accel-Path"
## respond with different headers to
# respond-with-headers: [X-Sendfile, X-Accel-Redirect]

View File

@ -107,7 +107,7 @@ type Chunk struct {
} }
func (server *Server) serveFile(w http.ResponseWriter, r *http.Request, path string) { func (server *Server) serveFile(w http.ResponseWriter, r *http.Request, path string) {
if location := r.Header.Get(server.Storage.Accel.EnableByHeader); server.Storage.Accel.EnableByHeader != "" && location != "" { if location := r.Header.Get(server.Storage.Local.Accel.EnableByHeader); server.Storage.Local.Accel.EnableByHeader != "" && location != "" {
relPath, err := filepath.Rel(server.Storage.Local.Path, path) relPath, err := filepath.Rel(server.Storage.Local.Path, path)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
@ -115,7 +115,7 @@ func (server *Server) serveFile(w http.ResponseWriter, r *http.Request, path str
} }
accelPath := filepath.Join(location, relPath) accelPath := filepath.Join(location, relPath)
for _, headerKey := range server.Storage.Accel.ResponseWithHeaders { for _, headerKey := range server.Storage.Local.Accel.RespondWithHeaders {
w.Header().Set(headerKey, accelPath) w.Header().Set(headerKey, accelPath)
} }