diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml new file mode 100644 index 0000000..0007c13 --- /dev/null +++ b/.gitea/workflows/build.yaml @@ -0,0 +1,83 @@ +name: Actions Build Binary +run-name: ${{ gitea.actor }} is building new image ๐Ÿš€ +on: push + +jobs: + Build-Binary: + runs-on: soaska + steps: + - run: echo "๐ŸŽ‰ The job was automatically triggered by a ${{ gitea.event_name }} event." + - name: Install Node.js, curl and go + run: | + apk add --no-cache nodejs curl go git + - name: Check out repository code + uses: actions/checkout@v3 + - name: List files in the repository + run: | + ls ${{ gitea.workspace }} + - run: echo "๐Ÿ This job's status is ${{ job.status }}." + - name: Install go dependencies + run: | + go mod download + - name: Build Binary + run: | + go build -o $(basename ${{ gitea.repository }}) . + env: + ENVIRONMENT: production + - name: Upload binary to soaska.ru + run: | + package_name=$(basename ${{ gitea.repository }}) + branch_name=$(git rev-parse --abbrev-ref HEAD) + current_date=$(git log -1 --format=%cd --date=format:'%Y-%m-%d-%H-%M-%S') + curl --user ${{ secrets.username }}:${{ secrets.password }} \ + --upload-file $package_name \ + https://soaska.ru/api/packages/${{ secrets.username }}/generic/$package_name-$branch_name/$current_date/$package_name + env: + ENVIRONMENT: production + Build-Docker-Image: + runs-on: soaska + needs: Build-Binary + steps: + - run: echo "๐ŸŽ‰ The job was automatically triggered by a ${{ gitea.event_name }} event." + - name: Install Node.js and curl + run: | + apk add --no-cache nodejs curl git + - name: Check out repository code + uses: actions/checkout@v3 + - name: List files in the repository + run: | + ls ${{ gitea.workspace }} + - run: echo "๐Ÿ This job's status is ${{ job.status }}." + - name: Install Docker + run: | + apk add --no-cache docker + - name: Start Docker service + run: | + dockerd & + sleep 5 + docker info + - name: Get Binary from soaska.ru + run: | + package_name=$(basename ${{ gitea.repository }}) + branch_name=$(git rev-parse --abbrev-ref HEAD) + current_date=$(git log -1 --format=%cd --date=format:'%Y-%m-%d-%H-%M-%S') + curl --user ${{ secrets.username }}:${{ secrets.password }} \ + --output main \ + https://soaska.ru/api/packages/${{ secrets.username }}/generic/$package_name-$branch_name/$current_date/$package_name + - name: List files in the repository + run: | + ls ${{ gitea.workspace }} + - name: Build Dockerfile + run: | + docker build -t $(basename ${{ github.repository}} | tr '[:upper:]' '[:lower:]') . + docker image ls + - name: Upload Docker image to soaska.ru + run: | + package_name=$(basename ${{ github.repository}} | tr '[:upper:]' '[:lower:]') + branch_name=$(git rev-parse --abbrev-ref HEAD | tr '[:upper:]' '[:lower:]') + + docker login -u ${{ secrets.username }} -p ${{ secrets.password }} soaska.ru + docker tag $package_name:latest soaska.ru/${{ secrets.username }}/$package_name:$branch_name + docker tag $package_name:latest soaska.ru/${{ secrets.username }}/$package_name:latest + docker push soaska.ru/${{ secrets.username }}/$package_name:$branch_name + docker push soaska.ru/${{ secrets.username }}/$package_name:latest diff --git a/.gitignore b/.gitignore index adf8f72..5a4fa54 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +<<<<<<< HEAD +<<<<<<< HEAD # ---> Go # If you prefer the allow list template instead of the deny list, see community template: # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore @@ -21,3 +23,13 @@ # Go workspace file go.work +======= +.env +.vscode +icon.jpg +>>>>>>> beta +======= +.env +.vscode +icon.jpg +>>>>>>> beta diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e5494bc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,4 @@ +FROM alpine +WORKDIR /root/ +COPY main . +CMD ["./main"] \ No newline at end of file diff --git a/LICENSE b/LICENSE index 2b8f9a6..a2d25f2 100644 --- a/LICENSE +++ b/LICENSE @@ -6,4 +6,12 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +<<<<<<< HEAD +<<<<<<< HEAD THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +======= +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +>>>>>>> beta +======= +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +>>>>>>> beta diff --git a/example.env b/example.env new file mode 100644 index 0000000..cc60f5b --- /dev/null +++ b/example.env @@ -0,0 +1,6 @@ +# ะŸั€ะธะผะตั€ั‹ ะฟะตั€ะตะผะตะฝะฝั‹ั… +S3_ACCESS_KEY_ID=your_access_key_id +S3_SECRET_ACCESS_KEY=your_secret_access_key +S3_BUCKET_NAME=your_bucket_name +S3_ENDPOINT_URL=your_endpoint +PORT=8080 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f9a47ac --- /dev/null +++ b/go.mod @@ -0,0 +1,20 @@ +module soaska.ru/soaska/MinIO-Share-S3 + +go 1.21 + +require ( + github.com/gorilla/mux v1.8.1 + github.com/minio/minio-go v6.0.14+incompatible +) + +require github.com/stretchr/testify v1.8.4 // indirect + +require ( + github.com/go-ini/ini v1.67.0 // indirect + github.com/joho/godotenv v1.5.1 + github.com/mitchellh/go-homedir v1.1.0 // indirect + golang.org/x/crypto v0.18.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..1a06872 --- /dev/null +++ b/go.sum @@ -0,0 +1,26 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/minio/minio-go v6.0.14+incompatible h1:fnV+GD28LeqdN6vT2XdGKW8Qe/IfjJDswNVuni6km9o= +github.com/minio/minio-go v6.0.14+incompatible/go.mod h1:7guKYtitv8dktvNUGrhzmNlA5wrAABTQXCoesZdFQO8= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..542b964 --- /dev/null +++ b/readme.md @@ -0,0 +1,40 @@ +# MinIO Share S3 + +MinIO Share S3 is a project that enables users to embed links to objects in MinIO and easily share them with others. + +## Features + +- **Object Embedding**: Users can generate embeddable links for objects stored in MinIO. +- **Easy Sharing**: The generated links can be easily shared with others, allowing them to access the objects. +- **Access Control**: Users can control the access permissions for the shared objects, ensuring data security using MinIO polices. +- **Simplified Workflow**: MinIO Share S3 provides a simple usage to share all your files in bucket + +## Installation + +To install MinIO Share S3, follow these steps: + +1. Clone the repository: `git clone https://soaska.ru/soaska/minio-share-s3.git` +2. Install the required dependencies: `go mod download` +3. Configure the MinIO connection settings in the `.env` file. +4. Start the application: `go run` + +You also can download precompiled binary from packages or set up docker container using `soaska.ru/soaska/minio-share-s3:master` image. + +## Usage + +Once the application is running, you can perform the following actions: + +1. Upload objects to MinIO. +2. Generate access key and set up polices. +3. Set up MinIO Share S3. +4. Share links! + +All environment variables are described in `sample.env` file. + +## Contributing + +Contributions are welcome! If you would like to contribute to MinIO Share S3, please open issue. + +## License + +MinIO Share S3 is licensed under the [MIT License](https://opensource.org/licenses/MIT). diff --git a/run.go b/run.go new file mode 100644 index 0000000..82c3e6d --- /dev/null +++ b/run.go @@ -0,0 +1,130 @@ +package main + +import ( + "fmt" + "io" + "log" + "net/http" + "os" + "time" + + "github.com/gorilla/mux" + "github.com/joho/godotenv" + "github.com/minio/minio-go" +) + +func main() { + // Get environment variables + accessKeyID, secretAccessKey, bucketName, endpoint, port := getEnv() + + // Initialize MinIO client + minioClient, err := minio.New(endpoint, accessKeyID, secretAccessKey, true) + if err != nil { + log.Fatal(err) + } + + // Create a new router + router := mux.NewRouter() + + // Define the file handler + router.HandleFunc("/{path:.*}", func(w http.ResponseWriter, r *http.Request) { + log.Printf("Received request: %s %s", r.Method, r.RequestURI) + vars := mux.Vars(r) + path := vars["path"] + + log.Print("Path: ", path) + + // Get the file from S3 + object, err := minioClient.GetObject(bucketName, path, minio.GetObjectOptions{}) + if err != nil { + if minio.ToErrorResponse(err).Code == "NoSuchKey" { + log.Printf("Object not found in S3: %s", err) + http.Error(w, "Object not found", http.StatusNotFound) + return + } + log.Printf("Error getting object from S3: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Get the object info + info, err := object.Stat() + if err != nil { + if minio.ToErrorResponse(err).Code == "NoSuchKey" { + log.Printf("Object not found in S3: %s", err) + http.Error(w, "Object not found", http.StatusNotFound) + return + } + log.Printf("Error getting object info: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Set the content type header + contentType := info.ContentType + w.Header().Set("Content-Type", contentType) + + // Copy the file to the response writer + _, err = io.Copy(w, object) + if err != nil { + log.Printf("Error copying file to response writer: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + }).Methods("GET") + + router.Use(loggingMiddleware) + + // Start the server + + if port == "" { + port = "8080" + } + addr := fmt.Sprintf(":%s", port) + log.Printf("Server listening on %s", addr) + log.Fatal(http.ListenAndServe(addr, router)) +} + +func getEnv() (string, string, string, string, string) { + if os.Getenv("ENVIRONMENT") != "production" { + err := godotenv.Load() + if err != nil { + log.Fatal("Error loading .env file") + } + } + + // Get environment variables + port := os.Getenv("PORT") + accessKeyID := os.Getenv("S3_ACCESS_KEY_ID") + secretAccessKey := os.Getenv("S3_SECRET_ACCESS_KEY") + bucketName := os.Getenv("S3_BUCKET_NAME") + endpoint := os.Getenv("S3_ENDPOINT_URL") + + return accessKeyID, secretAccessKey, bucketName, endpoint, port +} + +func loggingMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + startTime := time.Now() + + next.ServeHTTP(w, r) + + duration := time.Since(startTime) + log.Printf("Processed request: %s %s. Duration: %v\n", r.Method, r.RequestURI, duration) + }) +} + +func GetObjectList(bucketName string, minioClient *minio.Client) ([]string, error) { + var objectList []string + + // Get the list of objects + objectCh := minioClient.ListObjects(bucketName, "", true, nil) + for object := range objectCh { + if object.Err != nil { + return nil, object.Err + } + objectList = append(objectList, object.Key) + } + + return objectList, nil +}