├── .dockerignore ├── .env.example ├── .github └── workflows │ └── build.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── assets └── .gitignore ├── build.sh ├── clear_retention.sh ├── go.mod ├── go.sum ├── main.go ├── reload.sh ├── start_server.sh └── watch.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | README.md 3 | .idea 4 | .gitignore 5 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_DIR=./ 2 | # When running as a service container because, 3 | # access is private to the docker container's internal network 4 | #USERNAME=joe 5 | #PASSWORD=secret 6 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: Test 3 | 4 | jobs: 5 | test: 6 | strategy: 7 | matrix: 8 | go-version: [1.17.x] 9 | # os: [ubuntu-latest, macos-latest, windows-latest] 10 | os: [ubuntu-latest] 11 | runs-on: ${{ matrix.os }} 12 | steps: 13 | - name: Cancel Previous Runs 14 | uses: styfle/cancel-workflow-action@0.9.1 15 | with: 16 | access_token: ${{ github.token }} 17 | - name: Setup go 18 | uses: actions/setup-go@v2 19 | with: 20 | go-version: ${{ matrix.go-version }} 21 | 22 | - name: Checkout code 23 | uses: actions/checkout@v2 24 | - name: Install Tools 25 | run: | 26 | go install github.com/securego/gosec/v2/cmd/gosec@latest 27 | go install honnef.co/go/tools/cmd/staticcheck@latest 28 | 29 | - name: Build 30 | run: go build main.go 31 | - name: Vet 32 | run: go vet ./... 33 | - name: Lint 34 | run: staticcheck 35 | - name: Secure 36 | run: gosec ./... 37 | - name: Test 38 | run: go test ./... -v -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_STORE 3 | build/ 4 | *.pid 5 | jmeter.log 6 | coverage.txt 7 | coverage.html 8 | c.out 9 | _test 10 | .idea 11 | *.iml 12 | *.out 13 | *.log 14 | .env 15 | main 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.17.0-alpine 2 | 3 | WORKDIR /app 4 | 5 | COPY .env.example ./.env 6 | COPY go.mod ./ 7 | COPY go.sum ./ 8 | RUN go mod download 9 | RUN mkdir assets 10 | 11 | COPY *.go ./ 12 | 13 | RUN go build -o /cache-http 14 | 15 | # Because this is designed to run on a private Docker network 16 | # this will not conflict with anything running on port 80 on the host 17 | EXPOSE 80 18 | 19 | # Listen to requests for all IP addresses so that the container 20 | # can respond both the private name that Github Actions may assign 21 | # as a service container, while also responding to "localhost" requests 22 | # if you map the port to be accessible directly from the host. 23 | # 24 | # If you want to expose this service to the host and not the whole 25 | # internet, make sure you Docker port numbers restrict to localhost: 26 | # -p 127.0.0.1:3000:80 27 | 28 | CMD [ "/cache-http", "--host=", "--port=80" ] 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Pulkit Kathuria 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | Alternate solution to action/cache for GHES and self-hosted runners on Docker. 4 | [Github's "action/cache" plugin current doesn't support GHES and self-hosted runners.] 5 | (https://github.com/actions/cache/issues/505) 6 | 7 | ## Github Enterprise / self-hosted runner workflow 8 | 9 | The syntax is varies depending on whether your app that needs the cache is running 10 | in a Docker container or directly on the host. 11 | 12 | Both cases the companion ["action-cache-http"](https://github.com/marketplace/actions/action-cache-http) Action. 13 | 14 | When your app is built in a docker container, Github Actions will automatically will assign 15 | the host name "cache-http" to this service container, so the configuration will 16 | access by that host on port 80. 17 | 18 | When your app is built directly on the host, you'll need to map port 80 inside the cache-http 19 | container to a free port on your host. In the example below, we use port 3000. 20 | 21 | In both cases we use a bind mount to allow the service container to persist the assets on the host. 22 | 23 | Currently, there is no automated cache pruning but see [clear\_retention.sh(./clear\_retention.sh) 24 | for an example that you could run on a cron job or system timer. 25 | 26 | Note: There's no authentication on the default Docker image. It's assumed that access to 27 | the service will be restricted to Dockerized builts or only to "localhost" requests from 28 | your CI server. 29 | 30 | 31 | ```yml 32 | on: [push] 33 | name: "Continuous Integration" 34 | 35 | jobs: 36 | test: 37 | strategy: 38 | matrix: 39 | node-versions: ['10.16.3', '14.9'] 40 | runs-on: self-hosted 41 | # Cache service for GHE / self-hosted runners 42 | services: 43 | cache-http: 44 | image: docker.pkg.github.com/SOMEPATH/FIXME/cache-http:3 45 | # For now you still need to authenticate when accessing public packages 46 | credentials: 47 | username: $GITHUB_ACTOR 48 | password: ${{ secrets.SOME_SECRET }} 49 | # The left side is any path on your host you want to store the cache 50 | # Make sure the directory exists. Right side must be "/app/assets!" 51 | volumes: 52 | - /home/github/.cache/cache-http/assets:/app/assets 53 | # Only map ports for direct-on-host case. Not needed for Dockerized builds! 54 | ports: 55 | - 127.0.0.1:3000:80 56 | 57 | 58 | steps: 59 | - name: Setup Node.js ${{ matrix.node-versions }} 60 | uses: actions/setup-node@v1 61 | with: 62 | node-version: ${{ matrix.node-versions }} 63 | 64 | - name: Checkout 65 | uses: actions/checkout@v2 66 | 67 | # - name: Yarn Install (without cache) 68 | # run: yarn install 69 | 70 | - name: Yarn Install (with cache) 71 | uses: kevincobain2000/action-cache-http@v3 72 | with: 73 | version: ${{ matrix.node-versions }} 74 | lock_file: yarn.lock 75 | install_command: yarn install 76 | operating_dir: ./ 77 | destination_folder: node_modules 78 | # For Dockerized builds, this should match the service name. 79 | # cache_http_api: "http://cache-http" 80 | # For direct-on-host builds, use the custom port you mapped above 81 | cache_http_api: "http://127.0.0.1:3000" 82 | http_proxy: "" 83 | ``` 84 | 85 | If for some reason you want to use the Dockerized version of this service, below are details 86 | on how to run it yourself. Also see the example `.sh` scripts distributed with the project. 87 | 88 | **Without Docker** 89 | 90 | ```yml 91 | - name: Yarn Install (with cache) 92 | uses: kevincobain2000/action-cache-http@v3 93 | with: 94 | version: ${{ matrix.node-versions }} 95 | lock_file: yarn.lock 96 | install_command: yarn install 97 | operating_dir: ./ 98 | destination_folder: node_modules 99 | # For Dockerized builds, this should match the service name. 100 | # cache_http_api: "http://cache-http" 101 | # For direct-on-host builds, use the custom port you mapped above 102 | cache_http_api: "http://127.0.0.1:3000" 103 | http_proxy: "" 104 | ``` 105 | 106 | #### Run 107 | ``` 108 | go run main.go 3000 109 | ``` 110 | 111 | #### Deploy 112 | 113 | ``` 114 | go build -o main 115 | ./main -host=localhost -port=3000 -pidDir=./ > /dev/null 2>&1 & 116 | ``` 117 | 118 | #### Development 119 | 120 | ``` 121 | go get github.com/cespare/reflex 122 | reflex -r '\.go$' -s -- sh -c 'go run main.go 3000' 123 | ``` 124 | 125 | ``` 126 | curl -X GET -u joe:secret localhost:3000/health 127 | ``` 128 | 129 | ``` 130 | curl -X POST -u joe:secret --form file=@/path/to/file/sample.tar.gz localhost:3000/upload 131 | curl -X GET -u joe:secret localhost:3000/assets/sample.tar.gz 132 | ``` 133 | -------------------------------------------------------------------------------- /assets/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | */ 3 | !.gitignore -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | GOOS=linux GOARCH=amd64 go build main.go -------------------------------------------------------------------------------- /clear_retention.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | export $(egrep -v '^#' .env | xargs) 4 | # * * * * * cd /path/to.dir/&& ./clear_retention.sh 5 | echo "clearing old files" 6 | find $APP_DIR/assets/*.tar.gz -mtime +7 -exec rm -rf {} \; 7 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kevincobain2000/go-get-post 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 7 | github.com/joho/godotenv v1.4.0 8 | github.com/labstack/echo/v4 v4.6.1 9 | ) 10 | 11 | require ( 12 | github.com/golang-jwt/jwt v3.2.2+incompatible // indirect 13 | github.com/labstack/gommon v0.3.0 // indirect 14 | github.com/mattn/go-colorable v0.1.8 // indirect 15 | github.com/mattn/go-isatty v0.0.14 // indirect 16 | github.com/valyala/bytebufferpool v1.0.0 // indirect 17 | github.com/valyala/fasttemplate v1.2.1 // indirect 18 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect 19 | golang.org/x/net v0.0.0-20210913180222-943fd674d43e // indirect 20 | golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 // indirect 21 | golang.org/x/text v0.3.7 // indirect 22 | golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect 23 | ) 24 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 h1:6VSn3hB5U5GeA6kQw4TwWIWbOhtvR2hmbBJnTOtqTWc= 4 | github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6/go.mod h1:YxOVT5+yHzKvwhsiSIWmbAYM3Dr9AEEbER2dVayfBkg= 5 | github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= 6 | github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= 7 | github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= 8 | github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 9 | github.com/labstack/echo/v4 v4.6.1 h1:OMVsrnNFzYlGSdaiYGHbgWQnr+JM7NG+B9suCPie14M= 10 | github.com/labstack/echo/v4 v4.6.1/go.mod h1:RnjgMWNDB9g/HucVWhQYNQP9PvbYf6adqftqryo7s9k= 11 | github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= 12 | github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= 13 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 14 | github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= 15 | github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 16 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 17 | github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= 18 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 19 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 20 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 21 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 22 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 23 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 24 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 25 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 26 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 27 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 28 | github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= 29 | github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= 30 | github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 31 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= 32 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 33 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 34 | golang.org/x/net v0.0.0-20210913180222-943fd674d43e h1:+b/22bPvDYt4NPDcy4xAGCmON713ONAWFeY3Z7I3tR8= 35 | golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 36 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 37 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 38 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 39 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 40 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 41 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 42 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 43 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 44 | golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 h1:xrCZDmdtoloIiooiA9q0OQb9r8HejIHYoHGhGCe1pGg= 45 | golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 46 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 47 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 48 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 49 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 50 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 51 | golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= 52 | golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 53 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 54 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 55 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 56 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 57 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "io/ioutil" 6 | "log" 7 | "net/http" 8 | "os" 9 | "path/filepath" 10 | "strconv" 11 | "strings" 12 | "syscall" 13 | "time" 14 | 15 | "github.com/fvbock/endless" 16 | "github.com/joho/godotenv" 17 | echo "github.com/labstack/echo/v4" 18 | middleware "github.com/labstack/echo/v4/middleware" 19 | ) 20 | 21 | // successResponse ... 22 | type successResponse struct { 23 | Status bool `json:"status"` 24 | } 25 | 26 | const assetsPath = "assets/" 27 | 28 | // Params for the server 29 | type Params struct { 30 | Host string `json:"host"` 31 | Port string `json:"port"` 32 | PidDir string `json:"pid"` 33 | } 34 | 35 | func main() { 36 | e := echo.New() 37 | 38 | loadEnv() 39 | 40 | e.Use(middleware.Logger()) 41 | e.Use(middleware.Recover()) 42 | e.Use(touch()) 43 | if os.Getenv("USERNAME") != "" && os.Getenv("PASSWORD") != "" { 44 | basicAuth(e) 45 | } 46 | 47 | e.GET("/health", health) 48 | e.Static("/assets", assetsPath) 49 | e.POST("/upload", upload) 50 | 51 | params := cliParams() 52 | 53 | serveGracefully(e, params.Host, params.Port, params.PidDir) 54 | 55 | } 56 | 57 | //named paramters 58 | func cliParams() Params { 59 | params := Params{ 60 | Host: "localhost", 61 | Port: "80", //testing railway 62 | PidDir: "./", 63 | } 64 | //go run main.go -host=localhost -port=3000 -pidDir=./ 65 | for _, arg := range os.Args { 66 | if strings.HasPrefix(arg, "-host=") || strings.HasPrefix(arg, "--host=") { 67 | params.Host = strings.ReplaceAll(arg, "-host=", "") 68 | params.Host = strings.ReplaceAll(arg, "--host=", "") 69 | } 70 | if strings.HasPrefix(arg, "-port=") || strings.HasPrefix(arg, "--port=") { 71 | params.Port = strings.ReplaceAll(arg, "--port=", "") 72 | } 73 | if strings.HasPrefix(arg, "-pidDir=") || strings.HasPrefix(arg, "--pidDir=") { 74 | params.PidDir = strings.ReplaceAll(arg, "--pidDir=", "") 75 | } 76 | } 77 | return params 78 | } 79 | 80 | func touch() echo.MiddlewareFunc { 81 | return func(next echo.HandlerFunc) echo.HandlerFunc { 82 | return func(c echo.Context) error { 83 | req := c.Request() 84 | uri := req.RequestURI 85 | if strings.HasPrefix(uri, "/assets/") { 86 | filename := strings.ReplaceAll(uri, "/assets/", "") 87 | currenttime := time.Now().Local() 88 | 89 | err := os.Chtimes(assetsPath+filename, currenttime, currenttime) 90 | if err != nil { 91 | log.Println(err) 92 | } 93 | } 94 | err := next(c) 95 | if err != nil { 96 | return err 97 | } 98 | 99 | return nil 100 | } 101 | } 102 | } 103 | 104 | func basicAuth(e *echo.Echo) { 105 | e.Use(middleware.BasicAuth(func(username, password string, c echo.Context) (bool, error) { 106 | if username == os.Getenv("USERNAME") && password == os.Getenv("PASSWORD") { 107 | return true, nil 108 | } 109 | return false, nil 110 | })) 111 | } 112 | 113 | func serveGracefully(e *echo.Echo, host string, port, pidDir string) { 114 | e.Server.Addr = host + ":" + port 115 | server := endless.NewServer(e.Server.Addr, e) 116 | server.BeforeBegin = func(add string) { 117 | log.Print("info: actual pid is", syscall.Getpid()) 118 | pidFile := filepath.Join(pidDir, port+".pid") 119 | err := os.Remove(pidFile) 120 | if err != nil { 121 | log.Print("error: pid file error: ", err) 122 | } else { 123 | log.Print("success: pid file success: ", pidFile) 124 | } 125 | err = ioutil.WriteFile(pidFile, []byte(strconv.Itoa(os.Getpid())), 0600) 126 | if err != nil { 127 | log.Print("error: write pid file error: ", err) 128 | } else { 129 | log.Print("success: write pid file success: ", pidFile) 130 | } 131 | } 132 | if err := server.ListenAndServe(); err != nil { 133 | log.Print("critical: graceful error: ", err) 134 | } 135 | } 136 | 137 | func loadEnv() { 138 | err := godotenv.Load() 139 | if err != nil { 140 | log.Fatal("Error loading .env file") 141 | } 142 | } 143 | 144 | func health(c echo.Context) error { 145 | return c.JSON(http.StatusOK, &successResponse{Status: true}) 146 | } 147 | 148 | func upload(c echo.Context) error { 149 | //----------- 150 | // Read file 151 | //----------- 152 | 153 | // Source 154 | file, err := c.FormFile("file") 155 | if err != nil { 156 | log.Print(err.Error()) 157 | return err 158 | } 159 | src, err := file.Open() 160 | if err != nil { 161 | return err 162 | } 163 | defer src.Close() 164 | 165 | // Destination 166 | dst, err := os.Create(assetsPath + file.Filename) 167 | if err != nil { 168 | return err 169 | } 170 | 171 | // Copy 172 | if _, err = io.Copy(dst, src); err != nil { 173 | return err 174 | } 175 | 176 | return c.JSON(http.StatusOK, &successResponse{Status: true}) 177 | } 178 | -------------------------------------------------------------------------------- /reload.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | export $(egrep -v '^#' .env | xargs) 4 | 5 | kill -HUP `cat $APP_DIR/3000.pid` 6 | 7 | 8 | -------------------------------------------------------------------------------- /start_server.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | export $(egrep -v '^#' .env | xargs) 3 | 4 | ######################################################################## 5 | # 1.GO_SERVER_START_PID 6 | ######################################################################## 7 | port=$1 8 | pid=$2 9 | if [ -z "$pod" ]; then 10 | echo "Please provide port (first arg)" 11 | exit 12 | fi 13 | if [ -z "$pid" ]; then 14 | echo "Please provide pid dir as (second arg)" 15 | exit 16 | fi 17 | 18 | GO_SERVER_START_PID=`cat $APP_DIR/$1.pid` 19 | GO_SERVER_START_IsAlive=`ps ${GO_SERVER_START_PID} | wc -l` 20 | isAlive=`ps ${GO_SERVER_START_PID} | wc -l` 21 | 22 | if [ ${isAlive} = 2 ]; then 23 | echo "${TARGET} $1 is alive now" 24 | else 25 | echo "${TARGET} $1 is dead now" 26 | echo "Start Application on Port: ${1}"; 27 | 28 | $APP_DIR/main $1 $2 > /dev/null 2>&1 & 29 | fi 30 | ######################################################################## 31 | -------------------------------------------------------------------------------- /watch.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | reflex -r '\.go$' -s -- sh -c 'go run main.go 3000' 4 | --------------------------------------------------------------------------------