Conrad Hoffmann: 1 Allow setting custom Cache-Control headers 4 files changed, 50 insertions(+), 21 deletions(-)
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~sircmpwn/sr.ht-dev/patches/31117/mbox | git am -3Learn more about email & git
Extend the SiteConfig GQL data type with a list of FileConfig to allow specifying a list of (`glob`, `FileOptions`) pairs, where `FileOptions` is an extensible set of options, currently only containing a `cacheControl` attribute. For each published file that matches `glob`, the respective options will be applied. For `cacheControl`, the file will be served with a Cache-Control header of the same value. The implementation uses the S3/minio metadata for storing the headers. Any values provided are used as input for the version hash, to make sure changes to the options lead to actual new site versions. Fixes: https://todo.sr.ht/~sircmpwn/pages.sr.ht/10 --- gqlgen.yml | 3 --- graph/schema.graphqls | 18 +++++++++++++++ graph/schema.resolvers.go | 46 ++++++++++++++++++++++++--------------- server.go | 4 ++++ 4 files changed, 50 insertions(+), 21 deletions(-) diff --git a/gqlgen.yml b/gqlgen.yml index 28cd866..33d349a 100644 --- a/gqlgen.yml +++ b/gqlgen.yml @@ -60,6 +60,3 @@ models: Filter: model: - git.sr.ht/~sircmpwn/core-go/model.Filter - SiteConfig: - model: - - "map[string]interface{}" diff --git a/graph/schema.graphqls b/graph/schema.graphqls index 90935eb..ed17387 100644 --- a/graph/schema.graphqls +++ b/graph/schema.graphqls @@ -90,9 +90,27 @@ type SiteCursor { cursor: Cursor } +""" +Options for a file being served. +""" +input FileOptions { + "Value of the Cache-Control header to be used when serving the file." + cacheControl: String +} + +""" +Provides a way to configure options for a set of files matching the glob +pattern. +""" +input FileConfig { + glob: String! + options: FileOptions! +} + input SiteConfig { "Path to the file to serve for 404 Not Found responses" notFound: String + fileConfigs: [FileConfig!] } type Query { diff --git a/graph/schema.resolvers.go b/graph/schema.resolvers.go index 4b5a112..3528135 100644 --- a/graph/schema.resolvers.go +++ b/graph/schema.resolvers.go @@ -30,7 +30,7 @@ import ( minio "github.com/minio/minio-go/v7" ) -func (r *mutationResolver) Publish(ctx context.Context, domain string, content graphql.Upload, protocol *model.Protocol, subdirectory *string, siteConfig map[string]interface{}) (*model.Site, error) { +func (r *mutationResolver) Publish(ctx context.Context, domain string, content graphql.Upload, protocol *model.Protocol, subdirectory *string, siteConfig *model.SiteConfig) (*model.Site, error) { conf := config.ForContext(ctx) bucket, _ := conf.Get("pages.sr.ht", "s3-bucket") prefix, _ := conf.Get("pages.sr.ht", "s3-prefix") @@ -62,10 +62,8 @@ func (r *mutationResolver) Publish(ctx context.Context, domain string, content g } } - if value, ok := siteConfig["notFound"].(string); ok { - if value == "" { - return nil, fmt.Errorf("Invalid path siteConfig.notFound") - } + if siteConfig.NotFound != nil && *siteConfig.NotFound == "" { + return nil, fmt.Errorf("Invalid path siteConfig.notFound") } if strings.HasSuffix(domain, "."+userDomain) { @@ -156,21 +154,20 @@ func (r *mutationResolver) Publish(ctx context.Context, domain string, content g } notFound := site.NotFound - if value, ok := siteConfig["notFound"]; ok { - switch value.(type) { - case nil: - notFound = nil - case string: - value := path.Join("/", value.(string)) - notFound = &value - default: - panic("GraphQL schema validation broken") - } + if siteConfig.NotFound != nil { + value := path.Join("/", *siteConfig.NotFound) + notFound = &value } if notFound != nil { io.WriteString(sha, *notFound+"\n") } + for _, fileConfig := range siteConfig.FileConfigs { + if fileConfig.Options.CacheControl != nil { + io.WriteString(sha, fileConfig.Glob+"|"+*fileConfig.Options.CacheControl+"\n") + } + } + inputReader := io.TeeReader(content.File, sha) gzipReader, err := gzip.NewReader(inputReader) if err != nil { @@ -182,16 +179,29 @@ func (r *mutationResolver) Publish(ctx context.Context, domain string, content g var header *tar.Header for header, err = archive.Next(); err == nil; header, err = archive.Next() { + name := path.Clean(header.Name) + opts := minio.PutObjectOptions{} + if header.Typeflag != tar.TypeReg { continue } - fpath := path.Join(s3path, path.Clean(header.Name)) + for _, fileConfig := range siteConfig.FileConfigs { + match, err := path.Match(fileConfig.Glob, name) + if err != nil { + return err + } + if match && fileConfig.Options.CacheControl != nil { + opts.CacheControl = *fileConfig.Options.CacheControl + break + } + } + fpath := path.Join(s3path, name) _, err := mc.PutObject(ctx, bucket, fpath, - archive, header.Size, minio.PutObjectOptions{}) + archive, header.Size, opts) if err != nil { return err } - files = append(files, path.Clean(header.Name)) + files = append(files, name) createdFiles = append(createdFiles, fpath) } diff --git a/server.go b/server.go index ada1c8b..cbad65f 100644 --- a/server.go +++ b/server.go @@ -252,6 +252,10 @@ func ServeHTTP(conf ini.File, db *sql.DB, mc *minio.Client) *http.Server { } return } + cc := objectInfo.Metadata.Get("Cache-Control") + if cc != "" { + w.Header().Set("Cache-Control", cc) + } http.ServeContent(w, r, s3path, objectInfo.LastModified, object) })) return srv -- 2.35.2
builds.sr.ht <builds@sr.ht>pages.sr.ht/patches: FAILED in 2m20s [Allow setting custom Cache-Control headers][0] v4 from [Conrad Hoffmann][1] [0]: https://lists.sr.ht/~sircmpwn/sr.ht-dev/patches/31117 [1]: mailto:ch@bitfehler.net ✗ #736888 FAILED pages.sr.ht/patches/archlinux.yml https://builds.sr.ht/~sircmpwn/job/736888 ✓ #736887 SUCCESS pages.sr.ht/patches/alpine.yml https://builds.sr.ht/~sircmpwn/job/736887
Thanks! To git@git.sr.ht:~sircmpwn/pages.sr.ht 9acfa12..ac8a24f master -> master