diff --git a/main.go b/main.go index 52bc7ca..7dbfdbc 100644 --- a/main.go +++ b/main.go @@ -63,20 +63,23 @@ var cpuProfilePath, memProfilePath string func NewApp(ctx context.Context, config *domain.Config) (*App, error) { themeDir := os.DirFS(config.ThemeDir) contentDir := os.DirFS(config.ContentDir) - statics := staticfsrepo.NewFileServerStaticRepository(contentDir) + resources := staticfsrepo.NewFileServerStaticRepository(contentDir) sites := sitefsrepo.NewFileSystemSiteRepository(contentDir) - siter := siteucase.NewSiteUseCase(sites, statics) + siter := siteucase.NewSiteUseCase(sites, resources) funcMap, err := templateutil.New(themeDir, siter) if err != nil { logger.Fatalln("cannot setup template.FuncMap for templates: %w", err) } - staticer := staticucase.NewStaticUseCase(statics) + staticDir := os.DirFS(config.StaticDir) + statics := staticfsrepo.NewFileServerStaticRepository(staticDir) + // TODO(toby3d): use exists static use case or split that on static and resource modules? + resourcer := staticucase.NewStaticUseCase(resources) themes := themefsrepo.NewFileSystemThemeRepository(themeDir, funcMap) themer := themeucase.NewThemeUseCase(themes) pages := pagefsrepo.NewFileSystemPageRepository(contentDir) - pager := pageucase.NewPageUseCase(pages, statics) + pager := pageucase.NewPageUseCase(pages, resources) matcher := language.NewMatcher(message.DefaultCatalog.Languages()) @@ -98,32 +101,18 @@ func NewApp(ctx context.Context, config *domain.Config) (*App, error) { return } - lang := domain.NewLanguage(head) - if lang == domain.LanguageUnd { - // WARN(toby3d): fetch static resources from separated static directory instead of - // $HOME_CONTENT_DIR? - // - // Looks like what current logic is insecure, because resource from private page in - // content directory '/en/page/file.jpg' by lower use case execution can be accessed - // here by URL '/page/file.jpg'. - 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) - - return - } - - http.Error(w, err.Error(), http.StatusInternalServerError) - - return - } - - http.ServeContent(w, r, res.Name(), domain.ResourceModTime(res), res) + // TODO(toby3d): use exists static use case or split that on static and resource modules? + // INFO(toby3d): any static file is public by design, so it's safe to search it first before + // deep down to any page or it's resource which might be secured by middleware or something + // else. + static, err := statics.Get(r.Context(), r.URL.Path[1:]) + if err == nil { + http.ServeContent(w, r, static.Name(), domain.ResourceModTime(static), static) return } + lang := domain.NewLanguage(head) r.URL.Path = tail s, err := siter.Do(r.Context(), lang) @@ -141,7 +130,7 @@ func NewApp(ctx context.Context, config *domain.Config) (*App, error) { return } - res, err := staticer.Do(r.Context(), r.URL.Path) + res, err := resourcer.Do(r.Context(), r.URL.Path) if err != nil { if errors.Is(err, fs.ErrNotExist) { http.Error(w, err.Error(), http.StatusNotFound) @@ -225,6 +214,7 @@ func main() { for _, dir := range []*string{ &config.ContentDir, &config.ThemeDir, + &config.StaticDir, } { if *dir, err = filepath.Abs(filepath.Clean(*dir)); err != nil { logger.Fatalf("cannot format '%s' into absolute path: %s", *dir, err)