package gitea import ( "bytes" "fmt" "io" "io/fs" "net/http" "net/url" "strings" "sync" gclient "code.gitea.io/sdk/gitea" "github.com/spf13/viper" ) type Client struct { serverURL string token string giteapages string giteapagesAllowAll string gc *gclient.Client } func NewClient(serverURL, token, giteapages, giteapagesAllowAll string) (*Client, error) { if giteapages == "" { giteapages = "gitea-pages" } if giteapagesAllowAll == "" { giteapagesAllowAll = "gitea-pages-allowall" } gc, err := gclient.NewClient(serverURL, gclient.SetToken(token), gclient.SetGiteaVersion("")) if err != nil { return nil, err } return &Client{ serverURL: serverURL, token: token, gc: gc, giteapages: giteapages, giteapagesAllowAll: giteapagesAllowAll, }, nil } func (c *Client) Open(name, ref string) (fs.File, error) { owner, repo, filepath := splitName(name) // if repo is empty they want to have the gitea-pages repo if repo == "" { repo = c.giteapages filepath = "index.html" } // if filepath is empty they want to have the index.html if filepath == "" { filepath = "index.html" } // we need to check if the repo exists (and allows access) limited, allowall := c.allowsPages(owner, repo) if !limited && !allowall { // if we're checking the gitea-pages and it doesn't exist, return 404 if repo == c.giteapages && !c.hasRepoBranch(owner, repo, c.giteapages) { return nil, fs.ErrNotExist } // the repo didn't exist but maybe it's a filepath in the gitea-pages repo // so we need to check if the gitea-pages repo exists filepath = repo repo = c.giteapages if ref == "" { ref = c.giteapages } limited, allowall = c.allowsPages(owner, repo) if !limited && !allowall || !c.hasRepoBranch(owner, repo, c.giteapages) { return nil, fs.ErrNotExist } } hasConfig := true if err := c.readConfig(owner, repo); err != nil { // we don't need a config for gitea-pages // no config is only exposing the gitea-pages branch if repo != c.giteapages && !allowall { return nil, err } hasConfig = false } // if we don't have a config and the repo is the gitea-pages // always overwrite the ref to the gitea-pages branch if !hasConfig && (repo == c.giteapages || ref == c.giteapages) { ref = c.giteapages } else if !validRefs(ref, allowall) { return nil, fs.ErrNotExist } res, err := c.getRawFileOrLFS(owner, repo, filepath, ref) if err != nil { return nil, err } if strings.HasSuffix(filepath, ".md") { res, err = handleMD(res) if err != nil { return nil, err } } return &openFile{ content: res, name: filepath, }, nil } func (c *Client) getRawFileOrLFS(owner, repo, filepath, ref string) ([]byte, error) { var ( giteaURL string err error ) // TODO: make pr for go-sdk // gitea sdk doesn't support "media" type for lfs/non-lfs giteaURL, err = url.JoinPath(c.serverURL+"/api/v1/repos/", owner, repo, "media", filepath) if err != nil { return nil, err } giteaURL += "?ref=" + url.QueryEscape(ref) req, err := http.NewRequest(http.MethodGet, giteaURL, nil) if err != nil { return nil, err } req.Header.Add("Authorization", "token "+c.token) resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } switch resp.StatusCode { case http.StatusNotFound: return nil, fs.ErrNotExist case http.StatusOK: default: return nil, fmt.Errorf("unexpected status code '%d'", resp.StatusCode) } res, err := io.ReadAll(resp.Body) if err != nil { return nil, err } defer resp.Body.Close() return res, nil } var bufPool = sync.Pool{ New: func() any { return new(bytes.Buffer) }, } func handleMD(res []byte) ([]byte, error) { meta, resbody, err := extractFrontMatter(string(res)) if err != nil { return nil, err } resmd, err := markdown([]byte(resbody)) if err != nil { return nil, err } title, _ := meta["title"].(string) res = append([]byte("\n\n
\n