diff --git a/.github/workflows/docker-build-publish.yaml b/.github/workflows/docker-build-publish.yaml new file mode 100644 index 0000000..ef0fef9 --- /dev/null +++ b/.github/workflows/docker-build-publish.yaml @@ -0,0 +1,25 @@ +name: Docker Build & Publish + +# Trigger on all push events, new semantic version tags, and all PRs +on: + push: + branches: + - "main" + - "v[0-9].[0-9].x" + - "v[0-9].[0-9][0-9].x" + - "v[0-9].x" + tags: + - "v[0-9]+.[0-9]+.[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+" + pull_request: + +jobs: + docker-security-build: + permissions: + contents: write + packages: write + uses: celestiaorg/.github/.github/workflows/reusable_dockerfile_pipeline.yml@v0.2.8 # yamllint disable-line rule:line-length + with: + dockerfile: Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a4d2956 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +# Use the official Golang image to create a build artifact. +FROM golang:1.21.1 AS builder + +# Set the working directory inside the container. +WORKDIR /app + +# Copy go mod and sum files +#COPY go.mod go.sum ./ +COPY go.mod ./ + +# Download all dependencies. +RUN go mod download + +# Copy the source code into the container. +COPY . . + +# Build the Go app +RUN CGO_ENABLED=0 GOOS=linux go build -o main . + +# Use a lightweight image for the final image. +FROM alpine:3 + +# Copy the binary. +COPY --from=builder /app/main /app/main + +# Run the binary. +CMD ["/app/main"] diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..9999438 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/celestiaorg/autoscale-proxy + +go 1.21.1 diff --git a/main.go b/main.go new file mode 100644 index 0000000..75650b6 --- /dev/null +++ b/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "bytes" + "io" + "net/http" + "strings" +) + +func replaceDomainInResponse(originalSubdomain, replaceSubdomain, originalDomain string, buffer *bytes.Buffer) { + body := buffer.String() + fullReplace := replaceSubdomain + "." + "lunaroasis.net" // We know that statescale and snapscale are under this domain + fullOriginal := originalSubdomain + "." + originalDomain // Original domain can vary + replacedBody := strings.ReplaceAll(body, fullReplace, fullOriginal) + buffer.Reset() + buffer.WriteString(replacedBody) +} + +func proxyRequest(fullSubdomain, path string, buffer *bytes.Buffer, r *http.Request) (int, map[string]string, error) { + client := &http.Client{} + target := "https://" + fullSubdomain + ".lunaroasis.net" + path + newReq, err := http.NewRequest(r.Method, target, r.Body) + if err != nil { + return 0, nil, err + } + newReq.Header = r.Header + + resp, err := client.Do(newReq) + if err != nil { + return 0, nil, err + } + defer resp.Body.Close() + + headers := make(map[string]string) + for key, values := range resp.Header { + for _, value := range values { + headers[key] = value + } + } + + io.Copy(buffer, resp.Body) + return resp.StatusCode, headers, nil +} + +func handleRequest(w http.ResponseWriter, r *http.Request) { + hostParts := strings.Split(r.Host, ".") + if len(hostParts) < 3 { + http.Error(w, "Invalid domain", http.StatusBadRequest) + return + } + + subdomain := hostParts[0] // Extract original domain + originalDomain := strings.Join(hostParts[1:], ".") + + buffer := new(bytes.Buffer) + backupBuffer := new(bytes.Buffer) + + statusCode, headers, err := proxyRequest(subdomain+".statescale", r.RequestURI, buffer, r) + if err != nil || statusCode >= 400 { + backupStatusCode, backupHeaders, _ := proxyRequest(subdomain+".snapscale", r.RequestURI, backupBuffer, r) + replaceDomainInResponse(subdomain, subdomain+".snapscale", originalDomain, backupBuffer) + + for key, value := range backupHeaders { + w.Header().Set(key, value) + } + w.WriteHeader(backupStatusCode) + io.Copy(w, backupBuffer) + return + } + + replaceDomainInResponse(subdomain, subdomain+".statescale", originalDomain, buffer) + for key, value := range headers { + w.Header().Set(key, value) + } + w.WriteHeader(statusCode) + io.Copy(w, buffer) +} + +func main() { + http.HandleFunc("/", handleRequest) + http.ListenAndServe(":8080", nil) +}