切换存储方式
This commit is contained in:
@@ -45,7 +45,7 @@ func (a *DomainAlias) Bind(ctx context.Context, domains []string, owner, repo, b
|
||||
return err
|
||||
}
|
||||
}
|
||||
if domains == nil || len(domains) == 0 {
|
||||
if len(domains) == 0 {
|
||||
return nil
|
||||
}
|
||||
aliasMeta := &Alias{
|
||||
|
||||
@@ -2,6 +2,7 @@ package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
@@ -12,7 +13,7 @@ type BranchInfo struct {
|
||||
}
|
||||
|
||||
type Backend interface {
|
||||
Close() error
|
||||
io.Closer
|
||||
// Repos return repo name + default branch
|
||||
Repos(ctx context.Context, owner string) (map[string]string, error)
|
||||
// Branches return branch + commit id
|
||||
|
||||
@@ -57,7 +57,6 @@ func (c *CacheBackend) Repos(ctx context.Context, owner string) (map[string]stri
|
||||
}
|
||||
|
||||
func (c *CacheBackend) Branches(ctx context.Context, owner, repo string) (map[string]*BranchInfo, error) {
|
||||
ret := make(map[string]*BranchInfo)
|
||||
key := fmt.Sprintf("%s/%s", owner, repo)
|
||||
if load, b := c.cacheBranch.Load(ctx, key); b {
|
||||
return load, nil
|
||||
|
||||
@@ -70,24 +70,24 @@ func (p *PageDomain) ParseDomainMeta(ctx context.Context, domain, path, branch s
|
||||
return p.ReturnMeta(ctx, owner, domain, branch, pathArr)
|
||||
}
|
||||
|
||||
func (p *PageDomain) ReturnMeta(ctx context.Context, owner string, repo string, branch string, path []string) (*PageDomainContent, error) {
|
||||
func (p *PageDomain) ReturnMeta(ctx context.Context, owner, repo, branch string, path []string) (*PageDomainContent, error) {
|
||||
rel := &PageDomainContent{}
|
||||
if meta, err := p.GetMeta(ctx, owner, repo, branch); err == nil {
|
||||
rel.PageMetaContent = meta
|
||||
rel.Owner = owner
|
||||
rel.Repo = repo
|
||||
rel.Path = strings.Join(path, "/")
|
||||
if err = p.alias.Bind(ctx, meta.Alias, rel.Owner, rel.Repo, branch); err != nil {
|
||||
zap.L().Warn("别名绑定失败", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return rel, nil
|
||||
} else {
|
||||
meta, err := p.GetMeta(ctx, owner, repo, branch)
|
||||
if err != nil {
|
||||
zap.L().Debug("查询错误", zap.Error(err))
|
||||
if meta != nil {
|
||||
// 解析错误汇报
|
||||
return nil, errors.New(meta.ErrorMsg)
|
||||
}
|
||||
return nil, errors.Wrap(os.ErrNotExist, strings.Join(path, "/"))
|
||||
}
|
||||
return nil, errors.Wrap(os.ErrNotExist, strings.Join(path, "/"))
|
||||
rel.PageMetaContent = meta
|
||||
rel.Owner = owner
|
||||
rel.Repo = repo
|
||||
rel.Path = strings.Join(path, "/")
|
||||
if err = p.alias.Bind(ctx, meta.Alias, rel.Owner, rel.Repo, branch); err != nil {
|
||||
zap.L().Warn("别名绑定失败", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return rel, nil
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -31,7 +30,7 @@ type ServerMeta struct {
|
||||
Domain string
|
||||
|
||||
client *http.Client
|
||||
|
||||
|
||||
cache kv.KV
|
||||
ttl time.Duration
|
||||
|
||||
@@ -44,29 +43,29 @@ func NewServerMeta(client *http.Client, backend Backend, kv kv.KV, domain string
|
||||
|
||||
func (s *ServerMeta) GetMeta(ctx context.Context, owner, repo, branch string) (*PageMetaContent, error) {
|
||||
rel := NewPageMetaContent()
|
||||
if repos, err := s.Repos(ctx, owner); err != nil {
|
||||
repos, err := s.Repos(ctx, owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defBranch := repos[repo]
|
||||
if defBranch == "" {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
if branch == "" {
|
||||
branch = defBranch
|
||||
}
|
||||
}
|
||||
if branches, err := s.Branches(ctx, owner, repo); err != nil {
|
||||
defBranch := repos[repo]
|
||||
if defBranch == "" {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
if branch == "" {
|
||||
branch = defBranch
|
||||
}
|
||||
branches, err := s.Branches(ctx, owner, repo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
info := branches[branch]
|
||||
if info == nil {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
rel.CommitID = info.ID
|
||||
rel.LastModified = info.LastModified
|
||||
}
|
||||
info := branches[branch]
|
||||
if info == nil {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
rel.CommitID = info.ID
|
||||
rel.LastModified = info.LastModified
|
||||
|
||||
key := fmt.Sprintf("meta/%s/%s/%s", owner, repo, branch)
|
||||
key := s.cache.WithKey("meta", owner, repo, branch)
|
||||
cache, err := s.cache.Get(ctx, key)
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return nil, err
|
||||
@@ -97,14 +96,13 @@ func (s *ServerMeta) GetMeta(ctx context.Context, owner, repo, branch string) (*
|
||||
rel.IsPage = false
|
||||
_ = s.cache.Put(ctx, key, rel.String(), s.ttl)
|
||||
return nil, os.ErrNotExist
|
||||
} else {
|
||||
rel.IsPage = true
|
||||
}
|
||||
errFunc := func(err error) (*PageMetaContent, error) {
|
||||
rel.IsPage = true
|
||||
errCall := func(err error) error {
|
||||
rel.IsPage = false
|
||||
rel.ErrorMsg = err.Error()
|
||||
_ = s.cache.Put(ctx, key, rel.String(), s.ttl)
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
// 添加默认跳过的内容
|
||||
for _, defIgnore := range rel.Ignore {
|
||||
@@ -114,7 +112,7 @@ func (s *ServerMeta) GetMeta(ctx context.Context, owner, repo, branch string) (*
|
||||
if data, err := s.ReadString(ctx, owner, repo, rel.CommitID, ".pages.yaml"); err == nil {
|
||||
cfg := new(PageConfig)
|
||||
if err = yaml.Unmarshal([]byte(data), cfg); err != nil {
|
||||
return errFunc(err)
|
||||
return nil, errCall(err)
|
||||
}
|
||||
rel.VRoute = cfg.VirtualRoute
|
||||
// 处理 CNAME
|
||||
@@ -123,14 +121,14 @@ func (s *ServerMeta) GetMeta(ctx context.Context, owner, repo, branch string) (*
|
||||
if regexpHostname.MatchString(cname) && !strings.HasSuffix(strings.ToLower(cname), strings.ToLower(s.Domain)) {
|
||||
rel.Alias = append(rel.Alias, cname)
|
||||
} else {
|
||||
return errFunc(errors.New("invalid alias name " + cname))
|
||||
return nil, errCall(errors.New("invalid alias name " + cname))
|
||||
}
|
||||
}
|
||||
// 处理渲染器
|
||||
for sType, pattern := range cfg.Renders() {
|
||||
var r Render
|
||||
if r = GetRender(sType); r == nil {
|
||||
return errFunc(errors.Errorf("render not found %s", sType))
|
||||
return nil, errCall(errors.Errorf("render not found %s", sType))
|
||||
}
|
||||
if g, err := glob.Compile(strings.TrimSpace(pattern)); err == nil {
|
||||
rel.rendersL = append(rel.rendersL, &renderCompiler{
|
||||
@@ -138,7 +136,7 @@ func (s *ServerMeta) GetMeta(ctx context.Context, owner, repo, branch string) (*
|
||||
Render: r,
|
||||
})
|
||||
} else {
|
||||
return errFunc(err)
|
||||
return nil, errCall(err)
|
||||
}
|
||||
rel.Renders[sType] = append(rel.Renders[sType], pattern)
|
||||
}
|
||||
@@ -147,7 +145,7 @@ func (s *ServerMeta) GetMeta(ctx context.Context, owner, repo, branch string) (*
|
||||
if g, err := glob.Compile(pattern); err == nil {
|
||||
rel.ignoreL = append(rel.ignoreL, g)
|
||||
} else {
|
||||
return errFunc(err)
|
||||
return nil, errCall(err)
|
||||
}
|
||||
rel.Ignore = append(rel.Ignore, pattern)
|
||||
}
|
||||
@@ -157,17 +155,15 @@ func (s *ServerMeta) GetMeta(ctx context.Context, owner, repo, branch string) (*
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
path = "/" + path
|
||||
}
|
||||
if strings.HasSuffix(path, "/") {
|
||||
path = path[:len(path)-1]
|
||||
path = strings.TrimSuffix(path, "/")
|
||||
var rURL *url.URL
|
||||
if rURL, err = url.Parse(backend); err != nil {
|
||||
return nil, errCall(err)
|
||||
}
|
||||
var rUrl *url.URL
|
||||
if rUrl, err = url.Parse(backend); err != nil {
|
||||
return errFunc(err)
|
||||
if rURL.Scheme != "http" && rURL.Scheme != "https" {
|
||||
return nil, errCall(errors.New("invalid backend url " + backend))
|
||||
}
|
||||
if rUrl.Scheme != "http" && rUrl.Scheme != "https" {
|
||||
return errFunc(errors.New("invalid backend url " + backend))
|
||||
}
|
||||
rel.Proxy[path] = rUrl.String()
|
||||
rel.Proxy[path] = rURL.String()
|
||||
}
|
||||
} else {
|
||||
// 不存在配置,但也可以重定向
|
||||
|
||||
@@ -14,25 +14,25 @@ import (
|
||||
const GiteaMaxCount = 9999
|
||||
|
||||
type ProviderGitea struct {
|
||||
BaseUrl string
|
||||
BaseURL string
|
||||
Token string
|
||||
|
||||
gitea *gitea.Client
|
||||
}
|
||||
|
||||
func NewGitea(url string, token string) (*ProviderGitea, error) {
|
||||
func NewGitea(url, token string) (*ProviderGitea, error) {
|
||||
client, err := gitea.NewClient(url, gitea.SetGiteaVersion(""), gitea.SetToken(token))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ProviderGitea{
|
||||
BaseUrl: url,
|
||||
BaseURL: url,
|
||||
Token: token,
|
||||
gitea: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (g *ProviderGitea) Repos(ctx context.Context, owner string) (map[string]string, error) {
|
||||
func (g *ProviderGitea) Repos(_ context.Context, owner string) (map[string]string, error) {
|
||||
result := make(map[string]string)
|
||||
if repos, resp, err := g.gitea.ListOrgRepos(owner, gitea.ListOrgReposOptions{
|
||||
ListOptions: gitea.ListOptions{
|
||||
@@ -75,26 +75,26 @@ func (g *ProviderGitea) Repos(ctx context.Context, owner string) (map[string]str
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (g *ProviderGitea) Branches(ctx context.Context, owner, repo string) (map[string]*core.BranchInfo, error) {
|
||||
func (g *ProviderGitea) Branches(_ context.Context, owner, repo string) (map[string]*core.BranchInfo, error) {
|
||||
result := make(map[string]*core.BranchInfo)
|
||||
if branches, resp, err := g.gitea.ListRepoBranches(owner, repo, gitea.ListRepoBranchesOptions{
|
||||
branches, resp, err := g.gitea.ListRepoBranches(owner, repo, gitea.ListRepoBranchesOptions{
|
||||
ListOptions: gitea.ListOptions{
|
||||
PageSize: GiteaMaxCount,
|
||||
},
|
||||
}); err != nil {
|
||||
})
|
||||
if err != nil {
|
||||
if resp != nil {
|
||||
_ = resp.Body.Close()
|
||||
}
|
||||
return nil, err
|
||||
} else {
|
||||
if resp != nil {
|
||||
_ = resp.Body.Close()
|
||||
}
|
||||
for _, branch := range branches {
|
||||
result[branch.Name] = &core.BranchInfo{
|
||||
ID: branch.Commit.ID,
|
||||
LastModified: branch.Commit.Timestamp,
|
||||
}
|
||||
}
|
||||
if resp != nil {
|
||||
_ = resp.Body.Close()
|
||||
}
|
||||
for _, branch := range branches {
|
||||
result[branch.Name] = &core.BranchInfo{
|
||||
ID: branch.Commit.ID,
|
||||
LastModified: branch.Commit.Timestamp,
|
||||
}
|
||||
}
|
||||
if len(result) == 0 {
|
||||
@@ -104,7 +104,10 @@ func (g *ProviderGitea) Branches(ctx context.Context, owner, repo string) (map[s
|
||||
}
|
||||
|
||||
func (g *ProviderGitea) Open(ctx context.Context, client *http.Client, owner, repo, commit, path string, headers http.Header) (*http.Response, error) {
|
||||
giteaURL, err := url.JoinPath(g.BaseUrl, "api/v1/repos", owner, repo, "media", path)
|
||||
if headers == nil {
|
||||
headers = make(http.Header)
|
||||
}
|
||||
giteaURL, err := url.JoinPath(g.BaseURL, "api/v1/repos", owner, repo, "media", path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -113,11 +116,9 @@ func (g *ProviderGitea) Open(ctx context.Context, client *http.Client, owner, re
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if headers != nil {
|
||||
for key, values := range headers {
|
||||
for _, value := range values {
|
||||
req.Header.Add(key, value)
|
||||
}
|
||||
for key, values := range headers {
|
||||
for _, value := range values {
|
||||
req.Header.Add(key, value)
|
||||
}
|
||||
}
|
||||
req.Header.Add("Authorization", "token "+g.Token)
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -19,19 +20,19 @@ import (
|
||||
"gopkg.d7z.net/middleware/cache"
|
||||
"gopkg.d7z.net/middleware/kv"
|
||||
|
||||
stdErr "errors"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"gopkg.d7z.net/gitea-pages/pkg/core"
|
||||
"gopkg.d7z.net/gitea-pages/pkg/utils"
|
||||
|
||||
_ "gopkg.d7z.net/gitea-pages/pkg/renders"
|
||||
)
|
||||
|
||||
var portExp = regexp.MustCompile(`:\d+$`)
|
||||
|
||||
type ServerOptions struct {
|
||||
Domain string //默认域名
|
||||
Domain string // 默认域名
|
||||
DefaultBranch string // 默认分支
|
||||
|
||||
Alias kv.KV // 配置映射关系
|
||||
@@ -43,7 +44,7 @@ type ServerOptions struct {
|
||||
CacheBlobTTL time.Duration // 配置缓存时长
|
||||
CacheBlobLimit uint64 // blob最大缓存大小
|
||||
|
||||
HttpClient *http.Client //自定义客户端
|
||||
HTTPClient *http.Client // 自定义客户端
|
||||
|
||||
EnableRender bool // 允许渲染
|
||||
EnableProxy bool // 允许代理
|
||||
@@ -68,7 +69,7 @@ func DefaultOptions(domain string) ServerOptions {
|
||||
CacheBlobTTL: time.Minute,
|
||||
CacheBlobLimit: 1024 * 1024 * 10,
|
||||
|
||||
HttpClient: http.DefaultClient,
|
||||
HTTPClient: http.DefaultClient,
|
||||
|
||||
EnableRender: true,
|
||||
EnableProxy: true,
|
||||
@@ -95,9 +96,9 @@ var staticPrefix = "/.well-known/page-server/"
|
||||
|
||||
func NewPageServer(backend core.Backend, options ServerOptions) *Server {
|
||||
backend = core.NewCacheBackend(backend, options.CacheMeta, options.CacheMetaTTL)
|
||||
svcMeta := core.NewServerMeta(options.HttpClient, backend, options.CacheMeta, options.Domain, options.CacheMetaTTL)
|
||||
svcMeta := core.NewServerMeta(options.HTTPClient, backend, options.CacheMeta, options.Domain, options.CacheMetaTTL)
|
||||
pageMeta := core.NewPageDomain(svcMeta, options.Alias, options.Domain, options.DefaultBranch)
|
||||
reader := core.NewCacheBackendBlobReader(options.HttpClient, backend, options.CacheBlob, options.CacheBlobLimit)
|
||||
reader := core.NewCacheBackendBlobReader(options.HTTPClient, backend, options.CacheBlob, options.CacheBlobLimit)
|
||||
var fs http.Handler
|
||||
if options.StaticDir != "" {
|
||||
fs = http.StripPrefix(staticPrefix, http.FileServer(http.Dir(options.StaticDir)))
|
||||
@@ -112,15 +113,15 @@ func NewPageServer(backend core.Backend, options ServerOptions) *Server {
|
||||
}
|
||||
|
||||
func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
sessionId, _ := uuid.NewRandom()
|
||||
request.Header.Set("Session-ID", sessionId.String())
|
||||
sessionID, _ := uuid.NewRandom()
|
||||
request.Header.Set("Session-ID", sessionID.String())
|
||||
if s.fs != nil && strings.HasPrefix(request.URL.Path, staticPrefix) {
|
||||
s.fs.ServeHTTP(writer, request)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
zap.L().Error("panic!", zap.Any("error", e), zap.Any("id", sessionId))
|
||||
zap.L().Error("panic!", zap.Any("error", e), zap.Any("id", sessionID))
|
||||
if err, ok := e.(error); ok {
|
||||
s.options.DefaultErrorHandler(writer, request, err)
|
||||
}
|
||||
@@ -128,7 +129,7 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
}()
|
||||
err := s.Serve(writer, request)
|
||||
if err != nil {
|
||||
zap.L().Debug("错误请求", zap.Error(err), zap.Any("request", request.RequestURI), zap.Any("id", sessionId))
|
||||
zap.L().Debug("错误请求", zap.Error(err), zap.Any("request", request.RequestURI), zap.Any("id", sessionID))
|
||||
s.options.DefaultErrorHandler(writer, request, err)
|
||||
}
|
||||
}
|
||||
@@ -144,9 +145,9 @@ func (s *Server) Serve(writer http.ResponseWriter, request *http.Request) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
zap.L().Debug("获取请求", zap.Any("request", meta.Path))
|
||||
zap.L().Debug("new request", zap.Any("request path", meta.Path))
|
||||
if len(meta.Alias) > 0 && !slices.Contains(meta.Alias, domainHost) {
|
||||
zap.L().Debug("重定向地址", zap.Any("src", request.Host), zap.Any("dst", meta.Alias[0]))
|
||||
zap.L().Debug("redirect", zap.Any("src", request.Host), zap.Any("dst", meta.Alias[0]))
|
||||
http.Redirect(writer, request, fmt.Sprintf("https://%s/%s", meta.Alias[0], meta.Path), http.StatusFound)
|
||||
return nil
|
||||
}
|
||||
@@ -163,7 +164,7 @@ func (s *Server) Serve(writer http.ResponseWriter, request *http.Request) error
|
||||
request.URL.Path = targetPath
|
||||
request.RequestURI = request.URL.RequestURI()
|
||||
proxy := httputil.NewSingleHostReverseProxy(u)
|
||||
proxy.Transport = s.options.HttpClient.Transport
|
||||
proxy.Transport = s.options.HTTPClient.Transport
|
||||
|
||||
if host, _, err := net.SplitHostPort(request.RemoteAddr); err == nil {
|
||||
request.Header.Set("X-Real-IP", host)
|
||||
@@ -181,11 +182,11 @@ func (s *Server) Serve(writer http.ResponseWriter, request *http.Request) error
|
||||
}
|
||||
// 在非反向代理时处理目录访问
|
||||
if strings.HasSuffix(meta.Path, "/") || meta.Path == "" {
|
||||
meta.Path = meta.Path + "index.html"
|
||||
meta.Path += "index.html"
|
||||
}
|
||||
|
||||
// 如果不是反向代理路由则跳过任何配置
|
||||
if request.Method != "GET" {
|
||||
if request.Method != http.MethodGet {
|
||||
return os.ErrNotExist
|
||||
}
|
||||
var result io.ReadCloser
|
||||
@@ -225,18 +226,13 @@ func (s *Server) Serve(writer http.ResponseWriter, request *http.Request) error
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
if render := meta.TryRender(meta.Path, "/404.html"); render != nil && s.options.EnableRender {
|
||||
defer result.Close()
|
||||
if err = render.Render(writer, request, result); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
_, _ = io.Copy(writer, result)
|
||||
_ = result.Close()
|
||||
return render.Render(writer, request, result)
|
||||
}
|
||||
_, _ = io.Copy(writer, result)
|
||||
_ = result.Close()
|
||||
return nil
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
fileName := filepath.Base(meta.Path)
|
||||
render := meta.TryRender(meta.Path)
|
||||
@@ -257,7 +253,7 @@ func (s *Server) Serve(writer http.ResponseWriter, request *http.Request) error
|
||||
}
|
||||
} else {
|
||||
if reader, ok := result.(*utils.SizeReadCloser); ok && render == nil {
|
||||
writer.Header().Add("Content-Length", fmt.Sprintf("%d", reader.Size))
|
||||
writer.Header().Add("Content-Length", strconv.FormatUint(reader.Size, 10))
|
||||
}
|
||||
// todo(bug) : 直连模式下告知数据长度
|
||||
writer.Header().Add("X-CacheBlob", "MISS")
|
||||
@@ -276,11 +272,10 @@ func (s *Server) Serve(writer http.ResponseWriter, request *http.Request) error
|
||||
}
|
||||
|
||||
func (s *Server) Close() error {
|
||||
if err := s.options.Alias.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.options.CacheBlob.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.backend.Close()
|
||||
return stdErr.Join(
|
||||
s.options.CacheBlob.Close(),
|
||||
s.options.CacheMeta.Close(),
|
||||
s.options.Alias.Close(),
|
||||
s.backend.Close(),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user