Merge branch 'feature/resources' into develop
This commit is contained in:
commit
c4dd174c92
|
@ -1,52 +0,0 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"mime"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
Path string
|
||||
Updated time.Time
|
||||
Content []byte
|
||||
|
||||
// Only for graphics
|
||||
Width int
|
||||
Height int
|
||||
}
|
||||
|
||||
// LogicalName returns full file name without directory path.
|
||||
func (f File) LogicalName() string {
|
||||
return filepath.Base(f.Path)
|
||||
}
|
||||
|
||||
// BaseFileName returns file name without extention and directory path.
|
||||
func (f File) BaseFileName() string {
|
||||
base := filepath.Base(f.Path)
|
||||
|
||||
return strings.TrimSuffix(base, filepath.Ext(base))
|
||||
}
|
||||
|
||||
// Ext returns file extention.
|
||||
func (f File) Ext() string {
|
||||
if ext := filepath.Ext(f.Path); len(ext) > 1 {
|
||||
return ext[1:]
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// Dir returns file directory.
|
||||
func (f File) Dir() string {
|
||||
return filepath.Dir(f.Path)
|
||||
}
|
||||
|
||||
func (f File) MediaType() string {
|
||||
return mime.TypeByExtension(filepath.Ext(f.Path))
|
||||
}
|
||||
|
||||
func (f File) GoString() string {
|
||||
return "domain.File(" + f.Path + ")"
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package domain
|
||||
|
||||
import "path"
|
||||
|
||||
// TODO(toby3d): search by type or name/id.
|
||||
type Files []*File
|
||||
|
||||
func (f Files) GetMatch(pattern string) *File {
|
||||
for i := range f {
|
||||
if matched, err := path.Match(pattern, f[i].Path); err != nil || !matched {
|
||||
continue
|
||||
}
|
||||
|
||||
return f[i]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"mime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type MediaType struct {
|
||||
mainType string
|
||||
subType string
|
||||
mediaType string
|
||||
}
|
||||
|
||||
// NewMediaType creates a new MediaType domain from raw string in
|
||||
// the 'MainType/SubType' format.
|
||||
func NewMediaType(raw string) MediaType {
|
||||
parts := strings.Split(raw, "/")
|
||||
if len(parts) < 2 {
|
||||
return MediaType{}
|
||||
}
|
||||
|
||||
return MediaType{
|
||||
mainType: parts[0],
|
||||
subType: parts[1],
|
||||
mediaType: mime.FormatMediaType(strings.Join([]string{parts[0], parts[1]}, "/"), nil),
|
||||
}
|
||||
}
|
||||
|
||||
// Type returns main part of MediaType.
|
||||
// For 'image/jpeg' it returns 'image'.
|
||||
func (mt MediaType) MainType() string {
|
||||
return mt.mainType
|
||||
}
|
||||
|
||||
// Type returns sub part of MediaType.
|
||||
// For 'image/jpeg' it returns 'jpeg'.
|
||||
func (mt MediaType) SubType() string {
|
||||
return mt.subType
|
||||
}
|
||||
|
||||
// Type returns 'MainType/SubType' string.
|
||||
func (mt MediaType) Type() string {
|
||||
return mt.mediaType
|
||||
}
|
||||
|
||||
func (mt MediaType) String() string {
|
||||
return mt.mediaType
|
||||
}
|
||||
|
||||
func (mt MediaType) GoString() string {
|
||||
return "domain.MediaType(" + mt.mediaType + ")"
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package domain_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"source.toby3d.me/toby3d/home/internal/domain"
|
||||
)
|
||||
|
||||
func TestMediaType_Type(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for name, tc := range map[string]struct {
|
||||
input string
|
||||
expect string
|
||||
}{
|
||||
"exists": {input: "image/jpeg", expect: "image/jpeg"},
|
||||
"main": {input: "image/", expect: ""},
|
||||
"sub": {input: "/jpeg", expect: ""},
|
||||
"params": {input: "image/svg+xml", expect: "image/svg+xml"},
|
||||
} {
|
||||
tc, name := tc, name
|
||||
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := domain.NewMediaType(tc.input).Type()
|
||||
if actual != tc.expect {
|
||||
t.Errorf("got '%s', want '%s'", actual, tc.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -3,9 +3,9 @@ package domain
|
|||
import "golang.org/x/text/language"
|
||||
|
||||
type Page struct {
|
||||
Language language.Tag
|
||||
Params map[string]any
|
||||
Title string
|
||||
Content []byte
|
||||
Files Files
|
||||
Language language.Tag
|
||||
Params map[string]any
|
||||
Title string
|
||||
Content []byte
|
||||
Resources Resources
|
||||
}
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
"io"
|
||||
"mime"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
_ "golang.org/x/image/bmp"
|
||||
_ "golang.org/x/image/webp"
|
||||
)
|
||||
|
||||
type Resource struct {
|
||||
modTime time.Time
|
||||
reader io.ReadSeeker
|
||||
params map[string]any // TODO(toby3d): set from Page configuration
|
||||
mediaType MediaType
|
||||
key string
|
||||
name string // TODO(toby3d): set from Page configuration
|
||||
title string // TODO(toby3d): set from Page configuration
|
||||
resourceType ResourceType
|
||||
image image.Config
|
||||
}
|
||||
|
||||
func NewResource(modTime time.Time, content []byte, key string) *Resource {
|
||||
mediaType, _, _ := mime.ParseMediaType(mime.TypeByExtension(path.Ext(key)))
|
||||
out := &Resource{
|
||||
modTime: modTime,
|
||||
key: key,
|
||||
name: key, // TODO(toby3d): set from Page configuration
|
||||
title: "", // TODO(toby3d): set from Page configuration
|
||||
params: make(map[string]any), // TODO(toby3d): set from Page configuration
|
||||
mediaType: NewMediaType(mediaType),
|
||||
reader: bytes.NewReader(content),
|
||||
}
|
||||
|
||||
switch path.Ext(key) {
|
||||
default:
|
||||
out.resourceType, _ = ParseResourceType(out.mediaType.mainType)
|
||||
case ".md":
|
||||
out.resourceType = ResourceTypePage
|
||||
case ".webmanifest":
|
||||
out.resourceType = ResourceTypeText
|
||||
}
|
||||
|
||||
switch out.resourceType {
|
||||
case ResourceTypeImage:
|
||||
out.image, _, _ = image.DecodeConfig(out.reader)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (r Resource) Key() string {
|
||||
return r.key
|
||||
}
|
||||
|
||||
func (r Resource) Name() string {
|
||||
return r.name
|
||||
}
|
||||
|
||||
func (r Resource) MediaType() MediaType {
|
||||
return r.mediaType
|
||||
}
|
||||
|
||||
// Width returns width if current r is an image.
|
||||
func (r Resource) Width() int {
|
||||
return r.image.Width
|
||||
}
|
||||
|
||||
// Height returns height if current r is an image.
|
||||
func (r Resource) Height() int {
|
||||
return r.image.Height
|
||||
}
|
||||
|
||||
func (r Resource) ResourceType() ResourceType {
|
||||
return r.resourceType
|
||||
}
|
||||
|
||||
func (r Resource) Content() []byte {
|
||||
content, _ := io.ReadAll(r.reader)
|
||||
|
||||
return content
|
||||
}
|
||||
|
||||
func (r Resource) Read(p []byte) (int, error) {
|
||||
return r.reader.Read(p)
|
||||
}
|
||||
|
||||
func (r Resource) Seek(offset int64, whence int) (int64, error) {
|
||||
return r.reader.Seek(offset, whence)
|
||||
}
|
||||
|
||||
func (f Resource) GoString() string {
|
||||
return "domain.Resource(" + f.key + ")"
|
||||
}
|
||||
|
||||
// ResourceModTime used in http.ServeContent to get modtime of this resource.
|
||||
func ResourceModTime(r *Resource) time.Time {
|
||||
if r == nil {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
return r.modTime
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ResourceType struct {
|
||||
resourceType string
|
||||
}
|
||||
|
||||
var (
|
||||
ResourceTypeUnd ResourceType = ResourceType{} // "und"
|
||||
ResourceTypeAudio ResourceType = ResourceType{"audio"} // "audio"
|
||||
ResourceTypeImage ResourceType = ResourceType{"image"} // "image"
|
||||
ResourceTypePage ResourceType = ResourceType{"page"} // "page"
|
||||
ResourceTypeText ResourceType = ResourceType{"text"} // "text"
|
||||
ResourceTypeVideo ResourceType = ResourceType{"video"} // "video"
|
||||
)
|
||||
|
||||
var ErrResourceType error = errors.New("unsupported ResourceType enum")
|
||||
|
||||
var stringsResourceTypes = map[string]ResourceType{
|
||||
ResourceTypeAudio.resourceType: ResourceTypeAudio,
|
||||
ResourceTypeImage.resourceType: ResourceTypeImage,
|
||||
ResourceTypePage.resourceType: ResourceTypePage,
|
||||
ResourceTypeText.resourceType: ResourceTypeText,
|
||||
ResourceTypeVideo.resourceType: ResourceTypeVideo,
|
||||
}
|
||||
|
||||
func ParseResourceType(raw string) (ResourceType, error) {
|
||||
if rt, ok := stringsResourceTypes[strings.ToLower(raw)]; ok {
|
||||
return rt, nil
|
||||
}
|
||||
|
||||
return ResourceTypeUnd, fmt.Errorf("%w: got '%s', want %s", ErrResourceType, raw, stringsResourceTypes)
|
||||
}
|
||||
|
||||
func (rt ResourceType) String() string {
|
||||
if rt.resourceType != "" {
|
||||
return rt.resourceType
|
||||
}
|
||||
|
||||
return "und"
|
||||
}
|
||||
|
||||
func (rt ResourceType) GoString() string {
|
||||
return "domain.ResourceType(" + rt.String() + ")"
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"path"
|
||||
)
|
||||
|
||||
type Resources []*Resource
|
||||
|
||||
func (r Resources) GetType(targetType string) Resources {
|
||||
out := make(Resources, 0, len(r))
|
||||
|
||||
target, err := ParseResourceType(targetType)
|
||||
if err != nil {
|
||||
return out
|
||||
}
|
||||
|
||||
for i := range r {
|
||||
if r[i].resourceType != target {
|
||||
continue
|
||||
}
|
||||
|
||||
out = append(out, r[i])
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (r Resources) Get(path string) *Resource {
|
||||
for i := range r {
|
||||
if r[i].key != path {
|
||||
continue
|
||||
}
|
||||
|
||||
return r[i]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r Resources) Match(pattern string) Resources {
|
||||
out := make(Resources, 0, len(r))
|
||||
|
||||
for i := range r {
|
||||
if matched, err := path.Match(pattern, r[i].key); err != nil || !matched {
|
||||
continue
|
||||
}
|
||||
|
||||
out = append(out, r[i])
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (r Resources) GetMatch(pattern string) *Resource {
|
||||
for i := range r {
|
||||
if matched, err := path.Match(pattern, r[i].key); err != nil || !matched {
|
||||
continue
|
||||
}
|
||||
|
||||
return r[i]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -8,10 +8,10 @@ import (
|
|||
)
|
||||
|
||||
type Site struct {
|
||||
Language language.Tag
|
||||
BaseURL *url.URL
|
||||
TimeZone *time.Location
|
||||
Params map[string]any
|
||||
Title string
|
||||
Files Files
|
||||
Language language.Tag
|
||||
BaseURL *url.URL
|
||||
TimeZone *time.Location
|
||||
Params map[string]any
|
||||
Title string
|
||||
Resources Resources
|
||||
}
|
||||
|
|
|
@ -57,10 +57,10 @@ func (repo *fileSystemPageRepository) Get(ctx context.Context, lang language.Tag
|
|||
}
|
||||
|
||||
return &domain.Page{
|
||||
Language: lang,
|
||||
Title: data.Title,
|
||||
Content: data.Content,
|
||||
Params: data.Params,
|
||||
Files: make([]*domain.File, 0),
|
||||
Language: lang,
|
||||
Title: data.Title,
|
||||
Content: data.Content,
|
||||
Params: data.Params,
|
||||
Resources: make([]*domain.Resource, 0),
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -33,23 +33,23 @@ func TestGet(t *testing.T) {
|
|||
}{
|
||||
"index": {
|
||||
input: path.Join("index"),
|
||||
expect: &domain.Page{Content: []byte("index.md"), Files: make([]*domain.File, 0)},
|
||||
expect: &domain.Page{Content: []byte("index.md"), Resources: make([]*domain.Resource, 0)},
|
||||
},
|
||||
"file": {
|
||||
input: path.Join("file"),
|
||||
expect: &domain.Page{Content: []byte("file.md"), Files: make([]*domain.File, 0)},
|
||||
expect: &domain.Page{Content: []byte("file.md"), Resources: make([]*domain.Resource, 0)},
|
||||
},
|
||||
"folder": {
|
||||
input: path.Join("folder", "index"),
|
||||
expect: &domain.Page{Content: []byte("folder/index.md"), Files: make([]*domain.File, 0)},
|
||||
expect: &domain.Page{Content: []byte("folder/index.md"), Resources: make([]*domain.Resource, 0)},
|
||||
},
|
||||
"both-file": {
|
||||
input: path.Join("both"),
|
||||
expect: &domain.Page{Content: []byte("both.md"), Files: make([]*domain.File, 0)},
|
||||
expect: &domain.Page{Content: []byte("both.md"), Resources: make([]*domain.Resource, 0)},
|
||||
},
|
||||
"both-folder": {
|
||||
input: path.Join("both", "index"),
|
||||
expect: &domain.Page{Content: []byte("both/index.md"), Files: make([]*domain.File, 0)},
|
||||
expect: &domain.Page{Content: []byte("both/index.md"), Resources: make([]*domain.Resource, 0)},
|
||||
},
|
||||
} {
|
||||
name, tc := name, tc
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
"slices"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
|
||||
|
@ -50,19 +49,7 @@ func (ucase *pageUseCase) Do(ctx context.Context, lang language.Tag, p string) (
|
|||
continue
|
||||
}
|
||||
|
||||
if out.Files, _, err = ucase.statics.Fetch(ctx, path.Dir(targets[i])); err != nil {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
for j := 0; j < len(out.Files); j++ {
|
||||
if ext := out.Files[j].Ext(); ext != "html" && ext != "md" {
|
||||
continue
|
||||
}
|
||||
|
||||
out.Files = slices.Delete(out.Files, j, j+1)
|
||||
|
||||
j--
|
||||
}
|
||||
out.Resources, _, _ = ucase.statics.Fetch(ctx, path.Dir(targets[i]))
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package usecase
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
|
||||
|
@ -30,17 +29,7 @@ func (ucase *siteUseCase) Do(ctx context.Context, lang language.Tag) (*domain.Si
|
|||
return nil, fmt.Errorf("cannot find base site data: %w", err)
|
||||
}
|
||||
|
||||
if out.Files, _, err = ucase.statics.Fetch(ctx, "."); err == nil {
|
||||
for i := 0; i < len(out.Files); i++ {
|
||||
if ext := out.Files[i].Ext(); ext != "html" && ext != "md" {
|
||||
continue
|
||||
}
|
||||
|
||||
out.Files = slices.Delete(out.Files, i, i+1)
|
||||
|
||||
i--
|
||||
}
|
||||
}
|
||||
out.Resources, _, _ = ucase.statics.Fetch(ctx, ".")
|
||||
|
||||
sub, err := ucase.sites.Get(ctx, lang)
|
||||
if err != nil {
|
||||
|
|
|
@ -8,8 +8,11 @@ import (
|
|||
|
||||
type (
|
||||
Repository interface {
|
||||
Get(ctx context.Context, path string) (*domain.File, error)
|
||||
Fetch(ctx context.Context, dir string) (domain.Files, int, error)
|
||||
// Get returns Resource on path if exists
|
||||
Get(ctx context.Context, path string) (*domain.Resource, error)
|
||||
|
||||
// Fetch returns all resources from dir recursevly.
|
||||
Fetch(ctx context.Context, dir string) (domain.Resources, int, error)
|
||||
}
|
||||
|
||||
dummyRepository struct{}
|
||||
|
@ -19,8 +22,10 @@ func NewDummyRepository() dummyRepository {
|
|||
return dummyRepository{}
|
||||
}
|
||||
|
||||
func (dummyRepository) Get(ctx context.Context, path string) (*domain.File, error) { return nil, nil }
|
||||
func (dummyRepository) Get(ctx context.Context, path string) (*domain.Resource, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (dummyRepository) Fetch(ctx context.Context, dir string) (domain.Files, int, error) {
|
||||
func (dummyRepository) Fetch(ctx context.Context, dir string) (domain.Resources, int, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"image"
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
"io"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
_ "golang.org/x/image/bmp"
|
||||
_ "golang.org/x/image/webp"
|
||||
|
@ -30,10 +26,10 @@ func NewFileServerStaticRepository(root fs.FS) static.Repository {
|
|||
}
|
||||
}
|
||||
|
||||
func (repo *fileServerStaticRepository) Get(ctx context.Context, p string) (*domain.File, error) {
|
||||
func (repo *fileServerStaticRepository) Get(ctx context.Context, p string) (*domain.Resource, error) {
|
||||
info, err := fs.Stat(repo.root, p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot read static info on path '%s': %w", p, err)
|
||||
return nil, fmt.Errorf("cannot stat static on path '%s': %w", p, err)
|
||||
}
|
||||
|
||||
f, err := repo.root.Open(p)
|
||||
|
@ -47,46 +43,31 @@ func (repo *fileServerStaticRepository) Get(ctx context.Context, p string) (*dom
|
|||
return nil, fmt.Errorf("cannot read static content on path '%s': %w", p, err)
|
||||
}
|
||||
|
||||
out := &domain.File{
|
||||
Path: p,
|
||||
Updated: info.ModTime(),
|
||||
Content: content,
|
||||
}
|
||||
|
||||
parts := strings.Split(out.MediaType(), "/")
|
||||
if len(parts) < 2 || parts[0] != "image" {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
config, _, err := image.DecodeConfig(bytes.NewReader(content))
|
||||
if err != nil {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
out.Width, out.Height = config.Width, config.Height
|
||||
|
||||
return out, nil
|
||||
return domain.NewResource(info.ModTime(), content, p), nil
|
||||
}
|
||||
|
||||
func (repo *fileServerStaticRepository) Fetch(ctx context.Context, d string) (domain.Files, int, error) {
|
||||
entries, err := fs.ReadDir(repo.root, d)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("cannot read directory on path '%s': %w", d, err)
|
||||
func (repo *fileServerStaticRepository) Fetch(ctx context.Context, dir string) (domain.Resources, int, error) {
|
||||
targets := make([]string, 0)
|
||||
if err := fs.WalkDir(repo.root, dir, func(path string, de fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("received error while walking through '%s': %w", dir, err)
|
||||
}
|
||||
|
||||
if de.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
targets = append(targets, path)
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, 0, fmt.Errorf("cannot read directory on path '%s': %w", dir, err)
|
||||
}
|
||||
|
||||
out := make(domain.Files, 0, len(entries))
|
||||
out := make(domain.Resources, len(targets))
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
f, err := repo.Get(ctx, filepath.Join(d, entry.Name()))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
out = append(out, f)
|
||||
for i := range targets {
|
||||
out[i], _ = repo.Get(ctx, targets[i])
|
||||
}
|
||||
|
||||
return out, len(out), nil
|
||||
|
|
|
@ -7,5 +7,5 @@ import (
|
|||
)
|
||||
|
||||
type UseCase interface {
|
||||
Do(ctx context.Context, path string) (*domain.File, error)
|
||||
Do(ctx context.Context, path string) (*domain.Resource, error)
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ func NewStaticUseCase(statics static.Repository) static.UseCase {
|
|||
}
|
||||
}
|
||||
|
||||
func (ucase *staticUseCase) Do(ctx context.Context, p string) (*domain.File, error) {
|
||||
func (ucase *staticUseCase) Do(ctx context.Context, p string) (*domain.Resource, error) {
|
||||
p = strings.TrimPrefix(path.Clean(p), "/")
|
||||
|
||||
if ext := path.Ext(p); ext == ".html" || ext == ".md" {
|
||||
|
|
5
main.go
5
main.go
|
@ -5,7 +5,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
|
@ -104,7 +103,7 @@ func NewApp(ctx context.Context, config *domain.Config) (*App, error) {
|
|||
return
|
||||
}
|
||||
|
||||
f, err := staticer.Do(r.Context(), r.URL.Path)
|
||||
res, err := staticer.Do(r.Context(), r.URL.Path)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
|
@ -117,7 +116,7 @@ func NewApp(ctx context.Context, config *domain.Config) (*App, error) {
|
|||
return
|
||||
}
|
||||
|
||||
http.ServeContent(w, r, f.LogicalName(), f.Updated, bytes.NewReader(f.Content))
|
||||
http.ServeContent(w, r, res.Name(), domain.ResourceModTime(res), res)
|
||||
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue