github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/bootstrap/static.go (about) 1 package bootstrap 2 3 import ( 4 "bufio" 5 "encoding/json" 6 "io" 7 "io/fs" 8 "net/http" 9 "path/filepath" 10 11 "github.com/pkg/errors" 12 13 "github.com/cloudreve/Cloudreve/v3/pkg/conf" 14 "github.com/cloudreve/Cloudreve/v3/pkg/util" 15 16 "github.com/gin-contrib/static" 17 ) 18 19 const StaticFolder = "statics" 20 21 type GinFS struct { 22 FS http.FileSystem 23 } 24 25 type staticVersion struct { 26 Name string `json:"name"` 27 Version string `json:"version"` 28 } 29 30 // StaticFS 内置静态文件资源 31 var StaticFS static.ServeFileSystem 32 33 // Open 打开文件 34 func (b *GinFS) Open(name string) (http.File, error) { 35 return b.FS.Open(name) 36 } 37 38 // Exists 文件是否存在 39 func (b *GinFS) Exists(prefix string, filepath string) bool { 40 if _, err := b.FS.Open(filepath); err != nil { 41 return false 42 } 43 return true 44 } 45 46 // InitStatic 初始化静态资源文件 47 func InitStatic(statics fs.FS) { 48 if util.Exists(util.RelativePath(StaticFolder)) { 49 util.Log().Info("Folder with name \"statics\" already exists, it will be used to serve static files.") 50 StaticFS = static.LocalFile(util.RelativePath("statics"), false) 51 } else { 52 // 初始化静态资源 53 embedFS, err := fs.Sub(statics, "assets/build") 54 if err != nil { 55 util.Log().Panic("Failed to initialize static resources: %s", err) 56 } 57 58 StaticFS = &GinFS{ 59 FS: http.FS(embedFS), 60 } 61 } 62 // 检查静态资源的版本 63 f, err := StaticFS.Open("version.json") 64 if err != nil { 65 util.Log().Warning("Missing version identifier file in static resources, please delete \"statics\" folder and rebuild it.") 66 return 67 } 68 69 b, err := io.ReadAll(f) 70 if err != nil { 71 util.Log().Warning("Failed to read version identifier file in static resources, please delete \"statics\" folder and rebuild it.") 72 return 73 } 74 75 var v staticVersion 76 if err := json.Unmarshal(b, &v); err != nil { 77 util.Log().Warning("Failed to parse version identifier file in static resources: %s", err) 78 return 79 } 80 81 staticName := "cloudreve-frontend" 82 if conf.IsPro == "true" { 83 staticName += "-pro" 84 } 85 86 if v.Name != staticName { 87 util.Log().Warning("Static resource version mismatch, please delete \"statics\" folder and rebuild it.") 88 return 89 } 90 91 if v.Version != conf.RequiredStaticVersion { 92 util.Log().Warning("Static resource version mismatch [Current %s, Desired: %s],please delete \"statics\" folder and rebuild it.", v.Version, conf.RequiredStaticVersion) 93 return 94 } 95 } 96 97 // Eject 抽离内置静态资源 98 func Eject(statics fs.FS) { 99 // 初始化静态资源 100 embedFS, err := fs.Sub(statics, "assets/build") 101 if err != nil { 102 util.Log().Panic("Failed to initialize static resources: %s", err) 103 } 104 105 var walk func(relPath string, d fs.DirEntry, err error) error 106 walk = func(relPath string, d fs.DirEntry, err error) error { 107 if err != nil { 108 return errors.Errorf("Failed to read info of %q: %s, skipping...", relPath, err) 109 } 110 111 if !d.IsDir() { 112 // 写入文件 113 out, err := util.CreatNestedFile(filepath.Join(util.RelativePath(""), StaticFolder, relPath)) 114 defer out.Close() 115 116 if err != nil { 117 return errors.Errorf("Failed to create file %q: %s, skipping...", relPath, err) 118 } 119 120 util.Log().Info("Ejecting %q...", relPath) 121 obj, _ := embedFS.Open(relPath) 122 if _, err := io.Copy(out, bufio.NewReader(obj)); err != nil { 123 return errors.Errorf("Cannot write file %q: %s, skipping...", relPath, err) 124 } 125 } 126 return nil 127 } 128 129 // util.Log().Info("开始导出内置静态资源...") 130 err = fs.WalkDir(embedFS, ".", walk) 131 if err != nil { 132 util.Log().Error("Error occurs while ejecting static resources: %s", err) 133 return 134 } 135 util.Log().Info("Finish ejecting static resources.") 136 }