package main import ( "bytes" "encoding/base64" "encoding/json" "errors" "io/ioutil" "net/http" "net/url" "strings" ) type Storage interface { CreateFile(path string, file string, message string) (err error) UpdateFile(path string, file string, message string) (err error) ReadFile(path string) (content string, sha string, exists bool, err error) } // Gitea type Gitea struct { endpoint string token string } type giteaCommitRequest struct { Content string `json:"content"` Message string `json:"message"` SHA string `json:"sha,omitempty"` } type giteaReadResponse struct { Type string `json:"type"` Content string `json:"content"` SHA string `json:"sha"` } type giteaErrorResponse struct { Message string `json:"message"` } func (gitea *Gitea) CreateFile(path string, file string, message string) (err error) { request := &giteaCommitRequest{ Content: base64.StdEncoding.EncodeToString([]byte(file)), Message: message, } bytesRepresentation, err := json.Marshal(request) if err != nil { return errors.New("failed to marshal json before committing") } resp, err := http.Post(gitea.endpoint+url.QueryEscape(path)+"?access_token="+gitea.token, "application/json", bytes.NewBuffer(bytesRepresentation)) if err != nil || resp.StatusCode != 201 { return errors.New("failed to create file in repo") } return nil } func (gitea *Gitea) UpdateFile(path string, file string, message string) (err error) { _, sha, exists, err := gitea.ReadFile(path) if err != nil { return err } if !exists { // File doesn't exist, create it return gitea.CreateFile(path, file, message) } request := &giteaCommitRequest{ Content: base64.StdEncoding.EncodeToString([]byte(file)), Message: message, SHA: sha, } bytesRepresentation, err := json.Marshal(request) if err != nil { return errors.New("failed to marshal json before committing") } req, err := http.NewRequest(http.MethodPut, gitea.endpoint+url.QueryEscape(path)+"?access_token="+gitea.token, bytes.NewBuffer(bytesRepresentation)) if err != nil { return errors.New("error making update request") } req.Header.Set("Content-type", "application/json") client := &http.Client{} resp, err := client.Do(req) if err != nil || resp.StatusCode != 200 { return errors.New("failed to update file in repo") } return nil } func (gitea *Gitea) ReadFile(path string) (content string, sha string, exists bool, err error) { exists = false resp, err := http.Get(gitea.endpoint + url.QueryEscape(path) + "?access_token=" + gitea.token) if err != nil || resp.StatusCode != 200 { if resp != nil { defer resp.Body.Close() body, readErr := ioutil.ReadAll(resp.Body) if readErr != nil { err = errors.New("failed reading Gitea error response") return } errorResponse := &giteaErrorResponse{} marshalErr := json.Unmarshal(body, &errorResponse) if marshalErr != nil { err = errors.New("failed parsing Gitea error response") return } exists = !strings.Contains(errorResponse.Message, "does not exist") if !exists { return } } err = errors.New("failed to read file in repo") return } exists = true defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { err = errors.New("failed reading file in repo") return } readResponse := &giteaReadResponse{} err = json.Unmarshal(body, &readResponse) if err != nil { err = errors.New("failed parsing Gitea response") return } decodedContentBytes, err := base64.StdEncoding.DecodeString(readResponse.Content) if err != nil { err = errors.New("failed decoding file content") } content = string(decodedContentBytes) sha = readResponse.SHA return }