Files
gopeed/internal/githubmirror/config.go

91 lines
2.6 KiB
Go

// Package githubmirror rewrites GitHub URLs and executes mirrored operations.
package githubmirror
import (
"fmt"
"net/url"
"strings"
)
type Mirror struct {
Type string `json:"type"`
URL string `json:"url"`
IsBuiltIn bool `json:"isBuiltIn"`
IsDeleted bool `json:"isDeleted"`
}
type Config struct {
Enabled bool `json:"enabled"`
Mirrors []Mirror `json:"mirrors"`
}
func DefaultConfig() *Config {
return &Config{Enabled: true, Mirrors: []Mirror{
{Type: "jsdelivr", URL: "https://fastly.jsdelivr.net/gh", IsBuiltIn: true},
{Type: "ghProxy", URL: "https://fastgit.cc", IsBuiltIn: true},
{Type: "ghProxy", URL: "https://gh-proxy.com", IsBuiltIn: true},
{Type: "ghProxy", URL: "https://ghfast.top", IsBuiltIn: true},
}}
}
func (c *Config) Validate() error {
if len(c.Mirrors) > 32 {
return fmt.Errorf("too many GitHub mirrors")
}
for _, mirror := range c.Mirrors {
u, err := url.Parse(mirror.URL)
if err != nil || u == nil || (u.Scheme != "http" && u.Scheme != "https") || u.Host == "" || u.User != nil || u.RawQuery != "" || u.Fragment != "" {
return fmt.Errorf("invalid GitHub mirror URL")
}
if mirror.Type != "jsdelivr" && mirror.Type != "ghProxy" {
return fmt.Errorf("invalid GitHub mirror type: %s", mirror.Type)
}
}
return nil
}
// Kind describes what a candidate endpoint must support.
type Kind uint8
const (
Resource Kind = iota
Repository
)
// Candidates preserves the official URL first. jsDelivr is only eligible for
// raw source resources; Git operations use mirrors supporting Git Smart HTTP.
// GitHub API URLs are never mirrored.
func Candidates(c *Config, raw string, kind Kind) []string {
result := []string{raw}
u, err := url.Parse(raw)
if err != nil || c == nil || !c.Enabled || u.User != nil || (u.Scheme != "https" && u.Scheme != "http") {
return result
}
if u.Host != "github.com" && u.Host != "raw.githubusercontent.com" {
return result
}
seen := map[string]bool{raw: true}
for _, mirror := range c.Mirrors {
if mirror.IsDeleted {
continue
}
var candidate string
switch mirror.Type {
case "ghProxy":
candidate = strings.TrimRight(mirror.URL, "/") + "/" + raw
case "jsdelivr":
if kind == Repository || u.Host != "raw.githubusercontent.com" {
continue
}
parts := strings.SplitN(strings.TrimPrefix(u.EscapedPath(), "/"), "/", 4)
if len(parts) != 4 {
continue
}
candidate = strings.TrimRight(mirror.URL, "/") + "/" + parts[0] + "/" + parts[1] + "@" + parts[2] + "/" + parts[3]
}
if candidate != "" && !seen[candidate] {
result = append(result, candidate)
seen[candidate] = true
}
}
return result
}