Merge branch 'feature/static' into develop

This commit is contained in:
Maxim Lebedev 2023-11-13 07:25:55 +06:00
commit aff4976a40
Signed by: toby3d
GPG Key ID: 1F14E25B7C119FC5
3 changed files with 20 additions and 28 deletions

View File

@ -23,7 +23,7 @@ WORKDIR /
COPY --from=builder /app/home /home COPY --from=builder /app/home /home
VOLUME ["/content", "/theme"] VOLUME ["/content", "/theme", "/static"]
EXPOSE 3000 EXPOSE 3000

View File

@ -11,6 +11,7 @@ type Config struct {
ContentDir string `env:"CONTENT_DIR" envDefault:"content/"` ContentDir string `env:"CONTENT_DIR" envDefault:"content/"`
Host string `env:"HOST" envDefault:"0.0.0.0"` Host string `env:"HOST" envDefault:"0.0.0.0"`
ThemeDir string `env:"THEME_DIR" envDefault:"theme/"` ThemeDir string `env:"THEME_DIR" envDefault:"theme/"`
StaticDir string `env:"STATIC_DIR" envDefault:"static/"`
Port uint16 `env:"PORT" envDefault:"3000"` Port uint16 `env:"PORT" envDefault:"3000"`
} }
@ -21,6 +22,7 @@ func TestConfig(tb testing.TB) *Config {
ContentDir: "testdata/content/", ContentDir: "testdata/content/",
Host: "0.0.0.0", Host: "0.0.0.0",
ThemeDir: "testdata/theme/", ThemeDir: "testdata/theme/",
StaticDir: "testdata/static/",
Port: 3000, Port: 3000,
} }
} }

44
main.go
View File

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