Init V4 community edition (#2265)
* Init V4 community edition * Init V4 community edition
This commit is contained in:
206
application/statics/statics.go
Normal file
206
application/statics/statics.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package statics
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bufio"
|
||||
"crypto/sha256"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudreve/Cloudreve/v4/application/constants"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/logging"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/util"
|
||||
"github.com/gin-contrib/static"
|
||||
)
|
||||
|
||||
const StaticFolder = "statics"
|
||||
|
||||
//go:embed assets.zip
|
||||
var zipContent string
|
||||
|
||||
type GinFS struct {
|
||||
FS http.FileSystem
|
||||
}
|
||||
|
||||
type version struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// Open 打开文件
|
||||
func (b *GinFS) Open(name string) (http.File, error) {
|
||||
return b.FS.Open(name)
|
||||
}
|
||||
|
||||
// Exists 文件是否存在
|
||||
func (b *GinFS) Exists(prefix string, filepath string) bool {
|
||||
if _, err := b.FS.Open(filepath); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// NewServerStaticFS 初始化静态资源文件
|
||||
func NewServerStaticFS(l logging.Logger, statics fs.FS, isPro bool) (static.ServeFileSystem, error) {
|
||||
var staticFS static.ServeFileSystem
|
||||
if util.Exists(util.DataPath(StaticFolder)) {
|
||||
l.Info("Folder with %q already exists, it will be used to serve static files.", util.DataPath(StaticFolder))
|
||||
staticFS = static.LocalFile(util.DataPath(StaticFolder), false)
|
||||
} else {
|
||||
// 初始化静态资源
|
||||
embedFS, err := fs.Sub(statics, "assets/build")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize static resources: %w", err)
|
||||
}
|
||||
|
||||
staticFS = &GinFS{
|
||||
FS: http.FS(embedFS),
|
||||
}
|
||||
}
|
||||
// 检查静态资源的版本
|
||||
f, err := staticFS.Open("version.json")
|
||||
if err != nil {
|
||||
l.Warning("Missing version identifier file in static resources, please delete \"statics\" folder and rebuild it.")
|
||||
return staticFS, nil
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
l.Warning("Failed to read version identifier file in static resources, please delete \"statics\" folder and rebuild it.")
|
||||
return staticFS, nil
|
||||
}
|
||||
|
||||
var v version
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
l.Warning("Failed to parse version identifier file in static resources: %s", err)
|
||||
return staticFS, nil
|
||||
}
|
||||
|
||||
staticName := "cloudreve-frontend"
|
||||
if isPro {
|
||||
staticName += "-pro"
|
||||
}
|
||||
|
||||
if v.Name != staticName {
|
||||
l.Error("Static resource version mismatch, please delete \"statics\" folder and rebuild it.")
|
||||
}
|
||||
|
||||
if v.Version != constants.BackendVersion {
|
||||
l.Error("Static resource version mismatch [Current %s, Desired: %s],please delete \"statics\" folder and rebuild it.", v.Version, constants.BackendVersion)
|
||||
}
|
||||
|
||||
return staticFS, nil
|
||||
}
|
||||
|
||||
func NewStaticFS(l logging.Logger) fs.FS {
|
||||
zipReader, err := zip.NewReader(strings.NewReader(zipContent), int64(len(zipContent)))
|
||||
if err != nil {
|
||||
l.Panic("Static resource is not a valid zip file: %s", err)
|
||||
}
|
||||
|
||||
var files []file
|
||||
err = fs.WalkDir(zipReader, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot walk into %q: %w", path, err)
|
||||
}
|
||||
|
||||
if path == "." {
|
||||
return nil
|
||||
}
|
||||
|
||||
var f file
|
||||
if d.IsDir() {
|
||||
f.name = path + "/"
|
||||
} else {
|
||||
f.name = path
|
||||
|
||||
rc, err := zipReader.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("canot open %q: %w", path, err)
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
data, err := io.ReadAll(rc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot read %q: %w", path, err)
|
||||
}
|
||||
|
||||
f.data = string(data)
|
||||
|
||||
hash := sha256.Sum256(data)
|
||||
for i := range f.hash {
|
||||
f.hash[i] = ^hash[i]
|
||||
}
|
||||
}
|
||||
files = append(files, f)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
l.Panic("Failed to initialize static resources: %s", err)
|
||||
}
|
||||
|
||||
sort.Slice(files, func(i, j int) bool {
|
||||
fi, fj := files[i], files[j]
|
||||
di, ei, _ := split(fi.name)
|
||||
dj, ej, _ := split(fj.name)
|
||||
|
||||
if di != dj {
|
||||
return di < dj
|
||||
}
|
||||
return ei < ej
|
||||
})
|
||||
|
||||
var embedFS FS
|
||||
embedFS.files = &files
|
||||
return embedFS
|
||||
}
|
||||
|
||||
// Eject 抽离内置静态资源
|
||||
func Eject(l logging.Logger, statics fs.FS) error {
|
||||
// 初始化静态资源
|
||||
embedFS, err := fs.Sub(statics, "assets/build")
|
||||
if err != nil {
|
||||
l.Panic("Failed to initialize static resources: %s", err)
|
||||
}
|
||||
|
||||
var walk func(relPath string, d fs.DirEntry, err error) error
|
||||
walk = func(relPath string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read info of %q: %s, skipping...", relPath, err)
|
||||
}
|
||||
|
||||
if !d.IsDir() {
|
||||
// 写入文件
|
||||
dst := util.DataPath(filepath.Join(StaticFolder, relPath))
|
||||
out, err := util.CreatNestedFile(dst)
|
||||
defer out.Close()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create file %q: %s, skipping...", dst, err)
|
||||
}
|
||||
|
||||
l.Info("Ejecting %q...", dst)
|
||||
obj, _ := embedFS.Open(relPath)
|
||||
if _, err := io.Copy(out, bufio.NewReader(obj)); err != nil {
|
||||
return fmt.Errorf("cannot write file %q: %s, skipping...", relPath, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// util.Log().Info("开始导出内置静态资源...")
|
||||
err = fs.WalkDir(embedFS, ".", walk)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to eject static resources: %w", err)
|
||||
}
|
||||
|
||||
l.Info("Finish ejecting static resources.")
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user