[mirror] Scalable static site server for Git forges (like GitHub Pages)
1package git_pages
2
3import (
4 "fmt"
5 "slices"
6 "strings"
7
8 "github.com/valyala/fasttemplate"
9)
10
11type WildcardPattern struct {
12 Domain []string
13 CloneURL *fasttemplate.Template
14 IndexRepo *fasttemplate.Template
15 IndexBranch string
16 Authorization bool
17}
18
19func (pattern *WildcardPattern) GetHost() string {
20 parts := []string{"*"}
21 parts = append(parts, pattern.Domain...)
22 return strings.Join(parts, ".")
23}
24
25// Returns `subdomain, found` where if `found == true`, `subdomain` contains the part of `host`
26// corresponding to the * in the domain pattern.
27func (pattern *WildcardPattern) Matches(host string) (string, bool) {
28 hostParts := strings.Split(host, ".")
29 hostLen := len(hostParts)
30 patternLen := len(pattern.Domain)
31
32 // host must have at least one more part than the pattern domain
33 if hostLen <= patternLen {
34 return "", false
35 }
36
37 // break the host parts into <subdomain parts> and <domain parts>
38 mid := hostLen - patternLen
39 prefix := hostParts[:mid]
40 suffix := hostParts[mid:]
41
42 // check if the suffix matches the domain
43 if !slices.Equal(suffix, pattern.Domain) {
44 return "", false
45 }
46
47 // return all the subdomain parts
48 subdomain := strings.Join(prefix, ".")
49 return subdomain, true
50}
51
52func (pattern *WildcardPattern) ApplyTemplate(userName string, projectName string) (string, string) {
53 var repoURL string
54 var branch string
55 repoURLTemplate := pattern.CloneURL
56 if projectName == ".index" {
57 repoURL = repoURLTemplate.ExecuteString(map[string]any{
58 "user": userName,
59 "project": pattern.IndexRepo.ExecuteString(map[string]any{"user": userName}),
60 })
61 branch = pattern.IndexBranch
62 } else {
63 repoURL = repoURLTemplate.ExecuteString(map[string]any{
64 "user": userName,
65 "project": projectName,
66 })
67 branch = "pages"
68 }
69 return repoURL, branch
70}
71
72func TranslateWildcards(configs []WildcardConfig) ([]*WildcardPattern, error) {
73 var wildcardPatterns []*WildcardPattern
74 for _, config := range configs {
75 cloneURLTemplate, err := fasttemplate.NewTemplate(config.CloneURL, "<", ">")
76 if err != nil {
77 return nil, fmt.Errorf("wildcard pattern: clone URL: %w", err)
78 }
79
80 var indexRepoBranch string = config.IndexRepoBranch
81 indexRepoTemplate, err := fasttemplate.NewTemplate(config.IndexRepo, "<", ">")
82 if err != nil {
83 return nil, fmt.Errorf("wildcard pattern: index repo: %w", err)
84 }
85
86 authorization := false
87 if config.Authorization != "" {
88 if slices.Contains([]string{"gogs", "gitea", "forgejo"}, config.Authorization) {
89 // Currently these are the only supported forges, and the authorization mechanism
90 // is the same for all of them.
91 authorization = true
92 } else {
93 return nil, fmt.Errorf(
94 "wildcard pattern: unknown authorization mechanism: %s",
95 config.Authorization,
96 )
97 }
98 }
99
100 wildcardPatterns = append(wildcardPatterns, &WildcardPattern{
101 Domain: strings.Split(config.Domain, "."),
102 CloneURL: cloneURLTemplate,
103 IndexRepo: indexRepoTemplate,
104 IndexBranch: indexRepoBranch,
105 Authorization: authorization,
106 })
107 }
108 return wildcardPatterns, nil
109}