-
Notifications
You must be signed in to change notification settings - Fork 28
/
app_static_assets.go
84 lines (71 loc) · 2.24 KB
/
app_static_assets.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package webapp
import (
// nolint:gosec
"crypto/md5"
"errors"
"fmt"
"net/http"
"os"
"path"
aferomem "github.com/spf13/afero/mem"
"github.com/authgear/authgear-server/pkg/lib/web"
"github.com/authgear/authgear-server/pkg/util/filepathutil"
"github.com/authgear/authgear-server/pkg/util/httproute"
"github.com/authgear/authgear-server/pkg/util/httputil"
"github.com/authgear/authgear-server/pkg/util/resource"
)
func ConfigureAppStaticAssetsRoute(route httproute.Route) httproute.Route {
return route.
WithMethods("HEAD", "GET").
WithPathPattern("/" + web.AppAssetsURLDirname + "/*all")
}
type ResourceManager interface {
Read(desc resource.Descriptor, view resource.View) (interface{}, error)
Resolve(path string) (resource.Descriptor, bool)
}
type AppStaticAssetsHandler struct {
Resources ResourceManager
}
func (h *AppStaticAssetsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fileServer := http.StripPrefix("/"+web.AppAssetsURLDirname+"/", &httputil.FileServer{
FileSystem: h,
FallbackToIndexHTML: false,
})
fileServer.ServeHTTP(w, r)
}
func (h *AppStaticAssetsHandler) Open(name string) (http.File, error) {
p := path.Join(web.AppAssetsURLDirname, name)
filePath, hashInPath, ok := filepathutil.ParseHashedPath(p)
if !ok {
return nil, os.ErrNotExist
}
// Fallback ResourceManager
desc, ok := h.Resources.Resolve(filePath)
if !ok {
return nil, os.ErrNotExist
}
// We use EffectiveFile here because we want to return an exact match.
// The static asset URLs in the templates are computed by the resolver using EffectiveResource, which has handled localization.
result, err := h.Resources.Read(desc, resource.EffectiveFile{
Path: filePath,
})
if errors.Is(err, resource.ErrResourceNotFound) {
return nil, os.ErrNotExist
} else if err != nil {
return nil, err
}
bytes := result.([]byte)
if !web.LookLikeAHash(hashInPath) {
// check the hash
// md5 is used to compute the hash in the filename for caching purpose only
// nolint:gosec
dataHash := md5.Sum(bytes)
if fmt.Sprintf("%x", dataHash) != hashInPath {
return nil, os.ErrNotExist
}
}
data := aferomem.CreateFile(p)
file := aferomem.NewFileHandle(data)
_, _ = file.Write(bytes)
return aferomem.NewReadOnlyFileHandle(data), nil
}