package gitea import ( "fmt" "io" "net/http" "time" "code.gitea.io/sdk/gitea" "git.polynom.me/rio/internal/dns" ) // Returns true if the repository at / exists, false if it // does not. type GetRepositoryMethod func(username, repositoryName string) (Repository, error) // Returns , nil if the file exists at path (relative to the repository) in // /@ exists. If not, returns "", error. type GetFileMethod func(username, repositoryName, branch, path string, since *time.Time) ([]byte, bool, error) // Returns the result of a CNAME lookup for @domain. type LookupCNAMEMethod func(domain string) (string, error) // Return the repository the domain should point to by looking up the TXT record // "_rio-pages.". type LookupRepoTXTMethod func(domain string) (string, error) // Check of the repository / contains the specified // branch. type HasBranchMethod func(username, repositoryName, branchName string) bool // Check if the specified username exists. type HasUserMethod func(username string) bool type Repository struct { Name string } type GiteaClient struct { Token string GetRepository GetRepositoryMethod HasBranch HasBranchMethod HasUser HasUserMethod GetFile GetFileMethod LookupCNAME LookupCNAMEMethod LookupRepoTXT LookupRepoTXTMethod } func NewGiteaClient(giteaUrl string, token string, giteaClient *gitea.Client) GiteaClient { return GiteaClient{ Token: token, GetRepository: func(username, repositoryName string) (Repository, error) { repo, _, err := giteaClient.GetRepo(username, repositoryName) if err != nil { return Repository{}, err } return Repository{ Name: repo.Name, }, nil }, HasBranch: func(username, repositoryName, branchName string) bool { res, _, err := giteaClient.ListRepoBranches(username, repositoryName, gitea.ListRepoBranchesOptions{}) if err != nil { return false } for _, branch := range res { if branch.Name == branchName { return true } } return false }, HasUser: func(username string) bool { _, _, err := giteaClient.GetUserInfo(username) return err == nil }, GetFile: func(username, repositoryName, branch, path string, since *time.Time) ([]byte, bool, error) { // We have to do the raw request manually because the Gitea SDK does not allow // passing the If-Modfied-Since header. apiUrl := fmt.Sprintf( "%s/api/v1/repos/%s/%s/raw/%s?ref=%s", giteaUrl, username, repositoryName, path, branch, ) client := &http.Client{} req, err := http.NewRequest("GET", apiUrl, nil) if since != nil { sinceFormat := since.Format(time.RFC1123) req.Header.Add("If-Modified-Since", sinceFormat) // Add authentication, if we have a token if token != "" { req.Header.Add("Authorization", "token "+token) } } resp, err := client.Do(req) if err != nil { return []byte{}, true, err } defer resp.Body.Close() content, err := io.ReadAll(resp.Body) if err != nil { return []byte{}, true, err } else if resp.StatusCode == 302 { return []byte{}, false, nil } else if resp.StatusCode == 404 { return []byte{}, false, fmt.Errorf("File does not exist") } else { return content, true, err } }, LookupCNAME: func(domain string) (string, error) { return dns.LookupCNAME(domain) }, LookupRepoTXT: func(domain string) (string, error) { return dns.LookupRepoTXT(domain) }, } }