diff --git a/bootstrap/static.go b/bootstrap/static.go index 09e393dc..973174d7 100644 --- a/bootstrap/static.go +++ b/bootstrap/static.go @@ -1,21 +1,27 @@ package bootstrap import ( + "bufio" + "embed" "encoding/json" "io" - "io/ioutil" + "io/fs" "net/http" - "path" + "os" + "path/filepath" + + "github.com/pkg/errors" "github.com/cloudreve/Cloudreve/v3/pkg/conf" "github.com/cloudreve/Cloudreve/v3/pkg/util" - _ "github.com/cloudreve/Cloudreve/v3/statik" + "github.com/gin-contrib/static" - "github.com/rakyll/statik/fs" ) const StaticFolder = "statics" +var StaticEmbed embed.FS + type GinFS struct { FS http.FileSystem } @@ -35,124 +41,105 @@ func (b *GinFS) Open(name string) (http.File, error) { // Exists 文件是否存在 func (b *GinFS) Exists(prefix string, filepath string) bool { - if _, err := b.FS.Open(filepath); err != nil { return false } return true - } // InitStatic 初始化静态资源文件 func InitStatic() { - var err error - if util.Exists(util.RelativePath(StaticFolder)) { util.Log().Info("检测到 statics 目录存在,将使用此目录下的静态资源文件") StaticFS = static.LocalFile(util.RelativePath("statics"), false) - - // 检查静态资源的版本 - f, err := StaticFS.Open("version.json") - if err != nil { - util.Log().Warning("静态资源版本标识文件不存在,请重新构建或删除 statics 目录") - return - } - - b, err := ioutil.ReadAll(f) + } else { + // 初始化静态资源 + embedFS, err := fs.Sub(StaticEmbed, "assets/build") if err != nil { - util.Log().Warning("无法读取静态资源文件版本,请重新构建或删除 statics 目录") - return + util.Log().Panic("无法初始化静态资源, %s", err) } - var v staticVersion - if err := json.Unmarshal(b, &v); err != nil { - util.Log().Warning("无法解析静态资源文件版本, %s", err) - return - } + StaticFS = &GinFS{} + StaticFS.(*GinFS).FS = http.FS(embedFS) + } + // 检查静态资源的版本 + f, err := StaticFS.Open("version.json") + if err != nil { + util.Log().Warning("静态资源版本标识文件不存在,请重新构建或删除 statics 目录") + return + } - staticName := "cloudreve-frontend" - if conf.IsPro == "true" { - staticName += "-pro" - } + b, err := io.ReadAll(f) + if err != nil { + util.Log().Warning("无法读取静态资源文件版本,请重新构建或删除 statics 目录") + return + } - if v.Name != staticName { - util.Log().Warning("静态资源版本不匹配,请重新构建或删除 statics 目录") - return - } + var v staticVersion + if err := json.Unmarshal(b, &v); err != nil { + util.Log().Warning("无法解析静态资源文件版本, %s", err) + return + } - if v.Version != conf.RequiredStaticVersion { - util.Log().Warning("静态资源版本不匹配 [当前 %s, 需要: %s],请重新构建或删除 statics 目录", v.Version, conf.RequiredStaticVersion) - return - } + staticName := "cloudreve-frontend" + if conf.IsPro == "true" { + staticName += "-pro" + } - } else { - StaticFS = &GinFS{} - StaticFS.(*GinFS).FS, err = fs.New() - if err != nil { - util.Log().Panic("无法初始化静态资源, %s", err) - } + if v.Name != staticName { + util.Log().Warning("静态资源版本不匹配,请重新构建或删除 statics 目录") + return } + if v.Version != conf.RequiredStaticVersion { + util.Log().Warning("静态资源版本不匹配 [当前 %s, 需要: %s],请重新构建或删除 statics 目录", v.Version, conf.RequiredStaticVersion) + return + } } // Eject 抽离内置静态资源 func Eject() { - staticFS, err := fs.New() + // 初始化静态资源 + embedFS, err := fs.Sub(StaticEmbed, "assets/build") if err != nil { util.Log().Panic("无法初始化静态资源, %s", err) } - root, err := staticFS.Open("/") - if err != nil { - util.Log().Panic("根目录不存在, %s", err) - } - - var walk func(relPath string, object http.File) - walk = func(relPath string, object http.File) { - stat, err := object.Stat() + var walk func(relPath string, d fs.DirEntry, err error) error + walk = func(relPath string, d fs.DirEntry, err error) error { if err != nil { - util.Log().Error("无法获取[%s]的信息, %s, 跳过...", relPath, err) - return + return errors.Errorf("无法获取[%s]的信息, %s, 跳过...", relPath, err) } - if !stat.IsDir() { + if !d.IsDir() { // 写入文件 - out, err := util.CreatNestedFile(util.RelativePath(StaticFolder + relPath)) + out, err := util.CreatNestedFile(filepath.Join(util.ExecPath(), StaticFolder, relPath)) defer out.Close() if err != nil { - util.Log().Error("无法创建文件[%s], %s, 跳过...", relPath, err) - return + return errors.Errorf("无法创建文件[%s], %s, 跳过...", relPath, err) } util.Log().Info("导出 [%s]...", relPath) - if _, err := io.Copy(out, object); err != nil { - util.Log().Error("无法写入文件[%s], %s, 跳过...", relPath, err) - return + obj, _ := embedFS.Open(relPath) + if _, err := io.Copy(out, bufio.NewReader(obj)); err != nil { + return errors.Errorf("无法写入文件[%s], %s, 跳过...", relPath, err) } } else { - // 列出目录 - objects, err := object.Readdir(0) - if err != nil { - util.Log().Error("无法步入子目录[%s], %s, 跳过...", relPath, err) - return - } - - // 递归遍历子目录 - for _, newObject := range objects { - newPath := path.Join(relPath, newObject.Name()) - newRoot, err := staticFS.Open(newPath) - if err != nil { - util.Log().Error("无法打开对象[%s], %s, 跳过...", newPath, err) - continue - } - walk(newPath, newRoot) + // 创建目录 + if err := os.MkdirAll(filepath.Join(util.ExecPath(), StaticFolder, relPath), 0755); err != nil { + return errors.Errorf("无法创建目录[%s], %s, 跳过...", relPath, err) } - + util.Log().Info("创建目录 [%s]...", relPath) } + return nil } - util.Log().Info("开始导出内置静态资源...") - walk("/", root) + // util.Log().Info("开始导出内置静态资源...") + err = fs.WalkDir(embedFS, ".", walk) + if err != nil { + util.Log().Error("导出内置静态资源遇到错误:, %s", err) + return + } util.Log().Info("内置静态资源导出完成") -} +} \ No newline at end of file diff --git a/go.mod b/go.mod index 349fedf7..a98154c2 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,11 @@ module github.com/cloudreve/Cloudreve/v3 -go 1.13 +go 1.17 require ( github.com/DATA-DOG/go-sqlmock v1.3.3 github.com/aliyun/aliyun-oss-go-sdk v2.0.5+incompatible github.com/aws/aws-sdk-go v1.31.5 - github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect github.com/duo-labs/webauthn v0.0.0-20191119193225-4bf9a0f776d4 github.com/fatih/color v1.7.0 github.com/gin-contrib/cors v1.3.0 @@ -23,16 +22,12 @@ require ( github.com/hashicorp/go-version v1.2.0 github.com/jinzhu/gorm v1.9.11 github.com/juju/ratelimit v1.0.1 - github.com/mattn/go-colorable v0.1.4 // indirect github.com/mojocn/base64Captcha v0.0.0-20190801020520-752b1cd608b2 - github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/pkg/errors v0.9.1 github.com/pquerna/otp v1.2.0 github.com/qiniu/api.v7/v7 v7.4.0 github.com/rafaeljusto/redigomock v0.0.0-20191117212112-00b2509252a1 - github.com/rakyll/statik v0.1.7 github.com/robfig/cron/v3 v3.0.1 - github.com/smartystreets/goconvey v1.6.4 // indirect github.com/speps/go-hashids v2.0.0+incompatible github.com/stretchr/testify v1.5.1 github.com/tencentcloud/tencentcloud-sdk-go v3.0.125+incompatible @@ -40,8 +35,53 @@ require ( github.com/upyun/go-sdk v2.1.0+incompatible golang.org/x/image v0.0.0-20211028202545-6944b10bf410 golang.org/x/text v0.3.6 - gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/go-playground/validator.v9 v9.29.1 +) + +require ( + cloud.google.com/go v0.37.4 // indirect + github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect + github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect + github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect + github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect + github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 // indirect + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.12.1 // indirect + github.com/go-playground/universal-translator v0.16.0 // indirect + github.com/go-sql-driver/mysql v1.5.0 // indirect + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/golang/protobuf v1.3.2 // indirect + github.com/google/certificate-transparency-go v1.0.21 // indirect + github.com/gorilla/context v1.1.1 // indirect + github.com/gorilla/securecookie v1.1.1 // indirect + github.com/gorilla/sessions v1.1.3 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jmespath/go-jmespath v0.3.0 // indirect + github.com/json-iterator/go v1.1.7 // indirect + github.com/katzenpost/core v0.0.7 // indirect + github.com/leodido/go-urn v1.1.0 // indirect + github.com/lib/pq v1.1.1 // indirect + github.com/mattn/go-colorable v0.1.4 // indirect + github.com/mattn/go-isatty v0.0.9 // indirect + github.com/mattn/go-sqlite3 v1.11.0 // indirect + github.com/mitchellh/mapstructure v1.1.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/mozillazg/go-httpheader v0.2.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/quasoft/memstore v0.0.0-20180925164028-84a050167438 // indirect + github.com/satori/go.uuid v1.2.0 // indirect + github.com/smartystreets/goconvey v1.6.4 // indirect + github.com/stretchr/objx v0.2.0 // indirect + github.com/ugorji/go/codec v1.1.7 // indirect + golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 // indirect + golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a // indirect + golang.org/x/time v0.0.0-20181108054448-85acf8d2951c // indirect + gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/ini.v1 v1.51.0 // indirect gopkg.in/mail.v2 v2.3.1 // indirect + gopkg.in/yaml.v2 v2.2.2 // indirect ) diff --git a/go.sum b/go.sum index 402c072f..188c1ecc 100644 --- a/go.sum +++ b/go.sum @@ -12,7 +12,6 @@ github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7I github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/aliyun/aliyun-oss-go-sdk v2.0.0/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/aliyun/aliyun-oss-go-sdk v2.0.5+incompatible h1:A3oZlWPD/Poa19FvNbw+Zu4yKAurDBTjlRDilYGBiS4= github.com/aliyun/aliyun-oss-go-sdk v2.0.5+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -179,8 +178,6 @@ github.com/mojocn/base64Captcha v0.0.0-20190801020520-752b1cd608b2/go.mod h1:wAQ github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ= github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= -github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -206,8 +203,6 @@ github.com/quasoft/memstore v0.0.0-20180925164028-84a050167438 h1:jnz/4VenymvySj github.com/quasoft/memstore v0.0.0-20180925164028-84a050167438/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg= github.com/rafaeljusto/redigomock v0.0.0-20191117212112-00b2509252a1 h1:leEwA4MD1ew0lNgzz6Q4G76G3AEfeci+TMggN6WuFRs= github.com/rafaeljusto/redigomock v0.0.0-20191117212112-00b2509252a1/go.mod h1:JaY6n2sDr+z2WTsXkOmNRUfDy6FN0L6Nk7x06ndm4tY= -github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= -github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= @@ -247,7 +242,6 @@ golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/image v0.0.0-20190501045829-6d32002ffd75 h1:TbGuee8sSq15Iguxu4deQ7+Bqq/d2rsQejGcEtADAMQ= golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ= golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= @@ -289,7 +283,6 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/main.go b/main.go index d71e5873..71731832 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "embed" "flag" "github.com/cloudreve/Cloudreve/v3/bootstrap" @@ -15,6 +16,9 @@ var ( scriptName string ) +//go:embed assets/build +var StaticEmbed embed.FS + func init() { flag.StringVar(&confPath, "c", util.RelativePath("conf.ini"), "配置文件路径") flag.BoolVar(&isEject, "eject", false, "导出内置静态资源") diff --git a/pkg/util/path.go b/pkg/util/path.go index ff51d579..6cba7122 100644 --- a/pkg/util/path.go +++ b/pkg/util/path.go @@ -56,3 +56,10 @@ func RelativePath(name string) string { e, _ := os.Executable() return filepath.Join(filepath.Dir(e), name) } + + +// ExecPath 获取可执行文件的路径 +func ExecPath() string { + e, _ := os.Executable() + return filepath.Dir(e) +}