# Conflicts: # README.md # go.sumtags/v1.2.0
@@ -0,0 +1,89 @@ | |||||
name: Deploy multi-architecture Docker images for transfer.sh with buildx | |||||
on: | |||||
schedule: | |||||
- cron: '0 0 * * *' # everyday at midnight UTC | |||||
pull_request: | |||||
branches: master | |||||
push: | |||||
branches: master | |||||
tags: | |||||
- v* | |||||
jobs: | |||||
buildx: | |||||
runs-on: ubuntu-latest | |||||
steps: | |||||
- | |||||
name: Checkout | |||||
uses: actions/checkout@v2 | |||||
- | |||||
name: Prepare | |||||
id: prepare | |||||
run: | | |||||
DOCKER_IMAGE=dutchcoders/transfer.sh | |||||
DOCKER_PLATFORMS=linux/amd64,linux/arm/v7,linux/arm64,linux/386 | |||||
VERSION=edge | |||||
if [[ $GITHUB_REF == refs/tags/* ]]; then | |||||
VERSION=${GITHUB_REF#refs/tags/v} | |||||
fi | |||||
if [ "${{ github.event_name }}" = "schedule" ]; then | |||||
VERSION=nightly | |||||
fi | |||||
TAGS="--tag ${DOCKER_IMAGE}:${VERSION}" | |||||
if [ $VERSION = edge -o $VERSION = nightly ]; then | |||||
TAGS="$TAGS --tag ${DOCKER_IMAGE}:latest" | |||||
fi | |||||
echo ::set-output name=docker_image::${DOCKER_IMAGE} | |||||
echo ::set-output name=version::${VERSION} | |||||
echo ::set-output name=buildx_args::--platform ${DOCKER_PLATFORMS} \ | |||||
--build-arg VERSION=${VERSION} \ | |||||
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \ | |||||
--build-arg VCS_REF=${GITHUB_SHA::8} \ | |||||
${TAGS} . | |||||
- | |||||
name: Set up QEMU | |||||
uses: docker/setup-qemu-action@v1 | |||||
with: | |||||
platforms: all | |||||
- | |||||
name: Set up Docker Buildx | |||||
id: buildx | |||||
uses: docker/setup-buildx-action@v1 | |||||
with: | |||||
version: latest | |||||
- | |||||
name: Available platforms | |||||
run: echo ${{ steps.buildx.outputs.platforms }} | |||||
- | |||||
name: Docker Buildx (build) | |||||
run: | | |||||
docker buildx build --no-cache --pull --output "type=image,push=false" ${{ steps.prepare.outputs.buildx_args }} | |||||
- | |||||
name: Docker Login | |||||
if: success() && github.event_name != 'pull_request' | |||||
env: | |||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} | |||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} | |||||
run: | | |||||
echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin | |||||
- | |||||
name: Docker Buildx (push) | |||||
if: success() && github.event_name != 'pull_request' | |||||
run: | | |||||
docker buildx build --output "type=image,push=true" ${{ steps.prepare.outputs.buildx_args }} | |||||
- | |||||
name: Docker Check Manifest | |||||
if: always() && github.event_name != 'pull_request' | |||||
run: | | |||||
docker run --rm mplatform/mquery ${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.version }} | |||||
- | |||||
name: Clear | |||||
if: always() && github.event_name != 'pull_request' | |||||
run: | | |||||
rm -f ${HOME}/.docker/config.json |
@@ -12,7 +12,7 @@ WORKDIR /go/src/github.com/dutchcoders/transfer.sh | |||||
ENV GO111MODULE=on | ENV GO111MODULE=on | ||||
# build & install server | # build & install server | ||||
RUN go get -u ./... && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags -a -tags netgo -ldflags '-w -extldflags "-static"' -o /go/bin/transfersh github.com/dutchcoders/transfer.sh | |||||
RUN go get -u ./... && CGO_ENABLED=0 go build -ldflags -a -tags netgo -ldflags '-w -extldflags "-static"' -o /go/bin/transfersh github.com/dutchcoders/transfer.sh | |||||
FROM scratch AS final | FROM scratch AS final | ||||
LABEL maintainer="Andrea Spacca <andrea.spacca@gmail.com>" | LABEL maintainer="Andrea Spacca <andrea.spacca@gmail.com>" | ||||
@@ -76,42 +76,46 @@ https://transfer.sh/1lDau/test.txt --> https://transfer.sh/inline/1lDau/test.txt | |||||
Parameter | Description | Value | Env | Parameter | Description | Value | Env | ||||
--- | --- | --- | --- | --- | --- | --- | --- | ||||
listener | port to use for http (:80) | | | |||||
profile-listener | port to use for profiler (:6060)| | | |||||
force-https | redirect to https | false | | |||||
tls-listener | port to use for https (:443) | | | |||||
tls-listener-only | flag to enable tls listener only | | | |||||
tls-cert-file | path to tls certificate | | | |||||
tls-private-key | path to tls private key | | | |||||
http-auth-user | user for basic http auth on upload | | | |||||
http-auth-pass | pass for basic http auth on upload | | | |||||
ip-whitelist | comma separated list of ips allowed to connect to the service | | | |||||
ip-blacklist | comma separated list of ips not allowed to connect to the service | | | |||||
temp-path | path to temp folder | system temp | | |||||
web-path | path to static web files (for development or custom front end) | | | |||||
proxy-path | path prefix when service is run behind a proxy | | | |||||
ga-key | google analytics key for the front end | | | |||||
uservoice-key | user voice key for the front end | | | |||||
listener | port to use for http (:80) | | LISTENER | | |||||
profile-listener | port to use for profiler (:6060) | | PROFILE_LISTENER | | |||||
force-https | redirect to https | false | FORCE_HTTPS | |||||
tls-listener | port to use for https (:443) | | TLS_LISTENER | | |||||
tls-listener-only | flag to enable tls listener only | | TLS_LISTENER_ONLY | | |||||
tls-cert-file | path to tls certificate | | TLS_CERT_FILE | | |||||
tls-private-key | path to tls private key | | TLS_PRIVATE_KEY | | |||||
http-auth-user | user for basic http auth on upload | | HTTP_AUTH_USER | | |||||
http-auth-pass | pass for basic http auth on upload | | HTTP_AUTH_PASS | | |||||
ip-whitelist | comma separated list of ips allowed to connect to the service | | IP_WHITELIST | | |||||
ip-blacklist | comma separated list of ips not allowed to connect to the service | | IP_BLACKLIST | | |||||
temp-path | path to temp folder | system temp | TEMP_PATH | | |||||
web-path | path to static web files (for development or custom front end) | | WEB_PATH | | |||||
proxy-path | path prefix when service is run behind a proxy | | PROXY_PATH | | |||||
proxy-port | port of the proxy when the service is run behind a proxy | | PROXY_PORT | | |||||
ga-key | google analytics key for the front end | | GA_KEY | | |||||
provider | which storage provider to use | (s3, storj, gdrive or local) | | provider | which storage provider to use | (s3, storj, gdrive or local) | | ||||
aws-access-key | aws access key | | AWS_ACCESS_KEY | |||||
aws-secret-key | aws access key | | AWS_SECRET_KEY | |||||
bucket | aws bucket | | BUCKET | |||||
s3-endpoint | Custom S3 endpoint. | | | |||||
s3-region | region of the s3 bucket | eu-west-1 | S3_REGION | |||||
s3-no-multipart | disables s3 multipart upload | false | | | |||||
s3-path-style | Forces path style URLs, required for Minio. | false | | | |||||
storj-access | Access for the project | | STORJ_ACCESS | |||||
storj-bucket | Bucket to use within the project | | STORJ_BUCKET | |||||
basedir | path storage for local/gdrive provider| | | |||||
gdrive-client-json-filepath | path to oauth client json config for gdrive provider| | | |||||
gdrive-local-config-path | path to store local transfer.sh config cache for gdrive provider| | | |||||
gdrive-chunk-size | chunk size for gdrive upload in megabytes, must be lower than available memory (8 MB) | | | |||||
lets-encrypt-hosts | hosts to use for lets encrypt certificates (comma seperated) | | | |||||
log | path to log file| | | |||||
uservoice-key | user voice key for the front end | | USERVOICE_KEY | | |||||
aws-access-key | aws access key | | AWS_ACCESS_KEY | | |||||
aws-secret-key | aws access key | | AWS_SECRET_KEY | | |||||
bucket | aws bucket | | BUCKET | | |||||
s3-endpoint | Custom S3 endpoint. | | S3_ENDPOINT | | |||||
s3-region | region of the s3 bucket | eu-west-1 | S3_REGION | | |||||
s3-no-multipart | disables s3 multipart upload | false | S3_NO_MULTIPART | | |||||
s3-path-style | Forces path style URLs, required for Minio. | false | S3_PATH_STYLE | | |||||
storj-access | Access for the project | | STORJ_ACCESS | | |||||
storj-bucket | Bucket to use within the project | | STORJ_BUCKET | | |||||
basedir | path storage for local/gdrive provider | | BASEDIR | | |||||
gdrive-client-json-filepath | path to oauth client json config for gdrive provider | | GDRIVE_CLIENT_JSON_FILEPATH | | |||||
gdrive-local-config-path | path to store local transfer.sh config cache for gdrive provider| | GDRIVE_LOCAL_CONFIG_PATH | | |||||
gdrive-chunk-size | chunk size for gdrive upload in megabytes, must be lower than available memory (8 MB) | | GDRIVE_CHUNK_SIZE | | |||||
lets-encrypt-hosts | hosts to use for lets encrypt certificates (comma seperated) | | HOSTS | | |||||
log | path to log file| | LOG | | |||||
cors-domains | comma separated list of domains for CORS, setting it enable CORS | | CORS_DOMAINS | | |||||
clamav-host | host for clamav feature | | CLAMAV_HOST | | |||||
rate-limit | request per minute | | RATE_LIMIT | | |||||
If you want to use TLS using lets encrypt certificates, set lets-encrypt-hosts to your domain, set tls-listener to :443 and enable force-https. | If you want to use TLS using lets encrypt certificates, set lets-encrypt-hosts to your domain, set tls-listener to :443 and enable force-https. | ||||
If you want to use TLS using your own certificates, set tls-listener to :443, force-https, tls-cert=file and tls-private-key. | |||||
If you want to use TLS using your own certificates, set tls-listener to :443, force-https, tls-cert-file and tls-private-key. | |||||
## Development | ## Development | ||||
@@ -12,7 +12,7 @@ import ( | |||||
"google.golang.org/api/googleapi" | "google.golang.org/api/googleapi" | ||||
) | ) | ||||
var Version = "1.1.4" | |||||
var Version = "1.1.7" | |||||
var helpTemplate = `NAME: | var helpTemplate = `NAME: | ||||
{{.Name}} - {{.Usage}} | {{.Name}} - {{.Usage}} | ||||
@@ -37,6 +37,7 @@ var globalFlags = []cli.Flag{ | |||||
Name: "listener", | Name: "listener", | ||||
Usage: "127.0.0.1:8080", | Usage: "127.0.0.1:8080", | ||||
Value: "127.0.0.1:8080", | Value: "127.0.0.1:8080", | ||||
EnvVar: "LISTENER", | |||||
}, | }, | ||||
// redirect to https? | // redirect to https? | ||||
// hostnames | // hostnames | ||||
@@ -44,57 +45,75 @@ var globalFlags = []cli.Flag{ | |||||
Name: "profile-listener", | Name: "profile-listener", | ||||
Usage: "127.0.0.1:6060", | Usage: "127.0.0.1:6060", | ||||
Value: "", | Value: "", | ||||
EnvVar: "PROFILE_LISTENER", | |||||
}, | }, | ||||
cli.BoolFlag{ | cli.BoolFlag{ | ||||
Name: "force-https", | Name: "force-https", | ||||
Usage: "", | Usage: "", | ||||
EnvVar: "FORCE_HTTPS", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "tls-listener", | Name: "tls-listener", | ||||
Usage: "127.0.0.1:8443", | Usage: "127.0.0.1:8443", | ||||
Value: "", | Value: "", | ||||
EnvVar: "TLS_LISTENER", | |||||
}, | }, | ||||
cli.BoolFlag{ | cli.BoolFlag{ | ||||
Name: "tls-listener-only", | Name: "tls-listener-only", | ||||
Usage: "", | Usage: "", | ||||
EnvVar: "TLS_LISTENER_ONLY", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "tls-cert-file", | Name: "tls-cert-file", | ||||
Value: "", | Value: "", | ||||
EnvVar: "TLS_CERT_FILE", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "tls-private-key", | Name: "tls-private-key", | ||||
Value: "", | Value: "", | ||||
EnvVar: "TLS_PRIVATE_KEY", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "temp-path", | Name: "temp-path", | ||||
Usage: "path to temp files", | Usage: "path to temp files", | ||||
Value: os.TempDir(), | Value: os.TempDir(), | ||||
EnvVar: "TEMP_PATH", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "web-path", | Name: "web-path", | ||||
Usage: "path to static web files", | Usage: "path to static web files", | ||||
Value: "", | Value: "", | ||||
EnvVar: "WEB_PATH", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "proxy-path", | Name: "proxy-path", | ||||
Usage: "path prefix when service is run behind a proxy", | Usage: "path prefix when service is run behind a proxy", | ||||
Value: "", | Value: "", | ||||
EnvVar: "PROXY_PATH", | |||||
}, | |||||
cli.StringFlag{ | |||||
Name: "proxy-port", | |||||
Usage: "port of the proxy when the service is run behind a proxy", | |||||
Value: "", | |||||
EnvVar: "PROXY_PORT", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "ga-key", | Name: "ga-key", | ||||
Usage: "key for google analytics (front end)", | Usage: "key for google analytics (front end)", | ||||
Value: "", | Value: "", | ||||
EnvVar: "GA_KEY", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "uservoice-key", | Name: "uservoice-key", | ||||
Usage: "key for user voice (front end)", | Usage: "key for user voice (front end)", | ||||
Value: "", | Value: "", | ||||
EnvVar: "USERVOICE_KEY", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "provider", | Name: "provider", | ||||
Usage: "s3|gdrive|local", | Usage: "s3|gdrive|local", | ||||
Value: "", | Value: "", | ||||
EnvVar: "PROVIDER", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "s3-endpoint", | Name: "s3-endpoint", | ||||
@@ -129,25 +148,30 @@ var globalFlags = []cli.Flag{ | |||||
cli.BoolFlag{ | cli.BoolFlag{ | ||||
Name: "s3-no-multipart", | Name: "s3-no-multipart", | ||||
Usage: "Disables S3 Multipart Puts", | Usage: "Disables S3 Multipart Puts", | ||||
EnvVar: "S3_NO_MULTIPART", | |||||
}, | }, | ||||
cli.BoolFlag{ | cli.BoolFlag{ | ||||
Name: "s3-path-style", | Name: "s3-path-style", | ||||
Usage: "Forces path style URLs, required for Minio.", | Usage: "Forces path style URLs, required for Minio.", | ||||
EnvVar: "S3_PATH_STYLE", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "gdrive-client-json-filepath", | Name: "gdrive-client-json-filepath", | ||||
Usage: "", | Usage: "", | ||||
Value: "", | Value: "", | ||||
EnvVar: "GDRIVE_CLIENT_JSON_FILEPATH", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "gdrive-local-config-path", | Name: "gdrive-local-config-path", | ||||
Usage: "", | Usage: "", | ||||
Value: "", | Value: "", | ||||
EnvVar: "GDRIVE_LOCAL_CONFIG_PATH", | |||||
}, | }, | ||||
cli.IntFlag{ | cli.IntFlag{ | ||||
Name: "gdrive-chunk-size", | Name: "gdrive-chunk-size", | ||||
Usage: "", | Usage: "", | ||||
Value: googleapi.DefaultUploadChunkSize / 1024 / 1024, | Value: googleapi.DefaultUploadChunkSize / 1024 / 1024, | ||||
EnvVar: "GDRIVE_CHUNK_SIZE", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "storj-access", | Name: "storj-access", | ||||
@@ -165,7 +189,7 @@ var globalFlags = []cli.Flag{ | |||||
Name: "rate-limit", | Name: "rate-limit", | ||||
Usage: "requests per minute", | Usage: "requests per minute", | ||||
Value: 0, | Value: 0, | ||||
EnvVar: "", | |||||
EnvVar: "RATE_LIMIT", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "lets-encrypt-hosts", | Name: "lets-encrypt-hosts", | ||||
@@ -177,11 +201,13 @@ var globalFlags = []cli.Flag{ | |||||
Name: "log", | Name: "log", | ||||
Usage: "/var/log/transfersh.log", | Usage: "/var/log/transfersh.log", | ||||
Value: "", | Value: "", | ||||
EnvVar: "LOG", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "basedir", | Name: "basedir", | ||||
Usage: "path to storage", | Usage: "path to storage", | ||||
Value: "", | Value: "", | ||||
EnvVar: "BASEDIR", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "clamav-host", | Name: "clamav-host", | ||||
@@ -198,26 +224,37 @@ var globalFlags = []cli.Flag{ | |||||
cli.BoolFlag{ | cli.BoolFlag{ | ||||
Name: "profiler", | Name: "profiler", | ||||
Usage: "enable profiling", | Usage: "enable profiling", | ||||
EnvVar: "PROFILER", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "http-auth-user", | Name: "http-auth-user", | ||||
Usage: "user for http basic auth", | Usage: "user for http basic auth", | ||||
Value: "", | Value: "", | ||||
EnvVar: "HTTP_AUTH_USER", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "http-auth-pass", | Name: "http-auth-pass", | ||||
Usage: "pass for http basic auth", | Usage: "pass for http basic auth", | ||||
Value: "", | Value: "", | ||||
EnvVar: "HTTP_AUTH_PASS", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "ip-whitelist", | Name: "ip-whitelist", | ||||
Usage: "comma separated list of ips allowed to connect to the service", | Usage: "comma separated list of ips allowed to connect to the service", | ||||
Value: "", | Value: "", | ||||
EnvVar: "IP_WHITELIST", | |||||
}, | }, | ||||
cli.StringFlag{ | cli.StringFlag{ | ||||
Name: "ip-blacklist", | Name: "ip-blacklist", | ||||
Usage: "comma separated list of ips not allowed to connect to the service", | Usage: "comma separated list of ips not allowed to connect to the service", | ||||
Value: "", | Value: "", | ||||
EnvVar: "IP_BLACKLIST", | |||||
}, | |||||
cli.StringFlag{ | |||||
Name: "cors-domains", | |||||
Usage: "comma separated list of domains allowed for CORS requests", | |||||
Value: "", | |||||
EnvVar: "CORS_DOMAINS", | |||||
}, | }, | ||||
} | } | ||||
@@ -226,7 +263,7 @@ type Cmd struct { | |||||
} | } | ||||
func VersionAction(c *cli.Context) { | func VersionAction(c *cli.Context) { | ||||
fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh: Easy file sharing from the command line"))) | |||||
fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh %s: Easy file sharing from the command line", Version))) | |||||
} | } | ||||
func New() *Cmd { | func New() *Cmd { | ||||
@@ -257,6 +294,10 @@ func New() *Cmd { | |||||
options = append(options, server.Listener(v)) | options = append(options, server.Listener(v)) | ||||
} | } | ||||
if v := c.String("cors-domains"); v != "" { | |||||
options = append(options, server.CorsDomains(v)) | |||||
} | |||||
if v := c.String("tls-listener"); v == "" { | if v := c.String("tls-listener"); v == "" { | ||||
} else if c.Bool("tls-listener-only") { | } else if c.Bool("tls-listener-only") { | ||||
options = append(options, server.TLSListener(v, true)) | options = append(options, server.TLSListener(v, true)) | ||||
@@ -276,6 +317,10 @@ func New() *Cmd { | |||||
options = append(options, server.ProxyPath(v)) | options = append(options, server.ProxyPath(v)) | ||||
} | } | ||||
if v := c.String("proxy-port"); v != "" { | |||||
options = append(options, server.ProxyPort(v)) | |||||
} | |||||
if v := c.String("ga-key"); v != "" { | if v := c.String("ga-key"); v != "" { | ||||
options = append(options, server.GoogleAnalytics(v)) | options = append(options, server.GoogleAnalytics(v)) | ||||
} | } | ||||
@@ -16,6 +16,7 @@ require ( | |||||
github.com/garyburd/redigo v1.6.0 // indirect | github.com/garyburd/redigo v1.6.0 // indirect | ||||
github.com/golang/gddo v0.0.0-20200310004957-95ce5a452273 | github.com/golang/gddo v0.0.0-20200310004957-95ce5a452273 | ||||
github.com/golang/protobuf v1.3.5 // indirect | github.com/golang/protobuf v1.3.5 // indirect | ||||
github.com/gorilla/handlers v1.4.2 | |||||
github.com/gorilla/mux v1.7.4 | github.com/gorilla/mux v1.7.4 | ||||
github.com/gorilla/securecookie v1.1.1 // indirect | github.com/gorilla/securecookie v1.1.1 // indirect | ||||
github.com/jmespath/go-jmespath v0.3.0 // indirect | github.com/jmespath/go-jmespath v0.3.0 // indirect | ||||
@@ -136,6 +136,8 @@ github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk | |||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= | ||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= | github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= | ||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= | ||||
github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg= | |||||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= | |||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= | github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= | ||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= | github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= | ||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= | github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= | ||||
@@ -158,9 +158,9 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { | |||||
} | } | ||||
relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename)) | relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename)) | ||||
resolvedURL := resolveURL(r, relativeURL) | |||||
resolvedURL := resolveURL(r, relativeURL, s.proxyPort) | |||||
relativeURLGet, _ := url.Parse(path.Join(s.proxyPath, getPathPart, token, filename)) | relativeURLGet, _ := url.Parse(path.Join(s.proxyPath, getPathPart, token, filename)) | ||||
resolvedURLGet := resolveURL(r, relativeURLGet) | |||||
resolvedURLGet := resolveURL(r, relativeURLGet, s.proxyPort) | |||||
var png []byte | var png []byte | ||||
png, err = qrcode.Encode(resolvedURL, qrcode.High, 150) | png, err = qrcode.Encode(resolvedURL, qrcode.High, 150) | ||||
if err != nil { | if err != nil { | ||||
@@ -170,8 +170,8 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { | |||||
qrCode := base64.StdEncoding.EncodeToString(png) | qrCode := base64.StdEncoding.EncodeToString(png) | ||||
hostname := getURL(r).Host | |||||
webAddress := resolveWebAddress(r, s.proxyPath) | |||||
hostname := getURL(r, s.proxyPort).Host | |||||
webAddress := resolveWebAddress(r, s.proxyPath, s.proxyPort) | |||||
data := struct { | data := struct { | ||||
ContentType string | ContentType string | ||||
@@ -212,8 +212,8 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { | |||||
func (s *Server) viewHandler(w http.ResponseWriter, r *http.Request) { | func (s *Server) viewHandler(w http.ResponseWriter, r *http.Request) { | ||||
// vars := mux.Vars(r) | // vars := mux.Vars(r) | ||||
hostname := getURL(r).Host | |||||
webAddress := resolveWebAddress(r, s.proxyPath) | |||||
hostname := getURL(r, s.proxyPort).Host | |||||
webAddress := resolveWebAddress(r, s.proxyPath, s.proxyPort) | |||||
data := struct { | data := struct { | ||||
Hostname string | Hostname string | ||||
@@ -339,7 +339,7 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) { | |||||
filename = url.PathEscape(filename) | filename = url.PathEscape(filename) | ||||
relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename)) | relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename)) | ||||
fmt.Fprintln(w, getURL(r).ResolveReference(relativeURL).String()) | |||||
fmt.Fprintln(w, getURL(r, s.proxyPort).ResolveReference(relativeURL).String()) | |||||
cleanTmpFile(file) | cleanTmpFile(file) | ||||
} | } | ||||
@@ -500,15 +500,15 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) { | |||||
relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename)) | relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename)) | ||||
deleteURL, _ := url.Parse(path.Join(s.proxyPath, token, filename, metadata.DeletionToken)) | deleteURL, _ := url.Parse(path.Join(s.proxyPath, token, filename, metadata.DeletionToken)) | ||||
w.Header().Set("X-Url-Delete", resolveURL(r, deleteURL)) | |||||
w.Header().Set("X-Url-Delete", resolveURL(r, deleteURL, s.proxyPort)) | |||||
fmt.Fprint(w, resolveURL(r, relativeURL)) | |||||
fmt.Fprint(w, resolveURL(r, relativeURL, s.proxyPort)) | |||||
} | } | ||||
func resolveURL(r *http.Request, u *url.URL) string { | |||||
func resolveURL(r *http.Request, u *url.URL, proxyPort string) string { | |||||
r.URL.Path = "" | r.URL.Path = "" | ||||
return getURL(r).ResolveReference(u).String() | |||||
return getURL(r, proxyPort).ResolveReference(u).String() | |||||
} | } | ||||
func resolveKey(key, proxyPath string) string { | func resolveKey(key, proxyPath string) string { | ||||
@@ -525,8 +525,8 @@ func resolveKey(key, proxyPath string) string { | |||||
return key | return key | ||||
} | } | ||||
func resolveWebAddress(r *http.Request, proxyPath string) string { | |||||
url := getURL(r) | |||||
func resolveWebAddress(r *http.Request, proxyPath string, proxyPort string) string { | |||||
url := getURL(r, proxyPort) | |||||
var webAddress string | var webAddress string | ||||
@@ -544,7 +544,7 @@ func resolveWebAddress(r *http.Request, proxyPath string) string { | |||||
return webAddress | return webAddress | ||||
} | } | ||||
func getURL(r *http.Request) *url.URL { | |||||
func getURL(r *http.Request, proxyPort string) *url.URL { | |||||
u, _ := url.Parse(r.URL.String()) | u, _ := url.Parse(r.URL.String()) | ||||
if r.TLS != nil { | if r.TLS != nil { | ||||
@@ -555,16 +555,25 @@ func getURL(r *http.Request) *url.URL { | |||||
u.Scheme = "http" | u.Scheme = "http" | ||||
} | } | ||||
if u.Host != "" { | |||||
} else if host, port, err := net.SplitHostPort(r.Host); err != nil { | |||||
u.Host = r.Host | |||||
} else { | |||||
if port == "80" && u.Scheme == "http" { | |||||
u.Host = host | |||||
} else if port == "443" && u.Scheme == "https" { | |||||
if u.Host == "" { | |||||
host, port, err := net.SplitHostPort(r.Host) | |||||
if err != nil { | |||||
host = r.Host | |||||
port = "" | |||||
} | |||||
if len(proxyPort) != 0 { | |||||
port = proxyPort | |||||
} | |||||
if len(port) == 0 { | |||||
u.Host = host | u.Host = host | ||||
} else { | } else { | ||||
u.Host = net.JoinHostPort(host, port) | |||||
if port == "80" && u.Scheme == "http" { | |||||
u.Host = host | |||||
} else if port == "443" && u.Scheme == "https" { | |||||
u.Host = host | |||||
} else { | |||||
u.Host = net.JoinHostPort(host, port) | |||||
} | |||||
} | } | ||||
} | } | ||||
@@ -614,9 +623,7 @@ func (s *Server) CheckMetadata(token, filename string, increaseDownload bool) (M | |||||
var metadata Metadata | var metadata Metadata | ||||
r, _, err := s.storage.Get(token, fmt.Sprintf("%s.metadata", filename)) | r, _, err := s.storage.Get(token, fmt.Sprintf("%s.metadata", filename)) | ||||
if s.storage.IsNotExist(err) { | |||||
return metadata, nil | |||||
} else if err != nil { | |||||
if err != nil { | |||||
return metadata, err | return metadata, err | ||||
} | } | ||||
@@ -1011,7 +1018,7 @@ func (s *Server) RedirectHandler(h http.Handler) http.HandlerFunc { | |||||
} else if r.Header.Get("X-Forwarded-Proto") == "https" { | } else if r.Header.Get("X-Forwarded-Proto") == "https" { | ||||
} else if r.URL.Scheme == "https" { | } else if r.URL.Scheme == "https" { | ||||
} else { | } else { | ||||
u := getURL(r) | |||||
u := getURL(r, s.proxyPort) | |||||
u.Scheme = "https" | u.Scheme = "https" | ||||
http.Redirect(w, r, u.String(), http.StatusPermanentRedirect) | http.Redirect(w, r, u.String(), http.StatusPermanentRedirect) | ||||
@@ -26,7 +26,10 @@ package server | |||||
import ( | import ( | ||||
"errors" | "errors" | ||||
gorillaHandlers "github.com/gorilla/handlers" | |||||
"log" | "log" | ||||
crypto_rand "crypto/rand" | |||||
"encoding/binary" | |||||
"math/rand" | "math/rand" | ||||
"mime" | "mime" | ||||
"net/http" | "net/http" | ||||
@@ -85,6 +88,13 @@ func Listener(s string) OptionFn { | |||||
} | } | ||||
func CorsDomains(s string) OptionFn { | |||||
return func(srvr *Server) { | |||||
srvr.CorsDomains = s | |||||
} | |||||
} | |||||
func GoogleAnalytics(gaKey string) OptionFn { | func GoogleAnalytics(gaKey string) OptionFn { | ||||
return func(srvr *Server) { | return func(srvr *Server) { | ||||
srvr.gaKey = gaKey | srvr.gaKey = gaKey | ||||
@@ -131,6 +141,12 @@ func ProxyPath(s string) OptionFn { | |||||
} | } | ||||
} | } | ||||
func ProxyPort(s string) OptionFn { | |||||
return func(srvr *Server) { | |||||
srvr.proxyPort = s | |||||
} | |||||
} | |||||
func TempPath(s string) OptionFn { | func TempPath(s string) OptionFn { | ||||
return func(srvr *Server) { | return func(srvr *Server) { | ||||
if s[len(s)-1:] != "/" { | if s[len(s)-1:] != "/" { | ||||
@@ -270,11 +286,13 @@ type Server struct { | |||||
webPath string | webPath string | ||||
proxyPath string | proxyPath string | ||||
proxyPort string | |||||
gaKey string | gaKey string | ||||
userVoiceKey string | userVoiceKey string | ||||
TLSListenerOnly bool | TLSListenerOnly bool | ||||
CorsDomains string | |||||
ListenerString string | ListenerString string | ||||
TLSListenerString string | TLSListenerString string | ||||
ProfileListenerString string | ProfileListenerString string | ||||
@@ -297,7 +315,11 @@ func New(options ...OptionFn) (*Server, error) { | |||||
} | } | ||||
func init() { | func init() { | ||||
rand.Seed(time.Now().UTC().UnixNano()) | |||||
var seedBytes [8]byte | |||||
if _, err := crypto_rand.Read(seedBytes[:]); err != nil { | |||||
panic("cannot obtain cryptographically secure seed") | |||||
} | |||||
rand.Seed(int64(binary.LittleEndian.Uint64(seedBytes[:]))) | |||||
} | } | ||||
func (s *Server) Run() { | func (s *Server) Run() { | ||||
@@ -413,11 +435,24 @@ func (s *Server) Run() { | |||||
s.logger.Printf("Transfer.sh server started.\nusing temp folder: %s\nusing storage provider: %s", s.tempPath, s.storage.Type()) | s.logger.Printf("Transfer.sh server started.\nusing temp folder: %s\nusing storage provider: %s", s.tempPath, s.storage.Type()) | ||||
var cors func(http.Handler) http.Handler | |||||
if len(s.CorsDomains) > 0 { | |||||
cors = gorillaHandlers.CORS( | |||||
gorillaHandlers.AllowedHeaders([]string{"*"}), | |||||
gorillaHandlers.AllowedOrigins(strings.Split(s.CorsDomains, ",")), | |||||
gorillaHandlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"}), | |||||
) | |||||
} else { | |||||
cors = func(h http.Handler) http.Handler { | |||||
return h | |||||
} | |||||
} | |||||
h := handlers.PanicHandler( | h := handlers.PanicHandler( | ||||
IPFilterHandler( | IPFilterHandler( | ||||
handlers.LogHandler( | handlers.LogHandler( | ||||
LoveHandler( | LoveHandler( | ||||
s.RedirectHandler(r)), | |||||
s.RedirectHandler(cors(r))), | |||||
handlers.NewLogOptions(s.logger.Printf, "_default_"), | handlers.NewLogOptions(s.logger.Printf, "_default_"), | ||||
), | ), | ||||
s.ipFilterOptions, | s.ipFilterOptions, | ||||