diff --git a/conf/config.example.toml b/conf/config.example.toml index b3fba93..83c4459 100644 --- a/conf/config.example.toml +++ b/conf/config.example.toml @@ -16,6 +16,7 @@ clone-url = "https://codeberg.org//.git" index-repo = "pages" index-repo-branch = "main" authorization = "forgejo" +max-preview-lifetime = "7d" [fallback] # non-default section proxy-to = "https://codeberg.page" diff --git a/src/auth.go b/src/auth.go index 49bba1c..59628c3 100644 --- a/src/auth.go +++ b/src/auth.go @@ -11,6 +11,7 @@ import ( "path" "slices" "strings" + "time" "golang.org/x/net/idna" ) @@ -116,6 +117,8 @@ type Authorization struct { branch string // The authorized forge user. forgeUser *ForgeUser + // If zero, any expiration is allowed. If not, site must expire no later than this time. + expiresNoLater time.Time } func (auth *Authorization) ForgeRepoURL() string { @@ -671,6 +674,7 @@ func authorizeForgeWildcard(r *http.Request) (*Authorization, error) { errs = append(errs, err) } else { auth.branch = branch + auth.expiresNoLater = time.Now().AddDate(0, 0, int(pattern.MaxPreviewLifetime)) return auth, nil } } diff --git a/src/config.go b/src/config.go index 0985fa6..e443e97 100644 --- a/src/config.go +++ b/src/config.go @@ -79,12 +79,13 @@ type ServerConfig struct { } type WildcardConfig struct { - Domain string `toml:"domain"` - PreviewDomain string `toml:"preview-domain"` - CloneURL string `toml:"clone-url"` // URL template, not an exact URL - IndexRepo string `toml:"index-repo" default:"pages"` - IndexRepoBranch string `toml:"index-repo-branch" default:"pages"` - Authorization string `toml:"authorization"` + Domain string `toml:"domain"` + PreviewDomain string `toml:"preview-domain"` + CloneURL string `toml:"clone-url"` // URL template, not an exact URL + IndexRepo string `toml:"index-repo" default:"pages"` + IndexRepoBranch string `toml:"index-repo-branch" default:"pages"` + Authorization string `toml:"authorization"` + MaxPreviewLifetime uint `toml:"max-preview-lifetime"` // in days } type FallbackConfig struct { diff --git a/src/pages.go b/src/pages.go index da55153..7d1fb67 100644 --- a/src/pages.go +++ b/src/pages.go @@ -492,7 +492,9 @@ func checkDryRun(w http.ResponseWriter, r *http.Request) bool { return false } -func getUpdateOptions(w http.ResponseWriter, r *http.Request) (opts UpdateOptions, ok bool) { +func getUpdateOptions( + w http.ResponseWriter, r *http.Request, auth *Authorization, +) (opts UpdateOptions, ok bool) { var err error if config.Feature("expiration") { @@ -507,6 +509,11 @@ func getUpdateOptions(w http.ResponseWriter, r *http.Request) (opts UpdateOption return } } + if !auth.expiresNoLater.IsZero() { + if opts.expiresAt.IsZero() || opts.expiresAt.After(auth.expiresNoLater) { + opts.expiresAt = auth.expiresNoLater + } + } } ok = true @@ -530,11 +537,6 @@ func putPage(w http.ResponseWriter, r *http.Request) error { return err } - opts, ok := getUpdateOptions(w, r) - if !ok { - return nil - } - ctx, cancel := context.WithTimeout(r.Context(), time.Duration(config.Limits.UpdateTimeout)) defer cancel() @@ -565,6 +567,11 @@ func putPage(w http.ResponseWriter, r *http.Request) error { return err } + opts, ok := getUpdateOptions(w, r, auth) + if !ok { + return nil + } + if checkDryRun(w, r) { return nil } @@ -582,6 +589,11 @@ func putPage(w http.ResponseWriter, r *http.Request) error { repoURL := auth.ForgeRepoURL() + opts, ok := getUpdateOptions(w, r, auth) + if !ok { + return nil + } + if checkDryRun(w, r) { return nil } @@ -609,11 +621,6 @@ func patchPage(w http.ResponseWriter, r *http.Request) error { return err } - opts, ok := getUpdateOptions(w, r) - if !ok { - return nil - } - auth, err := AuthorizeUpdateFromArchive(r) if err != nil { return err @@ -622,6 +629,11 @@ func patchPage(w http.ResponseWriter, r *http.Request) error { principal := GetPrincipal(r.Context()) copyForgeAuthToPrincipal(principal, auth) + opts, ok := getUpdateOptions(w, r, auth) + if !ok { + return nil + } + if checkDryRun(w, r) { return nil } diff --git a/src/update.go b/src/update.go index 5867d17..6f18a76 100644 --- a/src/update.go +++ b/src/update.go @@ -100,6 +100,9 @@ func Update( case UpdateNoChange: status = "unchanged" } + if newManifest.ExpiresAt != nil { + logc.Printf(ctx, "expire %s: at %s", webRoot, newManifest.ExpiresAt.AsTime()) + } if storedManifest.Commit != nil { logc.Printf(ctx, "update %s ok: %s %s", webRoot, *storedManifest.Commit, status) } else { diff --git a/src/wildcard.go b/src/wildcard.go index f57d8d2..5093c25 100644 --- a/src/wildcard.go +++ b/src/wildcard.go @@ -9,12 +9,13 @@ import ( ) type WildcardPattern struct { - Domain []string - PreviewDomain []string - CloneURL *fasttemplate.Template - IndexRepo *fasttemplate.Template - IndexBranch string - Authorization bool + Domain []string + PreviewDomain []string + CloneURL *fasttemplate.Template + IndexRepo *fasttemplate.Template + IndexBranch string + Authorization bool + MaxPreviewLifetime uint } func (pattern *WildcardPattern) GetHost() string { @@ -123,12 +124,13 @@ func TranslateWildcards(wildcardConfigs []WildcardConfig) ([]*WildcardPattern, e } wildcardPatterns = append(wildcardPatterns, &WildcardPattern{ - Domain: strings.Split(wildcardConfig.Domain, "."), - PreviewDomain: strings.Split(wildcardConfig.PreviewDomain, "."), - CloneURL: cloneURLTemplate, - IndexRepo: indexRepoTemplate, - IndexBranch: indexRepoBranch, - Authorization: authorization, + Domain: strings.Split(wildcardConfig.Domain, "."), + PreviewDomain: strings.Split(wildcardConfig.PreviewDomain, "."), + CloneURL: cloneURLTemplate, + IndexRepo: indexRepoTemplate, + IndexBranch: indexRepoBranch, + Authorization: authorization, + MaxPreviewLifetime: wildcardConfig.MaxPreviewLifetime, }) } return wildcardPatterns, nil