BedrockFetch/tea/tea.go
Matheus Sampaio Queiroga 5368ef3f25
All checks were successful
Find and Upload Minecraft Server versions / build (push) Successful in 2m26s
MIgration from minecraft.azureedge.net to minecraft.net
Signed-off-by: Matheus Sampaio Queiroga <srherobrine20@gmail.com>
2025-01-20 17:01:56 -03:00

286 lines
8.8 KiB
Go

package tea
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"strings"
"time"
"sirherobrine23.com.br/go-bds/go-bds/request/v2"
)
const Repo string = "go-bds/BedrockFetch" // Default repository
var (
Token string = (func() string {
if ghToken := os.Getenv("GH_TOKEN"); len(ghToken) > 0 {
return ghToken
} else if githubToken := os.Getenv("GITHUB_TOKEN"); len(githubToken) > 0 {
return githubToken
} else if giteaToken := os.Getenv("GITEA_TOKEN"); len(giteaToken) > 0 {
return giteaToken
}
return ""
})()
RootURL string = func() string {
if data := os.Getenv("GITHUB_SERVER_URL"); data != "" {
return data
}
return "https://sirherobrine23.com.br"
}()
GiteaReleaseURI = fmt.Sprintf("%s/api/v1/repos/%s/releases", RootURL, Repo) // Gitea api release
ErrToken error = errors.New("set Gitea token in Env (GH_TOKEN || GITHUB_TOKEN || GITEA_TOKEN)")
ErrBackend error = errors.New("catch 500 error in server api")
ErrNoExist error = errors.New("repository, organization or endpoint dont exists")
)
type GiteaAsset struct {
Id int64 `json:"id"`
FileSize int64 `json:"size"`
Downloads int64 `json:"download_count"`
Uuid string `json:"uuid"`
FileUrl string `json:"browser_download_url"`
FileName string `json:"name"`
Create time.Time `json:"created_at"`
}
type GiteaRelease struct {
ReleaseId int64 `json:"id,omitempty"` // Release id
TagName string `json:"tag_name"` // Release tag name
Name string `json:"name,omitempty"` // Release name
Body string `json:"body,omitempty"` // Release body
IsPrerelease bool `json:"prerelease"` // Set Pre-release
IsDraft bool `json:"draft"` // Set release to draft
FileAssets []GiteaAsset `json:"assets,omitempty"` // Files in release
}
type giteaError struct {
Errors []string `json:"errors"`
TeaError string `json:"error"`
Message string `json:"message"`
Url string `json:"url"`
}
func ErrRes5xx(res *http.Response) (*http.Response, error) {
if strings.Contains(res.Header.Get("Content-Type"), "application/json") {
var data giteaError
defer res.Body.Close()
bodyBuff, err := io.ReadAll(res.Body)
if err != nil {
return nil, errors.Join(ErrBackend, err)
}
if err := json.Unmarshal(bodyBuff, &data); err != nil {
return nil, errors.Join(ErrBackend, ErrNoExist)
} else if len(data.Errors) > 0 {
return nil, errors.Join(ErrBackend, fmt.Errorf("gitea api, errors: %s, %s", data.Message, strings.Join(data.Errors, ", ")))
} else if data.Message != "" {
return nil, errors.Join(ErrBackend, fmt.Errorf("backed error: %s", data.Message))
}
}
return nil, ErrBackend
}
// Get release, if not exists create
func Release(IsPrerelease, Draft bool, TagName, Name, Body string) (*GiteaRelease, error) {
if TagName = strings.TrimSpace(TagName); len(TagName) == 0 {
return nil, fmt.Errorf("set valid tag to create release")
} else if Name = strings.TrimSpace(Name); len(Name) == 0 {
Name = TagName
}
var giteaRel GiteaRelease
_, err := request.JSONDo(fmt.Sprintf("%s/tags/%s", GiteaReleaseURI, TagName), &giteaRel, &request.Options{
Header: map[string]string{
"Authorization": fmt.Sprintf("token %s", Token),
"Content-Type": "application/json",
},
CodeProcess: map[int]request.CodeCallback{
500: ErrRes5xx,
501: ErrRes5xx,
401: func(res *http.Response) (*http.Response, error) { return nil, ErrToken },
404: func(res *http.Response) (*http.Response, error) {
defer res.Body.Close()
return request.Request(GiteaReleaseURI, &request.Options{
Method: "POST",
Header: map[string]string{
"Authorization": fmt.Sprintf("token %s", Token),
"Content-Type": "application/json",
},
Body: GiteaRelease{
TagName: TagName,
Name: Name,
Body: Body,
IsPrerelease: IsPrerelease,
IsDraft: Draft,
},
CodeProcess: map[int]request.CodeCallback{
500: ErrRes5xx,
501: ErrRes5xx,
401: func(res *http.Response) (*http.Response, error) { return nil, ErrToken },
404: func(res *http.Response) (*http.Response, error) {
if strings.Contains(res.Header.Get("Content-Type"), "application/json") {
var data giteaError
defer res.Body.Close()
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, ErrNoExist
} else if len(data.Errors) > 0 {
return nil, fmt.Errorf("gitea api, errors: %s, %s", data.Message, strings.Join(data.Errors, ", "))
}
return nil, errors.New(data.Message)
}
return nil, ErrNoExist
},
},
})
},
},
})
return &giteaRel, err
}
func (rel GiteaRelease) ExistsFile(filename string) bool {
return rel.GetFile(filename) != nil
}
func (rel GiteaRelease) GetFile(filename string) *GiteaAsset {
for _, asset := range rel.FileAssets {
if asset.FileName == filename {
return &asset
}
}
return nil
}
func (asset GiteaAsset) Download() (io.ReadCloser, error) {
res, err := request.Request(asset.FileUrl, &request.Options{Header: map[string]string{"Authorization": fmt.Sprintf("token %s", Token)}})
if err != nil {
return nil, err
}
return res.Body, err
}
type UploadMap map[string]io.Reader
func (rel *GiteaRelease) Upload(filename string, fileBody io.Reader) (*GiteaAsset, error) {
body := new(bytes.Buffer)
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("attachment", filename)
if err != nil {
return nil, err
} else if _, err = io.Copy(part, fileBody); err != nil {
return nil, err
} else if err = writer.Close(); err != nil {
return nil, err
}
asset, _, err := request.JSON[GiteaAsset](fmt.Sprintf("%s/%d/assets", GiteaReleaseURI, rel.ReleaseId), &request.Options{
Method: "POST",
Body: body,
Header: map[string]string{
"Authorization": fmt.Sprintf("token %s", Token),
"Content-Type": writer.FormDataContentType(),
},
CodeProcess: map[int]request.CodeCallback{
500: ErrRes5xx,
501: ErrRes5xx,
-1: func(res *http.Response) (*http.Response, error) {
if res.StatusCode != 201 {
return nil, fmt.Errorf("cannot upload %q to release, code: %d (%s)", filename, res.StatusCode, res.Status)
}
return res, nil
},
},
})
if err != nil {
return nil, err
}
rel.FileAssets = append(rel.FileAssets, asset)
return &asset, nil
}
func (rel *GiteaRelease) DeleteRelease() error {
if rel.ReleaseId > 0 {
_, err := request.Request(fmt.Sprintf("%s/%d", GiteaReleaseURI, rel.ReleaseId), &request.Options{
Method: "DELETE",
Header: map[string]string{
"Authorization": fmt.Sprintf("token %s", Token),
"Content-Type": "application/json",
},
CodeProcess: map[int]request.CodeCallback{
500: ErrRes5xx,
501: ErrRes5xx,
404: func(res *http.Response) (*http.Response, error) { return nil, ErrNoExist },
204: func(res *http.Response) (*http.Response, error) { return nil, nil },
401: func(res *http.Response) (*http.Response, error) { return nil, ErrToken },
},
})
return err
} else if rel.TagName != "" {
_, err := request.Request(fmt.Sprintf("%s/tags/%s", GiteaReleaseURI, rel.TagName), &request.Options{
Method: "DELETE",
Header: map[string]string{
"Authorization": fmt.Sprintf("token %s", Token),
"Content-Type": "application/json",
},
CodeProcess: map[int]request.CodeCallback{
500: ErrRes5xx,
501: ErrRes5xx,
404: func(res *http.Response) (*http.Response, error) { return nil, ErrNoExist },
204: func(res *http.Response) (*http.Response, error) { return nil, nil },
401: func(res *http.Response) (*http.Response, error) { return nil, ErrToken },
},
})
return err
}
return nil
}
func (rel *GiteaRelease) UpdateRelease() error {
_, err := request.JSONDo(fmt.Sprintf("%s/%d", GiteaReleaseURI, rel.ReleaseId), rel, &request.Options{
Method: "PATCH",
Header: map[string]string{
"Authorization": fmt.Sprintf("token %s", Token),
"Content-Type": "application/json",
},
Body: rel,
CodeProcess: map[int]request.CodeCallback{
505: ErrRes5xx,
501: ErrRes5xx,
401: func(res *http.Response) (*http.Response, error) { return nil, ErrToken },
404: func(res *http.Response) (*http.Response, error) {
return request.Request(GiteaReleaseURI, &request.Options{
Method: "POST",
Header: map[string]string{
"Authorization": fmt.Sprintf("token %s", Token),
"Content-Type": "application/json",
},
Body: GiteaRelease{
IsPrerelease: rel.IsPrerelease,
IsDraft: rel.IsDraft,
TagName: rel.TagName,
Name: rel.Name,
Body: rel.Body,
},
CodeProcess: map[int]request.CodeCallback{
505: ErrRes5xx,
501: ErrRes5xx,
401: func(res *http.Response) (*http.Response, error) { return nil, ErrToken },
},
})
},
},
})
return err
}