diff --git a/.gitignore b/.gitignore index 5e9c044..8cdd224 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,9 @@ src/tmp/localhost.pem src/www/html/index.html src/sys.uuid src/zoraxy -src/log/ \ No newline at end of file +src/log/ + + +# dev-tags +/Dockerfile +/Entrypoint.sh \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile index 2099358..5c3c5cd 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,19 +1,27 @@ +# Stage 1: Build Zoraxy FROM docker.io/golang:alpine AS build-zoraxy +# Install dependencies RUN mkdir -p /opt/zoraxy/source/ &&\ mkdir -p /usr/local/bin/ -# If you build it yourself, you will need to add the src directory into the docker directory. -COPY ./src/ /opt/zoraxy/source/ - WORKDIR /opt/zoraxy/source/ -RUN go mod tidy &&\ - go build -o /usr/local/bin/zoraxy &&\ +# Copy go.mod and go.sum first to leverage Docker cache +COPY ./src/go.mod ./src/go.sum ./ +RUN go mod tidy + +# Copy the rest of the source code +COPY ./src/ . + +# Build the application +RUN go build -o /usr/local/bin/zoraxy &&\ chmod 755 /usr/local/bin/zoraxy +# Stage 2: Build ZeroTier FROM docker.io/ubuntu:latest AS build-zerotier +# Install dependencies RUN mkdir -p /opt/zerotier/source/ &&\ mkdir -p /usr/local/bin/ @@ -22,6 +30,7 @@ WORKDIR /opt/zerotier/source/ RUN apt-get update -y &&\ apt-get install -y curl jq build-essential pkg-config clang cargo libssl-dev +# Download and build ZeroTier RUN curl -Lo ZeroTierOne.tar.gz https://codeload.github.com/zerotier/ZeroTierOne/tar.gz/refs/tags/1.10.6 &&\ tar -xzvf ZeroTierOne.tar.gz &&\ cd ZeroTierOne-* &&\ @@ -29,19 +38,24 @@ RUN curl -Lo ZeroTierOne.tar.gz https://codeload.github.com/zerotier/ZeroTierOne mv ./zerotier-one /usr/local/bin/zerotier-one &&\ chmod 755 /usr/local/bin/zerotier-one +# Stage 3: Final image FROM docker.io/ubuntu:latest +# Install runtime dependencies RUN apt-get update -y &&\ apt-get install -y bash sudo netcat-openbsd libssl-dev ca-certificates +# Copy entrypoint script COPY --chmod=700 ./entrypoint.sh /opt/zoraxy/ + +# Copy built binaries from previous stages COPY --from=build-zoraxy /usr/local/bin/zoraxy /usr/local/bin/zoraxy COPY --from=build-zerotier /usr/local/bin/zerotier-one /usr/local/bin/zerotier-one WORKDIR /opt/zoraxy/config/ +# Set environment variables ENV ZEROTIER="false" - ENV AUTORENEW="86400" ENV CFGUPGRADE="true" ENV DB="auto" @@ -60,9 +74,12 @@ ENV WEBROOT="./www" ENV ZTAUTH="" ENV ZTPORT="9993" +# Define volumes VOLUME [ "/opt/zoraxy/config/" ] +# Set entrypoint ENTRYPOINT [ "/opt/zoraxy/entrypoint.sh" ] +# Healthcheck HEALTHCHECK --interval=15s --timeout=5s --start-period=10s --retries=3 CMD nc -vz 127.0.0.1 $PORT || exit 1 diff --git a/src/config.go b/src/config.go index eacea02..62468f8 100644 --- a/src/config.go +++ b/src/config.go @@ -54,6 +54,11 @@ func LoadReverseProxyConfig(configFilepath string) error { return err } + //Make sure the tags are not nil + if thisConfigEndpoint.Tags == nil { + thisConfigEndpoint.Tags = []string{} + } + //Matching domain not set. Assume root if thisConfigEndpoint.RootOrMatchingDomain == "" { thisConfigEndpoint.RootOrMatchingDomain = "/" @@ -175,8 +180,8 @@ func ExportConfigAsZip(w http.ResponseWriter, r *http.Request) { // Set the Content-Type header to indicate it's a zip file w.Header().Set("Content-Type", "application/zip") - // Set the Content-Disposition header to specify the file name - w.Header().Set("Content-Disposition", "attachment; filename=\"config.zip\"") + // Set the Content-Disposition header to specify the file name, add timestamp to the filename + w.Header().Set("Content-Disposition", "attachment; filename=\"zoraxy-config-"+time.Now().Format("2006-01-02-15-04-05")+".zip\"") // Create a zip writer zipWriter := zip.NewWriter(w) diff --git a/src/mod/dynamicproxy/typedef.go b/src/mod/dynamicproxy/typedef.go index c7a060d..2919a7e 100644 --- a/src/mod/dynamicproxy/typedef.go +++ b/src/mod/dynamicproxy/typedef.go @@ -194,6 +194,7 @@ type ProxyEndpoint struct { //Internal Logic Elements parent *Router `json:"-"` + Tags []string // Tags for the proxy endpoint } /* diff --git a/src/reverseproxy.go b/src/reverseproxy.go index 3ecdcb2..2f73e07 100644 --- a/src/reverseproxy.go +++ b/src/reverseproxy.go @@ -305,6 +305,15 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { } } + tagStr, _ := utils.PostPara(r, "tags") + tags := []string{} + if tagStr != "" { + tags = strings.Split(tagStr, ",") + for i := range tags { + tags[i] = strings.TrimSpace(tags[i]) + } + } + var proxyEndpointCreated *dynamicproxy.ProxyEndpoint if eptype == "host" { rootOrMatchingDomain, err := utils.PostPara(r, "rootname") @@ -375,6 +384,8 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { // Rate Limit RequireRateLimit: requireRateLimit, RateLimit: int64(proxyRateLimit), + + Tags: tags, } preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&thisProxyEndpoint) @@ -533,6 +544,15 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { return } + tagStr, _ := utils.PostPara(r, "tags") + tags := []string{} + if tagStr != "" { + tags = strings.Split(tagStr, ",") + for i := range tags { + tags[i] = strings.TrimSpace(tags[i]) + } + } + //Generate a new proxyEndpoint from the new config newProxyEndpoint := dynamicproxy.CopyEndpoint(targetProxyEntry) newProxyEndpoint.BypassGlobalTLS = bypassGlobalTLS @@ -557,6 +577,7 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { newProxyEndpoint.RateLimit = proxyRateLimit newProxyEndpoint.UseStickySession = useStickySession newProxyEndpoint.DisableUptimeMonitor = disbleUtm + newProxyEndpoint.Tags = tags //Prepare to replace the current routing rule readyRoutingRule, err := dynamicProxyRouter.PrepareProxyRoute(newProxyEndpoint) diff --git a/src/web/components/httprp.html b/src/web/components/httprp.html index 8ecd402..1624fc5 100644 --- a/src/web/components/httprp.html +++ b/src/web/components/httprp.html @@ -12,6 +12,19 @@ min-width: 200px; } +
Host | Destination | Virtual Directory | +Tags | Advanced Settings | Actions | @@ -124,6 +138,9 @@${vdList} | ++ ${subd.Tags.map(tag => `${tag}`).join("")} + | ${subd.AuthenticationProvider.AuthMethod == 0x1?` Basic Auth`:``} ${subd.AuthenticationProvider.AuthMethod == 0x2?` Authelia`:``} @@ -142,12 +159,43 @@ | `); }); + populateTagFilterDropdown(data); } resolveAccessRuleNameOnHostRPlist(); }); } + // Function to populate the tag filter dropdown + function populateTagFilterDropdown(data) { + let tags = new Set(); + data.forEach(subd => { + subd.Tags.forEach(tag => tags.add(tag)); + }); + tags = Array.from(tags).sort((a, b) => a.localeCompare(b)); + let dropdownMenu = $("#tagFilterDropdown .menu"); + dropdownMenu.html('
---|