github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/pkg/assets/statik/statik.go (about) 1 // Copyright 2014 Google Inc. All Rights Reserved. 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 statik contains an HTTP file system that works with zip contents. 16 package statik 17 18 import ( 19 "bytes" 20 "crypto/sha256" 21 "encoding/hex" 22 "encoding/pem" 23 "fmt" 24 "io" 25 "sync" 26 27 "github.com/andybalholm/brotli" 28 "github.com/cozy/cozy-stack/pkg/assets/model" 29 "github.com/cozy/cozy-stack/pkg/config/config" 30 ) 31 32 var globalAssets sync.Map // {context:path -> *Asset} 33 34 // Register registers brotli contents data, later used to 35 // initialize the statik file system. 36 func Register(brotliData string) { 37 if brotliData == "" { 38 panic("statik/fs: no zip data registered") 39 } 40 if err := uncompress([]byte(brotliData)); err != nil { 41 panic(fmt.Errorf("statik/fs: error uncompressed data: %s", err)) 42 } 43 } 44 45 func uncompress(data []byte) error { 46 for { 47 block, rest := pem.Decode(data) 48 if block == nil { 49 break 50 } 51 brotliData := block.Bytes 52 br := brotli.NewReader(bytes.NewReader(brotliData)) 53 h := sha256.New() 54 r := io.TeeReader(br, h) 55 rawData, err := io.ReadAll(r) 56 if err != nil { 57 return err 58 } 59 60 name := block.Headers["Name"] 61 opt := model.AssetOption{ 62 Name: name, 63 Context: config.DefaultInstanceContext, 64 Shasum: hex.EncodeToString(h.Sum(nil)), 65 } 66 asset := model.NewAsset(opt, rawData, brotliData) 67 StoreAsset(asset) 68 data = rest 69 } 70 return nil 71 } 72 73 // StoreAsset stores in memory a static asset 74 func StoreAsset(asset *model.Asset) { 75 globalAssets.Store(asset.Name, asset) 76 } 77 78 // UnstoreAsset removes a static asset from the memory list 79 func UnstoreAsset(asset *model.Asset) { 80 globalAssets.Delete(asset.Name) 81 } 82 83 // GetAsset returns the asset with the given name. 84 func GetAsset(name string) *model.Asset { 85 if v, ok := globalAssets.Load(name); ok { 86 return v.(*model.Asset) 87 } 88 return nil 89 } 90 91 // Foreach iterates on the static assets. 92 func Foreach(predicate func(name string, f *model.Asset)) { 93 globalAssets.Range(func(key interface{}, v interface{}) bool { 94 predicate(key.(string), v.(*model.Asset)) 95 return true 96 }) 97 }