205 lines
5.1 KiB
Go
205 lines
5.1 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Repo struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
type ContentsResponse struct {
|
|
Content []byte `json:"content"` // Assume base64 "encoding" parameter in Gitea response, and use Go's auto decode
|
|
}
|
|
|
|
type TopicsResponse struct {
|
|
Topics []string `json:"topics"`
|
|
}
|
|
|
|
type ReaddirEntry struct {
|
|
Name string `json:"name"`
|
|
Path string `json:"path"`
|
|
Size int `json:"size"`
|
|
RawURL string `json:"download_url"`
|
|
}
|
|
|
|
func (rde ReaddirEntry) isImage() bool {
|
|
return strings.HasSuffix(rde.Name, `.png`) || strings.HasSuffix(rde.Name, `.jpg`) || strings.HasSuffix(rde.Name, `.jpeg`)
|
|
}
|
|
|
|
type MarkdownRequest struct {
|
|
Context string
|
|
Mode string
|
|
Text string
|
|
Wiki bool
|
|
}
|
|
|
|
// repos gets a list of Git repositories in this organisation.
|
|
func (this *Application) repos() ([]Repo, error) {
|
|
resp, err := http.Get(this.cfg.Gitea.URL + `api/v1/orgs/` + url.PathEscape(this.cfg.Gitea.Org) + `/repos`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
|
|
}
|
|
|
|
var repos []Repo
|
|
err = json.NewDecoder(resp.Body).Decode(&repos)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return repos, nil
|
|
}
|
|
|
|
// repoFile gets a single file from the default branch of the git repository
|
|
// Usually the default branch is `master`.
|
|
func (this *Application) repoFile(repo, filename string) ([]byte, error) {
|
|
resp, err := http.Get(this.cfg.Gitea.URL + `api/v1/repos/` + url.PathEscape(this.cfg.Gitea.Org) + `/` + url.PathEscape(repo) + `/contents/` + url.PathEscape(filename))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
|
|
}
|
|
|
|
var cr ContentsResponse
|
|
err = json.NewDecoder(resp.Body).Decode(&cr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return cr.Content, nil
|
|
}
|
|
|
|
func (this *Application) filesInDirectory(repo, dir string) ([]ReaddirEntry, error) {
|
|
resp, err := http.Get(this.cfg.Gitea.URL + `api/v1/repos/` + url.PathEscape(this.cfg.Gitea.Org) + `/` + url.PathEscape(repo) + `/contents/` + dir) // n.b. $dir param not escaped
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
|
|
// "No files found" happens with a HTTP 500/404 error depending on Gitea version. Catch this special case
|
|
if resp.StatusCode == 500 || resp.StatusCode == 404 {
|
|
b, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if strings.Contains(string(b), `does not exist`) {
|
|
return []ReaddirEntry{}, nil // no files found
|
|
}
|
|
}
|
|
|
|
return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
|
|
}
|
|
|
|
var ret []ReaddirEntry
|
|
err = json.NewDecoder(resp.Body).Decode(&ret)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
// imageFilesForRepo finds documentation images for the repository.
|
|
// It searches the dist/ and doc/ subdirectories.
|
|
func (this *Application) imageFilesForRepo(repo string) ([]ReaddirEntry, error) {
|
|
|
|
ret := []ReaddirEntry{}
|
|
|
|
for _, dirName := range []string{`dist`, `doc`} {
|
|
|
|
files, err := this.filesInDirectory(repo, dirName)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("readdir(%s): %w", dirName, err)
|
|
}
|
|
|
|
for _, f := range files {
|
|
if f.isImage() {
|
|
ret = append(ret, f)
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
func (this *Application) topicsForRepo(repo string) ([]string, error) {
|
|
resp, err := http.Get(this.cfg.Gitea.URL + `api/v1/repos/` + url.PathEscape(this.cfg.Gitea.Org) + `/` + url.PathEscape(repo) + `/topics`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
|
|
}
|
|
|
|
var tr TopicsResponse
|
|
err = json.NewDecoder(resp.Body).Decode(&tr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return tr.Topics, nil
|
|
}
|
|
|
|
// renderMarkdown calls the remote Gitea server's own markdown renderer.
|
|
func (this *Application) renderMarkdown(repoName string, body string) ([]byte, error) {
|
|
req := MarkdownRequest{
|
|
Context: this.cfg.Gitea.URL + url.PathEscape(this.cfg.Gitea.Org) + `/` + url.PathEscape(repoName) + `/src/branch/master`,
|
|
Mode: "gfm", // magic constant - Github Flavoured Markdown
|
|
Text: body,
|
|
}
|
|
|
|
jb, err := json.Marshal(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resp, err := http.Post(this.cfg.Gitea.URL+`api/v1/markdown/`, `application/json`, bytes.NewReader(jb))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
|
|
}
|
|
|
|
return ioutil.ReadAll(resp.Body)
|
|
}
|
|
|
|
// renderMarkdownRaw calls the remote Gitea server's own markdown renderer.
|
|
func (this *Application) renderMarkdownRaw(body []byte) ([]byte, error) {
|
|
resp, err := http.Post(this.cfg.Gitea.URL+`api/v1/markdown/raw`, `text/plain`, bytes.NewReader(body))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
|
|
}
|
|
|
|
return ioutil.ReadAll(resp.Body)
|
|
}
|