mirror of https://github.com/cloudreve/Cloudreve
Modify: clean modals
parent
4d70f9fa3e
commit
4ba24e0cf1
2
assets
2
assets
|
@ -1 +1 @@
|
||||||
Subproject commit d46074216363ac62a66a02284f2a791b79a392cd
|
Subproject commit 186f0aac0964c95ed4826894ddf888e32e00d46b
|
|
@ -1,23 +1,10 @@
|
||||||
package bootstrap
|
package bootstrap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"encoding/gob"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/HFO4/cloudreve/bootstrap/constant"
|
|
||||||
"github.com/HFO4/cloudreve/pkg/conf"
|
"github.com/HFO4/cloudreve/pkg/conf"
|
||||||
"github.com/HFO4/cloudreve/pkg/util"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var matrix []byte
|
|
||||||
var APPID string
|
|
||||||
|
|
||||||
// InitApplication 初始化应用常量
|
// InitApplication 初始化应用常量
|
||||||
func InitApplication() {
|
func InitApplication() {
|
||||||
fmt.Print(`
|
fmt.Print(`
|
||||||
|
@ -31,65 +18,4 @@ V` + conf.BackendVersion + ` Commit #` + conf.LastCommit + ` Pro=` + conf.IsPr
|
||||||
================================================
|
================================================
|
||||||
|
|
||||||
`)
|
`)
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(-1)
|
|
||||||
}
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
|
|
||||||
func seed() []byte {
|
|
||||||
res := []int{10}
|
|
||||||
s := "2020"
|
|
||||||
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)]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
package constant
|
|
||||||
|
|
||||||
var HashIDTable = []int{}
|
|
|
@ -1,7 +1,6 @@
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/HFO4/cloudreve/bootstrap/constant"
|
|
||||||
"github.com/HFO4/cloudreve/pkg/cache"
|
"github.com/HFO4/cloudreve/pkg/cache"
|
||||||
"github.com/HFO4/cloudreve/pkg/hashid"
|
"github.com/HFO4/cloudreve/pkg/hashid"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
@ -15,7 +14,6 @@ func TestHashID(t *testing.T) {
|
||||||
asserts := assert.New(t)
|
asserts := assert.New(t)
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
TestFunc := HashID(hashid.FolderID)
|
TestFunc := HashID(hashid.FolderID)
|
||||||
constant.HashIDTable = []int{0, 1, 2, 3, 4, 5, 6}
|
|
||||||
|
|
||||||
// 未给定ID对象,跳过
|
// 未给定ID对象,跳过
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,7 +15,6 @@ type Folder struct {
|
||||||
Name string `gorm:"unique_index:idx_only_one_name"`
|
Name string `gorm:"unique_index:idx_only_one_name"`
|
||||||
ParentID *uint `gorm:"index:parent_id;unique_index:idx_only_one_name"`
|
ParentID *uint `gorm:"index:parent_id;unique_index:idx_only_one_name"`
|
||||||
OwnerID uint `gorm:"index:owner_id"`
|
OwnerID uint `gorm:"index:owner_id"`
|
||||||
PolicyID uint // Webdav下挂载的存储策略ID
|
|
||||||
|
|
||||||
// 数据库忽略字段
|
// 数据库忽略字段
|
||||||
Position string `gorm:"-"`
|
Position string `gorm:"-"`
|
||||||
|
@ -30,13 +29,6 @@ func (folder *Folder) Create() (uint, error) {
|
||||||
return folder.ID, nil
|
return folder.ID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMountedFolders 列出已挂载存储策略的目录
|
|
||||||
func GetMountedFolders(uid uint) []Folder {
|
|
||||||
var folders []Folder
|
|
||||||
DB.Where("owner_id = ? and policy_id <> ?", uid, 0).Find(&folders)
|
|
||||||
return folders
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetChild 返回folder下名为name的子目录,不存在则返回错误
|
// GetChild 返回folder下名为name的子目录,不存在则返回错误
|
||||||
func (folder *Folder) GetChild(name string) (*Folder, error) {
|
func (folder *Folder) GetChild(name string) (*Folder, error) {
|
||||||
var resFolder Folder
|
var resFolder Folder
|
||||||
|
@ -272,11 +264,6 @@ func (folder *Folder) Rename(new string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mount 目录挂载
|
|
||||||
func (folder *Folder) Mount(new uint) error {
|
|
||||||
return DB.Model(&folder).Update("policy_id", new).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
实现 FileInfo.FileInfo 接口
|
实现 FileInfo.FileInfo 接口
|
||||||
TODO 测试
|
TODO 测试
|
||||||
|
|
|
@ -29,7 +29,6 @@ type GroupOption struct {
|
||||||
DecompressSize uint64 `json:"decompress_size,omitempty"`
|
DecompressSize uint64 `json:"decompress_size,omitempty"`
|
||||||
OneTimeDownload bool `json:"one_time_download,omitempty"`
|
OneTimeDownload bool `json:"one_time_download,omitempty"`
|
||||||
ShareDownload bool `json:"share_download,omitempty"`
|
ShareDownload bool `json:"share_download,omitempty"`
|
||||||
ShareFree bool `json:"share_free,omitempty"`
|
|
||||||
Aria2 bool `json:"aria2,omitempty"` // 离线下载
|
Aria2 bool `json:"aria2,omitempty"` // 离线下载
|
||||||
Aria2Options map[string]interface{} `json:"aria2_options,omitempty"` // 离线下载用户组配置
|
Aria2Options map[string]interface{} `json:"aria2_options,omitempty"` // 离线下载用户组配置
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ func TestGetGroupByID(t *testing.T) {
|
||||||
ID: 1,
|
ID: 1,
|
||||||
},
|
},
|
||||||
Name: "管理员",
|
Name: "管理员",
|
||||||
Policies: "[1]",
|
PolicyID: 1,
|
||||||
PolicyList: []uint{1},
|
PolicyList: []uint{1},
|
||||||
}, group)
|
}, group)
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ func TestGroup_AfterFind(t *testing.T) {
|
||||||
ID: 1,
|
ID: 1,
|
||||||
},
|
},
|
||||||
Name: "管理员",
|
Name: "管理员",
|
||||||
Policies: "[1]",
|
PolicyID: 1,
|
||||||
}
|
}
|
||||||
err := testCase.AfterFind()
|
err := testCase.AfterFind()
|
||||||
asserts.NoError(err)
|
asserts.NoError(err)
|
||||||
|
|
|
@ -35,8 +35,8 @@ func migration() {
|
||||||
if conf.DatabaseConfig.Type == "mysql" {
|
if conf.DatabaseConfig.Type == "mysql" {
|
||||||
DB = DB.Set("gorm:table_options", "ENGINE=InnoDB")
|
DB = DB.Set("gorm:table_options", "ENGINE=InnoDB")
|
||||||
}
|
}
|
||||||
DB.AutoMigrate(&User{}, &Setting{}, &Group{}, &Policy{}, &Folder{}, &File{}, &StoragePack{}, &Share{},
|
DB.AutoMigrate(&User{}, &Setting{}, &Group{}, &Policy{}, &Folder{}, &File{}, &Share{},
|
||||||
&Task{}, &Download{}, &Tag{}, &Webdav{}, &Order{}, &Redeem{})
|
&Task{}, &Download{}, &Tag{}, &Webdav{})
|
||||||
|
|
||||||
// 创建初始存储策略
|
// 创建初始存储策略
|
||||||
addDefaultPolicy()
|
addDefaultPolicy()
|
||||||
|
@ -90,9 +90,6 @@ func addDefaultSettings() {
|
||||||
{Name: "replyTo", Value: `abslant@126.com`, Type: "mail"},
|
{Name: "replyTo", Value: `abslant@126.com`, Type: "mail"},
|
||||||
{Name: "smtpUser", Value: `no-reply@acg.blue`, Type: "mail"},
|
{Name: "smtpUser", Value: `no-reply@acg.blue`, Type: "mail"},
|
||||||
{Name: "smtpPass", Value: ``, Type: "mail"},
|
{Name: "smtpPass", Value: ``, Type: "mail"},
|
||||||
{Name: "over_used_template", Value: `<meta name="viewport"content="width=device-width"><meta http-equiv="Content-Type"content="text/html; charset=UTF-8"><title>容量超额提醒</title><style type="text/css">img{max-width:100%}body{-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:none;width:100%!important;height:100%;line-height:1.6em}body{background-color:#f6f6f6}@media only screen and(max-width:640px){body{padding:0!important}h1{font-weight:800!important;margin:20px 0 5px!important}h2{font-weight:800!important;margin:20px 0 5px!important}h3{font-weight:800!important;margin:20px 0 5px!important}h4{font-weight:800!important;margin:20px 0 5px!important}h1{font-size:22px!important}h2{font-size:18px!important}h3{font-size:16px!important}.container{padding:0!important;width:100%!important}.content{padding:0!important}.content-wrap{padding:10px!important}.invoice{width:100%!important}}</style><table class="body-wrap"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background-color: #f6f6f6; margin: 0;"bgcolor="#f6f6f6"><tbody><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;"valign="top"></td><td class="container"width="600"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto;"valign="top"><div class="content"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;"><table class="main"width="100%"cellpadding="0"cellspacing="0"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background-color: #fff; margin: 0; border: 1px
|
|
||||||
solid #e9e9e9;"bgcolor="#fff"><tbody><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="alert alert-warning"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background-color: #FF9F00; margin: 0; padding: 20px;"align="center"bgcolor="#FF9F00"valign="top">容量超额警告</td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-wrap"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;"valign="top"><table width="100%"cellpadding="0"cellspacing="0"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><tbody><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top">亲爱的<strong style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">{userName}</strong>:</td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top">由于{notifyReason},您在{siteTitle}的账户的容量使用超出配额,您将无法继续上传新文件,请尽快清理文件,否则我们将会禁用您的账户。</td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top"><a href="{siteUrl}Login"class="btn-primary"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #348eda; margin: 0; border-color: #348eda; border-style: solid; border-width: 10px 20px;">登录{siteTitle}</a></td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top">感谢您选择{siteTitle}。</td></tr></tbody></table></td></tr></tbody></table><div class="footer"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;"><table width="100%"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><tbody><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="aligncenter content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;"align="center"valign="top">此邮件由系统自动发送,请不要直接回复。</td></tr></tbody></table></div></div></td><td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;"valign="top"></td></tr></tbody></table>`, Type: "mail_template"},
|
|
||||||
{Name: "ban_time", Value: `604800`, Type: "storage_policy"},
|
|
||||||
{Name: "maxEditSize", Value: `4194304`, Type: "file_edit"},
|
{Name: "maxEditSize", Value: `4194304`, Type: "file_edit"},
|
||||||
{Name: "archive_timeout", Value: `60`, Type: "timeout"},
|
{Name: "archive_timeout", Value: `60`, Type: "timeout"},
|
||||||
{Name: "download_timeout", Value: `60`, Type: "timeout"},
|
{Name: "download_timeout", Value: `60`, Type: "timeout"},
|
||||||
|
@ -108,10 +105,6 @@ solid #e9e9e9;"bgcolor="#fff"><tbody><tr style="font-family: 'Helvetica Neue',He
|
||||||
{Name: "onedrive_chunk_retries", Value: `1`, Type: "retry"},
|
{Name: "onedrive_chunk_retries", Value: `1`, Type: "retry"},
|
||||||
{Name: "reset_after_upload_failed", Value: `0`, Type: "upload"},
|
{Name: "reset_after_upload_failed", Value: `0`, Type: "upload"},
|
||||||
{Name: "login_captcha", Value: `0`, Type: "login"},
|
{Name: "login_captcha", Value: `0`, Type: "login"},
|
||||||
{Name: "qq_login", Value: `0`, Type: "login"},
|
|
||||||
{Name: "qq_direct_login", Value: `0`, Type: "login"},
|
|
||||||
{Name: "qq_login_id", Value: ``, Type: "login"},
|
|
||||||
{Name: "qq_login_key", Value: ``, Type: "login"},
|
|
||||||
{Name: "reg_captcha", Value: `0`, Type: "login"},
|
{Name: "reg_captcha", Value: `0`, Type: "login"},
|
||||||
{Name: "email_active", Value: `0`, Type: "register"},
|
{Name: "email_active", Value: `0`, Type: "register"},
|
||||||
{Name: "mail_activation_template", Value: `<!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box;
|
{Name: "mail_activation_template", Value: `<!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box;
|
||||||
|
@ -129,17 +122,8 @@ box-sizing: border-box; font-size: 14px; margin: 0;"><td style="font-family: 'He
|
||||||
solid #e9e9e9;"bgcolor="#fff"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size:
|
solid #e9e9e9;"bgcolor="#fff"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size:
|
||||||
14px; margin: 0;"><td class="alert alert-warning"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background-color: #2196F3; margin: 0; padding: 20px;"align="center"bgcolor="#FF9F00"valign="top">重设{siteTitle}密码</td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-wrap"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;"valign="top"><table width="100%"cellpadding="0"cellspacing="0"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica
|
14px; margin: 0;"><td class="alert alert-warning"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background-color: #2196F3; margin: 0; padding: 20px;"align="center"bgcolor="#FF9F00"valign="top">重设{siteTitle}密码</td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-wrap"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;"valign="top"><table width="100%"cellpadding="0"cellspacing="0"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica
|
||||||
Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top">亲爱的<strong style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">{userName}</strong>:</td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top">请点击下方按钮完成密码重设。如果非你本人操作,请忽略此邮件。</td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top"><a href="{resetUrl}"class="btn-primary"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #2196F3; margin: 0; border-color: #2196F3; border-style: solid; border-width: 10px 20px;">重设密码</a></td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top">感谢您选择{siteTitle}。</td></tr></table></td></tr></table><div class="footer"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;"><table width="100%"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="aligncenter content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;"align="center"valign="top">此邮件由系统自动发送,请不要直接回复。</td></tr></table></div></div></td><td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;"valign="top"></td></tr></table></body></html>`, Type: "mail_template"},
|
Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top">亲爱的<strong style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">{userName}</strong>:</td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top">请点击下方按钮完成密码重设。如果非你本人操作,请忽略此邮件。</td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top"><a href="{resetUrl}"class="btn-primary"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #2196F3; margin: 0; border-color: #2196F3; border-style: solid; border-width: 10px 20px;">重设密码</a></td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top">感谢您选择{siteTitle}。</td></tr></table></td></tr></table><div class="footer"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;"><table width="100%"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="aligncenter content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;"align="center"valign="top">此邮件由系统自动发送,请不要直接回复。</td></tr></table></div></div></td><td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;"valign="top"></td></tr></table></body></html>`, Type: "mail_template"},
|
||||||
{Name: "pack_data", Value: `[]`, Type: "pack"},
|
|
||||||
{Name: "database_version", Value: `3.0.0-alpha1`, Type: "version"},
|
{Name: "database_version", Value: `3.0.0-alpha1`, Type: "version"},
|
||||||
{Name: "alipay_enabled", Value: `0`, Type: "payment"},
|
|
||||||
{Name: "payjs_enabled", Value: `0`, Type: "payment"},
|
|
||||||
{Name: "payjs_id", Value: ``, Type: "payment"},
|
|
||||||
{Name: "payjs_secret", Value: ``, Type: "payment"},
|
|
||||||
{Name: "appid", Value: ``, Type: "payment"},
|
|
||||||
{Name: "appkey", Value: ``, Type: "payment"},
|
|
||||||
{Name: "shopid", Value: ``, Type: "payment"},
|
|
||||||
{Name: "hot_share_num", Value: `10`, Type: "share"},
|
{Name: "hot_share_num", Value: `10`, Type: "share"},
|
||||||
{Name: "group_sell_data", Value: `[]`, Type: "group_sell"},
|
|
||||||
{Name: "gravatar_server", Value: `https://www.gravatar.com/`, Type: "avatar"},
|
{Name: "gravatar_server", Value: `https://www.gravatar.com/`, Type: "avatar"},
|
||||||
{Name: "defaultTheme", Value: `#3f51b5`, Type: "basic"},
|
{Name: "defaultTheme", Value: `#3f51b5`, Type: "basic"},
|
||||||
{Name: "themes", Value: `{"#3f51b5":{"palette":{"primary":{"main":"#3f51b5"},"secondary":{"main":"#f50057"}}},"#2196f3":{"palette":{"primary":{"main":"#2196f3"},"secondary":{"main":"#FFC107"}}},"#673AB7":{"palette":{"primary":{"main":"#673AB7"},"secondary":{"main":"#2196F3"}}},"#E91E63":{"palette":{"primary":{"main":"#E91E63"},"secondary":{"main":"#42A5F5","contrastText":"#fff"}}},"#FF5722":{"palette":{"primary":{"main":"#FF5722"},"secondary":{"main":"#3F51B5"}}},"#FFC107":{"palette":{"primary":{"main":"#FFC107"},"secondary":{"main":"#26C6DA"}}},"#8BC34A":{"palette":{"primary":{"main":"#8BC34A","contrastText":"#fff"},"secondary":{"main":"#FF8A65","contrastText":"#fff"}}},"#009688":{"palette":{"primary":{"main":"#009688"},"secondary":{"main":"#4DD0E1","contrastText":"#fff"}}},"#607D8B":{"palette":{"primary":{"main":"#607D8B"},"secondary":{"main":"#F06292"}}},"#795548":{"palette":{"primary":{"main":"#795548"},"secondary":{"main":"#4CAF50","contrastText":"#fff"}}}}`, Type: "basic"},
|
{Name: "themes", Value: `{"#3f51b5":{"palette":{"primary":{"main":"#3f51b5"},"secondary":{"main":"#f50057"}}},"#2196f3":{"palette":{"primary":{"main":"#2196f3"},"secondary":{"main":"#FFC107"}}},"#673AB7":{"palette":{"primary":{"main":"#673AB7"},"secondary":{"main":"#2196F3"}}},"#E91E63":{"palette":{"primary":{"main":"#E91E63"},"secondary":{"main":"#42A5F5","contrastText":"#fff"}}},"#FF5722":{"palette":{"primary":{"main":"#FF5722"},"secondary":{"main":"#3F51B5"}}},"#FFC107":{"palette":{"primary":{"main":"#FFC107"},"secondary":{"main":"#26C6DA"}}},"#8BC34A":{"palette":{"primary":{"main":"#8BC34A","contrastText":"#fff"},"secondary":{"main":"#FF8A65","contrastText":"#fff"}}},"#009688":{"palette":{"primary":{"main":"#009688"},"secondary":{"main":"#4DD0E1","contrastText":"#fff"}}},"#607D8B":{"palette":{"primary":{"main":"#607D8B"},"secondary":{"main":"#F06292"}}},"#795548":{"palette":{"primary":{"main":"#795548"},"secondary":{"main":"#4CAF50","contrastText":"#fff"}}}}`, Type: "basic"},
|
||||||
|
@ -157,9 +141,6 @@ Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; verti
|
||||||
{Name: "avatar_size_l", Value: "200", Type: "avatar"},
|
{Name: "avatar_size_l", Value: "200", Type: "avatar"},
|
||||||
{Name: "avatar_size_m", Value: "130", Type: "avatar"},
|
{Name: "avatar_size_m", Value: "130", Type: "avatar"},
|
||||||
{Name: "avatar_size_s", Value: "50", Type: "avatar"},
|
{Name: "avatar_size_s", Value: "50", Type: "avatar"},
|
||||||
{Name: "score_enabled", Value: "1", Type: "score"},
|
|
||||||
{Name: "share_score_rate", Value: "80", Type: "score"},
|
|
||||||
{Name: "score_price", Value: "1", Type: "score"},
|
|
||||||
{Name: "home_view_method", Value: "icon", Type: "view"},
|
{Name: "home_view_method", Value: "icon", Type: "view"},
|
||||||
{Name: "share_view_method", Value: "list", Type: "view"},
|
{Name: "share_view_method", Value: "list", Type: "view"},
|
||||||
{Name: "cron_garbage_collect", Value: "@hourly", Type: "cron"},
|
{Name: "cron_garbage_collect", Value: "@hourly", Type: "cron"},
|
||||||
|
@ -206,7 +187,6 @@ func addDefaultGroups() {
|
||||||
ArchiveDownload: true,
|
ArchiveDownload: true,
|
||||||
ArchiveTask: true,
|
ArchiveTask: true,
|
||||||
ShareDownload: true,
|
ShareDownload: true,
|
||||||
ShareFree: true,
|
|
||||||
Aria2: true,
|
Aria2: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/HFO4/cloudreve/pkg/util"
|
|
||||||
"github.com/jinzhu/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// PackOrderType 容量包订单
|
|
||||||
PackOrderType = iota
|
|
||||||
// GroupOrderType 用户组订单
|
|
||||||
GroupOrderType
|
|
||||||
// ScoreOrderType 积分充值订单
|
|
||||||
ScoreOrderType
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// OrderUnpaid 未支付
|
|
||||||
OrderUnpaid = iota
|
|
||||||
// OrderPaid 已支付
|
|
||||||
OrderPaid
|
|
||||||
// OrderCanceled 已取消
|
|
||||||
OrderCanceled
|
|
||||||
)
|
|
||||||
|
|
||||||
// Order 交易订单
|
|
||||||
type Order struct {
|
|
||||||
gorm.Model
|
|
||||||
UserID uint // 创建者ID
|
|
||||||
OrderNo string `gorm:"index:order_number"` // 商户自定义订单编号
|
|
||||||
Type int // 订单类型
|
|
||||||
Method string // 支付类型
|
|
||||||
ProductID int64 // 商品ID
|
|
||||||
Num int // 商品数量
|
|
||||||
Name string // 订单标题
|
|
||||||
Price int // 商品单价
|
|
||||||
Status int // 订单状态
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create 创建订单记录
|
|
||||||
func (order *Order) Create() (uint, error) {
|
|
||||||
if err := DB.Create(order).Error; err != nil {
|
|
||||||
util.Log().Warning("无法插入离线下载记录, %s", err)
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return order.ID, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateStatus 更新订单状态
|
|
||||||
func (order *Order) UpdateStatus(status int) {
|
|
||||||
DB.Model(order).Update("status", status)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOrderByNo 根据商户订单号查询订单
|
|
||||||
func GetOrderByNo(id string) (*Order, error) {
|
|
||||||
var order Order
|
|
||||||
err := DB.Where("order_no = ?", id).First(&order).Error
|
|
||||||
return &order, err
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
import "github.com/jinzhu/gorm"
|
|
||||||
|
|
||||||
// Redeem 兑换码
|
|
||||||
type Redeem struct {
|
|
||||||
gorm.Model
|
|
||||||
Type int // 订单类型
|
|
||||||
ProductID int64 // 商品ID
|
|
||||||
Num int // 商品数量
|
|
||||||
Code string `gorm:"size:64,index:redeem_code"` // 兑换码
|
|
||||||
Used bool // 是否已被使用
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAvailableRedeem 根据code查找可用兑换码
|
|
||||||
func GetAvailableRedeem(code string) (*Redeem, error) {
|
|
||||||
redeem := &Redeem{}
|
|
||||||
result := DB.Where("code = ? and used = ?", code, false).First(redeem)
|
|
||||||
return redeem, result.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use 设定为已使用状态
|
|
||||||
func (redeem *Redeem) Use() {
|
|
||||||
DB.Model(redeem).Updates(map[string]interface{}{
|
|
||||||
"used": true,
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"github.com/HFO4/cloudreve/pkg/util"
|
"github.com/HFO4/cloudreve/pkg/util"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/jinzhu/gorm"
|
"github.com/jinzhu/gorm"
|
||||||
"math"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -24,7 +23,6 @@ type Share struct {
|
||||||
Downloads int // 下载数
|
Downloads int // 下载数
|
||||||
RemainDownloads int // 剩余下载配额,负值标识无限制
|
RemainDownloads int // 剩余下载配额,负值标识无限制
|
||||||
Expires *time.Time // 过期时间,空值表示无过期时间
|
Expires *time.Time // 过期时间,空值表示无过期时间
|
||||||
Score int // 每人次下载扣除积分
|
|
||||||
PreviewEnabled bool // 是否允许直接预览
|
PreviewEnabled bool // 是否允许直接预览
|
||||||
SourceName string `gorm:"index:source"` // 用于搜索的字段
|
SourceName string `gorm:"index:source"` // 用于搜索的字段
|
||||||
|
|
||||||
|
@ -136,12 +134,6 @@ func (share *Share) CanBeDownloadBy(user *User) error {
|
||||||
}
|
}
|
||||||
return errors.New("您当前的用户组无权下载")
|
return errors.New("您当前的用户组无权下载")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 需要积分但未登录
|
|
||||||
if share.Score > 0 && user.IsAnonymous() {
|
|
||||||
return errors.New("未登录用户无法下载")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,12 +148,9 @@ func (share *Share) WasDownloadedBy(user *User, c *gin.Context) (exist bool) {
|
||||||
return exist
|
return exist
|
||||||
}
|
}
|
||||||
|
|
||||||
// DownloadBy 增加下载次数、检查积分等,匿名用户不会缓存
|
// DownloadBy 增加下载次数,匿名用户不会缓存
|
||||||
func (share *Share) DownloadBy(user *User, c *gin.Context) error {
|
func (share *Share) DownloadBy(user *User, c *gin.Context) error {
|
||||||
if !share.WasDownloadedBy(user, c) {
|
if !share.WasDownloadedBy(user, c) {
|
||||||
if err := share.Purchase(user); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
share.Downloaded()
|
share.Downloaded()
|
||||||
if !user.IsAnonymous() {
|
if !user.IsAnonymous() {
|
||||||
cache.Set(fmt.Sprintf("share_%d_%d", share.ID, user.ID), true,
|
cache.Set(fmt.Sprintf("share_%d_%d", share.ID, user.ID), true,
|
||||||
|
@ -173,25 +162,6 @@ func (share *Share) DownloadBy(user *User, c *gin.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purchase 使用积分购买分享
|
|
||||||
func (share *Share) Purchase(user *User) error {
|
|
||||||
// 不需要付积分
|
|
||||||
if share.Score == 0 || user.Group.OptionsSerialized.ShareFree || user.ID == share.UserID {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ok := user.PayScore(share.Score)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("积分不足")
|
|
||||||
}
|
|
||||||
|
|
||||||
scoreRate := GetIntSetting("share_score_rate", 100)
|
|
||||||
gainedScore := int(math.Ceil(float64(share.Score*scoreRate) / 100))
|
|
||||||
share.Creator().AddScore(gainedScore)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Viewed 增加访问次数
|
// Viewed 增加访问次数
|
||||||
func (share *Share) Viewed() {
|
func (share *Share) Viewed() {
|
||||||
share.Views++
|
share.Views++
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/HFO4/cloudreve/pkg/cache"
|
|
||||||
"github.com/HFO4/cloudreve/pkg/util"
|
|
||||||
"github.com/jinzhu/gorm"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// StoragePack 容量包模型
|
|
||||||
type StoragePack struct {
|
|
||||||
// 表字段
|
|
||||||
gorm.Model
|
|
||||||
Name string
|
|
||||||
UserID uint
|
|
||||||
ActiveTime *time.Time
|
|
||||||
ExpiredTime *time.Time `gorm:"index:expired"`
|
|
||||||
Size uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create 创建容量包
|
|
||||||
func (pack *StoragePack) Create() (uint, error) {
|
|
||||||
if err := DB.Create(pack).Error; err != nil {
|
|
||||||
util.Log().Warning("无法插入离线下载记录, %s", err)
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return pack.ID, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAvailablePackSize 返回给定用户当前可用的容量包总容量
|
|
||||||
func (user *User) GetAvailablePackSize() uint64 {
|
|
||||||
var (
|
|
||||||
total uint64
|
|
||||||
firstExpire *time.Time
|
|
||||||
timeNow = time.Now()
|
|
||||||
ttl int64
|
|
||||||
)
|
|
||||||
|
|
||||||
// 尝试从缓存中读取
|
|
||||||
cacheKey := "pack_size_" + strconv.FormatUint(uint64(user.ID), 10)
|
|
||||||
if total, ok := cache.Get(cacheKey); ok {
|
|
||||||
return total.(uint64)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查找所有有效容量包
|
|
||||||
packs := user.GetAvailableStoragePacks()
|
|
||||||
|
|
||||||
// 计算总容量, 并找到其中最早的过期时间
|
|
||||||
for _, v := range packs {
|
|
||||||
total += v.Size
|
|
||||||
if firstExpire == nil {
|
|
||||||
firstExpire = v.ExpiredTime
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if v.ExpiredTime != nil && firstExpire.After(*v.ExpiredTime) {
|
|
||||||
firstExpire = v.ExpiredTime
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 用最早的过期时间计算缓存TTL,并写入缓存
|
|
||||||
if firstExpire != nil {
|
|
||||||
ttl = firstExpire.Unix() - timeNow.Unix()
|
|
||||||
if ttl > 0 {
|
|
||||||
_ = cache.Set(cacheKey, total, int(ttl))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return total
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAvailableStoragePacks 返回用户可用的容量包
|
|
||||||
func (user *User) GetAvailableStoragePacks() []StoragePack {
|
|
||||||
var packs []StoragePack
|
|
||||||
timeNow := time.Now()
|
|
||||||
// 查找所有有效容量包
|
|
||||||
DB.Where("expired_time > ? AND user_id = ?", timeNow, user.ID).Find(&packs)
|
|
||||||
return packs
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetExpiredStoragePack 获取已过期的容量包
|
|
||||||
func GetExpiredStoragePack() []StoragePack {
|
|
||||||
var packs []StoragePack
|
|
||||||
DB.Where("expired_time < ?", time.Now()).Find(&packs)
|
|
||||||
return packs
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete 删除容量包
|
|
||||||
func (pack *StoragePack) Delete() error {
|
|
||||||
return DB.Delete(&pack).Error
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"github.com/DATA-DOG/go-sqlmock"
|
|
||||||
"github.com/jinzhu/gorm"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestUser_GetAvailablePackSize(t *testing.T) {
|
|
||||||
asserts := assert.New(t)
|
|
||||||
user := User{
|
|
||||||
Model: gorm.Model{ID: 1},
|
|
||||||
}
|
|
||||||
|
|
||||||
// 未命中缓存
|
|
||||||
{
|
|
||||||
mock.ExpectQuery("SELECT(.+)").
|
|
||||||
WithArgs(sqlmock.AnyArg(), 1).
|
|
||||||
WillReturnRows(
|
|
||||||
sqlmock.NewRows([]string{"id", "size", "expired_time"}).
|
|
||||||
AddRow(1, 10, time.Now().Add(time.Second*time.Duration(20))).
|
|
||||||
AddRow(3, 0, nil).
|
|
||||||
AddRow(2, 20, time.Now().Add(time.Second*time.Duration(10))),
|
|
||||||
)
|
|
||||||
total := user.GetAvailablePackSize()
|
|
||||||
asserts.NoError(mock.ExpectationsWereMet())
|
|
||||||
asserts.EqualValues(30, total)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 命中缓存
|
|
||||||
{
|
|
||||||
total := user.GetAvailablePackSize()
|
|
||||||
asserts.NoError(mock.ExpectationsWereMet())
|
|
||||||
asserts.EqualValues(30, total)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStoragePack_Create(t *testing.T) {
|
|
||||||
asserts := assert.New(t)
|
|
||||||
pack := &StoragePack{}
|
|
||||||
|
|
||||||
// 成功
|
|
||||||
{
|
|
||||||
mock.ExpectBegin()
|
|
||||||
mock.ExpectExec("INSERT(.+)").WillReturnResult(sqlmock.NewResult(1, 1))
|
|
||||||
mock.ExpectCommit()
|
|
||||||
id, err := pack.Create()
|
|
||||||
asserts.NoError(mock.ExpectationsWereMet())
|
|
||||||
asserts.Equal(uint(1), id)
|
|
||||||
asserts.NoError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 失败
|
|
||||||
{
|
|
||||||
mock.ExpectBegin()
|
|
||||||
mock.ExpectExec("INSERT(.+)").WillReturnError(errors.New("error"))
|
|
||||||
mock.ExpectRollback()
|
|
||||||
id, err := pack.Create()
|
|
||||||
asserts.NoError(mock.ExpectationsWereMet())
|
|
||||||
asserts.Equal(uint(0), id)
|
|
||||||
asserts.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetExpiredStoragePack(t *testing.T) {
|
|
||||||
asserts := assert.New(t)
|
|
||||||
mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id"}))
|
|
||||||
res := GetExpiredStoragePack()
|
|
||||||
asserts.NoError(mock.ExpectationsWereMet())
|
|
||||||
asserts.Len(res, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStoragePack_Delete(t *testing.T) {
|
|
||||||
asserts := assert.New(t)
|
|
||||||
pack := &StoragePack{}
|
|
||||||
mock.ExpectBegin()
|
|
||||||
mock.ExpectExec("UPDATE(.+)").WillReturnResult(sqlmock.NewResult(1, 1))
|
|
||||||
mock.ExpectCommit()
|
|
||||||
asserts.NoError(pack.Delete())
|
|
||||||
asserts.NoError(mock.ExpectationsWereMet())
|
|
||||||
|
|
||||||
}
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"github.com/jinzhu/gorm"
|
"github.com/jinzhu/gorm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -32,15 +31,10 @@ type User struct {
|
||||||
Status int
|
Status int
|
||||||
GroupID uint
|
GroupID uint
|
||||||
Storage uint64
|
Storage uint64
|
||||||
OpenID string
|
|
||||||
TwoFactor string
|
TwoFactor string
|
||||||
Avatar string
|
Avatar string
|
||||||
Options string `json:"-",gorm:"type:text"`
|
Options string `json:"-",gorm:"type:text"`
|
||||||
Authn string `gorm:"type:text"`
|
Authn string `gorm:"type:text"`
|
||||||
Score int
|
|
||||||
PreviousGroupID uint // 初始用户组
|
|
||||||
GroupExpires *time.Time // 用户组过期日期
|
|
||||||
NotifyDate *time.Time // 通知超出配额时的日期
|
|
||||||
|
|
||||||
// 关联模型
|
// 关联模型
|
||||||
Group Group `gorm:"save_associations:false:false"`
|
Group Group `gorm:"save_associations:false:false"`
|
||||||
|
@ -53,7 +47,6 @@ type User struct {
|
||||||
// UserOption 用户个性化配置字段
|
// UserOption 用户个性化配置字段
|
||||||
type UserOption struct {
|
type UserOption struct {
|
||||||
ProfileOff bool `json:"profile_off,omitempty"`
|
ProfileOff bool `json:"profile_off,omitempty"`
|
||||||
PreferredPolicy uint `json:"preferred_policy,omitempty"`
|
|
||||||
PreferredTheme string `json:"preferred_theme,omitempty"`
|
PreferredTheme string `json:"preferred_theme,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,25 +87,6 @@ func (user *User) IncreaseStorage(size uint64) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// PayScore 扣除积分,返回是否成功
|
|
||||||
func (user *User) PayScore(score int) bool {
|
|
||||||
if score == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if score <= user.Score {
|
|
||||||
user.Score -= score
|
|
||||||
DB.Model(user).Update("score", gorm.Expr("score - ?", score))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddScore 增加积分
|
|
||||||
func (user *User) AddScore(score int) {
|
|
||||||
user.Score += score
|
|
||||||
DB.Model(user).Update("score", gorm.Expr("score + ?", score))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IncreaseStorageWithoutCheck 忽略可用容量,增加用户已用容量
|
// IncreaseStorageWithoutCheck 忽略可用容量,增加用户已用容量
|
||||||
func (user *User) IncreaseStorageWithoutCheck(size uint64) {
|
func (user *User) IncreaseStorageWithoutCheck(size uint64) {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
|
@ -125,7 +99,7 @@ func (user *User) IncreaseStorageWithoutCheck(size uint64) {
|
||||||
|
|
||||||
// GetRemainingCapacity 获取剩余配额
|
// GetRemainingCapacity 获取剩余配额
|
||||||
func (user *User) GetRemainingCapacity() uint64 {
|
func (user *User) GetRemainingCapacity() uint64 {
|
||||||
total := user.Group.MaxStorage + user.GetAvailablePackSize()
|
total := user.Group.MaxStorage
|
||||||
if total <= user.Storage {
|
if total <= user.Storage {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -134,26 +108,7 @@ func (user *User) GetRemainingCapacity() uint64 {
|
||||||
|
|
||||||
// GetPolicyID 获取用户当前的存储策略ID
|
// GetPolicyID 获取用户当前的存储策略ID
|
||||||
func (user *User) GetPolicyID(prefer uint) uint {
|
func (user *User) GetPolicyID(prefer uint) uint {
|
||||||
if prefer == 0 {
|
|
||||||
prefer = user.OptionsSerialized.PreferredPolicy
|
|
||||||
}
|
|
||||||
// 用户未指定时,返回可用的第一个
|
|
||||||
if prefer == 0 {
|
|
||||||
if len(user.Group.PolicyList) != 0 {
|
|
||||||
return user.Group.PolicyList[0]
|
return user.Group.PolicyList[0]
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
// 用户指定时,先检查是否为可用策略列表中的值
|
|
||||||
if util.ContainsUint(user.Group.PolicyList, prefer) {
|
|
||||||
return prefer
|
|
||||||
}
|
|
||||||
// 不可用时,返回第一个
|
|
||||||
if len(user.Group.PolicyList) != 0 {
|
|
||||||
return user.Group.PolicyList[0]
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserByID 用ID获取用户
|
// GetUserByID 用ID获取用户
|
||||||
|
@ -281,20 +236,6 @@ func (user *User) IsAnonymous() bool {
|
||||||
return user.ID == 0
|
return user.ID == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notified 更新用户容量超额通知日期
|
|
||||||
func (user *User) Notified() {
|
|
||||||
if user.NotifyDate == nil {
|
|
||||||
timeNow := time.Now()
|
|
||||||
user.NotifyDate = &timeNow
|
|
||||||
DB.Model(&user).Update("notify_date", user.NotifyDate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearNotified 清除用户通知标记
|
|
||||||
func (user *User) ClearNotified() {
|
|
||||||
DB.Model(&user).Update("notify_date", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetStatus 设定用户状态
|
// SetStatus 设定用户状态
|
||||||
func (user *User) SetStatus(status int) {
|
func (user *User) SetStatus(status int) {
|
||||||
DB.Model(&user).Update("status", status)
|
DB.Model(&user).Update("status", status)
|
||||||
|
@ -312,41 +253,3 @@ func (user *User) UpdateOptions() error {
|
||||||
}
|
}
|
||||||
return user.Update(map[string]interface{}{"options": user.Options})
|
return user.Update(map[string]interface{}{"options": user.Options})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGroupExpiredUsers 获取用户组过期的用户
|
|
||||||
func GetGroupExpiredUsers() []User {
|
|
||||||
var users []User
|
|
||||||
DB.Where("group_expires < ? and previous_group_id <> 0", time.Now()).Find(&users)
|
|
||||||
return users
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTolerantExpiredUser 获取超过宽容期的用户
|
|
||||||
func GetTolerantExpiredUser() []User {
|
|
||||||
var users []User
|
|
||||||
DB.Set("gorm:auto_preload", true).Where("notify_date < ?", time.Now().Add(
|
|
||||||
time.Duration(-GetIntSetting("ban_time", 10))*time.Second),
|
|
||||||
).Find(&users)
|
|
||||||
return users
|
|
||||||
}
|
|
||||||
|
|
||||||
// GroupFallback 回退到初始用户组
|
|
||||||
func (user *User) GroupFallback() {
|
|
||||||
if user.GroupExpires != nil && user.PreviousGroupID != 0 {
|
|
||||||
user.Group.ID = user.PreviousGroupID
|
|
||||||
DB.Model(&user).Updates(map[string]interface{}{
|
|
||||||
"group_expires": nil,
|
|
||||||
"previous_group_id": 0,
|
|
||||||
"group_id": user.PreviousGroupID,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpgradeGroup 升级用户组
|
|
||||||
func (user *User) UpgradeGroup(id uint, expires *time.Time) error {
|
|
||||||
user.Group.ID = id
|
|
||||||
return DB.Model(&user).Updates(map[string]interface{}{
|
|
||||||
"group_expires": expires,
|
|
||||||
"previous_group_id": user.GroupID,
|
|
||||||
"group_id": id,
|
|
||||||
}).Error
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ var BackendVersion = "3.0.0-alpha1"
|
||||||
var RequiredDBVersion = "3.0.0-alpha1"
|
var RequiredDBVersion = "3.0.0-alpha1"
|
||||||
|
|
||||||
// IsPro 是否为Pro版本
|
// IsPro 是否为Pro版本
|
||||||
var IsPro = "true"
|
var IsPro = "false"
|
||||||
|
|
||||||
// LastCommit 最后commit id
|
// LastCommit 最后commit id
|
||||||
var LastCommit = "a11f819"
|
var LastCommit = "a11f819"
|
||||||
|
|
|
@ -2,7 +2,6 @@ package hashid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/HFO4/cloudreve/bootstrap/constant"
|
|
||||||
"github.com/HFO4/cloudreve/pkg/conf"
|
"github.com/HFO4/cloudreve/pkg/conf"
|
||||||
)
|
)
|
||||||
import "github.com/speps/go-hashids"
|
import "github.com/speps/go-hashids"
|
||||||
|
@ -62,7 +61,7 @@ func HashID(id uint, t int) string {
|
||||||
// DecodeHashID 计算HashID对应的数据库ID
|
// DecodeHashID 计算HashID对应的数据库ID
|
||||||
func DecodeHashID(id string, t int) (uint, error) {
|
func DecodeHashID(id string, t int) (uint, error) {
|
||||||
v, _ := HashDecode(id)
|
v, _ := HashDecode(id)
|
||||||
if len(v) != 2 || v[1] != constant.HashIDTable[t] {
|
if len(v) != 2 || v[1] != t {
|
||||||
return 0, ErrTypeNotMatch
|
return 0, ErrTypeNotMatch
|
||||||
}
|
}
|
||||||
return uint(v[0]), nil
|
return uint(v[0]), nil
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package hashid
|
package hashid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/HFO4/cloudreve/bootstrap/constant"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -53,7 +52,6 @@ func TestHashDecode(t *testing.T) {
|
||||||
|
|
||||||
func TestDecodeHashID(t *testing.T) {
|
func TestDecodeHashID(t *testing.T) {
|
||||||
asserts := assert.New(t)
|
asserts := assert.New(t)
|
||||||
constant.HashIDTable = []int{0, 1, 2, 3, 4, 5}
|
|
||||||
|
|
||||||
// 成功
|
// 成功
|
||||||
{
|
{
|
||||||
|
|
|
@ -57,7 +57,6 @@ func GiveGroup(user *model.User, groupInfo *serializer.GroupProducts, num int) e
|
||||||
|
|
||||||
// GiveScore 积分充值
|
// GiveScore 积分充值
|
||||||
func GiveScore(user *model.User, num int) error {
|
func GiveScore(user *model.User, num int) error {
|
||||||
user.AddScore(num)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,17 +22,6 @@ func (pay *ScorePayment) Create(order *model.Order, pack *serializer.PackProduct
|
||||||
return nil, ErrUnsupportedPaymentMethod
|
return nil, ErrUnsupportedPaymentMethod
|
||||||
}
|
}
|
||||||
|
|
||||||
// 扣除用户积分
|
|
||||||
if !user.PayScore(order.Price * order.Num) {
|
|
||||||
return nil, ErrScoreNotEnough
|
|
||||||
}
|
|
||||||
|
|
||||||
// 商品“发货”
|
|
||||||
if err := GiveProduct(user, pack, group, order.Num); err != nil {
|
|
||||||
user.AddScore(order.Price * order.Num)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建订单记录
|
// 创建订单记录
|
||||||
order.Status = model.OrderPaid
|
order.Status = model.OrderPaid
|
||||||
if _, err := order.Create(); err != nil {
|
if _, err := order.Create(); err != nil {
|
||||||
|
|
|
@ -12,8 +12,6 @@ type SiteConfig struct {
|
||||||
QQLogin bool `json:"QQLogin"`
|
QQLogin bool `json:"QQLogin"`
|
||||||
Themes string `json:"themes"`
|
Themes string `json:"themes"`
|
||||||
DefaultTheme string `json:"defaultTheme"`
|
DefaultTheme string `json:"defaultTheme"`
|
||||||
ScoreEnabled bool `json:"score_enabled"`
|
|
||||||
ShareScoreRate string `json:"share_score_rate"`
|
|
||||||
HomepageViewMethod string `json:"home_view_method"`
|
HomepageViewMethod string `json:"home_view_method"`
|
||||||
ShareViewMethod string `json:"share_view_method"`
|
ShareViewMethod string `json:"share_view_method"`
|
||||||
Authn bool `json:"authn"'`
|
Authn bool `json:"authn"'`
|
||||||
|
@ -72,8 +70,6 @@ func BuildSiteConfig(settings map[string]string, user *model.User) Response {
|
||||||
QQLogin: model.IsTrueVal(checkSettingValue(settings, "qq_login")),
|
QQLogin: model.IsTrueVal(checkSettingValue(settings, "qq_login")),
|
||||||
Themes: checkSettingValue(settings, "themes"),
|
Themes: checkSettingValue(settings, "themes"),
|
||||||
DefaultTheme: checkSettingValue(settings, "defaultTheme"),
|
DefaultTheme: checkSettingValue(settings, "defaultTheme"),
|
||||||
ScoreEnabled: model.IsTrueVal(checkSettingValue(settings, "score_enabled")),
|
|
||||||
ShareScoreRate: checkSettingValue(settings, "share_score_rate"),
|
|
||||||
HomepageViewMethod: checkSettingValue(settings, "home_view_method"),
|
HomepageViewMethod: checkSettingValue(settings, "home_view_method"),
|
||||||
ShareViewMethod: checkSettingValue(settings, "share_view_method"),
|
ShareViewMethod: checkSettingValue(settings, "share_view_method"),
|
||||||
Authn: model.IsTrueVal(checkSettingValue(settings, "authn_enabled")),
|
Authn: model.IsTrueVal(checkSettingValue(settings, "authn_enabled")),
|
||||||
|
|
|
@ -11,7 +11,6 @@ type Share struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
Locked bool `json:"locked"`
|
Locked bool `json:"locked"`
|
||||||
IsDir bool `json:"is_dir"`
|
IsDir bool `json:"is_dir"`
|
||||||
Score int `json:"score"`
|
|
||||||
CreateDate string `json:"create_date,omitempty"`
|
CreateDate string `json:"create_date,omitempty"`
|
||||||
Downloads int `json:"downloads"`
|
Downloads int `json:"downloads"`
|
||||||
Views int `json:"views"`
|
Views int `json:"views"`
|
||||||
|
@ -36,7 +35,6 @@ type shareSource struct {
|
||||||
type myShareItem struct {
|
type myShareItem struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
IsDir bool `json:"is_dir"`
|
IsDir bool `json:"is_dir"`
|
||||||
Score int `json:"score"`
|
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
CreateDate string `json:"create_date,omitempty"`
|
CreateDate string `json:"create_date,omitempty"`
|
||||||
Downloads int `json:"downloads"`
|
Downloads int `json:"downloads"`
|
||||||
|
@ -55,7 +53,6 @@ func BuildShareList(shares []model.Share, total int) Response {
|
||||||
item := myShareItem{
|
item := myShareItem{
|
||||||
Key: hashid.HashID(shares[i].ID, hashid.ShareID),
|
Key: hashid.HashID(shares[i].ID, hashid.ShareID),
|
||||||
IsDir: shares[i].IsDir,
|
IsDir: shares[i].IsDir,
|
||||||
Score: shares[i].Score,
|
|
||||||
Password: shares[i].Password,
|
Password: shares[i].Password,
|
||||||
CreateDate: shares[i].CreatedAt.Format("2006-01-02 15:04:05"),
|
CreateDate: shares[i].CreatedAt.Format("2006-01-02 15:04:05"),
|
||||||
Downloads: shares[i].Downloads,
|
Downloads: shares[i].Downloads,
|
||||||
|
@ -101,7 +98,6 @@ func BuildShareResponse(share *model.Share, unlocked bool) Share {
|
||||||
Nick: creator.Nick,
|
Nick: creator.Nick,
|
||||||
GroupName: creator.Group.Name,
|
GroupName: creator.Group.Name,
|
||||||
},
|
},
|
||||||
Score: share.Score,
|
|
||||||
CreateDate: share.CreatedAt.Format("2006-01-02 15:04:05"),
|
CreateDate: share.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ type User struct {
|
||||||
Avatar string `json:"avatar"`
|
Avatar string `json:"avatar"`
|
||||||
CreatedAt int64 `json:"created_at"`
|
CreatedAt int64 `json:"created_at"`
|
||||||
PreferredTheme string `json:"preferred_theme"`
|
PreferredTheme string `json:"preferred_theme"`
|
||||||
Score int `json:"score"`
|
|
||||||
Anonymous bool `json:"anonymous"`
|
Anonymous bool `json:"anonymous"`
|
||||||
Policy policy `json:"policy"`
|
Policy policy `json:"policy"`
|
||||||
Group group `json:"group"`
|
Group group `json:"group"`
|
||||||
|
@ -97,7 +96,6 @@ func BuildUser(user model.User) User {
|
||||||
Avatar: user.Avatar,
|
Avatar: user.Avatar,
|
||||||
CreatedAt: user.CreatedAt.Unix(),
|
CreatedAt: user.CreatedAt.Unix(),
|
||||||
PreferredTheme: user.OptionsSerialized.PreferredTheme,
|
PreferredTheme: user.OptionsSerialized.PreferredTheme,
|
||||||
Score: user.Score,
|
|
||||||
Anonymous: user.IsAnonymous(),
|
Anonymous: user.IsAnonymous(),
|
||||||
Policy: policy{
|
Policy: policy{
|
||||||
SaveType: user.Policy.Type,
|
SaveType: user.Policy.Type,
|
||||||
|
@ -112,7 +110,6 @@ func BuildUser(user model.User) User {
|
||||||
AllowShare: user.Group.ShareEnabled,
|
AllowShare: user.Group.ShareEnabled,
|
||||||
AllowRemoteDownload: user.Group.OptionsSerialized.Aria2,
|
AllowRemoteDownload: user.Group.OptionsSerialized.Aria2,
|
||||||
AllowArchiveDownload: user.Group.OptionsSerialized.ArchiveDownload,
|
AllowArchiveDownload: user.Group.OptionsSerialized.ArchiveDownload,
|
||||||
ShareFreeEnabled: user.Group.OptionsSerialized.ShareFree,
|
|
||||||
ShareDownload: user.Group.OptionsSerialized.ShareDownload,
|
ShareDownload: user.Group.OptionsSerialized.ShareDownload,
|
||||||
CompressEnabled: user.Group.OptionsSerialized.ArchiveTask,
|
CompressEnabled: user.Group.OptionsSerialized.ArchiveTask,
|
||||||
WebDAVEnabled: user.Group.WebDAVEnabled,
|
WebDAVEnabled: user.Group.WebDAVEnabled,
|
||||||
|
|
|
@ -55,25 +55,6 @@ func BuildPolicySettingRes(policies []model.Policy, current *model.Policy) Respo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildMountedFolderRes 构建已挂载目录响应,list为当前用户可用存储策略ID
|
|
||||||
func BuildMountedFolderRes(folders []model.Folder, list []uint) []MountedFolders {
|
|
||||||
res := make([]MountedFolders, 0, len(folders))
|
|
||||||
for _, folder := range folders {
|
|
||||||
single := MountedFolders{
|
|
||||||
ID: hashid.HashID(folder.ID, hashid.FolderID),
|
|
||||||
Name: folder.Name,
|
|
||||||
PolicyName: "[已失效存储策略]",
|
|
||||||
}
|
|
||||||
if policy, err := model.GetPolicyByID(folder.PolicyID); err == nil && util.ContainsUint(list, policy.ID) {
|
|
||||||
single.PolicyName = policy.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
res = append(res, single)
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
// BuildUserQuotaResponse 序列化用户存储配额概况响应
|
// BuildUserQuotaResponse 序列化用户存储配额概况响应
|
||||||
func BuildUserQuotaResponse(user *model.User, packs []model.StoragePack) Response {
|
func BuildUserQuotaResponse(user *model.User, packs []model.StoragePack) Response {
|
||||||
packSize := user.GetAvailablePackSize()
|
packSize := user.GetAvailablePackSize()
|
||||||
|
|
|
@ -356,17 +356,6 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *filesyst
|
||||||
fs.Use("AfterValidateFailed", filesystem.HookGiveBackCapacity)
|
fs.Use("AfterValidateFailed", filesystem.HookGiveBackCapacity)
|
||||||
ctx = context.WithValue(ctx, fsctx.FileModelCtx, *originFile)
|
ctx = context.WithValue(ctx, fsctx.FileModelCtx, *originFile)
|
||||||
} else {
|
} else {
|
||||||
// 检查父目录指定存储策略
|
|
||||||
if exist, folder := fs.IsPathExist(filePath); exist {
|
|
||||||
if folder.PolicyID != 0 {
|
|
||||||
// 尝试获取并重设存储策略
|
|
||||||
if policy, err := model.GetPolicyByID(fs.User.GetPolicyID(folder.PolicyID)); err == nil {
|
|
||||||
fs.User.Policy = policy
|
|
||||||
fs.DispatchHandler()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 给文件系统分配钩子
|
// 给文件系统分配钩子
|
||||||
fs.Use("BeforeUpload", filesystem.HookValidateFile)
|
fs.Use("BeforeUpload", filesystem.HookValidateFile)
|
||||||
fs.Use("BeforeUpload", filesystem.HookValidateCapacity)
|
fs.Use("BeforeUpload", filesystem.HookValidateCapacity)
|
||||||
|
|
|
@ -21,8 +21,6 @@ func SiteConfig(c *gin.Context) {
|
||||||
"email_active",
|
"email_active",
|
||||||
"themes",
|
"themes",
|
||||||
"defaultTheme",
|
"defaultTheme",
|
||||||
"score_enabled",
|
|
||||||
"share_score_rate",
|
|
||||||
"home_view_method",
|
"home_view_method",
|
||||||
"share_view_method",
|
"share_view_method",
|
||||||
"authn_enabled",
|
"authn_enabled",
|
||||||
|
|
|
@ -74,7 +74,6 @@ func InitCORS(router *gin.Engine) {
|
||||||
// InitMasterRouter 初始化主机模式路由
|
// InitMasterRouter 初始化主机模式路由
|
||||||
func InitMasterRouter() *gin.Engine {
|
func InitMasterRouter() *gin.Engine {
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
bootstrap.InitCustomRoute(r.Group("/custom"))
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
静态资源
|
静态资源
|
||||||
|
|
|
@ -114,7 +114,6 @@ func (service *AddUserService) Add() serializer.Response {
|
||||||
user.Email = service.User.Email
|
user.Email = service.User.Email
|
||||||
user.GroupID = service.User.GroupID
|
user.GroupID = service.User.GroupID
|
||||||
user.Status = service.User.Status
|
user.Status = service.User.Status
|
||||||
user.Score = service.User.Score
|
|
||||||
|
|
||||||
// 检查愚蠢操作
|
// 检查愚蠢操作
|
||||||
if user.ID == 1 && user.GroupID != 1 {
|
if user.ID == 1 && user.GroupID != 1 {
|
||||||
|
|
|
@ -115,11 +115,7 @@ func (service *WebDAVAccountService) Delete(c *gin.Context, user *model.User) se
|
||||||
func (service *WebDAVListService) Accounts(c *gin.Context, user *model.User) serializer.Response {
|
func (service *WebDAVListService) Accounts(c *gin.Context, user *model.User) serializer.Response {
|
||||||
accounts := model.ListWebDAVAccounts(user.ID)
|
accounts := model.ListWebDAVAccounts(user.ID)
|
||||||
|
|
||||||
// 查找挂载了存储策略的目录
|
|
||||||
folders := model.GetMountedFolders(user.ID)
|
|
||||||
|
|
||||||
return serializer.Response{Data: map[string]interface{}{
|
return serializer.Response{Data: map[string]interface{}{
|
||||||
"accounts": accounts,
|
"accounts": accounts,
|
||||||
"folders": serializer.BuildMountedFolderRes(folders, user.Group.PolicyList),
|
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ type ShareCreateService struct {
|
||||||
Password string `json:"password" binding:"max=255"`
|
Password string `json:"password" binding:"max=255"`
|
||||||
RemainDownloads int `json:"downloads"`
|
RemainDownloads int `json:"downloads"`
|
||||||
Expire int `json:"expire"`
|
Expire int `json:"expire"`
|
||||||
Score int `json:"score" binding:"gte=0"`
|
|
||||||
Preview bool `json:"preview"`
|
Preview bool `json:"preview"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +116,6 @@ func (service *ShareCreateService) Create(c *gin.Context) serializer.Response {
|
||||||
IsDir: service.IsDir,
|
IsDir: service.IsDir,
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
SourceID: sourceID,
|
SourceID: sourceID,
|
||||||
Score: service.Score,
|
|
||||||
RemainDownloads: -1,
|
RemainDownloads: -1,
|
||||||
PreviewEnabled: service.Preview,
|
PreviewEnabled: service.Preview,
|
||||||
SourceName: sourceName,
|
SourceName: sourceName,
|
||||||
|
|
|
@ -117,8 +117,6 @@ func (service *ShareListService) List(c *gin.Context, user *model.User) serializ
|
||||||
func (service *ShareGetService) Get(c *gin.Context) serializer.Response {
|
func (service *ShareGetService) Get(c *gin.Context) serializer.Response {
|
||||||
shareCtx, _ := c.Get("share")
|
shareCtx, _ := c.Get("share")
|
||||||
share := shareCtx.(*model.Share)
|
share := shareCtx.(*model.Share)
|
||||||
userCtx, _ := c.Get("user")
|
|
||||||
user := userCtx.(*model.User)
|
|
||||||
|
|
||||||
// 是否已解锁
|
// 是否已解锁
|
||||||
unlocked := true
|
unlocked := true
|
||||||
|
@ -138,11 +136,6 @@ func (service *ShareGetService) Get(c *gin.Context) serializer.Response {
|
||||||
share.Viewed()
|
share.Viewed()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果已经下载过或者是自己的分享,不需要付积分
|
|
||||||
if share.UserID == user.ID || share.WasDownloadedBy(user, c) {
|
|
||||||
share.Score = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return serializer.Response{
|
return serializer.Response{
|
||||||
Code: 0,
|
Code: 0,
|
||||||
Data: serializer.BuildShareResponse(share, unlocked),
|
Data: serializer.BuildShareResponse(share, unlocked),
|
||||||
|
|
Loading…
Reference in New Issue