Archived
init
This commit is contained in:
273 files changed
+45396
No files matched your search
@@ -0,0 +1,125 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
// "bytes"
|
||||
// "crypto/aes"
|
||||
// "crypto/cipher"
|
||||
// "encoding/gob"
|
||||
// "encoding/json"
|
||||
"fmt"
|
||||
// "io/ioutil"
|
||||
// "os"
|
||||
// "strconv"
|
||||
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/conf"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/vol"
|
||||
|
||||
// "github.com/cloudreve/Cloudreve/v3/pkg/request"
|
||||
// "github.com/cloudreve/Cloudreve/v3/pkg/util"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var matrix []byte
|
||||
var APPID string
|
||||
|
||||
// InitApplication 初始化应用常量
|
||||
func InitApplication() {
|
||||
fmt.Print(`
|
||||
___ _ _
|
||||
/ __\ | ___ _ _ __| |_ __ _____ _____
|
||||
/ / | |/ _ \| | | |/ _ | '__/ _ \ \ / / _ \
|
||||
/ /___| | (_) | |_| | (_| | | | __/\ V / __/
|
||||
\____/|_|\___/ \__,_|\__,_|_| \___| \_/ \___|
|
||||
|
||||
V` + conf.BackendVersion + ` Commit #` + conf.LastCommit + ` Plus=` + conf.IsPlus + `
|
||||
================================================
|
||||
|
||||
`)
|
||||
// data, err := ioutil.ReadFile(util.RelativePath(string([]byte{107, 101, 121, 46, 98, 105, 110})))
|
||||
// if err != nil {
|
||||
// util.Log().Panic("%s", err)
|
||||
// }
|
||||
|
||||
//table := deSign(data)
|
||||
//constant.HashIDTable = table["table"].([]int)
|
||||
//APPID = table["id"].(string)
|
||||
//matrix = table["pic"].([]byte)
|
||||
APPID = `2018110303550773058`
|
||||
matrix = []byte{137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 2, 10, 0, 0, 1, 16, 8, 6, 0, 0, 0, 209, 100, 92, 53, 0, 0, 6, 112, 73, 68, 65, 84, 120, 156, 237, 214, 209, 142, 212, 54, 0, 64, 81, 187, 223, 4, 69, 162, 255, 255, 93, 230, 97, 50, 187, 147, 196, 119, 89, 84, 169, 165, 234, 57, 66, 66, 12, 153, 141, 99, 59, 222, 59, 191, 126, 251, 107, 141, 15, 173, 177, 214, 28, 99, 172, 49, 231, 124, 255, 116, 141, 49, 142, 127, 206, 211, 135, 99, 172, 57, 199, 124, 253, 254, 241, 175, 247, 207, 158, 151, 174, 49, 94, 175, 93, 107, 60, 110, 53, 199, 203, 173, 198, 26, 235, 254, 217, 26, 99, 30, 223, 63, 127, 56, 199, 120, 25, 219, 219, 253, 47, 151, 62, 199, 186, 249, 161, 99, 141, 251, 248, 231, 186, 223, 107, 141, 57, 142, 63, 207, 75, 199, 115, 50, 207, 227, 31, 99, 158, 198, 244, 28, 234, 58, 223, 107, 55, 166, 199, 211, 31, 227, 127, 157, 255, 199, 55, 215, 188, 204, 255, 28, 227, 50, 211, 99, 173, 121, 251, 248, 121, 233, 245, 251, 235, 184, 240, 190, 126, 231, 89, 121, 60, 235, 121, 79, 188, 173, 255, 120, 93, 171, 143, 238, 191, 110, 123, 229, 88, 213, 235, 19, 92, 198, 52, 114, 82, 31, 207, 243, 153, 231, 127, 95, 207, 211, 253, 215, 126, 255, 94, 215, 127, 191, 84, 253, 252, 99, 94, 118, 213, 118, 169, 30, 243, 113, 29, 211, 110, 254, 31, 235, 127, 25, 192, 118, 81, 251, 253, 217, 191, 127, 113, 255, 117, 93, 235, 113, 159, 255, 231, 11, 112, 154, 148, 15, 246, 255, 230, 249, 55, 155, 242, 56, 127, 110, 175, 197, 126, 255, 205, 57, 206, 75, 117, 188, 63, 215, 93, 241, 193, 249, 51, 175, 247, 223, 158, 95, 235, 184, 199, 79, 95, 170, 237, 248, 247, 75, 85, 251, 239, 57, 212, 203, 250, 199, 253, 215, 237, 252, 252, 236, 82, 237, 222, 191, 62, 191, 119, 231, 199, 120, 94, 121, 157, 255, 203, 248, 173, 223, 63, 181, 126, 239, 191, 107, 62, 241, 2, 30, 99, 157, 183, 115, 109, 126, 249, 246, 125, 237, 254, 227, 250, 178, 239, 39, 225, 163, 75, 47, 155, 224, 237, 192, 189, 140, 247, 111, 77, 194, 175, 109, 162, 253, 38, 156, 247, 159, 154, 191, 4, 206, 7, 240, 207, 54, 193, 53, 2, 238, 191, 196, 226, 16, 61, 14, 246, 243, 237, 118, 27, 246, 119, 142, 184, 205, 33, 178, 221, 152, 247, 251, 111, 131, 173, 94, 226, 205, 47, 241, 109, 196, 125, 244, 18, 237, 38, 96, 179, 169, 183, 251, 247, 250, 11, 244, 229, 251, 187, 3, 119, 31, 140, 215, 91, 109, 158, 255, 109, 175, 238, 46, 189, 63, 255, 24, 243, 118, 136, 61, 246, 218, 252, 220, 253, 71, 29, 120, 191, 16, 236, 231, 31, 26, 247, 223, 205, 223, 216, 30, 10, 255, 233, 253, 191, 157, 191, 62, 127, 106, 78, 119, 231, 215, 245, 210, 218, 255, 189, 127, 199, 245, 195, 199, 223, 187, 243, 123, 156, 173, 99, 173, 230, 237, 251, 251, 253, 119, 63, 171, 227, 253, 25, 251, 181, 218, 142, 127, 140, 177, 198, 206, 203, 92, 175, 245, 54, 203, 251, 107, 199, 237, 217, 120, 55, 191, 254, 249, 189, 230, 13, 0, 248, 159, 251, 227, 223, 30, 0, 0, 240, 251, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10, 0, 64, 18, 10,Line truncated
|
||||
vol.ClientSecret = APPID
|
||||
}
|
||||
|
||||
// InitCustomRoute 初始化自定义路由
|
||||
func InitCustomRoute(group *gin.RouterGroup) {
|
||||
group.GET(string([]byte{98, 103}), func(c *gin.Context) {
|
||||
c.Header("content-type", "image/png")
|
||||
c.Writer.Write(matrix)
|
||||
})
|
||||
group.GET("id", func(c *gin.Context) {
|
||||
c.String(200, APPID)
|
||||
})
|
||||
}
|
||||
|
||||
// func deSign(data []byte) map[string]interface{} {
|
||||
// res := decode(data, seed())
|
||||
// dec := gob.NewDecoder(bytes.NewReader(res))
|
||||
// obj := map[string]interface{}{}
|
||||
// err := dec.Decode(&obj)
|
||||
// if err != nil {
|
||||
// util.Log().Panic("您仍在使用旧版的授权文件,请前往 https://pro.cloudreve.org/ 登录下载最新的授权文件")
|
||||
// os.Exit(-1)
|
||||
// }
|
||||
// return checkKeyUpdate(obj)
|
||||
// }
|
||||
|
||||
// func checkKeyUpdate(table map[string]interface{}) map[string]interface{} {
|
||||
// if table["version"].(string) != conf.KeyVersion {
|
||||
// util.Log().Info("正在自动更新授权文件...")
|
||||
// reqBody := map[string]string{
|
||||
// "secret": table["secret"].(string),
|
||||
// "id": table["id"].(string),
|
||||
// }
|
||||
// reqBodyString, _ := json.Marshal(reqBody)
|
||||
// client := request.NewClient()
|
||||
// resp := client.Request("POST", "https://pro.cloudreve.org/Api/UpdateKey",
|
||||
// bytes.NewReader(reqBodyString)).CheckHTTPResponse(200)
|
||||
// if resp.Err != nil {
|
||||
// util.Log().Panic("授权文件更新失败, %s", resp.Err)
|
||||
// }
|
||||
// keyContent, _ := ioutil.ReadAll(resp.Response.Body)
|
||||
// ioutil.WriteFile(util.RelativePath(string([]byte{107, 101, 121, 46, 98, 105, 110})), keyContent, os.ModePerm)
|
||||
|
||||
// return deSign(keyContent)
|
||||
// }
|
||||
|
||||
// return table
|
||||
// }
|
||||
|
||||
// func seed() []byte {
|
||||
// res := []int{8}
|
||||
// s := "20210323"
|
||||
// m := 1 << 20
|
||||
// a := 9
|
||||
// b := 7
|
||||
// for i := 1; i < 23; i++ {
|
||||
// res = append(res, (a*res[i-1]+b)%m)
|
||||
// s += strconv.Itoa(res[i])
|
||||
// }
|
||||
// return []byte(s)
|
||||
// }
|
||||
|
||||
// func decode(cryted []byte, key []byte) []byte {
|
||||
// block, _ := aes.NewCipher(key[:32])
|
||||
// blockSize := block.BlockSize()
|
||||
// blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
|
||||
// orig := make([]byte, len(cryted))
|
||||
// blockMode.CryptBlocks(orig, cryted)
|
||||
// orig = pKCS7UnPadding(orig)
|
||||
// return orig
|
||||
// }
|
||||
|
||||
// func pKCS7UnPadding(origData []byte) []byte {
|
||||
// length := len(origData)
|
||||
// unpadding := int(origData[length-1])
|
||||
// return origData[:(length - unpadding)]
|
||||
// }
|
||||
@@ -0,0 +1,3 @@
|
||||
package constant
|
||||
|
||||
// var HashIDTable = []int{0, 1, 2, 3, 4, 5}
|
||||
@@ -0,0 +1,432 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package embed provides access to files embedded in the running Go program.
|
||||
//
|
||||
// Go source files that import "embed" can use the //go:embed directive
|
||||
// to initialize a variable of type string, []byte, or FS with the contents of
|
||||
// files read from the package directory or subdirectories at compile time.
|
||||
//
|
||||
// For example, here are three ways to embed a file named hello.txt
|
||||
// and then print its contents at run time.
|
||||
//
|
||||
// Embedding one file into a string:
|
||||
//
|
||||
// import _ "embed"
|
||||
//
|
||||
// //go:embed hello.txt
|
||||
// var s string
|
||||
// print(s)
|
||||
//
|
||||
// Embedding one file into a slice of bytes:
|
||||
//
|
||||
// import _ "embed"
|
||||
//
|
||||
// //go:embed hello.txt
|
||||
// var b []byte
|
||||
// print(string(b))
|
||||
//
|
||||
// Embedded one or more files into a file system:
|
||||
//
|
||||
// import "embed"
|
||||
//
|
||||
// //go:embed hello.txt
|
||||
// var f embed.FS
|
||||
// data, _ := f.ReadFile("hello.txt")
|
||||
// print(string(data))
|
||||
//
|
||||
// # Directives
|
||||
//
|
||||
// A //go:embed directive above a variable declaration specifies which files to embed,
|
||||
// using one or more path.Match patterns.
|
||||
//
|
||||
// The directive must immediately precede a line containing the declaration of a single variable.
|
||||
// Only blank lines and ‘//’ line comments are permitted between the directive and the declaration.
|
||||
//
|
||||
// The type of the variable must be a string type, or a slice of a byte type,
|
||||
// or FS (or an alias of FS).
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// package server
|
||||
//
|
||||
// import "embed"
|
||||
//
|
||||
// // content holds our static web server content.
|
||||
// //go:embed image/* template/*
|
||||
// //go:embed html/index.html
|
||||
// var content embed.FS
|
||||
//
|
||||
// The Go build system will recognize the directives and arrange for the declared variable
|
||||
// (in the example above, content) to be populated with the matching files from the file system.
|
||||
//
|
||||
// The //go:embed directive accepts multiple space-separated patterns for
|
||||
// brevity, but it can also be repeated, to avoid very long lines when there are
|
||||
// many patterns. The patterns are interpreted relative to the package directory
|
||||
// containing the source file. The path separator is a forward slash, even on
|
||||
// Windows systems. Patterns may not contain ‘.’ or ‘..’ or empty path elements,
|
||||
// nor may they begin or end with a slash. To match everything in the current
|
||||
// directory, use ‘*’ instead of ‘.’. To allow for naming files with spaces in
|
||||
// their names, patterns can be written as Go double-quoted or back-quoted
|
||||
// string literals.
|
||||
//
|
||||
// If a pattern names a directory, all files in the subtree rooted at that directory are
|
||||
// embedded (recursively), except that files with names beginning with ‘.’ or ‘_’
|
||||
// are excluded. So the variable in the above example is almost equivalent to:
|
||||
//
|
||||
// // content is our static web server content.
|
||||
// //go:embed image template html/index.html
|
||||
// var content embed.FS
|
||||
//
|
||||
// The difference is that ‘image/*’ embeds ‘image/.tempfile’ while ‘image’ does not.
|
||||
// Neither embeds ‘image/dir/.tempfile’.
|
||||
//
|
||||
// If a pattern begins with the prefix ‘all:’, then the rule for walking directories is changed
|
||||
// to include those files beginning with ‘.’ or ‘_’. For example, ‘all:image’ embeds
|
||||
// both ‘image/.tempfile’ and ‘image/dir/.tempfile’.
|
||||
//
|
||||
// The //go:embed directive can be used with both exported and unexported variables,
|
||||
// depending on whether the package wants to make the data available to other packages.
|
||||
// It can only be used with variables at package scope, not with local variables.
|
||||
//
|
||||
// Patterns must not match files outside the package's module, such as ‘.git/*’ or symbolic links.
|
||||
// Patterns must not match files whose names include the special punctuation characters " * < > ? ` ' | / \ and :.
|
||||
// Matches for empty directories are ignored. After that, each pattern in a //go:embed line
|
||||
// must match at least one file or non-empty directory.
|
||||
//
|
||||
// If any patterns are invalid or have invalid matches, the build will fail.
|
||||
//
|
||||
// # Strings and Bytes
|
||||
//
|
||||
// The //go:embed line for a variable of type string or []byte can have only a single pattern,
|
||||
// and that pattern can match only a single file. The string or []byte is initialized with
|
||||
// the contents of that file.
|
||||
//
|
||||
// The //go:embed directive requires importing "embed", even when using a string or []byte.
|
||||
// In source files that don't refer to embed.FS, use a blank import (import _ "embed").
|
||||
//
|
||||
// # File Systems
|
||||
//
|
||||
// For embedding a single file, a variable of type string or []byte is often best.
|
||||
// The FS type enables embedding a tree of files, such as a directory of static
|
||||
// web server content, as in the example above.
|
||||
//
|
||||
// FS implements the io/fs package's FS interface, so it can be used with any package that
|
||||
// understands file systems, including net/http, text/template, and html/template.
|
||||
//
|
||||
// For example, given the content variable in the example above, we can write:
|
||||
//
|
||||
// http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(content))))
|
||||
//
|
||||
// template.ParseFS(content, "*.tmpl")
|
||||
//
|
||||
// # Tools
|
||||
//
|
||||
// To support tools that analyze Go packages, the patterns found in //go:embed lines
|
||||
// are available in “go list” output. See the EmbedPatterns, TestEmbedPatterns,
|
||||
// and XTestEmbedPatterns fields in the “go help list” output.
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"time"
|
||||
)
|
||||
|
||||
// An FS is a read-only collection of files, usually initialized with a //go:embed directive.
|
||||
// When declared without a //go:embed directive, an FS is an empty file system.
|
||||
//
|
||||
// An FS is a read-only value, so it is safe to use from multiple goroutines
|
||||
// simultaneously and also safe to assign values of type FS to each other.
|
||||
//
|
||||
// FS implements fs.FS, so it can be used with any package that understands
|
||||
// file system interfaces, including net/http, text/template, and html/template.
|
||||
//
|
||||
// See the package documentation for more details about initializing an FS.
|
||||
type FS struct {
|
||||
// The compiler knows the layout of this struct.
|
||||
// See cmd/compile/internal/staticdata's WriteEmbed.
|
||||
//
|
||||
// The files list is sorted by name but not by simple string comparison.
|
||||
// Instead, each file's name takes the form "dir/elem" or "dir/elem/".
|
||||
// The optional trailing slash indicates that the file is itself a directory.
|
||||
// The files list is sorted first by dir (if dir is missing, it is taken to be ".")
|
||||
// and then by base, so this list of files:
|
||||
//
|
||||
// p
|
||||
// q/
|
||||
// q/r
|
||||
// q/s/
|
||||
// q/s/t
|
||||
// q/s/u
|
||||
// q/v
|
||||
// w
|
||||
//
|
||||
// is actually sorted as:
|
||||
//
|
||||
// p # dir=. elem=p
|
||||
// q/ # dir=. elem=q
|
||||
// w/ # dir=. elem=w
|
||||
// q/r # dir=q elem=r
|
||||
// q/s/ # dir=q elem=s
|
||||
// q/v # dir=q elem=v
|
||||
// q/s/t # dir=q/s elem=t
|
||||
// q/s/u # dir=q/s elem=u
|
||||
//
|
||||
// This order brings directory contents together in contiguous sections
|
||||
// of the list, allowing a directory read to use binary search to find
|
||||
// the relevant sequence of entries.
|
||||
files *[]file
|
||||
}
|
||||
|
||||
// split splits the name into dir and elem as described in the
|
||||
// comment in the FS struct above. isDir reports whether the
|
||||
// final trailing slash was present, indicating that name is a directory.
|
||||
func split(name string) (dir, elem string, isDir bool) {
|
||||
if name[len(name)-1] == '/' {
|
||||
isDir = true
|
||||
name = name[:len(name)-1]
|
||||
}
|
||||
i := len(name) - 1
|
||||
for i >= 0 && name[i] != '/' {
|
||||
i--
|
||||
}
|
||||
if i < 0 {
|
||||
return ".", name, isDir
|
||||
}
|
||||
return name[:i], name[i+1:], isDir
|
||||
}
|
||||
|
||||
// trimSlash trims a trailing slash from name, if present,
|
||||
// returning the possibly shortened name.
|
||||
func trimSlash(name string) string {
|
||||
if len(name) > 0 && name[len(name)-1] == '/' {
|
||||
return name[:len(name)-1]
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
var (
|
||||
_ fs.ReadDirFS = FS{}
|
||||
_ fs.ReadFileFS = FS{}
|
||||
)
|
||||
|
||||
// A file is a single file in the FS.
|
||||
// It implements fs.FileInfo and fs.DirEntry.
|
||||
type file struct {
|
||||
// The compiler knows the layout of this struct.
|
||||
// See cmd/compile/internal/staticdata's WriteEmbed.
|
||||
name string
|
||||
data string
|
||||
hash [16]byte // truncated SHA256 hash
|
||||
}
|
||||
|
||||
var (
|
||||
_ fs.FileInfo = (*file)(nil)
|
||||
_ fs.DirEntry = (*file)(nil)
|
||||
)
|
||||
|
||||
func (f *file) Name() string { _, elem, _ := split(f.name); return elem }
|
||||
func (f *file) Size() int64 { return int64(len(f.data)) }
|
||||
func (f *file) ModTime() time.Time { return time.Time{} }
|
||||
func (f *file) IsDir() bool { _, _, isDir := split(f.name); return isDir }
|
||||
func (f *file) Sys() any { return nil }
|
||||
func (f *file) Type() fs.FileMode { return f.Mode().Type() }
|
||||
func (f *file) Info() (fs.FileInfo, error) { return f, nil }
|
||||
|
||||
func (f *file) Mode() fs.FileMode {
|
||||
if f.IsDir() {
|
||||
return fs.ModeDir | 0555
|
||||
}
|
||||
return 0444
|
||||
}
|
||||
|
||||
// dotFile is a file for the root directory,
|
||||
// which is omitted from the files list in a FS.
|
||||
var dotFile = &file{name: "./"}
|
||||
|
||||
// lookup returns the named file, or nil if it is not present.
|
||||
func (f FS) lookup(name string) *file {
|
||||
if !fs.ValidPath(name) {
|
||||
// The compiler should never emit a file with an invalid name,
|
||||
// so this check is not strictly necessary (if name is invalid,
|
||||
// we shouldn't find a match below), but it's a good backstop anyway.
|
||||
return nil
|
||||
}
|
||||
if name == "." {
|
||||
return dotFile
|
||||
}
|
||||
if f.files == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Binary search to find where name would be in the list,
|
||||
// and then check if name is at that position.
|
||||
dir, elem, _ := split(name)
|
||||
files := *f.files
|
||||
i := sortSearch(len(files), func(i int) bool {
|
||||
idir, ielem, _ := split(files[i].name)
|
||||
return idir > dir || idir == dir && ielem >= elem
|
||||
})
|
||||
if i < len(files) && trimSlash(files[i].name) == name {
|
||||
return &files[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// readDir returns the list of files corresponding to the directory dir.
|
||||
func (f FS) readDir(dir string) []file {
|
||||
if f.files == nil {
|
||||
return nil
|
||||
}
|
||||
// Binary search to find where dir starts and ends in the list
|
||||
// and then return that slice of the list.
|
||||
files := *f.files
|
||||
i := sortSearch(len(files), func(i int) bool {
|
||||
idir, _, _ := split(files[i].name)
|
||||
return idir >= dir
|
||||
})
|
||||
j := sortSearch(len(files), func(j int) bool {
|
||||
jdir, _, _ := split(files[j].name)
|
||||
return jdir > dir
|
||||
})
|
||||
return files[i:j]
|
||||
}
|
||||
|
||||
// Open opens the named file for reading and returns it as an fs.File.
|
||||
//
|
||||
// The returned file implements io.Seeker when the file is not a directory.
|
||||
func (f FS) Open(name string) (fs.File, error) {
|
||||
file := f.lookup(name)
|
||||
if file == nil {
|
||||
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
|
||||
}
|
||||
if file.IsDir() {
|
||||
return &openDir{file, f.readDir(name), 0}, nil
|
||||
}
|
||||
return &openFile{file, 0}, nil
|
||||
}
|
||||
|
||||
// ReadDir reads and returns the entire named directory.
|
||||
func (f FS) ReadDir(name string) ([]fs.DirEntry, error) {
|
||||
file, err := f.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dir, ok := file.(*openDir)
|
||||
if !ok {
|
||||
return nil, &fs.PathError{Op: "read", Path: name, Err: errors.New("not a directory")}
|
||||
}
|
||||
list := make([]fs.DirEntry, len(dir.files))
|
||||
for i := range list {
|
||||
list[i] = &dir.files[i]
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// ReadFile reads and returns the content of the named file.
|
||||
func (f FS) ReadFile(name string) ([]byte, error) {
|
||||
file, err := f.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ofile, ok := file.(*openFile)
|
||||
if !ok {
|
||||
return nil, &fs.PathError{Op: "read", Path: name, Err: errors.New("is a directory")}
|
||||
}
|
||||
return []byte(ofile.f.data), nil
|
||||
}
|
||||
|
||||
// An openFile is a regular file open for reading.
|
||||
type openFile struct {
|
||||
f *file // the file itself
|
||||
offset int64 // current read offset
|
||||
}
|
||||
|
||||
var (
|
||||
_ io.Seeker = (*openFile)(nil)
|
||||
)
|
||||
|
||||
func (f *openFile) Close() error { return nil }
|
||||
func (f *openFile) Stat() (fs.FileInfo, error) { return f.f, nil }
|
||||
|
||||
func (f *openFile) Read(b []byte) (int, error) {
|
||||
if f.offset >= int64(len(f.f.data)) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
if f.offset < 0 {
|
||||
return 0, &fs.PathError{Op: "read", Path: f.f.name, Err: fs.ErrInvalid}
|
||||
}
|
||||
n := copy(b, f.f.data[f.offset:])
|
||||
f.offset += int64(n)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (f *openFile) Seek(offset int64, whence int) (int64, error) {
|
||||
switch whence {
|
||||
case 0:
|
||||
// offset += 0
|
||||
case 1:
|
||||
offset += f.offset
|
||||
case 2:
|
||||
offset += int64(len(f.f.data))
|
||||
}
|
||||
if offset < 0 || offset > int64(len(f.f.data)) {
|
||||
return 0, &fs.PathError{Op: "seek", Path: f.f.name, Err: fs.ErrInvalid}
|
||||
}
|
||||
f.offset = offset
|
||||
return offset, nil
|
||||
}
|
||||
|
||||
// An openDir is a directory open for reading.
|
||||
type openDir struct {
|
||||
f *file // the directory file itself
|
||||
files []file // the directory contents
|
||||
offset int // the read offset, an index into the files slice
|
||||
}
|
||||
|
||||
func (d *openDir) Close() error { return nil }
|
||||
func (d *openDir) Stat() (fs.FileInfo, error) { return d.f, nil }
|
||||
|
||||
func (d *openDir) Read([]byte) (int, error) {
|
||||
return 0, &fs.PathError{Op: "read", Path: d.f.name, Err: errors.New("is a directory")}
|
||||
}
|
||||
|
||||
func (d *openDir) ReadDir(count int) ([]fs.DirEntry, error) {
|
||||
n := len(d.files) - d.offset
|
||||
if n == 0 {
|
||||
if count <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, io.EOF
|
||||
}
|
||||
if count > 0 && n > count {
|
||||
n = count
|
||||
}
|
||||
list := make([]fs.DirEntry, n)
|
||||
for i := range list {
|
||||
list[i] = &d.files[d.offset+i]
|
||||
}
|
||||
d.offset += n
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// sortSearch is like sort.Search, avoiding an import.
|
||||
func sortSearch(n int, f func(int) bool) int {
|
||||
// Define f(-1) == false and f(n) == true.
|
||||
// Invariant: f(i-1) == false, f(j) == true.
|
||||
i, j := 0, n
|
||||
for i < j {
|
||||
h := int(uint(i+j) >> 1) // avoid overflow when computing h
|
||||
// i ≤ h < j
|
||||
if !f(h) {
|
||||
i = h + 1 // preserves f(i-1) == false
|
||||
} else {
|
||||
j = h // preserves f(j) == true
|
||||
}
|
||||
}
|
||||
// i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i.
|
||||
return i
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"crypto/sha256"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func NewFS(zipContent string) fs.FS {
|
||||
zipReader, err := zip.NewReader(strings.NewReader(zipContent), int64(len(zipContent)))
|
||||
if err != nil {
|
||||
util.Log().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 errors.Errorf("无法获取[%s]的信息, %s, 跳过...", 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 errors.Errorf("无法打开文件[%s], %s, 跳过...", path, err)
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
data, err := io.ReadAll(rc)
|
||||
if err != nil {
|
||||
return errors.Errorf("无法读取文件[%s], %s, 跳过...", 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 {
|
||||
util.Log().Panic("初始化静态资源失败: %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
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
|
||||
model "github.com/cloudreve/Cloudreve/v3/models"
|
||||
"github.com/cloudreve/Cloudreve/v3/models/scripts"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/aria2"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/auth"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/cache"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/cluster"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/conf"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/crontab"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/email"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/mq"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/task"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/wopi"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Init 初始化启动
|
||||
func Init(path string, statics fs.FS) {
|
||||
InitApplication()
|
||||
conf.Init(path)
|
||||
// Debug 关闭时,切换为生产模式
|
||||
if !conf.SystemConfig.Debug {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
}
|
||||
|
||||
dependencies := []struct {
|
||||
mode string
|
||||
factory func()
|
||||
}{
|
||||
{
|
||||
"both",
|
||||
func() {
|
||||
scripts.Init()
|
||||
},
|
||||
},
|
||||
{
|
||||
"both",
|
||||
func() {
|
||||
cache.Init()
|
||||
},
|
||||
},
|
||||
{
|
||||
"slave",
|
||||
func() {
|
||||
model.InitSlaveDefaults()
|
||||
},
|
||||
},
|
||||
{
|
||||
"slave",
|
||||
func() {
|
||||
cache.InitSlaveOverwrites()
|
||||
},
|
||||
},
|
||||
{
|
||||
"master",
|
||||
func() {
|
||||
model.Init()
|
||||
},
|
||||
},
|
||||
{
|
||||
"both",
|
||||
func() {
|
||||
cache.Restore(filepath.Join(model.GetSettingByName("temp_path"), cache.DefaultCacheFile))
|
||||
},
|
||||
},
|
||||
{
|
||||
"both",
|
||||
func() {
|
||||
task.Init()
|
||||
},
|
||||
},
|
||||
{
|
||||
"master",
|
||||
func() {
|
||||
cluster.Init()
|
||||
},
|
||||
},
|
||||
{
|
||||
"master",
|
||||
func() {
|
||||
aria2.Init(false, cluster.Default, mq.GlobalMQ)
|
||||
},
|
||||
},
|
||||
{
|
||||
"master",
|
||||
func() {
|
||||
email.Init()
|
||||
},
|
||||
},
|
||||
{
|
||||
"master",
|
||||
func() {
|
||||
crontab.Init()
|
||||
},
|
||||
},
|
||||
{
|
||||
"master",
|
||||
func() {
|
||||
InitStatic(statics)
|
||||
},
|
||||
},
|
||||
{
|
||||
"slave",
|
||||
func() {
|
||||
cluster.InitController()
|
||||
},
|
||||
},
|
||||
{
|
||||
"both",
|
||||
func() {
|
||||
auth.Init()
|
||||
},
|
||||
},
|
||||
{
|
||||
"master",
|
||||
func() {
|
||||
wopi.Init()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, dependency := range dependencies {
|
||||
if dependency.mode == conf.SystemConfig.Mode || dependency.mode == "both" {
|
||||
dependency.factory()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cloudreve/Cloudreve/v3/models/scripts/invoker"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/util"
|
||||
)
|
||||
|
||||
func RunScript(name string) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
if err := invoker.RunDBScript(name, ctx); err != nil {
|
||||
util.Log().Error("Failed to execute database script: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
util.Log().Info("Finish executing database script %q.", name)
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/conf"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/util"
|
||||
|
||||
"github.com/gin-contrib/static"
|
||||
)
|
||||
|
||||
const StaticFolder = "statics"
|
||||
|
||||
type GinFS struct {
|
||||
FS http.FileSystem
|
||||
}
|
||||
|
||||
type staticVersion struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// StaticFS 内置静态文件资源
|
||||
var StaticFS static.ServeFileSystem
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// InitStatic 初始化静态资源文件
|
||||
func InitStatic(statics fs.FS) {
|
||||
if util.Exists(util.RelativePath(StaticFolder)) {
|
||||
util.Log().Info("Folder with name \"statics\" already exists, it will be used to serve static files.")
|
||||
StaticFS = static.LocalFile(util.RelativePath("statics"), false)
|
||||
} else {
|
||||
// 初始化静态资源
|
||||
embedFS, err := fs.Sub(statics, "assets/build")
|
||||
if err != nil {
|
||||
util.Log().Panic("Failed to initialize static resources: %s", err)
|
||||
}
|
||||
|
||||
StaticFS = &GinFS{
|
||||
FS: http.FS(embedFS),
|
||||
}
|
||||
}
|
||||
// 检查静态资源的版本
|
||||
f, err := StaticFS.Open("version.json")
|
||||
if err != nil {
|
||||
util.Log().Warning("Missing version identifier file in static resources, please delete \"statics\" folder and rebuild it.")
|
||||
return
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
util.Log().Warning("Failed to read version identifier file in static resources, please delete \"statics\" folder and rebuild it.")
|
||||
return
|
||||
}
|
||||
|
||||
var v staticVersion
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
util.Log().Warning("Failed to parse version identifier file in static resources: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
staticName := "cloudreve-frontend"
|
||||
if conf.IsPlus == "true" {
|
||||
staticName += "-plus"
|
||||
}
|
||||
|
||||
if v.Name != staticName {
|
||||
util.Log().Warning("Static resource version mismatch, please delete \"statics\" folder and rebuild it.")
|
||||
return
|
||||
}
|
||||
|
||||
if v.Version != conf.RequiredStaticVersion {
|
||||
util.Log().Warning("Static resource version mismatch [Current %s, Desired: %s],please delete \"statics\" folder and rebuild it.", v.Version, conf.RequiredStaticVersion)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Eject 抽离内置静态资源
|
||||
func Eject(statics fs.FS) {
|
||||
// 初始化静态资源
|
||||
embedFS, err := fs.Sub(statics, "assets/build")
|
||||
if err != nil {
|
||||
util.Log().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 errors.Errorf("Failed to read info of %q: %s, skipping...", relPath, err)
|
||||
}
|
||||
|
||||
if !d.IsDir() {
|
||||
// 写入文件
|
||||
out, err := util.CreatNestedFile(filepath.Join(util.RelativePath(""), StaticFolder, relPath))
|
||||
|
||||
if err != nil {
|
||||
return errors.Errorf("Failed to create file %q: %s, skipping...", relPath, err)
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
util.Log().Info("Ejecting %q...", relPath)
|
||||
obj, _ := embedFS.Open(relPath)
|
||||
if _, err := io.Copy(out, bufio.NewReader(obj)); err != nil {
|
||||
return errors.Errorf("Cannot write file %q: %s, skipping...", relPath, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// util.Log().Info("开始导出内置静态资源...")
|
||||
err = fs.WalkDir(embedFS, ".", walk)
|
||||
if err != nil {
|
||||
util.Log().Error("Error occurs while ejecting static resources: %s", err)
|
||||
return
|
||||
}
|
||||
util.Log().Info("Finish ejecting static resources.")
|
||||
}
|
||||
Reference in new issue
Block a user