package repo import ( "errors" "strings" "time" "git.polynom.me/rio/internal/dns" "git.polynom.me/rio/internal/pages" "code.gitea.io/sdk/gitea" "github.com/patrickmn/go-cache" log "github.com/sirupsen/logrus" ) var ( pathCache = cache.New(1*time.Hour, 1*time.Hour) ) type PageCacheEntry struct { Repository *gitea.Repository Path string } func makePageCacheKey(domain, path string) string { return domain + "/" + path } // / Try to find the repository with name @reponame of the user @username. If @cname // / is not "", then it also verifies that the repository contains a "CNAME" with // / the value of @cname as its content. @host, @domain, and @path are passed for // / caching on success. func lookupRepositoryAndCache(username, reponame, host, domain, path, cname string, giteaClient *gitea.Client) (*gitea.Repository, error) { log.Debugf("Looking up repository %s/%s", username, reponame) repo, _, err := giteaClient.GetRepo(username, reponame) if err != nil { return nil, err } // Check if the CNAME file matches if cname != "" { file, _, err := giteaClient.GetFile( username, repo.Name, pages.PagesBranch, "CNAME", false, ) if err != nil { log.Errorf("Could not verify CNAME of %s/%s: %v\n", username, repo.Name, err) return nil, err } cnameContent := strings.Trim( string(file[:]), "\n", ) if cnameContent != cname { return nil, errors.New("CNAME mismatch") } } // Cache data pathCache.Set( makePageCacheKey(domain, path), PageCacheEntry{ repo, path, }, cache.DefaultExpiration, ) return repo, nil } func RepoFromPath(username, host, cname, path string, giteaClient *gitea.Client) (*gitea.Repository, string, error) { domain := host // Guess the repository key := makePageCacheKey(domain, path) entry, found := pathCache.Get(key) if found { pageEntry := entry.(PageCacheEntry) return pageEntry.Repository, pageEntry.Path, nil } pathParts := strings.Split(path, "/") if len(pathParts) > 1 { log.Debugf("Trying repository %s", pathParts[0]) modifiedPath := strings.Join(pathParts[1:], "/") repo, err := lookupRepositoryAndCache( username, pathParts[0], host, domain, modifiedPath, cname, giteaClient, ) if err == nil { return repo, modifiedPath, nil } } // Allow specifying the repository name in the TXT record reponame := domain lookupDomain := domain if cname != "" { lookupDomain = cname } repoLookup, err := dns.LookupRepoTXT(lookupDomain) if err != nil && repoLookup != "" { log.Infof( "TXT lookup for %s resulted in choosing repository %s", lookupDomain, repoLookup, ) reponame = repoLookup } else if cname != "" { // Allow naming the repository "example.org" (But give the TXT record preference) reponame = cname } log.Debugf("Trying repository %s/%s", username, reponame) repo, err := lookupRepositoryAndCache( username, reponame, host, domain, path, cname, giteaClient, ) return repo, path, err }