github.com/greenpau/go-authcrunch@v1.1.4/pkg/authn/ui/static.go (about) 1 // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package ui 16 17 import ( 18 "crypto/sha1" 19 "encoding/base64" 20 "fmt" 21 "io" 22 "os" 23 "path/filepath" 24 ) 25 26 // StaticAssets is an instance of StaticAssetLibrary containing plain HTML. 27 var StaticAssets *StaticAssetLibrary 28 29 // AppAssets is an instance of StaticAssetLibrary containing React Apps. 30 var AppAssets *StaticAssetLibrary 31 32 // StaticAsset is a single static web asset. 33 type StaticAsset struct { 34 Path string `json:"path,omitempty" xml:"path,omitempty" yaml:"path,omitempty"` 35 FsPath string `json:"fs_path,omitempty" xml:"fs_path,omitempty" yaml:"fs_path,omitempty"` 36 Restricted bool `json:"restricted,omitempty" xml:"restricted,omitempty" yaml:"restricted,omitempty"` 37 ContentType string `json:"content_type,omitempty" xml:"content_type,omitempty" yaml:"content_type,omitempty"` 38 Content string `json:"content,omitempty" xml:"content,omitempty" yaml:"content,omitempty"` 39 EncodedContent string `json:"encoded_content,omitempty" xml:"encoded_content,omitempty" yaml:"encoded_content,omitempty"` 40 Checksum string `json:"checksum,omitempty" xml:"checksum,omitempty" yaml:"checksum,omitempty"` 41 } 42 43 // StaticAssetLibrary contains a collection of static assets. 44 type StaticAssetLibrary struct { 45 items map[string]*StaticAsset 46 } 47 48 func init() { 49 var err error 50 StaticAssets, err = NewStaticAssetLibrary() 51 if err != nil { 52 panic(err) 53 } 54 AppAssets, err = NewAppAssetLibrary() 55 if err != nil { 56 panic(err) 57 } 58 } 59 60 func getContentType(filePath string) (string, error) { 61 ext := filepath.Ext(filePath) 62 switch ext { 63 case ".html": 64 return "text/html", nil 65 case ".ttf": 66 return "font/ttf", nil 67 case ".woff": 68 return "font/woff", nil 69 case ".woff2": 70 return "font/woff2", nil 71 case ".ico": 72 return "image/vnd.microsoft.icon", nil 73 case ".js": 74 return "application/javascript", nil 75 case ".css": 76 return "text/css", nil 77 case ".eot": 78 return "application/vnd.ms-fontobject", nil 79 case ".svg": 80 return "image/svg+xml", nil 81 case ".jpg", ".jpeg": 82 return "image/jpeg", nil 83 case ".png": 84 return "image/png", nil 85 case ".gif": 86 return "image/gif", nil 87 case ".json": 88 return "application/json", nil 89 case ".txt": 90 return "text/plain", nil 91 case ".xml": 92 return "application/xml", nil 93 default: 94 return "", fmt.Errorf("extension %q is not supported", ext) 95 } 96 } 97 98 // NewAppAssetLibrary returns an instance of StaticAssetLibrary. 99 func NewAppAssetLibrary() (*StaticAssetLibrary, error) { 100 sal := &StaticAssetLibrary{} 101 sal.items = make(map[string]*StaticAsset) 102 for path, embedPath := range embedPages { 103 b, err := embedFileSystem.ReadFile(embedPath) 104 if err != nil { 105 return nil, fmt.Errorf("app asset %s reading error: %s", embedPath, err) 106 } 107 ct, err := getContentType(embedPath) 108 if err != nil { 109 return nil, fmt.Errorf("app asset %s getting content type: %s", embedPath, err) 110 } 111 item := &StaticAsset{ 112 Path: path, 113 ContentType: ct, 114 Content: string(b), 115 } 116 h := sha1.New() 117 io.WriteString(h, item.Content) 118 item.Checksum = base64.URLEncoding.EncodeToString(h.Sum(nil)) 119 sal.items[path] = item 120 } 121 return sal, nil 122 } 123 124 // NewStaticAssetLibrary returns an instance of StaticAssetLibrary. 125 func NewStaticAssetLibrary() (*StaticAssetLibrary, error) { 126 sal := &StaticAssetLibrary{} 127 sal.items = make(map[string]*StaticAsset) 128 for path, item := range defaultStaticAssets { 129 s, err := base64.StdEncoding.DecodeString(item.EncodedContent) 130 if err != nil { 131 return nil, fmt.Errorf("static asset %s decoding error: %s", path, err) 132 } 133 item.Content = string(s) 134 h := sha1.New() 135 io.WriteString(h, item.Content) 136 item.Checksum = base64.URLEncoding.EncodeToString(h.Sum(nil)) 137 sal.items[path] = item 138 } 139 return sal, nil 140 } 141 142 // GetAsset returns an asset from path 143 func (sal *StaticAssetLibrary) GetAsset(path string) (*StaticAsset, error) { 144 if item, exists := sal.items[path]; exists { 145 return item, nil 146 } 147 return nil, fmt.Errorf("static asset %s not found", path) 148 } 149 150 // AddAsset adds asset to StaticAssetLibrary 151 func (sal *StaticAssetLibrary) AddAsset(path, contentType, fsPath string) error { 152 rawContent, err := os.ReadFile(fsPath) 153 if err != nil { 154 return fmt.Errorf("failed to load asset file %s: %s", fsPath, err) 155 } 156 item := &StaticAsset{ 157 Path: path, 158 ContentType: contentType, 159 EncodedContent: base64.StdEncoding.EncodeToString(rawContent), 160 } 161 s, err := base64.StdEncoding.DecodeString(item.EncodedContent) 162 if err != nil { 163 return fmt.Errorf("static asset %s decoding error: %s", path, err) 164 } 165 item.Content = string(s) 166 h := sha1.New() 167 io.WriteString(h, item.Content) 168 item.Checksum = base64.URLEncoding.EncodeToString(h.Sum(nil)) 169 sal.items[path] = item 170 return nil 171 }