2022-06-11 21:02:06 +00:00
|
|
|
package gitea
|
|
|
|
|
|
|
|
import (
|
2022-11-12 19:37:20 +00:00
|
|
|
"bytes"
|
2024-11-21 23:26:30 +00:00
|
|
|
"encoding/json"
|
2022-06-11 21:02:06 +00:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
2022-11-12 19:37:20 +00:00
|
|
|
"io"
|
|
|
|
"mime"
|
|
|
|
"net/http"
|
2022-06-11 21:02:06 +00:00
|
|
|
"net/url"
|
2022-11-12 19:37:20 +00:00
|
|
|
"path"
|
|
|
|
"strconv"
|
2022-06-13 18:07:32 +00:00
|
|
|
"strings"
|
2022-06-11 21:02:06 +00:00
|
|
|
"time"
|
|
|
|
|
2022-11-12 19:37:20 +00:00
|
|
|
"code.gitea.io/sdk/gitea"
|
2022-08-12 04:40:12 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
2022-11-12 19:37:20 +00:00
|
|
|
|
2024-02-15 16:08:29 +00:00
|
|
|
"codeberg.org/codeberg/pages/config"
|
2022-11-12 19:37:20 +00:00
|
|
|
"codeberg.org/codeberg/pages/server/cache"
|
2023-11-15 15:25:14 +00:00
|
|
|
"codeberg.org/codeberg/pages/server/version"
|
2022-06-11 21:02:06 +00:00
|
|
|
)
|
|
|
|
|
2022-11-12 19:37:20 +00:00
|
|
|
var ErrorNotFound = errors.New("not found")
|
|
|
|
|
2022-08-12 04:40:12 +00:00
|
|
|
const (
|
2023-11-15 15:25:14 +00:00
|
|
|
// cache key prefixes
|
2022-11-12 19:37:20 +00:00
|
|
|
branchTimestampCacheKeyPrefix = "branchTime"
|
|
|
|
defaultBranchCacheKeyPrefix = "defaultBranch"
|
|
|
|
rawContentCacheKeyPrefix = "rawContent"
|
2024-04-18 17:05:20 +00:00
|
|
|
ownerExistenceKeyPrefix = "ownerExist"
|
2022-11-12 19:37:20 +00:00
|
|
|
|
|
|
|
// pages server
|
|
|
|
PagesCacheIndicatorHeader = "X-Pages-Cache"
|
|
|
|
symlinkReadLimit = 10000
|
|
|
|
|
|
|
|
// gitea
|
2022-08-12 04:40:12 +00:00
|
|
|
giteaObjectTypeHeader = "X-Gitea-Object-Type"
|
2022-11-12 19:37:20 +00:00
|
|
|
objTypeSymlink = "symlink"
|
2022-06-11 21:02:06 +00:00
|
|
|
|
2022-11-12 19:37:20 +00:00
|
|
|
// std
|
2024-11-25 12:21:55 +00:00
|
|
|
ETagHeader = "ETag"
|
|
|
|
ContentTypeHeader = "Content-Type"
|
|
|
|
ContentLengthHeader = "Content-Length"
|
|
|
|
ContentEncodingHeader = "Content-Encoding"
|
2022-11-12 19:37:20 +00:00
|
|
|
)
|
2022-06-11 21:02:06 +00:00
|
|
|
|
|
|
|
type Client struct {
|
2022-11-12 19:37:20 +00:00
|
|
|
sdkClient *gitea.Client
|
2024-02-15 16:08:29 +00:00
|
|
|
responseCache cache.ICache
|
2022-06-11 21:02:06 +00:00
|
|
|
|
2022-11-12 19:43:44 +00:00
|
|
|
giteaRoot string
|
|
|
|
|
2022-08-12 04:40:12 +00:00
|
|
|
followSymlinks bool
|
|
|
|
supportLFS bool
|
2022-06-11 21:02:06 +00:00
|
|
|
|
2022-11-12 19:37:20 +00:00
|
|
|
forbiddenMimeTypes map[string]bool
|
|
|
|
defaultMimeType string
|
2022-06-13 18:07:32 +00:00
|
|
|
}
|
2022-06-11 21:02:06 +00:00
|
|
|
|
2024-05-26 14:45:03 +00:00
|
|
|
func NewClient(cfg config.ForgeConfig, respCache cache.ICache) (*Client, error) {
|
|
|
|
// url.Parse returns valid on almost anything...
|
|
|
|
rootURL, err := url.ParseRequestURI(cfg.Root)
|
2022-11-12 19:37:20 +00:00
|
|
|
if err != nil {
|
2024-05-26 14:45:03 +00:00
|
|
|
return nil, fmt.Errorf("invalid forgejo/gitea root url: %w", err)
|
2022-11-12 19:37:20 +00:00
|
|
|
}
|
2024-05-26 14:45:03 +00:00
|
|
|
giteaRoot := strings.TrimSuffix(rootURL.String(), "/")
|
2022-06-13 18:07:32 +00:00
|
|
|
|
2022-11-12 19:37:20 +00:00
|
|
|
stdClient := http.Client{Timeout: 10 * time.Second}
|
|
|
|
|
2024-02-15 16:08:29 +00:00
|
|
|
forbiddenMimeTypes := make(map[string]bool, len(cfg.ForbiddenMimeTypes))
|
|
|
|
for _, mimeType := range cfg.ForbiddenMimeTypes {
|
|
|
|
forbiddenMimeTypes[mimeType] = true
|
2022-11-12 19:37:20 +00:00
|
|
|
}
|
2024-02-15 16:08:29 +00:00
|
|
|
|
|
|
|
defaultMimeType := cfg.DefaultMimeType
|
2022-11-12 19:37:20 +00:00
|
|
|
if defaultMimeType == "" {
|
|
|
|
defaultMimeType = "application/octet-stream"
|
|
|
|
}
|
|
|
|
|
2023-11-15 15:25:14 +00:00
|
|
|
sdk, err := gitea.NewClient(
|
|
|
|
giteaRoot,
|
|
|
|
gitea.SetHTTPClient(&stdClient),
|
2024-02-15 16:08:29 +00:00
|
|
|
gitea.SetToken(cfg.Token),
|
2023-11-15 15:25:14 +00:00
|
|
|
gitea.SetUserAgent("pages-server/"+version.Version),
|
|
|
|
)
|
|
|
|
|
2022-06-11 21:02:06 +00:00
|
|
|
return &Client{
|
2022-11-12 19:37:20 +00:00
|
|
|
sdkClient: sdk,
|
|
|
|
responseCache: respCache,
|
2022-08-12 04:40:12 +00:00
|
|
|
|
2022-11-12 19:43:44 +00:00
|
|
|
giteaRoot: giteaRoot,
|
|
|
|
|
2024-02-15 16:08:29 +00:00
|
|
|
followSymlinks: cfg.FollowSymlinks,
|
|
|
|
supportLFS: cfg.LFSEnabled,
|
2022-11-12 19:37:20 +00:00
|
|
|
|
|
|
|
forbiddenMimeTypes: forbiddenMimeTypes,
|
|
|
|
defaultMimeType: defaultMimeType,
|
2022-06-13 18:07:32 +00:00
|
|
|
}, err
|
2022-06-11 21:02:06 +00:00
|
|
|
}
|
|
|
|
|
2022-11-12 19:43:44 +00:00
|
|
|
func (client *Client) ContentWebLink(targetOwner, targetRepo, branch, resource string) string {
|
|
|
|
return path.Join(client.giteaRoot, targetOwner, targetRepo, "src/branch", branch, resource)
|
|
|
|
}
|
|
|
|
|
2022-06-11 21:02:06 +00:00
|
|
|
func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource string) ([]byte, error) {
|
2024-11-25 12:21:55 +00:00
|
|
|
reader, _, _, err := client.ServeRawContent(targetOwner, targetRepo, ref, resource, false)
|
2022-06-11 21:02:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-11-12 19:37:20 +00:00
|
|
|
defer reader.Close()
|
|
|
|
return io.ReadAll(reader)
|
2022-06-11 21:02:06 +00:00
|
|
|
}
|
|
|
|
|
2024-11-25 12:21:55 +00:00
|
|
|
func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource string, decompress bool) (io.ReadCloser, http.Header, int, error) {
|
2022-11-12 19:37:20 +00:00
|
|
|
cacheKey := fmt.Sprintf("%s/%s/%s|%s|%s", rawContentCacheKeyPrefix, targetOwner, targetRepo, ref, resource)
|
|
|
|
log := log.With().Str("cache_key", cacheKey).Logger()
|
2024-02-26 22:21:42 +00:00
|
|
|
log.Trace().Msg("try file in cache")
|
2022-11-12 19:37:20 +00:00
|
|
|
// handle if cache entry exist
|
2024-11-21 23:26:30 +00:00
|
|
|
if cacheMetadata, ok := client.responseCache.Get(cacheKey + "|Metadata"); ok {
|
|
|
|
var cache FileResponse
|
|
|
|
err := json.Unmarshal(cacheMetadata.([]byte), &cache)
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msgf("[cache] failed to unmarshal metadata for: %s", cacheKey)
|
|
|
|
return nil, nil, http.StatusNotFound, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !cache.Exists {
|
|
|
|
return nil, nil, http.StatusNotFound, ErrorNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
body, ok := client.responseCache.Get(cacheKey + "|Body")
|
|
|
|
if !ok {
|
|
|
|
log.Error().Msgf("[cache] failed to get body for: %s", cacheKey)
|
|
|
|
return nil, nil, http.StatusNotFound, ErrorNotFound
|
|
|
|
}
|
|
|
|
cache.Body = body.([]byte)
|
|
|
|
|
2024-11-25 12:21:55 +00:00
|
|
|
cachedHeader, cachedStatusCode := cache.createHttpResponse(cacheKey, decompress)
|
2022-11-12 19:37:20 +00:00
|
|
|
if cache.Exists {
|
|
|
|
if cache.IsSymlink {
|
|
|
|
linkDest := string(cache.Body)
|
|
|
|
log.Debug().Msgf("[cache] follow symlink from %q to %q", resource, linkDest)
|
2024-11-25 12:21:55 +00:00
|
|
|
return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest, decompress)
|
2024-11-21 23:26:30 +00:00
|
|
|
} else {
|
2024-02-26 22:21:42 +00:00
|
|
|
log.Debug().Msgf("[cache] return %d bytes", len(cache.Body))
|
2022-11-12 19:37:20 +00:00
|
|
|
return io.NopCloser(bytes.NewReader(cache.Body)), cachedHeader, cachedStatusCode, nil
|
|
|
|
}
|
2024-11-21 23:26:30 +00:00
|
|
|
} else {
|
|
|
|
return nil, nil, http.StatusNotFound, ErrorNotFound
|
2022-11-12 19:37:20 +00:00
|
|
|
}
|
2022-06-11 21:02:06 +00:00
|
|
|
}
|
2024-02-26 22:21:42 +00:00
|
|
|
log.Trace().Msg("file not in cache")
|
2022-11-12 19:37:20 +00:00
|
|
|
// not in cache, open reader via gitea api
|
|
|
|
reader, resp, err := client.sdkClient.GetFileReader(targetOwner, targetRepo, ref, resource, client.supportLFS)
|
|
|
|
if resp != nil {
|
|
|
|
switch resp.StatusCode {
|
|
|
|
case http.StatusOK:
|
|
|
|
// first handle symlinks
|
|
|
|
{
|
|
|
|
objType := resp.Header.Get(giteaObjectTypeHeader)
|
|
|
|
log.Trace().Msgf("server raw content object %q", objType)
|
|
|
|
if client.followSymlinks && objType == objTypeSymlink {
|
|
|
|
defer reader.Close()
|
|
|
|
// read limited chars for symlink
|
|
|
|
linkDestBytes, err := io.ReadAll(io.LimitReader(reader, symlinkReadLimit))
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, http.StatusInternalServerError, err
|
|
|
|
}
|
|
|
|
linkDest := strings.TrimSpace(string(linkDestBytes))
|
|
|
|
|
2023-03-11 05:07:17 +00:00
|
|
|
// handle relative links
|
|
|
|
// we first remove the link from the path, and make a relative join (resolve parent paths like "/../" too)
|
|
|
|
linkDest = path.Join(path.Dir(resource), linkDest)
|
|
|
|
|
2022-11-12 19:37:20 +00:00
|
|
|
// we store symlink not content to reduce duplicates in cache
|
2024-02-26 22:21:42 +00:00
|
|
|
fileResponse := FileResponse{
|
2022-11-12 19:37:20 +00:00
|
|
|
Exists: true,
|
|
|
|
IsSymlink: true,
|
|
|
|
Body: []byte(linkDest),
|
|
|
|
ETag: resp.Header.Get(ETagHeader),
|
2024-02-26 22:21:42 +00:00
|
|
|
}
|
|
|
|
log.Trace().Msgf("file response has %d bytes", len(fileResponse.Body))
|
2024-11-21 23:26:30 +00:00
|
|
|
jsonToCache, err := json.Marshal(fileResponse)
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msgf("[cache] marshaling json metadata for %q has returned an error", cacheKey)
|
|
|
|
}
|
|
|
|
if err := client.responseCache.Set(cacheKey+"|Metadata", jsonToCache, fileCacheTimeout); err != nil {
|
|
|
|
log.Error().Err(err).Msg("[cache] error on cache write")
|
|
|
|
}
|
|
|
|
if err := client.responseCache.Set(cacheKey+"|Body", fileResponse.Body, fileCacheTimeout); err != nil {
|
2022-11-12 19:37:20 +00:00
|
|
|
log.Error().Err(err).Msg("[cache] error on cache write")
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Debug().Msgf("follow symlink from %q to %q", resource, linkDest)
|
2024-11-25 12:21:55 +00:00
|
|
|
return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest, decompress)
|
2022-11-12 19:37:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now we are sure it's content so set the MIME type
|
2024-11-25 12:21:55 +00:00
|
|
|
mimeType, rawType := client.getMimeTypeByExtension(resource)
|
2024-11-21 23:26:30 +00:00
|
|
|
resp.Response.Header.Set(ContentTypeHeader, mimeType)
|
2024-11-25 12:21:55 +00:00
|
|
|
if decompress {
|
|
|
|
resp.Response.Header.Set(ContentTypeHeader, mimeType)
|
|
|
|
} else {
|
|
|
|
resp.Response.Header.Set(ContentTypeHeader, rawType)
|
|
|
|
}
|
2022-08-12 04:40:12 +00:00
|
|
|
|
2023-11-15 15:25:14 +00:00
|
|
|
// now we write to cache and respond at the same time
|
2022-11-12 19:37:20 +00:00
|
|
|
fileResp := FileResponse{
|
|
|
|
Exists: true,
|
|
|
|
ETag: resp.Header.Get(ETagHeader),
|
|
|
|
MimeType: mimeType,
|
2024-11-25 12:21:55 +00:00
|
|
|
RawMime: rawType,
|
2022-11-12 19:37:20 +00:00
|
|
|
}
|
|
|
|
return fileResp.CreateCacheReader(reader, client.responseCache, cacheKey), resp.Response.Header, resp.StatusCode, nil
|
2022-08-12 04:40:12 +00:00
|
|
|
|
2022-11-12 19:37:20 +00:00
|
|
|
case http.StatusNotFound:
|
2024-11-21 23:26:30 +00:00
|
|
|
jsonToCache, err := json.Marshal(FileResponse{ETag: resp.Header.Get(ETagHeader)})
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msgf("[cache] marshaling json metadata for %q has returned an error", cacheKey)
|
|
|
|
}
|
|
|
|
if err := client.responseCache.Set(cacheKey+"|Metadata", jsonToCache, fileCacheTimeout); err != nil {
|
2022-11-12 19:37:20 +00:00
|
|
|
log.Error().Err(err).Msg("[cache] error on cache write")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, resp.Response.Header, http.StatusNotFound, ErrorNotFound
|
|
|
|
default:
|
|
|
|
return nil, resp.Response.Header, resp.StatusCode, fmt.Errorf("unexpected status code '%d'", resp.StatusCode)
|
|
|
|
}
|
2022-06-11 21:02:06 +00:00
|
|
|
}
|
2022-11-12 19:37:20 +00:00
|
|
|
return nil, nil, http.StatusInternalServerError, err
|
2022-06-11 21:02:06 +00:00
|
|
|
}
|
|
|
|
|
2022-11-12 19:37:20 +00:00
|
|
|
func (client *Client) GiteaGetRepoBranchTimestamp(repoOwner, repoName, branchName string) (*BranchTimestamp, error) {
|
|
|
|
cacheKey := fmt.Sprintf("%s/%s/%s/%s", branchTimestampCacheKeyPrefix, repoOwner, repoName, branchName)
|
|
|
|
|
2024-11-21 23:26:30 +00:00
|
|
|
if stampRaw, ok := client.responseCache.Get(cacheKey); ok {
|
|
|
|
var stamp BranchTimestamp
|
|
|
|
err := json.Unmarshal(stampRaw.([]byte), &stamp)
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Bytes("stamp", stampRaw.([]byte)).Msgf("[cache] failed to unmarshal timestamp for: %s", cacheKey)
|
2022-11-12 19:37:20 +00:00
|
|
|
return &BranchTimestamp{}, ErrorNotFound
|
|
|
|
}
|
2024-11-21 23:26:30 +00:00
|
|
|
|
|
|
|
if stamp.NotFound {
|
|
|
|
log.Trace().Msgf("[cache] branch %q does not exist", branchName)
|
|
|
|
|
|
|
|
return &BranchTimestamp{}, ErrorNotFound
|
|
|
|
} else {
|
|
|
|
log.Trace().Msgf("[cache] use branch %q exist", branchName)
|
|
|
|
// This comes from the refactoring of the caching library.
|
|
|
|
// The branch as reported by the API was stored in the cache, and I'm not sure if there are
|
|
|
|
// situations where it differs from the name in the request, hence this is left here.
|
|
|
|
return &stamp, nil
|
|
|
|
}
|
2022-11-12 19:37:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
branch, resp, err := client.sdkClient.GetRepoBranch(repoOwner, repoName, branchName)
|
2022-06-11 21:02:06 +00:00
|
|
|
if err != nil {
|
2022-11-12 19:37:20 +00:00
|
|
|
if resp != nil && resp.StatusCode == http.StatusNotFound {
|
|
|
|
log.Trace().Msgf("[cache] set cache branch %q not found", branchName)
|
2024-11-21 23:26:30 +00:00
|
|
|
jsonToCache, err := json.Marshal(BranchTimestamp{NotFound: true})
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msgf("[cache] marshaling empty timestamp for '%s' has returned an error", cacheKey)
|
|
|
|
}
|
|
|
|
if err := client.responseCache.Set(cacheKey, jsonToCache, branchExistenceCacheTimeout); err != nil {
|
2022-11-12 19:37:20 +00:00
|
|
|
log.Error().Err(err).Msg("[cache] error on cache write")
|
|
|
|
}
|
|
|
|
return &BranchTimestamp{}, ErrorNotFound
|
|
|
|
}
|
|
|
|
return &BranchTimestamp{}, err
|
2022-06-11 21:02:06 +00:00
|
|
|
}
|
2022-11-12 19:37:20 +00:00
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
|
|
return &BranchTimestamp{}, fmt.Errorf("unexpected status code '%d'", resp.StatusCode)
|
2022-06-11 21:02:06 +00:00
|
|
|
}
|
2022-11-12 19:37:20 +00:00
|
|
|
|
|
|
|
stamp := &BranchTimestamp{
|
|
|
|
Branch: branch.Name,
|
|
|
|
Timestamp: branch.Commit.Timestamp,
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Trace().Msgf("set cache branch [%s] exist", branchName)
|
2024-11-21 23:26:30 +00:00
|
|
|
jsonToCache, err := json.Marshal(stamp)
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msgf("[cache] marshaling timestamp for %q has returned an error", cacheKey)
|
|
|
|
}
|
|
|
|
if err := client.responseCache.Set(cacheKey, jsonToCache, branchExistenceCacheTimeout); err != nil {
|
2022-11-12 19:37:20 +00:00
|
|
|
log.Error().Err(err).Msg("[cache] error on cache write")
|
|
|
|
}
|
|
|
|
return stamp, nil
|
2022-06-11 21:02:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (client *Client) GiteaGetRepoDefaultBranch(repoOwner, repoName string) (string, error) {
|
2022-11-12 19:37:20 +00:00
|
|
|
cacheKey := fmt.Sprintf("%s/%s/%s", defaultBranchCacheKeyPrefix, repoOwner, repoName)
|
|
|
|
|
2024-11-21 23:26:30 +00:00
|
|
|
if branch, ok := client.responseCache.Get(cacheKey); ok {
|
|
|
|
return string(branch.([]byte)), nil
|
2022-11-12 19:37:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
repo, resp, err := client.sdkClient.GetRepo(repoOwner, repoName)
|
2022-06-11 21:02:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2022-11-12 19:37:20 +00:00
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
|
|
return "", fmt.Errorf("unexpected status code '%d'", resp.StatusCode)
|
2022-06-11 21:02:06 +00:00
|
|
|
}
|
2022-11-12 19:37:20 +00:00
|
|
|
|
|
|
|
branch := repo.DefaultBranch
|
2024-11-21 23:26:30 +00:00
|
|
|
if err := client.responseCache.Set(cacheKey, []byte(branch), defaultBranchCacheTimeout); err != nil {
|
2022-11-12 19:37:20 +00:00
|
|
|
log.Error().Err(err).Msg("[cache] error on cache write")
|
|
|
|
}
|
|
|
|
return branch, nil
|
2022-06-11 21:02:06 +00:00
|
|
|
}
|
|
|
|
|
2024-04-18 17:05:20 +00:00
|
|
|
func (client *Client) GiteaCheckIfOwnerExists(owner string) (bool, error) {
|
|
|
|
cacheKey := fmt.Sprintf("%s/%s", ownerExistenceKeyPrefix, owner)
|
|
|
|
|
2024-11-21 23:26:30 +00:00
|
|
|
if existRaw, ok := client.responseCache.Get(cacheKey); ok && existRaw != nil {
|
|
|
|
exist, err := strconv.ParseBool(existRaw.(string))
|
|
|
|
return exist, err
|
2024-04-18 17:05:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_, resp, err := client.sdkClient.GetUserInfo(owner)
|
|
|
|
if resp.StatusCode == http.StatusOK && err == nil {
|
2024-11-21 23:26:30 +00:00
|
|
|
if err := client.responseCache.Set(cacheKey, []byte("true"), ownerExistenceCacheTimeout); err != nil {
|
2024-04-18 17:05:20 +00:00
|
|
|
log.Error().Err(err).Msg("[cache] error on cache write")
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
} else if resp.StatusCode != http.StatusNotFound {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, resp, err = client.sdkClient.GetOrg(owner)
|
|
|
|
if resp.StatusCode == http.StatusOK && err == nil {
|
2024-11-21 23:26:30 +00:00
|
|
|
if err := client.responseCache.Set(cacheKey, []byte("true"), ownerExistenceCacheTimeout); err != nil {
|
2024-04-18 17:05:20 +00:00
|
|
|
log.Error().Err(err).Msg("[cache] error on cache write")
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
} else if resp.StatusCode != http.StatusNotFound {
|
|
|
|
return false, err
|
|
|
|
}
|
2024-11-21 23:26:30 +00:00
|
|
|
if err := client.responseCache.Set(cacheKey, []byte("false"), ownerExistenceCacheTimeout); err != nil {
|
2024-04-18 17:05:20 +00:00
|
|
|
log.Error().Err(err).Msg("[cache] error on cache write")
|
|
|
|
}
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
2024-11-25 12:21:55 +00:00
|
|
|
func (client *Client) extToMime(ext string) string {
|
|
|
|
mimeType := mime.TypeByExtension(path.Ext(ext))
|
2022-11-12 19:37:20 +00:00
|
|
|
mimeTypeSplit := strings.SplitN(mimeType, ";", 2)
|
|
|
|
if client.forbiddenMimeTypes[mimeTypeSplit[0]] || mimeType == "" {
|
|
|
|
mimeType = client.defaultMimeType
|
|
|
|
}
|
2024-11-25 12:21:55 +00:00
|
|
|
log.Trace().Msgf("probe mime of extension '%q' is '%q'", ext, mimeType)
|
2022-06-11 21:02:06 +00:00
|
|
|
|
2024-11-21 23:26:30 +00:00
|
|
|
return mimeType
|
2022-06-11 21:02:06 +00:00
|
|
|
}
|
2024-11-25 12:21:55 +00:00
|
|
|
|
|
|
|
func (client *Client) getMimeTypeByExtension(resource string) (mimeType, rawType string) {
|
|
|
|
rawExt := path.Ext(resource)
|
|
|
|
innerExt := rawExt
|
|
|
|
switch rawExt {
|
|
|
|
case ".gz", ".br", ".zst":
|
|
|
|
innerExt = path.Ext(resource[:len(resource)-len(rawExt)])
|
|
|
|
}
|
|
|
|
rawType = client.extToMime(rawExt)
|
|
|
|
mimeType = rawType
|
|
|
|
if innerExt != rawExt {
|
|
|
|
mimeType = client.extToMime(innerExt)
|
|
|
|
}
|
|
|
|
log.Trace().Msgf("probe mime of %q is (%q / raw %q)", resource, mimeType, rawType)
|
|
|
|
return mimeType, rawType
|
|
|
|
}
|