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  }