Static is a handler function to serve static directory content from fsys. If a file resource is missing and indexFallback is true, the request will be forwarded to the base index.html (useful for SPA with pretty urls). NB! Expects the route to have a "{path...}" wildcard parameter. Special redire
(fsys fs.FS, indexFallback bool)
| 107 | // fsys := os.DirFS("./pb_public") |
| 108 | // router.GET("/files/{path...}", apis.Static(fsys, false)) |
| 109 | func Static(fsys fs.FS, indexFallback bool) func(*core.RequestEvent) error { |
| 110 | if fsys == nil { |
| 111 | panic("Static: the provided fs.FS argument is nil") |
| 112 | } |
| 113 | |
| 114 | return func(e *core.RequestEvent) error { |
| 115 | // disable the activity logger to avoid flooding with messages |
| 116 | // |
| 117 | // note: errors are still logged |
| 118 | if e.Get(requestEventKeySkipSuccessActivityLog) == nil { |
| 119 | e.Set(requestEventKeySkipSuccessActivityLog, true) |
| 120 | } |
| 121 | |
| 122 | filename := e.Request.PathValue(StaticWildcardParam) |
| 123 | filename = filepath.ToSlash(filepath.Clean(strings.TrimPrefix(filename, "/"))) |
| 124 | |
| 125 | // eagerly check for directory traversal |
| 126 | // |
| 127 | // note: this is just out of an abundance of caution because the fs.FS implementation could be non-std, |
| 128 | // but usually shouldn't be necessary since os.DirFS.Open is expected to fail if the filename starts with dots |
| 129 | if len(filename) > 2 && filename[0] == '.' && filename[1] == '.' && (filename[2] == '/' || filename[2] == '\\') { |
| 130 | if indexFallback && filename != router.IndexPage { |
| 131 | return e.FileFS(fsys, router.IndexPage) |
| 132 | } |
| 133 | return router.ErrFileNotFound |
| 134 | } |
| 135 | |
| 136 | fi, err := fs.Stat(fsys, filename) |
| 137 | if err != nil { |
| 138 | if indexFallback && filename != router.IndexPage { |
| 139 | return e.FileFS(fsys, router.IndexPage) |
| 140 | } |
| 141 | return router.ErrFileNotFound |
| 142 | } |
| 143 | |
| 144 | if fi.IsDir() { |
| 145 | // redirect to a canonical dir url, aka. with trailing slash |
| 146 | if !strings.HasSuffix(e.Request.URL.Path, "/") { |
| 147 | return e.Redirect(http.StatusMovedPermanently, safeRedirectPath(e.Request.URL.Path+"/")) |
| 148 | } |
| 149 | } else { |
| 150 | urlPath := e.Request.URL.Path |
| 151 | if strings.HasSuffix(urlPath, "/") { |
| 152 | // redirect to a non-trailing slash file route |
| 153 | urlPath = strings.TrimRight(urlPath, "/") |
| 154 | if len(urlPath) > 0 { |
| 155 | return e.Redirect(http.StatusMovedPermanently, safeRedirectPath(urlPath)) |
| 156 | } |
| 157 | } else if stripped, ok := strings.CutSuffix(urlPath, router.IndexPage); ok { |
| 158 | // redirect without the index.html |
| 159 | return e.Redirect(http.StatusMovedPermanently, safeRedirectPath(stripped)) |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | fileErr := e.FileFS(fsys, filename) |
| 164 | |
| 165 | if fileErr != nil && indexFallback && filename != router.IndexPage && errors.Is(fileErr, router.ErrFileNotFound) { |
| 166 | return e.FileFS(fsys, router.IndexPage) |
searching dependent graphs…